Browse Source

delete unverified user accounts older than a given time period

mrkvon 3 years ago
parent
commit
2993da3fc1
6 changed files with 144 additions and 4 deletions
  1. 9
    3
      config/default.js
  2. 4
    0
      jobs/index.js
  3. 13
    0
      jobs/users.js
  4. 38
    0
      models/user/index.js
  5. 2
    1
      models/user/schema.js
  6. 78
    0
      test/users.jobs.js

+ 9
- 3
config/default.js View File

@@ -1,12 +1,14 @@
1 1
 'use strict';
2 2
 
3 3
 module.exports = {
4
+  // arangodb database info
4 5
   database: {
5 6
     host: '127.0.0.1',
6 7
     port: 8529,
7 8
     database: 'ditup-dev',
8 9
     username: 'ditup-dev'
9 10
   },
11
+  // the url where the API lives
10 12
   url: {
11 13
     protocol: 'https',
12 14
     host: 'ditup.org',
@@ -15,15 +17,19 @@ module.exports = {
15 17
       return `${this.protocol}://${this.host}${this.path}`;
16 18
     }
17 19
   },
20
+  // the url where the app lives
18 21
   appUrl: {
19 22
     all: 'https://ditup.org'
20 23
   },
24
+  // password hashing and other
21 25
   security: {
22 26
     // iterations for pbkdf2 hashing of passwords
23 27
     iterations: 10000
24 28
   },
29
+  // randomization of user's location
25 30
   randomLocationOffset: {
26
-    min: 1000,
27
-    max: 2000
28
-  }
31
+    min: 1000, // meters
32
+    max: 2000 // meters
33
+  },
34
+  unverifiedUsersTTL: 24 * 3600 * 1000 // milliseconds
29 35
 };

+ 4
- 0
jobs/index.js View File

@@ -9,6 +9,7 @@
9 9
 const cron = require('node-cron'),
10 10
       _ = require('lodash'),
11 11
       tags = require('./tags'),
12
+      users = require('./users'),
12 13
       notifications = require('./notifications');
13 14
 
14 15
 const tasks = [];
@@ -20,6 +21,9 @@ exports.start = function () {
20 21
 
21 22
   // every 5 minutes send notifications about unread messages
22 23
   tasks.push(cron.schedule('0 */5 * * * *', notifications.messages));
24
+
25
+  // every 30 minutes delete unverified users
26
+  tasks.push(cron.schedule('0 */30 * * * *', users.deleteUnverified));
23 27
 };
24 28
 
25 29
 exports.stop = function () {

+ 13
- 0
jobs/users.js View File

@@ -0,0 +1,13 @@
1
+'use strict';
2
+
3
+const path = require('path'),
4
+      models = require(path.resolve('./models')),
5
+      config = require(path.resolve('./config'));
6
+
7
+async function deleteUnverifiedUsers() {
8
+  // returns a promise of a list of deleted tags
9
+  const age = config.unverifiedUsersTTL;
10
+  return await models.user.deleteUnverified(age);
11
+}
12
+
13
+exports.deleteUnverified = deleteUnverifiedUsers;

+ 38
- 0
models/user/index.js View File

@@ -299,6 +299,44 @@ class User extends Model {
299 299
 
300 300
     return output;
301 301
   }
302
+
303
+  /**
304
+   * counts users
305
+   *
306
+   * @param {Object} options
307
+   * @param {boolean} options.verified
308
+   */
309
+  static async count(options) {
310
+    const { verified } = options;
311
+    const query = `
312
+      FOR u IN users FILTER TO_BOOL(u.email) == @verified
313
+        COLLECT WITH COUNT INTO length
314
+        RETURN length`;
315
+    const params = { verified };
316
+    const count = await (await this.db.query(query, params)).next();
317
+
318
+    return count;
319
+  }
320
+
321
+  /**
322
+   * delete unverified users who are created more than ttl ago
323
+   *
324
+   * @param {number} ttl - time to live for unverified users in seconds
325
+   * @returns {number} - amount of deleted users
326
+   */
327
+  static async deleteUnverified(ttl) {
328
+    const query = `
329
+      FOR u IN users FILTER TO_BOOL(u.email) == false AND DATE_DIFF(u.created, @now, 'f') > @ttl
330
+        REMOVE u IN users
331
+        COLLECT WITH COUNT INTO length
332
+        RETURN length
333
+    `;
334
+    const params = { ttl, now: Date.now() };
335
+    const output = await (await this.db.query(query, params)).next();
336
+
337
+    return output;
338
+  }
339
+
302 340
 }
303 341
 
304 342
 module.exports = User;

+ 2
- 1
models/user/schema.js View File

@@ -21,6 +21,7 @@ module.exports = async function (user) {
21 21
       givenName: '',
22 22
       familyName: '',
23 23
       description: ''
24
-    }
24
+    },
25
+    created: Date.now()
25 26
   };
26 27
 };

+ 78
- 0
test/users.jobs.js View File

@@ -0,0 +1,78 @@
1
+'use strict';
2
+
3
+const path = require('path'),
4
+      sinon = require('sinon'),
5
+      should = require('should');
6
+
7
+
8
+const userJobs = require(path.resolve('./jobs/users')),
9
+      dbHandle = require(path.resolve('./test/handleDatabase')),
10
+      config = require(path.resolve('./config')),
11
+      models = require(path.resolve('./models'));
12
+
13
+describe('User jobs', function () {
14
+  let sandbox;
15
+
16
+  describe('Delete unverified users after a time period', function () {
17
+    const ttl = 1234567890;
18
+
19
+    beforeEach(function () {
20
+      sandbox = sinon.sandbox.create();
21
+      sandbox.useFakeTimers(1500000000000, 'Date');
22
+      sandbox.stub(config, 'unverifiedUsersTTL', ttl);
23
+    });
24
+
25
+    afterEach(function () {
26
+      sandbox.restore();
27
+    });
28
+
29
+
30
+    beforeEach(async function () {
31
+      const data = {
32
+        users: 5, // how many users to make
33
+        verifiedUsers: [0, 2, 4]
34
+      };
35
+
36
+      // create data in database
37
+      await dbHandle.fill(data);
38
+    });
39
+
40
+    // clear database after every test
41
+    afterEach(async function () {
42
+      await dbHandle.clear();
43
+    });
44
+
45
+    it('should delete unverified accounts which are older than the time period', async function () {
46
+      should(await models.user.count({ verified: false })).eql(2);
47
+
48
+      sandbox.clock.tick(ttl + 1);
49
+
50
+      await userJobs.deleteUnverified();
51
+
52
+      should(await models.user.count({ verified: false })).eql(0);
53
+    });
54
+
55
+    it('should not delete unverified accounts which are younger than the time period', async function () {
56
+      should(await models.user.count({ verified: false })).eql(2);
57
+
58
+      sandbox.clock.tick(ttl - 1);
59
+
60
+      await userJobs.deleteUnverified();
61
+
62
+      should(await models.user.count({ verified: false })).eql(2);
63
+    });
64
+
65
+    it('should not delete verified accounts', async function () {
66
+      should(await models.user.count({ verified: true })).eql(3);
67
+
68
+      sandbox.clock.tick(ttl + 1);
69
+
70
+      const count = await userJobs.deleteUnverified();
71
+
72
+      should(count).eql(2); // deleted only the 2 unverified users
73
+
74
+      should(await models.user.count({ verified: true })).eql(3);
75
+    });
76
+
77
+  });
78
+});