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

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

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

revision 368 by rakin, Mon Jul 29 17:29:49 2024 UTC revision 370 by rakin, Mon Jul 29 17:29:50 2024 UTC
# Line 1  Line 1 
1  import { dot, object } from "dot-object";  import { dot, object } from "dot-object";
2  import { body } from "express-validator";  import { body } from "express-validator";
3    import { z as zod, ZodSchema } from "zod";
4  import KeyValuePair from "../../types/KeyValuePair";  import KeyValuePair from "../../types/KeyValuePair";
5  import Controller from "../Controller";  import Controller from "../Controller";
6  import RequireAuth from "../middleware/RequireAuth";  import RequireAuth from "../middleware/RequireAuth";
7  import ValidatorError from "../middleware/ValidatorError";  import ValidatorError from "../middleware/ValidatorError";
8  import Request from "../Request";  import Request from "../Request";
9    import merge from 'ts-deepmerge';
10    
11  export default class ConfigController extends Controller {  export default class ConfigController extends Controller {
12      globalMiddleware(): Function[] {      globalMiddleware(): Function[] {
# Line 19  export default class ConfigController ex Line 21  export default class ConfigController ex
21          };          };
22      }      }
23    
24        private zodSchema() {
25            const snowflake = zod.string().regex(/\d+/, { message: "The given value is not a Snowflake" });
26    
27            const schema = zod.object({
28                "prefix": zod.string().optional(),
29                "debug": zod.boolean().optional(),
30                "mute_role": snowflake.optional(),
31                "gen_role": snowflake.optional(),
32                "logging_channel": snowflake.optional(),
33                "logging_channel_join_leave": snowflake.optional(),
34                "mod_role": snowflake.optional(),
35                "announcement_channel": snowflake.optional(),
36                "admin": snowflake.optional(),
37                "lockall": zod.array(zod.string()).optional(),
38                "warn_notallowed": zod.boolean().optional(),
39                "role_commands": zod.record(
40                    snowflake,
41                    zod.array(zod.string().min(1))
42                ).optional(),
43                "autoclear": zod.object({
44                    "enabled": zod.boolean().optional(),
45                    "channels": zod.array(snowflake).optional()
46                }).optional(),
47                "verification": zod.object({
48                    "enabled": zod.boolean().optional(),
49                    "role": snowflake.optional()
50                }).optional(),
51                "welcomer": zod.object({
52                    "enabled": zod.boolean().optional(),
53                    "channel": snowflake.optional(),
54                    "message": zod.string().min(1).or(zod.null()).optional(),
55                    "randomize": zod.boolean().optional()
56                }).optional(),
57                "cooldown": zod.object({
58                    "enabled": zod.boolean().optional(),
59                    "global": zod.any().optional(),
60                    "cmds": zod.object({}).optional()
61                }).optional(),
62                "starboard": zod.object({
63                    "enabled": zod.boolean().optional(),
64                    "reactions": zod.number().int().optional(),
65                    "channel": snowflake.optional()
66                }).optional(),
67                "autorole": zod.object({
68                    "enabled": zod.boolean().optional(),
69                    "roles": zod.array(snowflake).optional()
70                }).optional(),
71                "spam_filter": zod.object({
72                    "enabled": zod.boolean().optional(),
73                    "limit": zod.number().int().optional(),
74                    "time": zod.number().optional(),
75                    "diff": zod.number().optional(),
76                    "exclude": zod.array(snowflake).optional(),
77                    "samelimit": zod.number().int().optional(),
78                    "unmute_in": zod.number().optional()
79                }).optional(),
80                "raid": zod.object({
81                    "enabled": zod.boolean().optional(),
82                    "max_joins": zod.number().int().optional(),
83                    "time": zod.number().optional(),
84                    "channels": zod.array(snowflake).optional(),
85                    "exclude": zod.boolean().optional()
86                }).optional(),
87                "global_commands": zod.array(zod.string()).optional(),
88                "filters": zod.object({
89                    "ignore_staff": zod.boolean().optional(),
90                    "chars_repeated": zod.number().int().optional(),
91                    "words_repeated": zod.number().int().optional(),
92                    "words": zod.array(zod.string()).optional(),
93                    "tokens": zod.array(zod.string()).optional(),
94                    "invite_message": zod.string().optional(),
95                    "words_excluded": zod.array(snowflake).optional(),
96                    "domain_excluded": zod.array(snowflake).optional(),
97                    "invite_excluded": zod.array(snowflake).optional(),
98                    "words_enabled": zod.boolean().optional(),
99                    "invite_enabled": zod.boolean().optional(),
100                    "domain_enabled": zod.boolean().optional(),
101                    "regex": zod.boolean().optional(),
102                    "file_mimes_excluded": zod.array(zod.string()).optional(),
103                    "file_types_excluded": zod.array(zod.string()).optional(),
104                    "domains": zod.array(zod.string()).optional(),
105                    "regex_patterns": zod.array(zod.string()).optional(),
106                    "rickrolls_enabled": zod.boolean().optional(),
107                    "pings": zod.number().int().optional()
108                }).optional()
109            });
110    
111            return schema;
112        }
113    
114      public async index(request: Request) {      public async index(request: Request) {
115          const { id } = request.params;          const { id } = request.params;
116    
# Line 39  export default class ConfigController ex Line 131  export default class ConfigController ex
131    
132          console.log(config);                  console.log(config);        
133    
134          const currentConfigDotObject = dot(this.client.config.props[id]);          try {
135          const newConfigDotObject = {...currentConfigDotObject};              const currentConfigDotObject = dot(this.client.config.props[id]);
136                let newConfigDotObject = {...currentConfigDotObject};
137          console.log("Input: ", config);      
138                console.log("Input: ", config);
139          for (const configKey in config) {  
140              if (typeof currentConfigDotObject[configKey] === 'undefined') {              const result = this.zodSchema().safeParse(object({...config}));
141                  return this.response({ error: `The key '${configKey}' is not allowed` }, 422);  
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              if (config[configKey] !== null && config[configKey] !== null && typeof config[configKey] !== typeof currentConfigDotObject[configKey]) {              for (const configKey in config) {
147                  console.log(typeof config[configKey], typeof currentConfigDotObject[configKey]);                      const regexMatched = /(.+)\[\d+\]/g.test(configKey);
148                                    
149                  if (typeof currentConfigDotObject[configKey] === 'number' && typeof config[configKey] === 'string') {                  if (typeof currentConfigDotObject[configKey] === 'undefined' && !regexMatched) {
150                      const int = parseInt(config[configKey]);                      return this.response({ error: `The key '${configKey}' is not allowed` }, 422);
151                    }
152    
153                      if (int !== NaN) {                  if (!regexMatched && config[configKey] !== null && config[configKey] !== null && typeof config[configKey] !== typeof currentConfigDotObject[configKey]) {
154                          newConfigDotObject[configKey] = int;                      console.log(typeof config[configKey], typeof currentConfigDotObject[configKey]);    
155                          console.log("Updating: ", configKey, config[configKey], newConfigDotObject[configKey]);                      
156                          continue;                      if (typeof currentConfigDotObject[configKey] === 'number' && typeof config[configKey] === 'string') {
157                      }                          const int = parseInt(config[configKey]);
158    
159                            if (int !== NaN) {
160                                newConfigDotObject[configKey] = int;
161                                console.log("Updating: ", configKey, config[configKey], newConfigDotObject[configKey]);
162                                continue;
163                            }
164                        }
165                        
166                        return this.response({ error: `The key '${configKey}' has incompatible value type '${config[configKey] === null ? 'null' : typeof config[configKey]}'` }, 422);
167                  }                  }
168                    
169                  return this.response({ error: `The key '${configKey}' has incompatible value type '${config[configKey] === null ? 'null' : typeof config[configKey]}'` }, 422);                  console.log("Updating: ", configKey, config[configKey], newConfigDotObject[configKey]);
170              }              }
171    
             newConfigDotObject[configKey] = config[configKey];  
             console.log("Updating: ", configKey, config[configKey], newConfigDotObject[configKey]);  
         }  
172    
173          console.log("Output: ", newConfigDotObject);              newConfigDotObject = merge.withOptions({ mergeArrays: false }, object({...newConfigDotObject}), object({...config}));
174                console.log("Output: ", newConfigDotObject);
175    
176          this.client.config.props[id] = object({...newConfigDotObject});              this.client.config.props[id] = newConfigDotObject;
177          this.client.config.write();              this.client.config.write();
178    
179          return { message: "Configuration updated", previous: currentConfigDotObject, new: newConfigDotObject };              return { message: "Configuration updated", previous: currentConfigDotObject, new: dot(this.client.config.props[id]) };
180            }
181            catch (e) {
182                console.log(e);
183                return this.response({ error: 'Internal Server Error', error_type: 'internal' }, 500);
184            }
185      }      }
186  }  }

Legend:
Removed from v.368  
changed lines
  Added in v.370

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26