TheRealToxicDev a révisé ce gist . Aller à la révision
1 file changed, 0 insertions, 0 deletions
gistfile1.txt renommé en gistfile1.ts
Fichier renommé sans modifications
TheRealToxicDev a révisé ce gist . Aller à la révision
1 file changed, 301 insertions
gistfile1.txt(fichier créé)
@@ -0,0 +1,301 @@ | |||
1 | + | import { Logger } from "../log.client"; | |
2 | + | import * as DTypes from '../../types/db.types'; | |
3 | + | import { IInstance, IInstanceClient, InstanceInfo } from "../../types/instance"; | |
4 | + | import { InstanceErrors } from '../../types/err.types'; | |
5 | + | import { CordXError } from "../error.client"; | |
6 | + | ||
7 | + | export class InstanceClient implements IInstanceClient { | |
8 | + | private static instances: Map<string, InstanceClient> = new Map(); | |
9 | + | private errors: typeof InstanceErrors = InstanceErrors; | |
10 | + | private idleTimeoutDuration: number = 1000 * 60 * 20; | |
11 | + | private warningInterval: number = 1000 * 60 * 5; | |
12 | + | public instance: IInstance; | |
13 | + | ||
14 | + | private constructor(instance: any, name: string) { | |
15 | + | this.instance = { | |
16 | + | ...instance, | |
17 | + | id: InstanceClient.generateId(), | |
18 | + | name: name, | |
19 | + | createdAt: new Date(), | |
20 | + | isActive: true, | |
21 | + | isIdle: false, | |
22 | + | lastUsed: new Date().toLocaleTimeString(), | |
23 | + | idleTimeout: null, | |
24 | + | idleStart: null, | |
25 | + | logs: Logger.getInstance(name, false), | |
26 | + | create: this.createInstance, | |
27 | + | destroy: this.destroyInstance | |
28 | + | } | |
29 | + | } | |
30 | + | ||
31 | + | private static generateId(): string { | |
32 | + | let id: string; | |
33 | + | do { | |
34 | + | id = Math.floor(Math.random() * 9 + 1).toString(); | |
35 | + | for (let i = 0; i < 17; i++) { | |
36 | + | id += Math.floor(Math.random() * 10).toString(); | |
37 | + | } | |
38 | + | } while (InstanceClient.instances.has(id)); | |
39 | + | return id; | |
40 | + | } | |
41 | + | ||
42 | + | /** | |
43 | + | * @function setIdleState | |
44 | + | * @description Sets the instance to idle state. | |
45 | + | * @returns {void} | |
46 | + | * @memberof InstanceClient | |
47 | + | * @private internal use only | |
48 | + | * @memberof InstanceClient | |
49 | + | */ | |
50 | + | private async setIdleState(): Promise<void> { | |
51 | + | this.instance.isActive = false; | |
52 | + | this.instance.isIdle = true; | |
53 | + | this.instance.idleStart = Date.now(); | |
54 | + | await this.monitorState(this.instance.name); | |
55 | + | ||
56 | + | return this.instance.logs.trace(this.errors['INSTANCE_IDLE']({ | |
57 | + | message: `Instance: ${this.instance.name}(${this.instance.id}) is now circulating in the idle pool.`, | |
58 | + | details: [ | |
59 | + | `- This instance will be destroyed in ${this.idleTimeoutDuration / 1000 / 60} minutes.`, | |
60 | + | `- If you wish to keep this instance alive, please perform an operation on it to reset its active state.` | |
61 | + | ] | |
62 | + | })) | |
63 | + | } | |
64 | + | ||
65 | + | /** | |
66 | + | * @function setActiveState | |
67 | + | * @description Sets the instance to active state. | |
68 | + | * @returns {void} | |
69 | + | * @memberof InstanceClient | |
70 | + | * @private internal use only | |
71 | + | * @memberof InstanceClient | |
72 | + | */ | |
73 | + | private setActiveState(): void { | |
74 | + | this.instance.isActive = true; | |
75 | + | this.instance.idleStart = null; | |
76 | + | this.instance.lastUsed = new Date(); | |
77 | + | await this.monitorState(this.instance.name); | |
78 | + | ||
79 | + | if (this.instance.idleTimeout) { | |
80 | + | clearTimeout(this.instance.idleTimeout); | |
81 | + | this.instance.idleTimeout = null; | |
82 | + | this.instance.isIdle = false; | |
83 | + | this.instance.idleStart = null; | |
84 | + | } | |
85 | + | } | |
86 | + | ||
87 | + | private monitorState(): void { | |
88 | + | ||
89 | + | const instance = this.instance | |
90 | + | ||
91 | + | if (!instance) return; | |
92 | + | ||
93 | + | if (instance.idleTimeout) { | |
94 | + | clearInterval(instance.idleTimeout); | |
95 | + | instance.idleTimeout = null; | |
96 | + | instance.isIdle = false; | |
97 | + | instance.idleStart = null; | |
98 | + | } | |
99 | + | ||
100 | + | let count = 0; | |
101 | + | const max = instance.idleTimeoutDuration / instance.warningInterval; | |
102 | + | ||
103 | + | const logMessage = (idleDuration: number, fatal?: boolean = false) => { | |
104 | + | const method = fatal ? instance.logs.fatal : instance.logs.trace; | |
105 | + | method.call(instance.logs, this.errors['INSTANCE_MONITOR']({ | |
106 | + | message: `Instance: ${instance.name}(${instance.id}) has been idle for ${idleDuration / 1000 / 60} minutes.`, | |
107 | + | details: fatal ? [ | |
108 | + | `- This instance has been idle for way too long and will now be destroyed to prevent memory leaks/overloads.` | |
109 | + | ] : [ | |
110 | + | `- This instance will be destroyed in ${instance.idleTimeoutDuration / 1000 / 60} minutes.`, | |
111 | + | `- If you wish to keep this instance alive, please perform an operation on it to reset its active state.` | |
112 | + | ] | |
113 | + | })) | |
114 | + | } | |
115 | + | ||
116 | + | const warnAndClose = () => { | |
117 | + | if (instance.lastUsed) { | |
118 | + | const currentTime = new Date().getTime(); | |
119 | + | const lastUsedTime = instance.lastUsed ? instance.lastUsed.getTime() : currentTime; | |
120 | + | const idleDuration = currentTime - lastUsedTime; | |
121 | + | if (idleDuration < instance.idleTimeoutDuration) { | |
122 | + | logMessage(idleDuration); | |
123 | + | count++; | |
124 | + | if (count >= max) { | |
125 | + | logMessage(idleDuration, true); | |
126 | + | instance.destroy(); | |
127 | + | } | |
128 | + | } else { | |
129 | + | logMessage(idleDuration, true); | |
130 | + | instance.destroy(); | |
131 | + | } | |
132 | + | } | |
133 | + | } | |
134 | + | ||
135 | + | instance.idleTimeout = setInterval(warnAndClose, instance.warningInterval); | |
136 | + | } | |
137 | + | ||
138 | + | ||
139 | + | public createInstance(instance: InstanceClient, name: string): InstanceClient | CordXError { | |
140 | + | let exists = InstanceClient.instances.get(name); | |
141 | + | ||
142 | + | if (exists) return exists.instance.getInstance().catch((err: Error) => { | |
143 | + | let error = instance.errors['INSTANCE_CREATION_FAILED']({}); | |
144 | + | let { status, message = err.stack } = error; | |
145 | + | ||
146 | + | throw this.instance.logs.fatal({ status, message }); | |
147 | + | }) | |
148 | + | ||
149 | + | exists = new InstanceClient(instance, name); | |
150 | + | InstanceClient.instances.set(name, exists); | |
151 | + | ||
152 | + | return exists; | |
153 | + | } | |
154 | + | ||
155 | + | public destroyInstance(): boolean { | |
156 | + | const instance = this.instance; | |
157 | + | ||
158 | + | if (instance) { | |
159 | + | try { | |
160 | + | if (instance.idleTimeout) { | |
161 | + | clearInterval(instance.idleTimeout); | |
162 | + | instance.idleTimeout = null; | |
163 | + | } | |
164 | + | InstanceClient.instances.delete(instance.name); | |
165 | + | instance.logs.fatal(this.errors['INSTANCE_DESTROYED']({ | |
166 | + | message: `Instance: ${instance.name}(${instance.id}) has been destroyed.`, | |
167 | + | })) | |
168 | + | return true; | |
169 | + | } catch (error: Error) { | |
170 | + | if (error instanceof CordXError) { | |
171 | + | instance.logs.trace(this.errors['INSTANCE_DESTROY_FAILED']({ | |
172 | + | message: `Failed to destroy instance: ${instance.name}(${instance.id}).`, | |
173 | + | details: [ | |
174 | + | `- ${error.stack}` | |
175 | + | ] | |
176 | + | ||
177 | + | })); | |
178 | + | return false | |
179 | + | } else { | |
180 | + | instance.logs.trace(this.errors['INSTANCE_DESTROY_FAILED']({ | |
181 | + | message: `Failed to destroy instance: ${instance.name}(${instance.id}).`, | |
182 | + | details: [ | |
183 | + | `- ${error.stack}` | |
184 | + | ] | |
185 | + | })) | |
186 | + | return false; | |
187 | + | } | |
188 | + | } | |
189 | + | } | |
190 | + | ||
191 | + | this.errors['INSTANCE_NOT_FOUND']({ | |
192 | + | message: `That instance does not exist, or has already been destroyed.`, | |
193 | + | }) | |
194 | + | ||
195 | + | return false; | |
196 | + | } | |
197 | + | ||
198 | + | public static findInstance(name: string): any { | |
199 | + | const instance = InstanceClient.instances.get(name); | |
200 | + | ||
201 | + | if (!instance) return { | |
202 | + | success: false, | |
203 | + | message: `Instance: ${name} does not exist.` | |
204 | + | } | |
205 | + | ||
206 | + | return { | |
207 | + | success: true, | |
208 | + | instance: { | |
209 | + | id: instance.id, | |
210 | + | name: instance.name, | |
211 | + | created: instance.createdAt, | |
212 | + | active: instance.isActive, | |
213 | + | idle: instance.isIdle, | |
214 | + | } | |
215 | + | } | |
216 | + | } | |
217 | + | ||
218 | + | public static viewInstance(name: string): any { | |
219 | + | const instance = InstanceClient.instances.get(name); | |
220 | + | ||
221 | + | if (!instance) return { | |
222 | + | success: false, | |
223 | + | message: `Instance: ${name} does not exist.` | |
224 | + | } | |
225 | + | ||
226 | + | return { | |
227 | + | success: true, | |
228 | + | instance: { | |
229 | + | id: instance.id, | |
230 | + | name: instance.name, | |
231 | + | created: instance.createdAt, | |
232 | + | active: instance.isActive, | |
233 | + | idle: instance.isIdle, | |
234 | + | } | |
235 | + | } | |
236 | + | } | |
237 | + | ||
238 | + | /** | |
239 | + | * @function getInstance | |
240 | + | * @description Returns an instance of the specified name. If the instance does not exist, it will be created. | |
241 | + | * @param {InstanceClient} instance The instance to get/create. (usually a client or class). | |
242 | + | * @param {string} name The name of the instance to get/create (should be short and sweet). | |
243 | + | * @returns {InstanceClient | CordXError} Returns the instance of a client/class or errors out. | |
244 | + | * @memberof InstanceClient | |
245 | + | * @example | |
246 | + | * const instance = InstanceClient.getInstance(MongoClient, 'MongoDB'); | |
247 | + | * if (instance instanceof CordXError) return console.error(instance.message); | |
248 | + | * console.log(instance); | |
249 | + | */ | |
250 | + | private getInstance(): InstanceClient | CordXError { | |
251 | + | let exists = InstanceClient.instances.get(this.instance.name); | |
252 | + | ||
253 | + | if (!exists) exists = this.createInstance(this.instance, this.instance.name); | |
254 | + | if (!exists) return this.errors['INSTANCE_CREATION_FAILED']({ | |
255 | + | details: [ | |
256 | + | `- Failed to create instance: ${name}.`, | |
257 | + | `- Please check the logs for more information.`, | |
258 | + | `- If this issue persists, please contact Toxic Dev.` | |
259 | + | ] | |
260 | + | }) | |
261 | + | ||
262 | + | return exists; | |
263 | + | } | |
264 | + | ||
265 | + | public static getAllInstances(): InstanceInfo[] { | |
266 | + | return Array.from(InstanceClient.instances.values()).map(i => ({ | |
267 | + | id: i.instance.id, | |
268 | + | name: i.instance.name, | |
269 | + | created: i.instance.createdAt, | |
270 | + | active: i.instance.isActive, | |
271 | + | idle: i.instance.isIdle, | |
272 | + | })) | |
273 | + | } | |
274 | + | ||
275 | + | public static destroyAllInstances(): void { | |
276 | + | InstanceClient.instances.forEach((instance: InstanceClient) => { | |
277 | + | InstanceClient.destroyInstance(instance.name); | |
278 | + | }) | |
279 | + | } | |
280 | + | ||
281 | + | public static getInstanceCount(): number { | |
282 | + | return InstanceClient.instances.size; | |
283 | + | } | |
284 | + | ||
285 | + | public performOperation(operation: string): void { | |
286 | + | const instance = InstanceClient.instances.get(this.instance.name); | |
287 | + | ||
288 | + | if (!instance) return this.errors. | |
289 | + | ||
290 | + | if(this.instance) { | |
291 | + | instance.lastUsed = Date.now(); | |
292 | + | InstanceClient.monitorState(name); | |
293 | + | instance.logs.info(`Instance: ${instance.name}(${instance.id}) is performing operation: ${operation}`) | |
294 | + | if (instance.isIdle) { | |
295 | + | InstanceClient.setActiveState(name); | |
296 | + | } else { | |
297 | + | InstanceClient.setIdleState(name); | |
298 | + | } | |
299 | + | } | |
300 | + | } | |
301 | + | } |