Browse Source

Add new "delete-unused-accounts-cron" sample.

Change-Id: I882ac7a6292ca6348ffd8d155f2946303b6f5f92
ryanpbrewster-patch-1
Nicolas Garnier 8 years ago
parent
commit
bfaf1a71d3
  1. 6
      README.md
  2. 50
      delete-unused-accounts-cron/README.md
  3. 1
      delete-unused-accounts-cron/firebase.json
  4. 147
      delete-unused-accounts-cron/functions/index.js
  5. 13
      delete-unused-accounts-cron/functions/package.json
  6. 12
      delete-unused-accounts-cron/functions/service-account.json
  7. 3
      message-translation/functions/package.json

6
README.md

@ -131,6 +131,12 @@ Sends a Welcome email when a user signs-in for the first time.
Uses an Auth trigger.
### [Delete Inactive Users Accounts Cron](/delete-unused-accounts-cron)
Periodically deletes the accounts of users who have not signed in the last month.
Uses an HTTPS trigger.
## Contributing

50
delete-unused-accounts-cron/README.md

@ -0,0 +1,50 @@
# Periodically delete unused accounts.
This sample demonstrates how to delete the accounts of users who have not signed-in in the last month.
## Functions Code
See file [functions/index.js](functions/index.js) for the code.
Firebase Functions does not naticaly supports cron jobs. We are working around this by executing the code as an HTTPS triggered function. Then simply use an external service to periodically "ping" the URL.
Here is a non-exhaustive list of external services for cron jobs:
- https://www.setcronjob.com/
- https://cron-job.org/
- https://www.easycron.com/
The dependencies are listed in [functions/package.json](functions/package.json).
## Trigger rules
The function triggers when the HTTP URL of the Function is requested.
## Deploy and test
Set the `cron.key` Google Cloud environment variables to a randomly generated key, this will be used to authorize requests coming from the 3rd-party cron service. For this use:
```bash
firebase env:set cron.key="YOUR_KEY"
```
You can generate a random key, for instance, by running:
```bash
npm install -g crypto
node -e "console.log(require('crypto').randomBytes(20).toString('hex'))"
```
To set up the sample:
- Create a Firebase Project using the Firebase Developer Console
- Enable billing since Functions require billing.
- Download the service accounts using the Firebase Developer Console at: **"Wheely" Icon > Project Settings > Service Accounts > Generate New Private Key** and save it as `./functions/service-accounts.json`.
- Deploy your project using `firebase deploy`.
- Open an account with a 3rd party cron service (e.g. www.setcronjob.com, cron-job.org, www.easycron.com ...) and setup a daily cron job to hit the URL (don't forget to change `YOUR_KEY`):
```
https://us-central1-<project-id>.cloudfunctions.net/accountcleanup?key=YOUR_KEY
```

1
delete-unused-accounts-cron/firebase.json

@ -0,0 +1 @@
{}

147
delete-unused-accounts-cron/functions/index.js

@ -0,0 +1,147 @@
/**
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for t`he specific language governing permissions and
* limitations under the License.
*/
'use strict';
const functions = require('firebase-functions');
const firebaseAdmin = require('firebase-admin');
const serviceAccount = require('./service-account.json');
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert(serviceAccount),
databaseURL: `https://${serviceAccount.project_id}.firebaseio.com`
});
const google = require('googleapis');
const rp = require('request-promise');
const promisePool = require('es6-promise-pool');
const PromisePool = promisePool.PromisePool;
const MAX_CONCURRENT = 3;
/**
* When requested this Function will delete every user accounts that has been inactive for 30 days.
* The request needs to be authorized by passing a 'key' query parameter in the URL. This key must
* match a key set as an environment variable using `firebase env:set cron.key="YOUR_KEY"`.
*/
exports.accountcleanup = functions.https().onRequest((req, res) => {
const key = req.query.key;
// Exit if the keys don't match
if (key !== functions.env.cron.key) {
console.log('The key provided in the request does not match the key set in the environment. Check that', key,
'matches the cron.key attribute in `firebase env:get`');
return res.status(403).send('Security key does not match. Make sure your "key" URL query parameter matches the ' +
'cron.key environment variable.');
}
// We'll fetch all user details.
getUsers().then(users => {
// We'll use a pool so that we delete maximum `MAX_CONCURRENT` users in parallel.
const promisePool = new PromisePool(() => {
let user;
// We search for users that have not signed in in the last 30 days.
while (!user || user.metadata.lastSignedInAt.getTime() > Date.now() - 30 * 24 * 60 * 60 * 1000) {
if (users.length === 0) {
return null;
}
user = users.pop();
}
// If an inactive user is found we delete it.
return firebaseAdmin.auth().deleteUser(user.uid).then(() => {
console.log('Deleted user account', user.uid, 'because of inactivity');
}).catch(error => {
console.error('Deletion of inactive user account', user.uid, 'failed:', error);
});
}, MAX_CONCURRENT);
return promisePool.start().then(() => {
console.log('User cleanup finished');
res.send('User cleanup finished');
});
});
});
/**
* Returns the list of all users. Including additional metadata such as last sign-in Date.
*/
function getUsers() {
// Create a pool so that there is only `MAX_CONCURRENT` max parallel requests to fetch user details.
return getUserIds().then(userIds => {
const users = [];
const promisePool = new PromisePool(() => {
if (userIds.length === 0) {
return null;
}
const nextUserId = userIds.pop();
return firebaseAdmin.auth().getUser(nextUserId).then(user => {
users.push(user);
});
}, MAX_CONCURRENT);
return promisePool.start().then(() => users);
});
}
/**
* Returns the list of all user Ids.
*/
function getUserIds(userIds = [], nextPageToken, accessToken) {
return getAccessToken(accessToken).then(accessToken => {
const options = {
method: 'POST',
uri: 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/downloadAccount?fields=users/localId,nextPageToken&access_token=' + accessToken,
body: {
nextPageToken: nextPageToken,
maxResults: 1000
},
json: true // Automatically stringifies the body to JSON
};
return rp(options).then(resp => {
if (!resp.users) {
return userIds;
}
resp.users.forEach(user => {
userIds.push(user.localId);
});
if (resp.nextPageToken) {
return getUserIds(userIds, resp.nextPageToken, accessToken);
}
return userIds;
});
});
}
/**
* Returns an access token using the Service accounts credentials.
*/
function getAccessToken(accessToken) {
if (accessToken) {
return Promise.resolve(accessToken);
}
const jwtClient = new google.auth.JWT(serviceAccount.client_email, null,
serviceAccount.private_key, ['https://www.googleapis.com/auth/firebase'], null);
return new Promise((resolve, reject) => {
jwtClient.authorize((error, token) => {
if (error) {
console.error('Error while fetching access token for Service accounts', error);
return reject();
}
resolve(token.access_token);
});
});
}

