/[sudobot]/trunk/src/api/controllers/UserController.ts
ViewVC logotype

Annotation of /trunk/src/api/controllers/UserController.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 395 - (hide annotations)
Mon Jul 29 17:30:01 2024 UTC (8 months, 3 weeks ago) by rakin
File MIME type: application/typescript
File size: 5819 byte(s)
feat(api): allow admins to view/create users
1 rakin 393 /**
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 rakin 395 import Request from "../Request";
21 rakin 348 import User from "../../models/User";
22 rakin 326 import Controller from "../Controller";
23 rakin 349 import { body } from 'express-validator';
24 rakin 348 import bcrypt from 'bcrypt';
25     import jwt from 'jsonwebtoken';
26     import KeyValuePair from "../../types/KeyValuePair";
27     import Response from "../Response";
28 rakin 395 import { NextFunction, Response as ExpressResponse } from "express";
29 rakin 349 import ValidatorError from "../middleware/ValidatorError";
30     import RequireAuth from "../middleware/RequireAuth";
31 rakin 326
32 rakin 395 function RequireAdmin(request: Request, response: ExpressResponse, next: NextFunction) {
33     if (!request.user?.isAdmin) {
34     response.status(403).send({ error: "Forbidden", code: 403 });
35     return;
36     }
37    
38     next();
39     }
40    
41 rakin 326 export default class UserController extends Controller {
42 rakin 348 middleware(): KeyValuePair<Function[]> {
43     return {
44 rakin 395 index: [RequireAuth, RequireAdmin],
45 rakin 348 create: [
46 rakin 395 RequireAuth,
47     RequireAdmin,
48 rakin 348 body(["password"]).isLength({ min: 2 }),
49     body(["username"]).custom(async username => {
50     const user = await User.findOne({ username });
51    
52     if (user) {
53     return Promise.reject("Username is already in use");
54     }
55    
56     return username;
57     }),
58     body(["discord_id"]).custom(value => /\d+/g.test(value) ? value : Promise.reject("Invalid Snowflake Given"))
59 rakin 349 ],
60     login: [
61     body(["username", "password"]).isLength({ min: 2 }),
62     ],
63     delete: [
64     RequireAuth,
65     body(["username", "password"]).isLength({ min: 2 }),
66 rakin 348 ]
67     };
68 rakin 326 }
69 rakin 348
70 rakin 349 globalMiddleware(): Function[] {
71     return [ValidatorError];
72     }
73    
74 rakin 348 public async index() {
75 rakin 349 return await User.find().select(["_id", "username", "createdAt"]).limit(30);
76 rakin 348 }
77    
78     public async create(request: Request) {
79     const user = new User();
80    
81     user.username = request.body.username;
82     user.discord_id = request.body.discord_id;
83     user.createdAt = new Date();
84 rakin 349 user.tokenUpdatedAt = new Date();
85 rakin 348
86     try {
87     await user.save();
88     }
89     catch (e) {
90     return { error: "DB validation error", error_type: 'db_validation' };
91     }
92    
93     const salt = await bcrypt.genSalt();
94     user.password = await bcrypt.hash(request.body.password, salt);
95    
96     const token = await jwt.sign({
97     username: user.username,
98     discord_id: user.discord_id,
99     _id: user.id
100     }, process.env.JWT_SECRET!, {
101     expiresIn: "2 days",
102     issuer: "SudoBot API",
103     });
104    
105     user.token = token;
106    
107     try {
108     await user.save();
109     }
110     catch (e) {
111     return { error: "Token signing error", error_type: 'token_signing' };
112     }
113    
114     user.password = undefined;
115     return user;
116     }
117 rakin 349
118     public async delete(request: Request) {
119     const { username, password } = request.body;
120     const user = await User.findOne({ username });
121    
122     if (!user) {
123     return { error: "Username is incorrect." };
124     }
125    
126     if (!(await bcrypt.compare(password, user.password!))) {
127     return { error: "Password is incorrect." };
128     }
129    
130     await user.delete();
131    
132     user.password = undefined;
133     user.token = undefined;
134     user.tokenUpdatedAt = undefined;
135    
136     return {
137     message: "Account deletion successful",
138     user
139     };
140     }
141    
142     public async login(request: Request) {
143     const { username, password } = request.body;
144     const user = await User.findOne({ username });
145    
146     if (!user) {
147     return { error: "Username is incorrect." };
148     }
149    
150     if (!(await bcrypt.compare(password, user.password!))) {
151     return { error: "Password is incorrect." };
152     }
153    
154     let { token } = user;
155    
156     try {
157     if (!token) {
158     throw new Error("Token is not set");
159     }
160    
161     if (!jwt.verify(token, process.env.JWT_SECRET!)) {
162     throw new Error("Token is not valid");
163     }
164     }
165     catch (e) {
166     console.log(e);
167    
168     const newToken = await jwt.sign({
169     username: user.username,
170     discord_id: user.discord_id,
171     _id: user.id
172     }, process.env.JWT_SECRET!, {
173     expiresIn: "2 days",
174     issuer: "SudoBot API",
175     });
176    
177     token = newToken;
178     user.tokenUpdatedAt = new Date();
179     user.token = newToken;
180     await user.save();
181     }
182    
183     return {
184     message: "Login successful",
185     username,
186     token,
187 rakin 379 expires: new Date(user.tokenUpdatedAt!.getTime() + (2 * 24 * 60 * 60 * 1000)),
188     guilds: this.client.guilds.cache.filter(g => user.guilds.includes(g.id) ?? false)
189 rakin 349 };
190     }
191 rakin 326 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26