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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 326 by rakin, Mon Jul 29 17:29:33 2024 UTC revision 395 by rakin, Mon Jul 29 17:30:01 2024 UTC
# Line 1  Line 1 
1  import { Request } from "express";  /**
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";  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 Response from "../Response";
28    import { NextFunction, Response as ExpressResponse } from "express";
29    import ValidatorError from "../middleware/ValidatorError";
30    import RequireAuth from "../middleware/RequireAuth";
31    
32    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  export default class UserController extends Controller {  export default class UserController extends Controller {
42      public async index(request: Request) {      middleware(): KeyValuePair<Function[]> {
43          return { message: "Server is up." };          return {
44                index: [RequireAuth, RequireAdmin],
45                create: [
46                    RequireAuth,
47                    RequireAdmin,
48                    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                ],
60                login: [
61                    body(["username", "password"]).isLength({ min: 2 }),
62                ],
63                delete: [
64                    RequireAuth,
65                    body(["username", "password"]).isLength({ min: 2 }),
66                ]
67            };
68        }
69    
70        globalMiddleware(): Function[] {
71            return [ValidatorError];
72        }
73    
74        public async index() {
75            return await User.find().select(["_id", "username", "createdAt"]).limit(30);
76        }
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            user.tokenUpdatedAt = new Date();
85    
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    
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                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            };
190      }      }
191  }  }

Legend:
Removed from v.326  
changed lines
  Added in v.395

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26