-
Notifications
You must be signed in to change notification settings - Fork 33
/
user.js
133 lines (91 loc) · 3.26 KB
/
user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
'use strict';
const Util = require('util');
const Boom = require('@hapi/boom');
const Bounce = require('@hapi/bounce');
const Jwt = require('@hapi/jwt');
const Schmervice = require('@hapipal/schmervice');
const { UniqueViolationError } = require('objection');
const SecurePassword = require('secure-password');
module.exports = class UserService extends Schmervice.Service {
constructor(...args) {
super(...args);
const pwd = new SecurePassword();
this.pwd = {
hash: Util.promisify(pwd.hash.bind(pwd)),
verify: Util.promisify(pwd.verify.bind(pwd))
};
}
async findById(id, txn) {
const { User } = this.server.models();
return await User.query(txn).throwIfNotFound().findById(id);
}
async findByUsername(username, txn) {
const { User } = this.server.models();
return await User.query(txn).throwIfNotFound().first().where({ username });
}
async follow(currentUserId, id, txn) {
const { User } = this.server.models();
if (currentUserId === id) {
throw Boom.forbidden();
}
try {
await User.relatedQuery('following', txn).for(currentUserId).relate(id);
}
catch (err) {
Bounce.ignore(err, UniqueViolationError);
}
}
async unfollow(currentUserId, id, txn) {
const { User } = this.server.models();
if (currentUserId === id) {
throw Boom.forbidden();
}
await User.relatedQuery('following', txn).for(currentUserId)
.unrelate().where({ id });
}
async signup({ password, ...userInfo }, txn) {
const { User } = this.server.models();
const { id } = await User.query(txn).insert(userInfo);
await this.changePassword(id, password, txn);
return id;
}
async update(id, { password, ...userInfo }, txn) {
const { User } = this.server.models();
if (Object.keys(userInfo).length > 0) {
await User.query(txn).throwIfNotFound().where({ id }).patch(userInfo);
}
if (password) {
await this.changePassword(id, password, txn);
}
return id;
}
async login({ email, password }, txn) {
const { User } = this.server.models();
const user = await User.query(txn).throwIfNotFound().first().where({
email: User.raw('? collate nocase', email)
});
const passwordCheck = await this.pwd.verify(Buffer.from(password), user.password);
if (passwordCheck === SecurePassword.VALID_NEEDS_REHASH) {
await this.changePassword(user.id, password, txn);
}
else if (passwordCheck !== SecurePassword.VALID) {
throw User.createNotFoundError();
}
return user;
}
createToken(id) {
return Jwt.token.generate({ id }, {
key: this.options.jwtKey,
algorithm: 'HS256'
}, {
ttlSec: 7 * 24 * 60 * 60 // 7 days
});
}
async changePassword(id, password, txn) {
const { User } = this.server.models();
await User.query(txn).throwIfNotFound().where({ id }).patch({
password: await this.pwd.hash(Buffer.from(password))
});
return id;
}
};