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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26