signup-service 0.0.1

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.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # signup
package/lib/index.js ADDED
@@ -0,0 +1,360 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (_) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ var resources = (function () {
40
+ function resources() {
41
+ }
42
+ resources.email = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,4})$/i;
43
+ resources.phone = /^\d{5,14}$/;
44
+ resources.password = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/;
45
+ return resources;
46
+ }());
47
+ exports.resources = resources;
48
+ function initStatus(s) {
49
+ var error = (s && s.error ? s.error : 0);
50
+ var success = (s && s.success ? s.success : 1);
51
+ var username = (s && s.username ? s.username : 2);
52
+ var contact = (s && s.contact ? s.contact : 2);
53
+ var format_username = (s && s.format_username ? s.format_username : -1);
54
+ var format_contact = (s && s.format_contact ? s.format_contact : -2);
55
+ var format_password = (s && s.format_password ? s.format_password : -3);
56
+ return { error: error, success: success, username: username, contact: contact, format_username: format_username, format_contact: format_contact, format_password: format_password };
57
+ }
58
+ exports.initStatus = initStatus;
59
+ exports.initializeStatus = initStatus;
60
+ var SignupService = (function () {
61
+ function SignupService(status, repository, generateId, comparator, passcodeComparator, passcodeRepository, send, gen, expires, validate, checkContact) {
62
+ this.status = status;
63
+ this.repository = repository;
64
+ this.generateId = generateId;
65
+ this.comparator = comparator;
66
+ this.passcodeComparator = passcodeComparator;
67
+ this.passcodeRepository = passcodeRepository;
68
+ this.send = send;
69
+ this.expires = expires;
70
+ this.validate = validate;
71
+ this.checkContact = checkContact;
72
+ this.generate = gen;
73
+ this.register = this.register.bind(this);
74
+ this.signup = this.signup.bind(this);
75
+ this.verify = this.verify.bind(this);
76
+ this.check = this.check.bind(this);
77
+ this.createConfirmCode = this.createConfirmCode.bind(this);
78
+ }
79
+ SignupService.prototype.register = function (info) {
80
+ return this.signup(info);
81
+ };
82
+ SignupService.prototype.signup = function (info) {
83
+ return __awaiter(this, void 0, void 0, function () {
84
+ var errors, out, _i, errors_1, err, f, u, c, hashPassword, userId, ok, res;
85
+ return __generator(this, function (_a) {
86
+ switch (_a.label) {
87
+ case 0:
88
+ if (this.validate) {
89
+ errors = this.validate(info);
90
+ if (errors && errors.length > 0) {
91
+ out = false;
92
+ for (_i = 0, errors_1 = errors; _i < errors_1.length; _i++) {
93
+ err = errors_1[_i];
94
+ f = err.field;
95
+ if (f !== 'username' && f !== 'contact' && f !== 'password') {
96
+ out = true;
97
+ }
98
+ else {
99
+ if (f === 'username') {
100
+ return [2, this.status.format_username];
101
+ }
102
+ else if (f === 'contact') {
103
+ return [2, this.status.format_contact];
104
+ }
105
+ else if (f === 'password') {
106
+ return [2, this.status.format_password];
107
+ }
108
+ }
109
+ }
110
+ if (!out) {
111
+ return [2, { status: this.status.error, errors: errors }];
112
+ }
113
+ }
114
+ }
115
+ return [4, this.repository.checkUsername(info.username)];
116
+ case 1:
117
+ u = _a.sent();
118
+ if (u) {
119
+ return [2, this.status.username];
120
+ }
121
+ if (!this.checkContact) return [3, 3];
122
+ return [4, this.checkContact(info.contact)];
123
+ case 2:
124
+ c = _a.sent();
125
+ if (c) {
126
+ return [2, this.status.contact];
127
+ }
128
+ _a.label = 3;
129
+ case 3: return [4, this.comparator.hash(info.password)];
130
+ case 4:
131
+ hashPassword = _a.sent();
132
+ info.password = hashPassword;
133
+ userId = this.generateId();
134
+ return [4, this.repository.save(userId, info)];
135
+ case 5:
136
+ ok = _a.sent();
137
+ if (!ok) {
138
+ return [2, this.status.error];
139
+ }
140
+ return [4, this.createConfirmCode(userId, info)];
141
+ case 6:
142
+ res = _a.sent();
143
+ return [2, res ? this.status.success : this.status.error];
144
+ }
145
+ });
146
+ });
147
+ };
148
+ SignupService.prototype.createConfirmCode = function (userId, info) {
149
+ return __awaiter(this, void 0, void 0, function () {
150
+ var codeSendToEmail, codeHashed, expiredAt, saved, sent, ok;
151
+ return __generator(this, function (_a) {
152
+ switch (_a.label) {
153
+ case 0:
154
+ codeSendToEmail = this.generate();
155
+ return [4, this.passcodeComparator.hash(codeSendToEmail)];
156
+ case 1:
157
+ codeHashed = _a.sent();
158
+ expiredAt = addSeconds(new Date(), this.expires);
159
+ return [4, this.passcodeRepository.save(userId, codeHashed, expiredAt)];
160
+ case 2:
161
+ saved = _a.sent();
162
+ if (saved <= 0) {
163
+ return [2, false];
164
+ }
165
+ return [4, this.send('', codeSendToEmail, expiredAt, info.contact)];
166
+ case 3:
167
+ sent = _a.sent();
168
+ if (!sent) return [3, 5];
169
+ return [4, this.repository.verify(userId)];
170
+ case 4:
171
+ ok = _a.sent();
172
+ if (ok) {
173
+ return [2, true];
174
+ }
175
+ else {
176
+ return [2, false];
177
+ }
178
+ return [3, 6];
179
+ case 5: return [2, false];
180
+ case 6: return [2];
181
+ }
182
+ });
183
+ });
184
+ };
185
+ SignupService.prototype.verify = function (userId, code, password) {
186
+ var _this = this;
187
+ return this.check(userId, code).then(function (valid) {
188
+ if (!valid) {
189
+ return false;
190
+ }
191
+ else {
192
+ if (!password || password.length === 0) {
193
+ _this.passcodeRepository.delete(userId);
194
+ return _this.repository.activate(userId);
195
+ }
196
+ else {
197
+ return _this.comparator.hash(password).then(function (hashPassword) {
198
+ _this.passcodeRepository.delete(userId);
199
+ return _this.repository.activate(userId, hashPassword);
200
+ });
201
+ }
202
+ }
203
+ });
204
+ };
205
+ SignupService.prototype.check = function (userId, code) {
206
+ var _this = this;
207
+ return this.passcodeRepository.load(userId).then(function (pass) {
208
+ if (after(new Date, pass.expiredAt)) {
209
+ return false;
210
+ }
211
+ else {
212
+ return _this.passcodeComparator.compare(code, pass.code);
213
+ }
214
+ });
215
+ };
216
+ return SignupService;
217
+ }());
218
+ exports.SignupService = SignupService;
219
+ exports.UserRegistrationService = SignupService;
220
+ function addSeconds(date, number) {
221
+ var newDate = new Date(date);
222
+ newDate.setSeconds(newDate.getSeconds() + number);
223
+ return newDate;
224
+ }
225
+ exports.addSeconds = addSeconds;
226
+ function after(date1, date2) {
227
+ if (!date1 || !date2) {
228
+ return false;
229
+ }
230
+ if (date1.getTime() - date2.getTime() > 0) {
231
+ return true;
232
+ }
233
+ return false;
234
+ }
235
+ exports.after = after;
236
+ function generate(length, pad) {
237
+ if (!length) {
238
+ length = 6;
239
+ }
240
+ return padLeft(Math.floor(Math.random() * Math.floor(Math.pow(10, length) - 1)).toString(), length, pad);
241
+ }
242
+ exports.generate = generate;
243
+ function padLeft(str, length, pad) {
244
+ if (!str) {
245
+ return str;
246
+ }
247
+ if (typeof str !== 'string') {
248
+ str = '' + str;
249
+ }
250
+ if (str.length >= length) {
251
+ return str;
252
+ }
253
+ if (!pad) {
254
+ pad = '0';
255
+ }
256
+ var str2 = str;
257
+ while (str2.length < length) {
258
+ str2 = pad + str2;
259
+ }
260
+ return str2;
261
+ }
262
+ exports.padLeft = padLeft;
263
+ var Tel = (function () {
264
+ function Tel() {
265
+ }
266
+ Tel.isPhone = function (str) {
267
+ if (!str || str.length === 0 || str === '+') {
268
+ return false;
269
+ }
270
+ if (str.charAt(0) !== '+') {
271
+ return resources.phone.test(str);
272
+ }
273
+ else {
274
+ var phoneNumber = str.substring(1);
275
+ if (!resources.phonecodes) {
276
+ return resources.phone.test(phoneNumber);
277
+ }
278
+ else {
279
+ if (resources.phone.test(phoneNumber)) {
280
+ for (var degit = 1; degit <= 3; degit++) {
281
+ var countryCode = phoneNumber.substr(0, degit);
282
+ if (countryCode in resources.phonecodes) {
283
+ return true;
284
+ }
285
+ }
286
+ return false;
287
+ }
288
+ else {
289
+ return false;
290
+ }
291
+ }
292
+ }
293
+ };
294
+ return Tel;
295
+ }());
296
+ exports.Tel = Tel;
297
+ exports.tel = Tel;
298
+ function isPhone(str) {
299
+ return Tel.isPhone(str);
300
+ }
301
+ exports.isPhone = isPhone;
302
+ function isEmail(email) {
303
+ if (!email || email.length === 0) {
304
+ return false;
305
+ }
306
+ return resources.email.test(email);
307
+ }
308
+ exports.isEmail = isEmail;
309
+ function isUsername(u) {
310
+ if (isEmail(u)) {
311
+ return true;
312
+ }
313
+ if (isPhone(u)) {
314
+ return true;
315
+ }
316
+ var v = u + '@gmail.com';
317
+ return isEmail(v);
318
+ }
319
+ exports.isUsername = isUsername;
320
+ function isPassword(password) {
321
+ return resources.password.test(password);
322
+ }
323
+ exports.isPassword = isPassword;
324
+ var Validator = (function () {
325
+ function Validator(checkUsername, checkContact, passwordRequired, min, max) {
326
+ this.max = max;
327
+ this.min = (min !== undefined && min != null ? min : 6);
328
+ this.passwordRequired = (passwordRequired !== undefined && passwordRequired != null ? passwordRequired : true);
329
+ this.checkUsername = (checkUsername ? checkUsername : isUsername);
330
+ this.checkContact = (checkContact ? checkContact : isEmail);
331
+ this.validate = this.validate.bind(this);
332
+ }
333
+ Validator.prototype.validate = function (u) {
334
+ var errs = [];
335
+ if (!this.checkUsername(u.username)) {
336
+ errs.push({ field: 'username', code: 'username', message: 'username is not valid' });
337
+ }
338
+ else {
339
+ if (u.username.length < this.min) {
340
+ errs.push({ field: 'username', code: 'username', message: 'username must have at least ' + this.min + 'characters' });
341
+ }
342
+ else if (this.max && u.username.length > this.max) {
343
+ errs.push({ field: 'username', code: 'username', message: 'username must be less than ' + this.max + 'characters' });
344
+ }
345
+ }
346
+ if (!this.checkContact(u.contact)) {
347
+ errs.push({ field: 'contact', code: 'contact', message: 'contact is not valid' });
348
+ }
349
+ if (this.passwordRequired) {
350
+ if (!isPassword(u.password)) {
351
+ errs.push({ field: 'password', code: 'password', message: 'password is not valid' });
352
+ }
353
+ }
354
+ return errs;
355
+ };
356
+ return Validator;
357
+ }());
358
+ exports.Validator = Validator;
359
+ exports.SignupValidator = Validator;
360
+ exports.UserRegistrationValidator = Validator;
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "signup-service",
3
+ "version": "0.0.1",
4
+ "description": "signup",
5
+ "main": "./lib/index.js",
6
+ "types": "./src/index.ts",
7
+ "scripts": {
8
+ "build:lib": "tsc",
9
+ "clean:lib": "rimraf lib"
10
+ },
11
+ "devDependencies": {
12
+ "tslint": "5.10.0",
13
+ "typescript": "^3.8.3"
14
+ },
15
+ "publishConfig": {
16
+ "registry": "https://registry.npmjs.org/"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git@github.com/core-ts/signup"
21
+ },
22
+ "keywords": [
23
+ "signup"
24
+ ]
25
+ }
package/src/index.ts ADDED
@@ -0,0 +1,393 @@
1
+ // tslint:disable-next-line:class-name
2
+ export class resources {
3
+ static phonecodes?: Phones;
4
+ static email = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,4})$/i;
5
+ static phone = /^\d{5,14}$/;
6
+ static password = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/;
7
+ }
8
+ export interface User {
9
+ username: string;
10
+ password: string;
11
+ contact: string;
12
+ /*
13
+ email?: string;
14
+ phone?: string;
15
+ dateOfBirth?: Date;
16
+ gender?: string;
17
+ givenName?: string;
18
+ middleName?: string;
19
+ familyName?: string;
20
+ */
21
+ }
22
+ export type Info = User;
23
+ export type Signup = User;
24
+ export interface ErrorMessage {
25
+ field: string;
26
+ code: string;
27
+ message?: string;
28
+ }
29
+ export interface Result {
30
+ status: number|string;
31
+ errors?: ErrorMessage[];
32
+ message?: string;
33
+ }
34
+ export interface Status {
35
+ success: string|number;
36
+ username: string|number;
37
+ contact: string|number;
38
+ error: string|number;
39
+ format_username: string|number;
40
+ format_contact: string|number;
41
+ format_password: string|number;
42
+ }
43
+ export interface StatusConf {
44
+ error?: string|number;
45
+ success?: string|number;
46
+ username?: string|number;
47
+ contact?: string|number;
48
+ format_username: string|number;
49
+ format_contact: string|number;
50
+ format_password: string|number;
51
+ }
52
+ export function initStatus(s?: StatusConf): Status {
53
+ const error: number | string = (s && s.error ? s.error : 0);
54
+ const success: number | string = (s && s.success ? s.success : 1);
55
+ const username: number | string = (s && s.username ? s.username : 2);
56
+ const contact: number | string = (s && s.contact ? s.contact : 2);
57
+ const format_username: number | string = (s && s.format_username ? s.format_username : -1);
58
+ const format_contact: number | string = (s && s.format_contact ? s.format_contact : -2);
59
+ const format_password: number | string = (s && s.format_password ? s.format_password : -3);
60
+ return { error, success, username, contact, format_username, format_contact, format_password };
61
+ }
62
+ export const initializeStatus = initStatus;
63
+ export interface UserStatus {
64
+ registered: string;
65
+ codeSent: string;
66
+ activated: string;
67
+ }
68
+ export interface FieldConfig {
69
+ username?: string;
70
+ contact?: string;
71
+ password?: string;
72
+ status?: string;
73
+ maxPasswordAge?: string;
74
+ }
75
+ export interface Track {
76
+ createdAt: string;
77
+ createdBy: string;
78
+ updatedAt: string;
79
+ updatedBy: string;
80
+ version?: string;
81
+ }
82
+ export interface StringMap {
83
+ [key: string]: string;
84
+ }
85
+ export interface SignupConf {
86
+ status?: StatusConf;
87
+ expires: number;
88
+ userStatus: UserStatus;
89
+ maxPasswordAge?: number;
90
+ map?: StringMap;
91
+ track?: Track;
92
+ fields?: FieldConfig;
93
+ url?: string;
94
+ }
95
+ export interface SignupConfig {
96
+ user?: string;
97
+ password?: string;
98
+ }
99
+ export interface SignUpConfirmationConfig {
100
+ expires: number;
101
+ domain: string;
102
+ port: number;
103
+ secure: string;
104
+ }
105
+ export interface Repository<ID, T extends User> {
106
+ checkUsername(username: string): Promise<boolean>;
107
+ checkContact(contact: string): Promise<boolean>;
108
+ save(id: ID, info: T): Promise<boolean>;
109
+ verify(id: ID): Promise<boolean>;
110
+ activate(id: ID, password?: string): Promise<boolean>;
111
+ }
112
+ export interface Comparator {
113
+ compare(data: string, encrypted: string): Promise<boolean>;
114
+ hash(plaintext: string): Promise<string>;
115
+ }
116
+ export interface Passcode {
117
+ expiredAt: Date;
118
+ code: string;
119
+ }
120
+ export interface PasscodeRepository<ID> {
121
+ save(id: ID, passcode: string, expireAt: Date): Promise<number>;
122
+ load(id: ID): Promise<Passcode>;
123
+ delete(id: ID): Promise<number>;
124
+ }
125
+ /*
126
+ export interface PasscodeSender {
127
+ send(to: string, passcode: string, expireAt: Date, params: any): Promise<boolean>;
128
+ }
129
+ export interface PasscodeGenerator {
130
+ generate(): string;
131
+ }
132
+ export interface Validator {
133
+ validate(model: any): ErrorMessage[];
134
+ }
135
+ */
136
+ // tslint:disable-next-line:max-classes-per-file
137
+ export class SignupService<ID, T extends User> {
138
+ constructor(
139
+ public status: Status,
140
+ public repository: Repository<ID, T>,
141
+ public generateId: () => ID,
142
+ public comparator: Comparator,
143
+ public passcodeComparator: Comparator,
144
+ public passcodeRepository: PasscodeRepository<ID>,
145
+ public send: (to: string, passcode: string, expireAt: Date, params?: any) => Promise<boolean>,
146
+ gen: () => string,
147
+ public expires: number,
148
+ public validate?: (model: User) => ErrorMessage[],
149
+ public checkContact?: (username: string) => Promise<boolean>
150
+ ) {
151
+ this.generate = gen;
152
+ this.register = this.register.bind(this);
153
+ this.signup = this.signup.bind(this);
154
+ this.verify = this.verify.bind(this);
155
+ this.check = this.check.bind(this);
156
+ this.createConfirmCode = this.createConfirmCode.bind(this);
157
+ }
158
+ public generate: () => string;
159
+ register(info: T): Promise<Result|string|number> {
160
+ return this.signup(info);
161
+ }
162
+ async signup(info: T): Promise<Result|string|number> {
163
+ if (this.validate) {
164
+ const errors = this.validate(info);
165
+ if (errors && errors.length > 0) {
166
+ let out = false;
167
+ for (const err of errors) {
168
+ const f = err.field;
169
+ if (f !== 'username' && f !== 'contact' && f !== 'password') {
170
+ out = true;
171
+ } else {
172
+ if (f === 'username') {
173
+ return this.status.format_username;
174
+ } else if (f === 'contact') {
175
+ return this.status.format_contact;
176
+ } else if (f === 'password') {
177
+ return this.status.format_password;
178
+ }
179
+ }
180
+ }
181
+ if (!out) {
182
+ return { status: this.status.error, errors };
183
+ }
184
+ }
185
+ }
186
+
187
+ const u = await this.repository.checkUsername(info.username);
188
+ if (u) {
189
+ return this.status.username;
190
+ }
191
+ if (this.checkContact) {
192
+ const c = await this.checkContact(info.contact);
193
+ if (c) {
194
+ return this.status.contact;
195
+ }
196
+ }
197
+ const hashPassword = await this.comparator.hash(info.password);
198
+ info.password = hashPassword;
199
+
200
+ const userId = this.generateId();
201
+ const ok = await this.repository.save(userId, info);
202
+
203
+ if (!ok) {
204
+ return this.status.error;
205
+ }
206
+
207
+ const res = await this.createConfirmCode(userId, info);
208
+ return res ? this.status.success : this.status.error;
209
+ }
210
+
211
+ private async createConfirmCode(userId: ID, info: User): Promise<boolean> {
212
+ const codeSendToEmail = this.generate();
213
+ const codeHashed = await this.passcodeComparator.hash(codeSendToEmail);
214
+
215
+ const expiredAt = addSeconds(new Date(), this.expires);
216
+ const saved = await this.passcodeRepository.save(userId, codeHashed, expiredAt);
217
+ if (saved <= 0) {
218
+ return false;
219
+ }
220
+
221
+ const sent = await this.send('', codeSendToEmail, expiredAt, info.contact);
222
+ if (sent) {
223
+ const ok = await this.repository.verify(userId);
224
+ if (ok) {
225
+ return true;
226
+ } else {
227
+ return false;
228
+ }
229
+ } else {
230
+ return false;
231
+ }
232
+ }
233
+
234
+ verify(userId: ID, code: string, password: string): Promise<boolean> {
235
+ return this.check(userId, code).then(valid => {
236
+ if (!valid) {
237
+ return false;
238
+ } else {
239
+ if (!password || password.length === 0) {
240
+ this.passcodeRepository.delete(userId);
241
+ return this.repository.activate(userId);
242
+ } else {
243
+ return this.comparator.hash(password).then(hashPassword => {
244
+ this.passcodeRepository.delete(userId);
245
+ return this.repository.activate(userId, hashPassword);
246
+ });
247
+ }
248
+ }
249
+ });
250
+ }
251
+ private check(userId: ID, code: string): Promise<boolean> {
252
+ return this.passcodeRepository.load(userId).then(pass => {
253
+ if (after(new Date, pass.expiredAt)) {
254
+ return false;
255
+ } else {
256
+ return this.passcodeComparator.compare(code, pass.code);
257
+ }
258
+ });
259
+ }
260
+ }
261
+ export const UserRegistrationService = SignupService;
262
+ export function addSeconds(date: Date, number: number) {
263
+ const newDate = new Date(date);
264
+ newDate.setSeconds(newDate.getSeconds() + number);
265
+ return newDate;
266
+ }
267
+ export function after(date1?: Date, date2?: Date): boolean {
268
+ if (!date1 || !date2) {
269
+ return false;
270
+ }
271
+ if (date1.getTime() - date2.getTime() > 0) {
272
+ return true;
273
+ }
274
+ return false;
275
+ }
276
+
277
+ export function generate(length?: number, pad?: string): string {
278
+ if (!length) {
279
+ length = 6;
280
+ }
281
+ return padLeft(Math.floor(Math.random() * Math.floor(Math.pow(10, length) - 1)).toString(), length, pad);
282
+ }
283
+ export function padLeft(str: string, length: number, pad?: string) {
284
+ if (!str) {
285
+ return str;
286
+ }
287
+ if (typeof str !== 'string') {
288
+ str = '' + str;
289
+ }
290
+ if (str.length >= length) {
291
+ return str;
292
+ }
293
+ if (!pad) {
294
+ pad = '0';
295
+ }
296
+ let str2 = str;
297
+ while (str2.length < length) {
298
+ str2 = pad + str2;
299
+ }
300
+ return str2;
301
+ }
302
+ export interface Phones {
303
+ [key: string]: string;
304
+ }
305
+
306
+ // tslint:disable-next-line:max-classes-per-file
307
+ export class Tel {
308
+ static isPhone(str: string|null|undefined): boolean {
309
+ if (!str || str.length === 0 || str === '+') {
310
+ return false;
311
+ }
312
+ if (str.charAt(0) !== '+') {
313
+ return resources.phone.test(str);
314
+ } else {
315
+ const phoneNumber = str.substring(1);
316
+ if (!resources.phonecodes) {
317
+ return resources.phone.test(phoneNumber);
318
+ } else {
319
+ if (resources.phone.test(phoneNumber)) {
320
+ for (let degit = 1; degit <= 3; degit++) {
321
+ const countryCode = phoneNumber.substr(0, degit);
322
+ if (countryCode in resources.phonecodes) {
323
+ return true;
324
+ }
325
+ }
326
+ return false;
327
+ } else {
328
+ return false;
329
+ }
330
+ }
331
+ }
332
+ }
333
+ }
334
+ export const tel = Tel;
335
+ export function isPhone(str: string|null|undefined): boolean {
336
+ return Tel.isPhone(str);
337
+ }
338
+ export function isEmail(email: string|null|undefined): boolean {
339
+ if (!email || email.length === 0) {
340
+ return false;
341
+ }
342
+ return resources.email.test(email);
343
+ }
344
+ export function isUsername(u: string|null|undefined): boolean {
345
+ if (isEmail(u)) {
346
+ return true;
347
+ }
348
+ if (isPhone(u)) {
349
+ return true;
350
+ }
351
+ const v = u + '@gmail.com';
352
+ return isEmail(v);
353
+ }
354
+ export function isPassword(password: string): boolean {
355
+ return resources.password.test(password);
356
+ }
357
+ // tslint:disable-next-line:max-classes-per-file
358
+ export class Validator<T extends User> {
359
+ min: number;
360
+ passwordRequired: boolean;
361
+ checkContact: (u: string|null|undefined) => boolean;
362
+ checkUsername: (u: string|null|undefined) => boolean;
363
+ constructor(checkUsername?: (u: string|null|undefined) => boolean, checkContact?: (u: string|null|undefined) => boolean, passwordRequired?: boolean, min?: number, public max?: number) {
364
+ this.min = (min !== undefined && min != null ? min : 6);
365
+ this.passwordRequired = (passwordRequired !== undefined && passwordRequired != null ? passwordRequired : true);
366
+ this.checkUsername = (checkUsername ? checkUsername : isUsername);
367
+ this.checkContact = (checkContact ? checkContact : isEmail);
368
+ this.validate = this.validate.bind(this);
369
+ }
370
+ validate(u: T): ErrorMessage[] {
371
+ const errs: ErrorMessage[] = [];
372
+ if (!this.checkUsername(u.username)) {
373
+ errs.push({field: 'username', code: 'username', message: 'username is not valid'});
374
+ } else {
375
+ if (u.username.length < this.min) {
376
+ errs.push({field: 'username', code: 'username', message: 'username must have at least ' + this.min + 'characters'});
377
+ } else if (this.max && u.username.length > this.max) {
378
+ errs.push({field: 'username', code: 'username', message: 'username must be less than ' + this.max + 'characters'});
379
+ }
380
+ }
381
+ if (!this.checkContact(u.contact)) {
382
+ errs.push({field: 'contact', code: 'contact', message: 'contact is not valid'});
383
+ }
384
+ if (this.passwordRequired) {
385
+ if (!isPassword(u.password)) {
386
+ errs.push({field: 'password', code: 'password', message: 'password is not valid'});
387
+ }
388
+ }
389
+ return errs;
390
+ }
391
+ }
392
+ export const SignupValidator = Validator;
393
+ export const UserRegistrationValidator = Validator;
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compileOnSave": true,
3
+ "compilerOptions": {
4
+ "target": "es5",
5
+ "baseUrl": "./src",
6
+ "outDir": "./lib",
7
+ "module": "commonjs",
8
+ "moduleResolution": "node",
9
+ "strict": true,
10
+ "pretty": true,
11
+ "sourceMap": false,
12
+ "declaration": false,
13
+ "experimentalDecorators": false,
14
+ "removeComments": true,
15
+ "lib": [
16
+ "es5",
17
+ "es6"
18
+ ]
19
+ },
20
+ "include": [
21
+ "src/**/*.ts"
22
+ ],
23
+ "exclude": [
24
+ "node_modules"
25
+ ]
26
+ }