@tachybase/plugin-password-policy 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +1 -0
  2. package/client.d.ts +2 -0
  3. package/client.js +1 -0
  4. package/dist/client/IPFilterForm.d.ts +1 -0
  5. package/dist/client/PasswordAttemptForm.d.ts +1 -0
  6. package/dist/client/PasswordStrengthSettingsForm.d.ts +2 -0
  7. package/dist/client/SignInFailsTable.d.ts +2 -0
  8. package/dist/client/UserLocksTable.d.ts +2 -0
  9. package/dist/client/collections/signInFails.d.ts +2 -0
  10. package/dist/client/collections/userLocks.d.ts +2 -0
  11. package/dist/client/hooks/usePasswordStrength.d.ts +11 -0
  12. package/dist/client/hooks/usePasswordValidator.d.ts +16 -0
  13. package/dist/client/index.d.ts +5 -0
  14. package/dist/client/index.js +4 -0
  15. package/dist/client/locale.d.ts +6 -0
  16. package/dist/constants.d.ts +11 -0
  17. package/dist/constants.js +44 -0
  18. package/dist/externalVersion.js +10 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +39 -0
  21. package/dist/locale/en-US.json +107 -0
  22. package/dist/locale/zh-CN.json +107 -0
  23. package/dist/node_modules/geoip-lite/LICENSE +50 -0
  24. package/dist/node_modules/geoip-lite/data/city.checksum +1 -0
  25. package/dist/node_modules/geoip-lite/data/country.checksum +1 -0
  26. package/dist/node_modules/geoip-lite/data/geoip-city-names.dat +0 -0
  27. package/dist/node_modules/geoip-lite/data/geoip-city.dat +0 -0
  28. package/dist/node_modules/geoip-lite/data/geoip-city6.dat +0 -0
  29. package/dist/node_modules/geoip-lite/data/geoip-country.dat +0 -0
  30. package/dist/node_modules/geoip-lite/data/geoip-country6.dat +0 -0
  31. package/dist/node_modules/geoip-lite/lib/fsWatcher.js +83 -0
  32. package/dist/node_modules/geoip-lite/lib/geoip.js +1 -0
  33. package/dist/node_modules/geoip-lite/lib/utils.js +98 -0
  34. package/dist/node_modules/geoip-lite/node_modules/.bin/rimraf +17 -0
  35. package/dist/node_modules/geoip-lite/package.json +1 -0
  36. package/dist/node_modules/geoip-lite/scripts/updatedb.js +685 -0
  37. package/dist/node_modules/geoip-lite/test/geo-lookup.js +56 -0
  38. package/dist/node_modules/geoip-lite/test/memory_usage.js +3 -0
  39. package/dist/node_modules/geoip-lite/test/tests.js +197 -0
  40. package/dist/server/actions/IpFilterController.d.ts +7 -0
  41. package/dist/server/actions/IpFilterController.js +124 -0
  42. package/dist/server/actions/PasswordAttemptController.d.ts +7 -0
  43. package/dist/server/actions/PasswordAttemptController.js +123 -0
  44. package/dist/server/actions/PasswordStrengthController.d.ts +7 -0
  45. package/dist/server/actions/PasswordStrengthController.js +123 -0
  46. package/dist/server/actions/SignInFailsController.d.ts +5 -0
  47. package/dist/server/actions/SignInFailsController.js +156 -0
  48. package/dist/server/actions/UserLocksController.d.ts +4 -0
  49. package/dist/server/actions/UserLocksController.js +102 -0
  50. package/dist/server/collections/ipFilter.d.ts +2 -0
  51. package/dist/server/collections/ipFilter.js +51 -0
  52. package/dist/server/collections/passwordAttempt.d.ts +2 -0
  53. package/dist/server/collections/passwordAttempt.js +55 -0
  54. package/dist/server/collections/passwordHistory.d.ts +2 -0
  55. package/dist/server/collections/passwordHistory.js +46 -0
  56. package/dist/server/collections/passwordStrengthConfig.d.ts +2 -0
  57. package/dist/server/collections/passwordStrengthConfig.js +59 -0
  58. package/dist/server/collections/signInFail.d.ts +2 -0
  59. package/dist/server/collections/signInFail.js +56 -0
  60. package/dist/server/collections/userLocks.d.ts +2 -0
  61. package/dist/server/collections/userLocks.js +51 -0
  62. package/dist/server/collections/users.d.ts +2 -0
  63. package/dist/server/collections/users.js +42 -0
  64. package/dist/server/index.d.ts +1 -0
  65. package/dist/server/index.js +33 -0
  66. package/dist/server/plugin.d.ts +5 -0
  67. package/dist/server/plugin.js +129 -0
  68. package/dist/server/services/IPFilterService.d.ts +49 -0
  69. package/dist/server/services/IPFilterService.js +270 -0
  70. package/dist/server/services/PasswordAttemptService.d.ts +75 -0
  71. package/dist/server/services/PasswordAttemptService.js +595 -0
  72. package/dist/server/services/PasswordStrengthService.d.ts +28 -0
  73. package/dist/server/services/PasswordStrengthService.js +313 -0
  74. package/dist/types/geoip-lite.d.js +0 -0
  75. package/package.json +25 -0
  76. package/server.d.ts +2 -0
  77. package/server.js +1 -0
