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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26