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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 393 - (hide annotations)
Mon Jul 29 17:29:59 2024 UTC (8 months, 3 weeks ago) by rakin
File MIME type: application/typescript
File size: 11714 byte(s)
style: add license comments (#77)

* style: add license commits

* fix: shebang errors
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 352 import { dot, object } from "dot-object";
21     import { body } from "express-validator";
22 rakin 389 import { z as zod } from "zod";
23 rakin 352 import KeyValuePair from "../../types/KeyValuePair";
24 rakin 326 import Controller from "../Controller";
25 rakin 349 import RequireAuth from "../middleware/RequireAuth";
26 rakin 353 import ValidatorError from "../middleware/ValidatorError";
27 rakin 349 import Request from "../Request";
28 rakin 370 import merge from 'ts-deepmerge';
29 rakin 326
30     export default class ConfigController extends Controller {
31 rakin 349 globalMiddleware(): Function[] {
32 rakin 353 return [RequireAuth, ValidatorError];
33 rakin 349 }
34    
35 rakin 352 middleware(): KeyValuePair<Function[]> {
36     return {
37     update: [
38     body(["config"]).isObject()
39     ]
40     };
41     }
42    
43 rakin 379 private zodSchema(id: string) {
44 rakin 370 const snowflake = zod.string().regex(/\d+/, { message: "The given value is not a Snowflake" });
45 rakin 379 const config = this.client.config.props[id];
46 rakin 370
47     const schema = zod.object({
48     "prefix": zod.string().optional(),
49     "debug": zod.boolean().optional(),
50     "mute_role": snowflake.optional(),
51     "gen_role": snowflake.optional(),
52     "logging_channel": snowflake.optional(),
53     "logging_channel_join_leave": snowflake.optional(),
54     "mod_role": snowflake.optional(),
55     "announcement_channel": snowflake.optional(),
56     "admin": snowflake.optional(),
57     "lockall": zod.array(zod.string()).optional(),
58     "warn_notallowed": zod.boolean().optional(),
59     "role_commands": zod.record(
60     snowflake,
61     zod.array(zod.string().min(1))
62 rakin 379 ).optional().default({}),
63 rakin 370 "autoclear": zod.object({
64     "enabled": zod.boolean().optional(),
65 rakin 379 "channels": zod.array(snowflake).optional().default(config.autoclear.channels)
66 rakin 370 }).optional(),
67     "verification": zod.object({
68     "enabled": zod.boolean().optional(),
69     "role": snowflake.optional()
70     }).optional(),
71     "welcomer": zod.object({
72     "enabled": zod.boolean().optional(),
73     "channel": snowflake.optional(),
74     "message": zod.string().min(1).or(zod.null()).optional(),
75     "randomize": zod.boolean().optional()
76     }).optional(),
77     "cooldown": zod.object({
78     "enabled": zod.boolean().optional(),
79     "global": zod.any().optional(),
80     "cmds": zod.object({}).optional()
81     }).optional(),
82     "starboard": zod.object({
83     "enabled": zod.boolean().optional(),
84     "reactions": zod.number().int().optional(),
85     "channel": snowflake.optional()
86     }).optional(),
87     "autorole": zod.object({
88     "enabled": zod.boolean().optional(),
89 rakin 379 "roles": zod.array(snowflake).optional().default(config.autorole.roles)
90 rakin 370 }).optional(),
91     "spam_filter": zod.object({
92     "enabled": zod.boolean().optional(),
93     "limit": zod.number().int().optional(),
94     "time": zod.number().optional(),
95     "diff": zod.number().optional(),
96 rakin 379 "exclude": zod.array(snowflake).optional().default(config.spam_filter.exclude),
97 rakin 370 "samelimit": zod.number().int().optional(),
98     "unmute_in": zod.number().optional()
99     }).optional(),
100     "raid": zod.object({
101     "enabled": zod.boolean().optional(),
102     "max_joins": zod.number().int().optional(),
103     "time": zod.number().optional(),
104 rakin 379 "channels": zod.array(snowflake).optional().default(config.raid.channels),
105 rakin 370 "exclude": zod.boolean().optional()
106     }).optional(),
107 rakin 379 "global_commands": zod.array(zod.string()).optional().default(config.global_commands),
108 rakin 370 "filters": zod.object({
109     "ignore_staff": zod.boolean().optional(),
110     "chars_repeated": zod.number().int().optional(),
111     "words_repeated": zod.number().int().optional(),
112 rakin 379 "words": zod.array(zod.string()).optional().default(config.filters.words),
113     "tokens": zod.array(zod.string()).optional().default(config.filters.tokens),
114 rakin 370 "invite_message": zod.string().optional(),
115 rakin 379 "words_excluded": zod.array(snowflake).optional().default(config.filters.words_excluded),
116     "domain_excluded": zod.array(snowflake).optional().default(config.filters.domain_excluded),
117     "invite_excluded": zod.array(snowflake).optional().default(config.filters.invite_excluded),
118 rakin 370 "words_enabled": zod.boolean().optional(),
119     "invite_enabled": zod.boolean().optional(),
120     "domain_enabled": zod.boolean().optional(),
121     "regex": zod.boolean().optional(),
122 rakin 379 "file_mimes_excluded": zod.array(zod.string()).optional().default(config.filters.file_mimes_excluded),
123     "file_types_excluded": zod.array(zod.string()).optional().default(config.filters.file_types_excluded),
124     "domains": zod.array(zod.string()).optional().default(config.filters.domains),
125     "regex_patterns": zod.array(zod.string()).optional().default(config.filters.regex_patterns),
126 rakin 370 "rickrolls_enabled": zod.boolean().optional(),
127     "pings": zod.number().int().optional()
128     }).optional()
129     });
130    
131     return schema;
132     }
133    
134 rakin 326 public async index(request: Request) {
135 rakin 349 const { id } = request.params;
136    
137     if (!request.user?.guilds.includes(id)) {
138     return this.response({ error: "You don't have permission to access configuration of this guild." }, 403);
139     }
140    
141     if (id === "global" || !this.client.config.props[id]) {
142     return this.response({ error: "No configuration found for the given guild ID" }, 404);
143     }
144    
145     return this.client.config.props[id];
146 rakin 326 }
147    
148     public async update(request: Request) {
149 rakin 352 const { id } = request.params;
150 rakin 379 const { config: origconfig } = request.body;
151 rakin 368
152 rakin 379 console.log(origconfig);
153 rakin 368
154 rakin 370 try {
155 rakin 379 const currentConfigDotObject = this.client.config.props[id];
156    
157     console.log("Current config: ", currentConfigDotObject);
158    
159     const result = this.zodSchema(id).safeParse(object(origconfig));
160    
161     if (!result?.success) {
162     return this.response({ error: "The data schema does not match.", error_type: 'validation', errors: result.error.errors }, 422);
163     }
164    
165     const config = result.data;
166     const newConfig = merge.withOptions({ mergeArrays: false }, currentConfigDotObject, config);
167     const result2 = this.zodSchema(id).safeParse(newConfig);
168    
169     if (!result2?.success) {
170     console.log(result2.error.errors);
171     return this.response({ error: 'Internal Server Error (500)', error_type: 'internal' }, 500);
172     }
173    
174     console.log('Final', newConfig);
175     this.client.config.props[id] = newConfig;
176     this.client.config.write();
177     return { message: "Configuration updated", previous: dot(currentConfigDotObject), new: dot(this.client.config.props[id]) };
178     }
179     catch (e) {
180     console.log(e);
181     return this.response({ error: 'Internal Server Error', error_type: 'internal' }, 500);
182     }
183     }
184    
185     public async update2(request: Request) {
186     const { id } = request.params;
187     const { config: origconfig } = request.body;
188    
189     console.log(origconfig);
190    
191     try {
192 rakin 370 const currentConfigDotObject = dot(this.client.config.props[id]);
193     let newConfigDotObject = {...currentConfigDotObject};
194    
195 rakin 379 console.log("Current config: ", currentConfigDotObject);
196 rakin 352
197 rakin 379 const result = this.zodSchema(id).safeParse(object({...origconfig}));
198 rakin 370
199     if (!result?.success) {
200     return this.response({ error: "The data schema does not match.", error_type: 'validation', errors: result.error.errors }, 422);
201 rakin 352 }
202    
203 rakin 379 const config = result.data;
204    
205     console.log(config);
206    
207     for (const key in config) {
208     const configKey = key as keyof typeof config;
209 rakin 370 const regexMatched = /(.+)\[\d+\]/g.test(configKey);
210 rakin 368
211 rakin 370 if (typeof currentConfigDotObject[configKey] === 'undefined' && !regexMatched) {
212 rakin 379 console.log(configKey, config[configKey]);
213    
214     if (currentConfigDotObject[configKey] instanceof Array && config[configKey] instanceof Array) {
215     console.log('Array');
216     }
217     else
218     return this.response({ error: `The key '${configKey}' is not allowed` }, 422);
219 rakin 370 }
220 rakin 368
221 rakin 370 if (!regexMatched && config[configKey] !== null && config[configKey] !== null && typeof config[configKey] !== typeof currentConfigDotObject[configKey]) {
222     console.log(typeof config[configKey], typeof currentConfigDotObject[configKey]);
223    
224     if (typeof currentConfigDotObject[configKey] === 'number' && typeof config[configKey] === 'string') {
225 rakin 379 const int = parseInt(config[configKey]!.toString());
226 rakin 370
227     if (int !== NaN) {
228     newConfigDotObject[configKey] = int;
229     console.log("Updating: ", configKey, config[configKey], newConfigDotObject[configKey]);
230     continue;
231     }
232     }
233    
234     return this.response({ error: `The key '${configKey}' has incompatible value type '${config[configKey] === null ? 'null' : typeof config[configKey]}'` }, 422);
235 rakin 368 }
236 rakin 370
237     console.log("Updating: ", configKey, config[configKey], newConfigDotObject[configKey]);
238 rakin 352 }
239    
240 rakin 379 const newObj = object({...newConfigDotObject});
241     const configObj = object({...config});
242 rakin 352
243 rakin 379 // console.log("Newobj", newObj);
244     // console.log("Configobj", configObj);
245    
246     newConfigDotObject = merge.withOptions({ mergeArrays: false }, newObj, configObj);
247 rakin 370 console.log("Output: ", newConfigDotObject);
248 rakin 353
249 rakin 370 this.client.config.props[id] = newConfigDotObject;
250     this.client.config.write();
251 rakin 353
252 rakin 370 return { message: "Configuration updated", previous: currentConfigDotObject, new: dot(this.client.config.props[id]) };
253     }
254     catch (e) {
255     console.log(e);
256     return this.response({ error: 'Internal Server Error', error_type: 'internal' }, 500);
257     }
258 rakin 326 }
259     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26