@@ -0,0 +1,685 @@
1
+ // fetches and converts maxmind lite databases
2
+
3
+ 'use strict';
4
+
5
+ var user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.36 Safari/537.36';
6
+
7
+ var fs = require('fs');
8
+ var http = require('http');
9
+ var https = require('https');
10
+ var path = require('path');
11
+ var url = require('url');
12
+ var zlib = require('zlib');
13
+ var readline = require('readline');
14
+
15
+ fs.existsSync = fs.existsSync || path.existsSync;
16
+
17
+ var async = require('async');
18
+ var chalk = require('chalk');
19
+ var iconv = require('iconv-lite');
20
+ var lazy = require('lazy');
21
+ var rimraf = require('rimraf').sync;
22
+ var yauzl = require('yauzl');
23
+ var utils = require('../lib/utils');
24
+ var Address6 = require('ip-address').Address6;
25
+ var Address4 = require('ip-address').Address4;
26
+
27
+ var args = process.argv.slice(2);
28
+ var license_key = args.find(function(arg) {
29
+ return arg.match(/^license_key=[a-zA-Z0-9]+/) !== null;
30
+ });
31
+ if (typeof license_key === 'undefined' && typeof process.env.LICENSE_KEY !== 'undefined') {
32
+ license_key = 'license_key='+process.env.LICENSE_KEY;
33
+ }
34
+ var geodatadir = args.find(function(arg) {
35
+ return arg.match(/^geodatadir=[\w./]+/) !== null;
36
+ });
37
+ if (typeof geodatadir === 'undefined' && typeof process.env.GEODATADIR !== 'undefined') {
38
+ geodatadir = 'geodatadir='+process.env.GEODATADIR;
39
+ }
40
+ var dataPath = path.resolve(__dirname, '..', 'data');
41
+ if (typeof geodatadir !== 'undefined') {
42
+ dataPath = path.resolve(process.cwd(), geodatadir.split('=')[1]);
43
+ if (!fs.existsSync(dataPath)) {
44
+ console.log(chalk.red('ERROR') + ': Directory does\'t exist: ' + dataPath);
45
+ process.exit(1);
46
+ }
47
+ }
48
+ var tmpPath = process.env.GEOTMPDIR ? process.env.GEOTMPDIR : path.resolve(__dirname, '..', 'tmp');
49
+ var countryLookup = {};
50
+ var cityLookup = {NaN: -1};
51
+ var databases = [
52
+ {
53
+ type: 'country',
54
+ url: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&suffix=zip&'+license_key,
55
+ checksum: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&suffix=zip.sha256&'+license_key,
56
+ fileName: 'GeoLite2-Country-CSV.zip',
57
+ src: [
58
+ 'GeoLite2-Country-Locations-en.csv',
59
+ 'GeoLite2-Country-Blocks-IPv4.csv',
60
+ 'GeoLite2-Country-Blocks-IPv6.csv'
61
+ ],
62
+ dest: [
63
+ '',
64
+ 'geoip-country.dat',
65
+ 'geoip-country6.dat'
66
+ ]
67
+ },
68
+ {
69
+ type: 'city',
70
+ url: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City-CSV&suffix=zip&'+license_key,
71
+ checksum: 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City-CSV&suffix=zip.sha256&'+license_key,
72
+ fileName: 'GeoLite2-City-CSV.zip',
73
+ src: [
74
+ 'GeoLite2-City-Locations-en.csv',
75
+ 'GeoLite2-City-Blocks-IPv4.csv',
76
+ 'GeoLite2-City-Blocks-IPv6.csv'
77
+ ],
78
+ dest: [
79
+ 'geoip-city-names.dat',
80
+ 'geoip-city.dat',
81
+ 'geoip-city6.dat'
82
+ ]
83
+ }
84
+ ];
85
+
86
+ function mkdir(name) {
87
+ var dir = path.dirname(name);
88
+ if (!fs.existsSync(dir)) {
89
+ fs.mkdirSync(dir);
90
+ }
91
+ }
92
+
93
+ // Ref: http://stackoverflow.com/questions/8493195/how-can-i-parse-a-csv-string-with-javascript
94
+ // Return array of string values, or NULL if CSV string not well formed.
95
+ // Return array of string values, or NULL if CSV string not well formed.
96
+
97
+ function try_fixing_line(line) {
98
+ var pos1 = 0;
99
+ var pos2 = -1;
100
+ // escape quotes
101
+ line = line.replace(/""/,'\\"').replace(/'/g,"\\'");
102
+
103
+ while(pos1 < line.length && pos2 < line.length) {
104
+ pos1 = pos2;
105
+ pos2 = line.indexOf(',', pos1 + 1);
106
+ if(pos2 < 0) pos2 = line.length;
107
+ if(line.indexOf("'", (pos1 || 0)) > -1 && line.indexOf("'", pos1) < pos2 && line[pos1 + 1] != '"' && line[pos2 - 1] != '"') {
108
+ line = line.substr(0, pos1 + 1) + '"' + line.substr(pos1 + 1, pos2 - pos1 - 1) + '"' + line.substr(pos2, line.length - pos2);
109
+ pos2 = line.indexOf(',', pos2 + 1);
110
+ if(pos2 < 0) pos2 = line.length;
111
+ }
112
+ }
113
+ return line;
114
+ }
115
+
116
+ function CSVtoArray(text) {
117
+ var re_valid = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/;
118
+ var re_value = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;
119
+ // Return NULL if input string is not well formed CSV string.
120
+ if (!re_valid.test(text)){
121
+ text = try_fixing_line(text);
122
+ if(!re_valid.test(text))
123
+ return null;
124
+ }
125
+ var a = []; // Initialize array to receive values.
126
+ text.replace(re_value, // "Walk" the string using replace with callback.
127
+ function(m0, m1, m2, m3) {
128
+ // Remove backslash from \' in single quoted values.
129
+ if (m1 !== undefined) a.push(m1.replace(/\\'/g, "'"));
130
+ // Remove backslash from \" in double quoted values.
131
+ else if (m2 !== undefined) a.push(m2.replace(/\\"/g, '"').replace(/\\'/g, "'"));
132
+ else if (m3 !== undefined) a.push(m3);
133
+ return ''; // Return empty string.
134
+ });
135
+ // Handle special case of empty last value.
136
+ if (/,\s*$/.test(text)) a.push('');
137
+ return a;
138
+ }
139
+
140
+ function getHTTPOptions(downloadUrl) {
141
+ var options = url.parse(downloadUrl);
142
+ options.headers = {
143
+ 'User-Agent': user_agent
144
+ };
145
+
146
+ if (process.env.http_proxy || process.env.https_proxy) {
147
+ try {
148
+ var HttpsProxyAgent = require('https-proxy-agent');
149
+ options.agent = new HttpsProxyAgent(process.env.http_proxy || process.env.https_proxy);
150
+ }
151
+ catch (e) {
152
+ console.error("Install https-proxy-agent to use an HTTP/HTTPS proxy");
153
+ process.exit(-1);
154
+ }
155
+ }
156
+
157
+ return options;
158
+ }
159
+
160
+ function check(database, cb) {
161
+ if (args.indexOf("force") !== -1) {
162
+ //we are forcing database upgrade,
163
+ //so not even using checksums
164
+ return cb(null, database);
165
+ }
166
+
167
+ var checksumUrl = database.checksum;
168
+
169
+ if (typeof checksumUrl === "undefined") {
170
+ //no checksum url to check, skipping
171
+ return cb(null, database);
172
+ }
173
+
174
+ //read existing checksum file
175
+ fs.readFile(path.join(dataPath, database.type+".checksum"), {encoding: 'utf8'}, function(err, data) {
176
+ if (!err && data && data.length) {
177
+ database.checkValue = data;
178
+ }
179
+
180
+ console.log('Checking ', database.fileName);
181
+
182
+ function onResponse(response) {
183
+ var status = response.statusCode;
184
+
185
+ if(status === 301 || status === 302 || status === 303 || status === 307 || status === 308) {
186
+ return https.get(getHTTPOptions(response.headers.location), onResponse);
187
+ } else if (status !== 200) {
188
+ console.log(chalk.red('ERROR') + ': HTTP Request Failed [%d %s]', status, http.STATUS_CODES[status]);
189
+ client.abort();
190
+ process.exit(1);
191
+ }
192
+
193
+ var str = "";
194
+ response.on("data", function (chunk) {
195
+ str += chunk;
196
+ });
197
+
198
+ response.on("end", function () {
199
+ if (str && str.length) {
200
+ if (str == database.checkValue) {
201
+ console.log(chalk.green('Database "' + database.type + '" is up to date'));
202
+ database.skip = true;
203
+ }
204
+ else {
205
+ console.log(chalk.green('Database ' + database.type + ' has new data'));
206
+ database.checkValue = str;
207
+ }
208
+ }
209
+ else {
210
+ console.log(chalk.red('ERROR') + ': Could not retrieve checksum for', database.type, chalk.red('Aborting'));
211
+ console.log('Run with "force" to update without checksum');
212
+ client.abort();
213
+ process.exit(1);
214
+ }
215
+ cb(null, database);
216
+ });
217
+ }
218
+
219
+ var client = https.get(getHTTPOptions(checksumUrl), onResponse);
220
+ });
221
+ }
222
+
223
+ function fetch(database, cb) {
224
+
225
+ if (database.skip) {
226
+ return cb(null, null, null, database);
227
+ }
228
+
229
+ var downloadUrl = database.url;
230
+ var fileName = database.fileName;
231
+ var gzip = path.extname(fileName) === '.gz';
232
+
233
+ if (gzip) {
234
+ fileName = fileName.replace('.gz', '');
235
+ }
236
+
237
+ var tmpFile = path.join(tmpPath, fileName);
238
+
239
+ if (fs.existsSync(tmpFile)) {
240
+ return cb(null, tmpFile, fileName, database);
241
+ }
242
+
243
+ console.log('Fetching ', fileName);
244
+
245
+ function onResponse(response) {
246
+ var status = response.statusCode;
247
+
248
+ if(status === 301 || status === 302 || status === 303 || status === 307 || status === 308) {
249
+ return https.get(getHTTPOptions(response.headers.location), onResponse);
250
+ } else if (status !== 200) {
251
+ console.log(chalk.red('ERROR') + ': HTTP Request Failed [%d %s]', status, http.STATUS_CODES[status]);
252
+ client.abort();
253
+ process.exit(1);
254
+ }
255
+
256
+ var tmpFilePipe;
257
+ var tmpFileStream = fs.createWriteStream(tmpFile);
258
+
259
+ if (gzip) {
260
+ tmpFilePipe = response.pipe(zlib.createGunzip()).pipe(tmpFileStream);
261
+ } else {
262
+ tmpFilePipe = response.pipe(tmpFileStream);
263
+ }
264
+
265
+ tmpFilePipe.on('close', function() {
266
+ console.log(chalk.green(' DONE'));
267
+ cb(null, tmpFile, fileName, database);
268
+ });
269
+ }
270
+
271
+ mkdir(tmpFile);
272
+
273
+ var client = https.get(getHTTPOptions(downloadUrl), onResponse);
274
+
275
+ process.stdout.write('Retrieving ' + fileName + ' ...');
276
+ }
277
+
278
+ function extract(tmpFile, tmpFileName, database, cb) {
279
+ if (database.skip) {
280
+ return cb(null, database);
281
+ }
282
+
283
+ if (path.extname(tmpFileName) !== '.zip') {
284
+ cb(null, database);
285
+ } else {
286
+ process.stdout.write('Extracting ' + tmpFileName + ' ...');
287
+ yauzl.open(tmpFile, {autoClose: true, lazyEntries: true}, function(err, zipfile) {
288
+ if (err) {
289
+ throw err;
290
+ }
291
+ zipfile.readEntry();
292
+ zipfile.on("entry", function(entry) {
293
+ if (/\/$/.test(entry.fileName)) {
294
+ // Directory file names end with '/'.
295
+ // Note that entries for directories themselves are optional.
296
+ // An entry's fileName implicitly requires its parent directories to exist.
297
+ zipfile.readEntry();
298
+ } else {
299
+ // file entry
300
+ zipfile.openReadStream(entry, function(err, readStream) {
301
+ if (err) {
302
+ throw err;
303
+ }
304
+ readStream.on("end", function() {
305
+ zipfile.readEntry();
306
+ });
307
+ var filePath = entry.fileName.split("/");
308
+ // filePath will always have length >= 1, as split() always returns an array of at least one string
309
+ var fileName = filePath[filePath.length - 1];
310
+ readStream.pipe(fs.createWriteStream(path.join(tmpPath, fileName)));
311
+ });
312
+ }
313
+ });
314
+ zipfile.once("end", function() {
315
+ console.log(chalk.green(' DONE'));
316
+ cb(null, database);
317
+ });
318
+ });
319
+ }
320
+ }
321
+
322
+ function processLookupCountry(src, cb){
323
+ function processLine(line) {
324
+ var fields = CSVtoArray(line);
325
+ if (!fields || fields.length < 6) {
326
+ console.log("weird line: %s::", line);
327
+ return;
328
+ }
329
+ countryLookup[fields[0]] = fields[4];
330
+ }
331
+ var tmpDataFile = path.join(tmpPath, src);
332
+
333
+ process.stdout.write('Processing Lookup Data (may take a moment) ...');
334
+
335
+ lazy(fs.createReadStream(tmpDataFile))
336
+ .lines
337
+ .map(function(byteArray) {
338
+ return iconv.decode(byteArray, 'latin1');
339
+ })
340
+ .skip(1)
341
+ .map(processLine)
342
+ .on('pipe', function() {
343
+ console.log(chalk.green(' DONE'));
344
+ cb();
345
+ });
346
+ }
347
+
348
+ async function processCountryData(src, dest) {
349
+ var lines=0;
350
+ async function processLine(line) {
351
+ var fields = CSVtoArray(line);
352
+
353
+ if (!fields || fields.length < 6) {
354
+ console.log("weird line: %s::", line);
355
+ return;
356
+ }
357
+ lines++;
358
+
359
+ var sip;
360
+ var eip;
361
+ var rngip;
362
+ var cc = countryLookup[fields[1]];
363
+ var b;
364
+ var bsz;
365
+ var i;
366
+ if(cc){
367
+ if (fields[0].match(/:/)) {
368
+ // IPv6
369
+ bsz = 34;
370
+ rngip = new Address6(fields[0]);
371
+ sip = utils.aton6(rngip.startAddress().correctForm());
372
+ eip = utils.aton6(rngip.endAddress().correctForm());
373
+
374
+ b = Buffer.alloc(bsz);
375
+ for (i = 0; i < sip.length; i++) {
376
+ b.writeUInt32BE(sip[i], i * 4);
377
+ }
378
+
379
+ for (i = 0; i < eip.length; i++) {
380
+ b.writeUInt32BE(eip[i], 16 + (i * 4));
381
+ }
382
+ } else {
383
+ // IPv4
384
+ bsz = 10;
385
+
386
+ rngip = new Address4(fields[0]);
387
+ sip = parseInt(rngip.startAddress().bigInteger(),10);
388
+ eip = parseInt(rngip.endAddress().bigInteger(),10);
389
+
390
+ b = Buffer.alloc(bsz);
391
+ b.fill(0);
392
+ b.writeUInt32BE(sip, 0);
393
+ b.writeUInt32BE(eip, 4);
394
+ }
395
+
396
+ b.write(cc, bsz - 2);
397
+ if(Date.now() - tstart > 5000) {
398
+ tstart = Date.now();
399
+ process.stdout.write('\nStill working (' + lines + ') ...');
400
+ }
401
+
402
+ if(datFile._writableState.needDrain) {
403
+ return new Promise((resolve) => {
404
+ datFile.write(b, resolve);
405
+ });
406
+ } else {
407
+ return datFile.write(b);
408
+ }
409
+ }
410
+ }
411
+
412
+ var dataFile = path.join(dataPath, dest);
413
+ var tmpDataFile = path.join(tmpPath, src);
414
+
415
+ rimraf(dataFile);
416
+ mkdir(dataFile);
417
+
418
+ process.stdout.write('Processing Data (may take a moment) ...');
419
+ var tstart = Date.now();
420
+ var datFile = fs.createWriteStream(dataFile);
421
+
422
+ var rl = readline.createInterface({
423
+ input: fs.createReadStream(tmpDataFile),
424
+ crlfDelay: Infinity
425
+ });
426
+ var i = 0;
427
+ for await (var line of rl) {
428
+ i++;
429
+ if(i == 1) continue;
430
+ await processLine(line);
431
+ }
432
+ datFile.close();
433
+ console.log(chalk.green(' DONE'));
434
+ }
435
+
436
+ async function processCityData(src, dest) {
437
+ var lines = 0;
438
+ async function processLine(line) {
439
+ if (line.match(/^Copyright/) || !line.match(/\d/)) {
440
+ return;
441
+ }
442
+
443
+ var fields = CSVtoArray(line);
444
+ if (!fields) {
445
+ console.log("weird line: %s::", line);
446
+ return;
447
+ }
448
+ var sip;
449
+ var eip;
450
+ var rngip;
451
+ var locId;
452
+ var b;
453
+ var bsz;
454
+ var lat;
455
+ var lon;
456
+ var area;
457
+
458
+ var i;
459
+
460
+ lines++;
461
+
462
+ if (fields[0].match(/:/)) {
463
+ // IPv6
464
+ var offset = 0;
465
+ bsz = 48;
466
+ rngip = new Address6(fields[0]);
467
+ sip = utils.aton6(rngip.startAddress().correctForm());
468
+ eip = utils.aton6(rngip.endAddress().correctForm());
469
+ locId = parseInt(fields[1], 10);
470
+ locId = cityLookup[locId];
471
+
472
+ b = Buffer.alloc(bsz);
473
+ b.fill(0);
474
+
475
+ for (i = 0; i < sip.length; i++) {
476
+ b.writeUInt32BE(sip[i], offset);
477
+ offset += 4;
478
+ }
479
+
480
+ for (i = 0; i < eip.length; i++) {
481
+ b.writeUInt32BE(eip[i], offset);
482
+ offset += 4;
483
+ }
484
+ b.writeUInt32BE(locId>>>0, 32);
485
+
486
+ lat = Math.round(parseFloat(fields[7]) * 10000);
487
+ lon = Math.round(parseFloat(fields[8]) * 10000);
488
+ area = parseInt(fields[9], 10);
489
+ b.writeInt32BE(lat,36);
490
+ b.writeInt32BE(lon,40);
491
+ b.writeInt32BE(area,44);
492
+ } else {
493
+ // IPv4
494
+ bsz = 24;
495
+
496
+ rngip = new Address4(fields[0]);
497
+ sip = parseInt(rngip.startAddress().bigInteger(),10);
498
+ eip = parseInt(rngip.endAddress().bigInteger(),10);
499
+ locId = parseInt(fields[1], 10);
500
+ locId = cityLookup[locId];
501
+ b = Buffer.alloc(bsz);
502
+ b.fill(0);
503
+ b.writeUInt32BE(sip>>>0, 0);
504
+ b.writeUInt32BE(eip>>>0, 4);
505
+ b.writeUInt32BE(locId>>>0, 8);
506
+
507
+ lat = Math.round(parseFloat(fields[7]) * 10000);
508
+ lon = Math.round(parseFloat(fields[8]) * 10000);
509
+ area = parseInt(fields[9], 10);
510
+ b.writeInt32BE(lat,12);
511
+ b.writeInt32BE(lon,16);
512
+ b.writeInt32BE(area,20);
513
+ }
514
+
515
+ if(Date.now() - tstart > 5000) {
516
+ tstart = Date.now();
517
+ process.stdout.write('\nStill working (' + lines + ') ...');
518
+ }
519
+
520
+ if(datFile._writableState.needDrain) {
521
+ return new Promise((resolve) => {
522
+ datFile.write(b, resolve);
523
+ });
524
+ } else {
525
+ return datFile.write(b);
526
+ }
527
+ }
528
+
529
+ var dataFile = path.join(dataPath, dest);
530
+ var tmpDataFile = path.join(tmpPath, src);
531
+
532
+ rimraf(dataFile);
533
+
534
+ process.stdout.write('Processing Data (may take a moment) ...');
535
+ var tstart = Date.now();
536
+ var datFile = fs.createWriteStream(dataFile);
537
+
538
+ var rl = readline.createInterface({
539
+ input: fs.createReadStream(tmpDataFile),
540
+ crlfDelay: Infinity
541
+ });
542
+ var i = 0;
543
+ for await (var line of rl) {
544
+ i++;
545
+ if(i == 1) continue;
546
+ await processLine(line);
547
+ }
548
+ datFile.close();
549
+ }
550
+
551
+ function processCityDataNames(src, dest, cb) {
552
+ var locId = null;
553
+ var linesCount = 0;
554
+ function processLine(line) {
555
+ if (line.match(/^Copyright/) || !line.match(/\d/)) {
556
+ return;
557
+ }
558
+
559
+ var b;
560
+ var sz = 88;
561
+ var fields = CSVtoArray(line);
562
+ if (!fields) {
563
+ //lots of cities contain ` or ' in the name and can't be parsed correctly with current method
564
+ console.log("weird line: %s::", line);
565
+ return;
566
+ }
567
+
568
+ locId = parseInt(fields[0]);
569
+
570
+ cityLookup[locId] = linesCount;
571
+ var cc = fields[4];
572
+ var rg = fields[6];
573
+ var city = fields[10];
574
+ var metro = parseInt(fields[11]);
575
+ //other possible fields to include
576
+ var tz = fields[12];
577
+ var eu = fields[13];
578
+
579
+ b = Buffer.alloc(sz);
580
+ b.fill(0);
581
+ b.write(cc, 0);//country code
582
+ b.write(rg, 2);//region
583
+
584
+ if(metro) {
585
+ b.writeInt32BE(metro, 5);
586
+ }
587
+ b.write(eu,9);//is in eu
588
+ b.write(tz,10);//timezone
589
+ b.write(city, 42);//cityname
590
+
591
+ fs.writeSync(datFile, b, 0, b.length, null);
592
+ linesCount++;
593
+ }
594
+
595
+ var dataFile = path.join(dataPath, dest);
596
+ var tmpDataFile = path.join(tmpPath, src);
597
+
598
+ rimraf(dataFile);
599
+
600
+ var datFile = fs.openSync(dataFile, "w");
601
+
602
+ lazy(fs.createReadStream(tmpDataFile))
603
+ .lines
604
+ .map(function(byteArray) {
605
+ return iconv.decode(byteArray, 'utf-8');
606
+ })
607
+ .skip(1)
608
+ .map(processLine)
609
+ .on('pipe', cb);
610
+ }
611
+
612
+ function processData(database, cb) {
613
+ if (database.skip) {
614
+ return cb(null, database);
615
+ }
616
+
617
+ var type = database.type;
618
+ var src = database.src;
619
+ var dest = database.dest;
620
+
621
+ if (type === 'country') {
622
+ if(Array.isArray(src)){
623
+ processLookupCountry(src[0], function() {
624
+ processCountryData(src[1], dest[1]).then(() => {
625
+ return processCountryData(src[2], dest[2]);
626
+ }).then(() => {
627
+ cb(null, database);
628
+ });
629
+ });
630
+ }
631
+ else{
632
+ processCountryData(src, dest, function() {
633
+ cb(null, database);
634
+ });
635
+ }
636
+ } else if (type === 'city') {
637
+ processCityDataNames(src[0], dest[0], function() {
638
+ processCityData(src[1], dest[1]).then(() => {
639
+ console.log("city data processed");
640
+ return processCityData(src[2], dest[2]);
641
+ }).then(() => {
642
+ console.log(chalk.green(' DONE'));
643
+ cb(null, database);
644
+ });
645
+ });
646
+ }
647
+ }
648
+
649
+ function updateChecksum(database, cb) {
650
+ if (database.skip || !database.checkValue) {
651
+ //don't need to update checksums cause it was not fetched or did not change
652
+ return cb();
653
+ }
654
+ fs.writeFile(path.join(dataPath, database.type+".checksum"), database.checkValue, 'utf8', function(err){
655
+ if (err) console.log(chalk.red('Failed to Update checksums.'), "Database:", database.type);
656
+ cb();
657
+ });
658
+ }
659
+
660
+ if (!license_key) {
661
+ console.log(chalk.red('ERROR') + ': Missing license_key');
662
+ process.exit(1);
663
+ }
664
+
665
+ rimraf(tmpPath);
666
+ mkdir(tmpPath);
667
+
668
+ async.eachSeries(databases, function(database, nextDatabase) {
669
+
670
+ async.seq(check, fetch, extract, processData, updateChecksum)(database, nextDatabase);
671
+
672
+ }, function(err) {
673
+ if (err) {
674
+ console.log(chalk.red('Failed to Update Databases from MaxMind.'), err);
675
+ process.exit(1);
676
+ } else {
677
+ console.log(chalk.green('Successfully Updated Databases from MaxMind.'));
678
+ if (args.indexOf("debug") !== -1) {
679
+ console.log(chalk.yellow.bold('Notice: temporary files are not deleted for debug purposes.'));
680
+ } else {
681
+ rimraf(tmpPath);
682
+ }
683
+ process.exit(0);
684
+ }
685
+ });