Initial import
This commit is contained in:
		
							
								
								
									
										5
									
								
								.firebaserc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.firebaserc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| { | ||||
|   "projects": { | ||||
|     "default": "ha-client-c73c4" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										65
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal 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
									
								
							
							
						
						
									
										1
									
								
								firebase.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										1
									
								
								functions/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								functions/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| node_modules/ | ||||
							
								
								
									
										153
									
								
								functions/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								functions/index.js
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										2008
									
								
								functions/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										22
									
								
								functions/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								functions/package.json
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										3
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| { | ||||
|   "lockfileVersion": 1 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user