1 |
import { Request } from "express"; |
2 |
import User from "../../models/User"; |
3 |
import Controller from "../Controller"; |
4 |
import { body } from 'express-validator'; |
5 |
import bcrypt from 'bcrypt'; |
6 |
import jwt from 'jsonwebtoken'; |
7 |
import KeyValuePair from "../../types/KeyValuePair"; |
8 |
import Response from "../Response"; |
9 |
import ValidatorError from "../middleware/ValidatorError"; |
10 |
import RequireAuth from "../middleware/RequireAuth"; |
11 |
|
12 |
export default class UserController extends Controller { |
13 |
middleware(): KeyValuePair<Function[]> { |
14 |
return { |
15 |
create: [ |
16 |
body(["password"]).isLength({ min: 2 }), |
17 |
body(["username"]).custom(async username => { |
18 |
const user = await User.findOne({ username }); |
19 |
|
20 |
if (user) { |
21 |
return Promise.reject("Username is already in use"); |
22 |
} |
23 |
|
24 |
return username; |
25 |
}), |
26 |
body(["discord_id"]).custom(value => /\d+/g.test(value) ? value : Promise.reject("Invalid Snowflake Given")) |
27 |
], |
28 |
login: [ |
29 |
body(["username", "password"]).isLength({ min: 2 }), |
30 |
], |
31 |
delete: [ |
32 |
RequireAuth, |
33 |
body(["username", "password"]).isLength({ min: 2 }), |
34 |
] |
35 |
}; |
36 |
} |
37 |
|
38 |
globalMiddleware(): Function[] { |
39 |
return [ValidatorError]; |
40 |
} |
41 |
|
42 |
public async index() { |
43 |
return new Response(403); |
44 |
return await User.find().select(["_id", "username", "createdAt"]).limit(30); |
45 |
} |
46 |
|
47 |
public async create(request: Request) { |
48 |
return new Response(403); |
49 |
|
50 |
const user = new User(); |
51 |
|
52 |
user.username = request.body.username; |
53 |
user.discord_id = request.body.discord_id; |
54 |
user.createdAt = new Date(); |
55 |
user.tokenUpdatedAt = new Date(); |
56 |
|
57 |
try { |
58 |
await user.save(); |
59 |
} |
60 |
catch (e) { |
61 |
return { error: "DB validation error", error_type: 'db_validation' }; |
62 |
} |
63 |
|
64 |
const salt = await bcrypt.genSalt(); |
65 |
user.password = await bcrypt.hash(request.body.password, salt); |
66 |
|
67 |
const token = await jwt.sign({ |
68 |
username: user.username, |
69 |
discord_id: user.discord_id, |
70 |
_id: user.id |
71 |
}, process.env.JWT_SECRET!, { |
72 |
expiresIn: "2 days", |
73 |
issuer: "SudoBot API", |
74 |
}); |
75 |
|
76 |
user.token = token; |
77 |
|
78 |
try { |
79 |
await user.save(); |
80 |
} |
81 |
catch (e) { |
82 |
return { error: "Token signing error", error_type: 'token_signing' }; |
83 |
} |
84 |
|
85 |
user.password = undefined; |
86 |
return user; |
87 |
} |
88 |
|
89 |
public async delete(request: Request) { |
90 |
const { username, password } = request.body; |
91 |
const user = await User.findOne({ username }); |
92 |
|
93 |
if (!user) { |
94 |
return { error: "Username is incorrect." }; |
95 |
} |
96 |
|
97 |
if (!(await bcrypt.compare(password, user.password!))) { |
98 |
return { error: "Password is incorrect." }; |
99 |
} |
100 |
|
101 |
await user.delete(); |
102 |
|
103 |
user.password = undefined; |
104 |
user.token = undefined; |
105 |
user.tokenUpdatedAt = undefined; |
106 |
|
107 |
return { |
108 |
message: "Account deletion successful", |
109 |
user |
110 |
}; |
111 |
} |
112 |
|
113 |
public async login(request: Request) { |
114 |
const { username, password } = request.body; |
115 |
const user = await User.findOne({ username }); |
116 |
|
117 |
if (!user) { |
118 |
return { error: "Username is incorrect." }; |
119 |
} |
120 |
|
121 |
if (!(await bcrypt.compare(password, user.password!))) { |
122 |
return { error: "Password is incorrect." }; |
123 |
} |
124 |
|
125 |
let { token } = user; |
126 |
|
127 |
try { |
128 |
if (!token) { |
129 |
throw new Error("Token is not set"); |
130 |
} |
131 |
|
132 |
if (!jwt.verify(token, process.env.JWT_SECRET!)) { |
133 |
throw new Error("Token is not valid"); |
134 |
} |
135 |
} |
136 |
catch (e) { |
137 |
console.log(e); |
138 |
|
139 |
const newToken = await jwt.sign({ |
140 |
username: user.username, |
141 |
discord_id: user.discord_id, |
142 |
_id: user.id |
143 |
}, process.env.JWT_SECRET!, { |
144 |
expiresIn: "2 days", |
145 |
issuer: "SudoBot API", |
146 |
}); |
147 |
|
148 |
token = newToken; |
149 |
user.tokenUpdatedAt = new Date(); |
150 |
user.token = newToken; |
151 |
await user.save(); |
152 |
} |
153 |
|
154 |
return { |
155 |
message: "Login successful", |
156 |
username, |
157 |
token, |
158 |
expires: new Date(user.tokenUpdatedAt!.getTime() + (2 * 24 * 60 * 60 * 1000)), |
159 |
guilds: this.client.guilds.cache.filter(g => user.guilds.includes(g.id) ?? false) |
160 |
}; |
161 |
} |
162 |
} |