Initial import

This commit is contained in:
estevez-dev 2019-06-16 23:07:52 +03:00
parent ccdc09ff63
commit 6dc4e2e902
8 changed files with 2258 additions and 0 deletions

5
.firebaserc Normal file
View File

@ -0,0 +1,5 @@
{
"projects": {
"default": "ha-client-c73c4"
}
}

65
.gitignore vendored Normal file
View File

@ -0,0 +1,65 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
# Firebase cache
.firebase/
# Firebase config
# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env

1
firebase.json Normal file
View File

@ -0,0 +1 @@
{}

1
functions/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

153
functions/index.js Normal file
View File

@ -0,0 +1,153 @@
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
var db = admin.firestore();
const MAX_NOTIFICATIONS_PER_DAY = 150;
exports.sendPushNotification = functions.https.onRequest(async (req, res) => {
console.log('Received payload', req.body);
var today = getToday();
var token = req.body.push_token;
var ref = db.collection('rateLimits').doc(today).collection('tokens').doc(token);
var payload = {
notification: {
body: req.body.message
},
android: {
priority: 'HIGH',
notification: {
sound: 'default',
icon: 'mini_icon',
channel_id: 'ha_notify'
}
},
token: token,
};
if(req.body.title) {
payload.notification.title = req.body.title;
}
if(req.body.data) {
if(req.body.data.android) {
payload.android = req.body.data.android;
}
if(req.body.data.apns) {
payload.apns = req.body.data.apns;
}
if(req.body.data.data) {
payload.data = req.body.data.data;
}
if(req.body.data.webpush) {
payload.webpush = req.body.data.webpush;
}
}
console.log('Notification payload', JSON.stringify(payload));
var docExists = false;
var docData = {
deliveredCount: 0,
errorCount: 0,
totalCount: 0,
};
try {
let currentDoc = await ref.get();
docExists = currentDoc.exists;
if(currentDoc.exists) {
docData = currentDoc.data();
}
} catch(err) {
console.error('Error getting document!', err);
return handleError(res, 'getDoc', err);
}
if(docData.deliveredCount > MAX_NOTIFICATIONS_PER_DAY) {
return res.status(429).send({
errorType: 'RateLimited',
message: 'The given target has reached the maximum number of notifications allowed per day. Please try again later.',
target: token,
rateLimits: getRateLimitsObject(docData),
});
}
docData.totalCount = docData.totalCount + 1;
var messageId;
try {
messageId = await admin.messaging().send(payload);
docData.deliveredCount = docData.deliveredCount + 1;
} catch(err) {
docData.errorCount = docData.errorCount + 1;
await setRateLimitDoc(ref, docExists, docData, res);
return handleError(res, 'sendNotification', err);
}
console.log('Successfully sent message:', messageId);
await setRateLimitDoc(ref, docExists, docData, res);
return res.status(201).send({
messageId: messageId,
sentPayload: payload,
target: token,
rateLimits: getRateLimitsObject(docData),
});
});
async function setRateLimitDoc(ref, docExists, docData, res) {
try {
if(docExists) {
console.log('Updating existing doc!');
await ref.update(docData);
} else {
console.log('Creating new doc!');
await ref.set(docData);
}
} catch(err) {
if(docExists) {
console.error('Error updating document!', err);
} else {
console.error('Error creating document!', err);
}
return handleError(res, 'setDocument', err);
}
return true;
}
function handleError(res, step, incomingError) {
if (!incomingError) return null;
console.error('InternalError during', step, incomingError);
return res.status(500).send({
errorType: 'InternalError',
errorStep: step,
message: incomingError.message,
});
}
function getToday() {
var today = new Date();
var dd = String(today.getDate()).padStart(2, '0');
var mm = String(today.getMonth() + 1).padStart(2, '0');
var yyyy = today.getFullYear();
return yyyy + mm + dd;
}
function getRateLimitsObject(doc) {
var d = new Date();
return {
successful: (doc.deliveredCount || 0),
errors: (doc.errorCount || 0),
total: (doc.totalCount || 0),
maximum: MAX_NOTIFICATIONS_PER_DAY,
remaining: (MAX_NOTIFICATIONS_PER_DAY - doc.deliveredCount),
resetsAt: new Date(d.getFullYear(), d.getMonth(), d.getDate()+1)
};
}

2008
functions/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

22
functions/package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "8"
},
"dependencies": {
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.0.0"
},
"devDependencies": {
"firebase-functions-test": "^0.1.6"
},
"private": true
}

3
package-lock.json generated Normal file
View File

@ -0,0 +1,3 @@
{
"lockfileVersion": 1
}