Zuletzt aktiv 1705997430

gistfile1.ts Orginalformat
1import { Logger } from "../log.client";
2import * as DTypes from '../../types/db.types';
3import { IInstance, IInstanceClient, InstanceInfo } from "../../types/instance";
4import { InstanceErrors } from '../../types/err.types';
5import { CordXError } from "../error.client";
6
7export 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}