/[sudobot]/branches/4.x/src/api/controllers/UserController.ts
ViewVC logotype

Contents of /branches/4.x/src/api/controllers/UserController.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (show annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 6258 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 /**
2 * This file is part of SudoBot.
3 *
4 * Copyright (C) 2021-2022 OSN Inc.
5 *
6 * SudoBot is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * SudoBot is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with SudoBot. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 import Request from "../Request";
21 import User from "../../models/User";
22 import Controller from "../Controller";
23 import { body } from 'express-validator';
24 import bcrypt from 'bcrypt';
25 import jwt from 'jsonwebtoken';
26 import KeyValuePair from "../../types/KeyValuePair";
27 import { NextFunction, Response as ExpressResponse } from "express";
28 import ValidatorError from "../middleware/ValidatorError";
29 import RequireAuth from "../middleware/RequireAuth";
30 import { User as DiscordUser } from "discord.js";
31 import Response from "../Response";
32
33 function RequireAdmin(request: Request, response: ExpressResponse, next: NextFunction) {
34 if (!request.user?.isAdmin) {
35 response.status(403).send({ error: "Forbidden", code: 403 });
36 return;
37 }
38
39 next();
40 }
41
42 export default class UserController extends Controller {
43 middleware(): KeyValuePair<Function[]> {
44 return {
45 index: [RequireAuth, RequireAdmin],
46 create: [
47 RequireAuth,
48 RequireAdmin,
49 body(["password"]).isLength({ min: 2 }),
50 body(["username"]).custom(async username => {
51 const user = await User.findOne({ username });
52
53 if (user) {
54 return Promise.reject("Username is already in use");
55 }
56
57 return username;
58 }),
59 body(["discord_id"]).custom(value => /\d+/g.test(value) ? value : Promise.reject("Invalid Snowflake Given"))
60 ],
61 login: [
62 body(["username", "password"]).isLength({ min: 2 }),
63 ],
64 delete: [
65 RequireAuth,
66 body(["username", "password"]).isLength({ min: 2 }),
67 ]
68 };
69 }
70
71 globalMiddleware(): Function[] {
72 return [ValidatorError];
73 }
74
75 public async index() {
76 return await User.find().select(["_id", "username", "createdAt"]).limit(30);
77 }
78
79 public async create(request: Request) {
80 const user = new User();
81
82 user.username = request.body.username;
83 user.discord_id = request.body.discord_id;
84 user.createdAt = new Date();
85 user.tokenUpdatedAt = new Date();
86
87 try {
88 await user.save();
89 }
90 catch (e) {
91 return { error: "DB validation error", error_type: 'db_validation' };
92 }
93
94 const salt = await bcrypt.genSalt();
95 user.password = await bcrypt.hash(request.body.password, salt);
96
97 const token = await jwt.sign({
98 username: user.username,
99 discord_id: user.discord_id,
100 _id: user.id
101 }, process.env.JWT_SECRET!, {
102 expiresIn: "2 days",
103 issuer: "SudoBot API",
104 });
105
106 user.token = token;
107
108 try {
109 await user.save();
110 }
111 catch (e) {
112 return { error: "Token signing error", error_type: 'token_signing' };
113 }
114
115 user.password = undefined;
116 return user;
117 }
118
119 public async delete(request: Request) {
120 const { username, password } = request.body;
121 const user = await User.findOne({ username });
122
123 if (!user) {
124 return { error: "Username is incorrect." };
125 }
126
127 if (!(await bcrypt.compare(password, user.password!))) {
128 return { error: "Password is incorrect." };
129 }
130
131 await user.delete();
132
133 user.password = undefined;
134 user.token = undefined;
135 user.tokenUpdatedAt = undefined;
136
137 return {
138 message: "Account deletion successful",
139 user
140 };
141 }
142
143 public async login(request: Request) {
144 const { username, password } = request.body;
145 const user = await User.findOne({ username });
146
147 if (!user) {
148 return new Response(400, { error: "Username or password is incorrect." });
149 }
150
151 if (!(await bcrypt.compare(password, user.password!))) {
152 return new Response(401, { error: "Username or password is incorrect." });
153 }
154
155 let { token } = user;
156
157 try {
158 if (!token) {
159 throw new Error("Token is not set");
160 }
161
162 if (!jwt.verify(token, process.env.JWT_SECRET!)) {
163 throw new Error("Token is not valid");
164 }
165 }
166 catch (e) {
167 console.log(e);
168
169 const newToken = await jwt.sign({
170 username: user.username,
171 discord_id: user.discord_id,
172 _id: user.id
173 }, process.env.JWT_SECRET!, {
174 expiresIn: "2 days",
175 issuer: "SudoBot API",
176 });
177
178 token = newToken;
179 user.tokenUpdatedAt = new Date();
180 user.token = newToken;
181 await user.save();
182 }
183
184 let discordUser: DiscordUser | undefined;
185
186 try {
187 discordUser = await this.client.users.fetch(user.discord_id);
188 }
189 catch (e) {
190 console.log(e);
191 }
192
193 console.log(this.client.guilds.cache.map(g => g.id));
194 console.log(user.guilds);
195
196 return {
197 message: "Login successful",
198 username,
199 token,
200 user: discordUser,
201 expires: new Date(user.tokenUpdatedAt!.getTime() + (2 * 24 * 60 * 60 * 1000)),
202 guilds: this.client.guilds.cache.filter(g => user.guilds.includes(g.id))
203 };
204 }
205 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26