13
delete-unused-accounts-cron/functions/package.json

@ -0,0 +1,13 @@
{
"name": "fcm-notifications-functions",
"description": "Send FCM notifications Firebase Functions sample",
"dependencies": {
"es6-promise-pool": "^2.4.4",
"firebase": "^3.6",
"firebase-admin": "^4.0.1",
"firebase-functions": "https://storage.googleapis.com/firebase-preview-drop/node/firebase-functions/firebase-functions-preview.latest.tar.gz",
"request": "^2.79.0",
"request-promise": "^4.1.1",
"request-promise-native": "^1.0.3"
}
}

12
delete-unused-accounts-cron/functions/service-account.json

@ -0,0 +1,12 @@
{
"type": "service_account",
"project_id": "functions-fcm",
"private_key_id": "f49aa12fc8ce9ddc7c5a5f2e56bfb34a4b1376cd",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQCe42jBELgCnq\n94iq74kLeyzwLGEsbfdVang7F5Lf76+BIhQ7LziVRmMNJcvwRz/UptGRc58v0xFz\n55a9hCRuRFN04glUOPS+lNzNtJN9cGSsZZV+36h6/nvEnnuJxHGXZtmTD8ymeaJs\nqL0RxAa6RSwNkweYV0mgCILqX9+qcrU494n3grWVgWop4HrV+IWzvZ9QMHb9K4w1\nwQoAFpiwD0B45abgPT68UbFWcy8wYoQgXwrvKc3n2z4RevfTi5EUmd76fd52PKBc\nAbLa/NcHB9iQ0+rR/u9zLAZ3Rm/1Wso+99hBCFMLHZHa+ujFI+q4Y+Fyw2SLgVgF\ni9iBCFvTAgMBAAECggEBAKCJmT6Ula6nRzGftOIbmEi407O+u3n6netXDtKi3V9b\nabaforcNOH/Q4izKJvcTNEmYNY4liAjyurwTUXqLl0VUCobeys4oaY0L+NvwZgRd\nkAKNHXDbjPrkmIPgvHpSkkmAP5PBlG4+3L029TfZakuhh14uQKUpbpJFHylXtJSl\nLVzWqVD0d3P1Z5KqHau8FLOtq+TwlL9tQjh1Ym6wqvdqMi4NHkdMCVIWtXyfI8xw\nGKaPlLMx1hYsRbbcnnxzzauZrgEOyT/q4Q1Hn7eENl1j0SVyTe01tJA2Odw8NkIu\npmyeMIztc/WqsOml+zGwgajw/EjJ6TabZnj13Bii9JkCgYEA8rvtG6wIGOZlQ3hJ\nbwip9LFIMZZiI0EDytnansAOVujIE7/UY2/wfyE4aOZgHBOWzbbUkZ2S4vQ0DPeg\nmQ6As1Kqo4g8zdXZjqgqVj4I83WNPFHTCrSXaK47p4YtCcHBy2M3Pu6t/1vQ5fMP\nFlo57M8Ex5MNDMGE21F4tAusNNUCgYEA22iVyqxLsL4W0CidOZ+1snxaxjtKDHt9\nBCSrs06itcnnhTSLBI55BHqI6stb+GHbZOaZ6/lSJjPKr+IFy5T1BzfgiMEcY6ee\nsxU+5lQxJ6D1JKkLdUtNppRKCyx0q2G7qscD7CpmBpXCKt/FnBqpqtjfBsqT1dhZ\nXoG/LczkQgcCgYASOAbA9/WXoNti6Ali+xx+kDvh9O6ixMN7G0Tse2/YGBrEWLah\nTAqaEC1Cul/mW5YLFLj5wQEAZeHuQzvboRoJ25+RLK2bqXxt17Nty7QySdVy/JVB\njXJ72fACT/DbdZ6NHIJOB+4pZ4PTbp3oSJdmbddm/2OQXIoTSBcuNF4VjQKBgFt+\nXGB8wr98NUUueonqCLnaU3wwgyt7X2GX7SXDl+RYwrvwcjw/MUXl1yyaCssj+3oz\nE2KswE3/8PixNxtzDU6qRW6hoLYJ0wr4xBcGas0MuM1F1OpfsYzSb6IDMs+43KpV\nfVRBRfRfBO4eDGiRUclV0IMjfMyDAJmBX3i45UKHAoGBAMyKh8ysZuZypcwuWXHn\ncUEv3YjciGzfhShVCyCLJ21IEr/Fpt4DwIO8lyaL1s1ez0q39QnTc/ZeHG6Q6trZ\nCpT+zExvENE2tRQFR/M4eEwsZM+dOYkywoM4SDmEzck+Nt9FtlkL+tR1DB+vT0/x\ns67Nlpbyq0AbxtptOSW7HWMp\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-oz89s@functions-fcm.iam.gserviceaccount.com",
"client_id": "115601641616008366344",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-oz89s%40functions-fcm.iam.gserviceaccount.com"
}

3
message-translation/functions/package.json

@ -4,7 +4,6 @@
"dependencies": {
"firebase": "^3.6",
"firebase-functions": "https://storage.googleapis.com/firebase-preview-drop/node/firebase-functions/firebase-functions-preview.latest.tar.gz",
"request-promise": "^2.0.0",
"rsvp": "^3.1.0"
"request-promise": "^2.0.0"
}
}

Loading…
Cancel
Save