@prestizni-software/server-dem 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,77 @@
1
+ import { Socket } from "socket.io-client";
2
+ import { AutoUpdateManager } from "./AutoUpdateManagerClass.js";
3
+ import { createAutoUpdatedClass } from "./AutoUpdatedClientObjectClass.js";
4
+ import { Constructor, IsData, LoggersType } from "./CommonTypes.js";
5
+ export type WrappedInstances<T extends Record<string, Constructor<any>>> = {
6
+ [K in keyof T]: AutoUpdateClientManager<T[K]>;
7
+ };
8
+
9
+ // ---------------------- Factory ----------------------
10
+ export async function AUCManagerFactory<
11
+ T extends Record<string, Constructor<any>>
12
+ >(defs: T, loggers: LoggersType, socket: Socket): Promise<WrappedInstances<T>> {
13
+ const classers = {} as WrappedInstances<T>;
14
+
15
+ for (const key in defs) {
16
+ const Model = defs[key];
17
+ const c = new AutoUpdateClientManager(
18
+ Model,
19
+ loggers,
20
+ socket,
21
+ classers as any
22
+ );
23
+ classers[key] = c;
24
+ }
25
+
26
+ return classers;
27
+ }
28
+
29
+ export class AutoUpdateClientManager<
30
+ T extends Constructor<any>
31
+ > extends AutoUpdateManager<T> {
32
+ constructor(
33
+ classParam: T,
34
+ loggers: LoggersType,
35
+ socket: Socket,
36
+ classers: Record<string, AutoUpdateManager<any>>
37
+ ) {
38
+ super(classParam, socket, loggers, classers);
39
+ socket.emit("startup" + classParam.name, async (data: string[]) => {
40
+ for (const id of data) {
41
+ this.classes[id] = await this.handleGetMissingObject(id);
42
+ this.classesAsArray.push(this.classes[id]);
43
+ }
44
+ });
45
+ socket.on("new" + classParam.name, async (id: string) => {
46
+ this.classes[id] = await this.handleGetMissingObject(id);
47
+ this.classesAsArray.push(this.classes[id]);
48
+ });
49
+ socket.on("delete" + classParam.name, (id: string) => {
50
+ this.deleteObject(id);
51
+ });
52
+ }
53
+
54
+ protected async handleGetMissingObject(_id: string) {
55
+ if (!this.classers) throw new Error(`No classers.`);
56
+ return await createAutoUpdatedClass(
57
+ this.classParam,
58
+ this.socket,
59
+ _id,
60
+ this.loggers,
61
+ this.classers
62
+ );
63
+ }
64
+
65
+ public async createObject(data: IsData<InstanceType<T>>) {
66
+ if (!this.classers) throw new Error(`No classers.`);
67
+ const object = await createAutoUpdatedClass(
68
+ this.classParam,
69
+ this.socket,
70
+ data,
71
+ this.loggers,
72
+ this.classers
73
+ );
74
+ this.classes[object._id as any] = object;
75
+ return object;
76
+ }
77
+ }
@@ -0,0 +1,71 @@
1
+ import { AutoUpdated } from "./AutoUpdatedClientObjectClass.js";
2
+ import { Constructor, IsData, LoggersType, SocketType } from "./CommonTypes.js";
3
+
4
+ export abstract class AutoUpdateManager<T extends Constructor<any>> {
5
+ protected classes: { [_id: string]: AutoUpdated<T> } = {};
6
+ public socket: SocketType;
7
+ protected classParam: T;
8
+ protected properties: (keyof T)[];
9
+ protected classers: Record<string, AutoUpdateManager<any>>;
10
+ protected loggers: LoggersType = {
11
+ info: () => {},
12
+ debug: () => {},
13
+ error: () => {},
14
+ warn: () => {},
15
+ };
16
+ protected classesAsArray: AutoUpdated<T>[] = [];
17
+ constructor(
18
+ classParam: T,
19
+ socket: SocketType,
20
+ loggers: LoggersType,
21
+ classers: Record<string, AutoUpdateManager<any>>
22
+ ) {
23
+ this.classers = classers;
24
+ this.socket = socket;
25
+ this.classParam = classParam;
26
+ this.properties = Reflect.getMetadata(
27
+ "props",
28
+ Object.getPrototypeOf(classParam)
29
+ );
30
+ loggers.warn = loggers.warn ?? loggers.info;
31
+ this.loggers = loggers;
32
+ }
33
+
34
+
35
+ public getObject(
36
+ _id: string
37
+ ): AutoUpdated<T> | null {
38
+ return this.classes[_id];
39
+ }
40
+
41
+ public async deleteObject(_id: string): Promise<void> {
42
+ if (typeof this.classes[_id] === "string")
43
+ this.classes[_id] = await this.handleGetMissingObject(this.classes[_id]);
44
+ (this.classes[_id]).destroy();
45
+ this.classesAsArray.splice(this.classesAsArray.indexOf(this.classes[_id]), 1);
46
+ delete this.classes[_id];
47
+ }
48
+
49
+ public get objectIDs(): string[] {
50
+ return Object.keys(this.classes);
51
+ }
52
+
53
+ public get objects(): { [_id: string]: AutoUpdated<T> | string } {
54
+ return this.classes;
55
+ }
56
+
57
+ public get objectsAsArray(): AutoUpdated<T>[] {
58
+ return this.classesAsArray;
59
+ }
60
+
61
+ public get className(): string {
62
+ return this.classParam.name;
63
+ }
64
+
65
+ protected abstract handleGetMissingObject(
66
+ _id: string
67
+ ): Promise<AutoUpdated<T>>;
68
+ public abstract createObject(
69
+ data: IsData<InstanceType<T>>
70
+ ): Promise<AutoUpdated<T>>;
71
+ }
@@ -0,0 +1,222 @@
1
+ import { Server, Socket } from "socket.io";
2
+ import { AutoUpdateManager } from "./AutoUpdateManagerClass.js";
3
+ import { createAutoUpdatedClass } from "./AutoUpdatedServerObjectClass.js";
4
+ import {
5
+ Constructor,
6
+ IsData,
7
+ LoggersType,
8
+ Paths,
9
+ ServerResponse,
10
+ ServerUpdateRequest,
11
+ } from "./CommonTypes.js";
12
+ import { BeAnObject, ReturnModelType } from "@typegoose/typegoose/lib/types.js";
13
+ export type WrappedInstances<T extends Record<string, Constructor<any>>> = {
14
+ [K in keyof T]: AutoUpdateServerManager<T[K]>;
15
+ };
16
+ type AccessDefinitions<C extends Constructor<any>> = {
17
+ [K in Paths<C>]: {
18
+ access?: string[];
19
+ update?: boolean;
20
+ };
21
+ };
22
+
23
+ export type AutoStatusDefinitions<C extends Constructor<any>> = {
24
+ statusProperty: Paths<C>;
25
+ statusEnum: Record<string, string | number>;
26
+ definitions: Record<string, {}>;
27
+ };
28
+
29
+ export function createAutoStatusDefinitions<
30
+ C extends Constructor<any>,
31
+ E extends { [k: string]: string | number }
32
+ >(def: {
33
+ class: C
34
+ statusProperty: Paths<C>;
35
+ statusEnum: E;
36
+ definitions: { [K in keyof E]: {} };
37
+ }): AutoStatusDefinitions<C> & { statusEnum: E; definitions: { [K in keyof E]: {} } } {
38
+ return def;
39
+ }
40
+
41
+ export type AUSOptions<C extends Constructor<any>> = {
42
+ accessDefinitions?: Partial<AccessDefinitions<C>>;
43
+ autoStatusDefinitions?: Partial<AutoStatusDefinitions<C>>;
44
+ }
45
+
46
+ type ServerManagerDefinition<C extends Constructor<any>> = {
47
+ class: C;
48
+ model: ReturnModelType<C, BeAnObject>;
49
+ options?: AUSOptions<C>;
50
+ };
51
+
52
+ export async function AUSManagerFactory<
53
+ T extends Record<string, Constructor<any>>
54
+ >(
55
+ defs: { [K in keyof T]: ServerManagerDefinition<T[K]> },
56
+ loggers: LoggersType,
57
+ socket: Server
58
+ ): Promise<{ [K in keyof T]: AutoUpdateServerManager<T[K]> }> {
59
+ const classers: any = {};
60
+
61
+ let i = 0;
62
+ for (const key in defs) {
63
+ const def = defs[key];
64
+ const c = new AutoUpdateServerManager(
65
+ def.class,
66
+ loggers,
67
+ socket,
68
+ def.model,
69
+ classers,
70
+ def.options
71
+ ) as any;
72
+ i++;
73
+ classers[key] = c;
74
+ await c.loadDB();
75
+ }
76
+
77
+ return classers as WrappedInstances<T>;
78
+ }
79
+
80
+ export class AutoUpdateServerManager<
81
+ T extends Constructor<any>
82
+ > extends AutoUpdateManager<T> {
83
+ public readonly model: ReturnModelType<T, BeAnObject>;
84
+ private readonly clientSockets: Set<Socket> = new Set<Socket>();
85
+
86
+ constructor(
87
+ classParam: T,
88
+ loggers: LoggersType,
89
+ socket: Server,
90
+ model: ReturnModelType<T, BeAnObject>,
91
+ classers: Record<string, AutoUpdateManager<any>>,
92
+ options?: AUSOptions<T>
93
+ ) {
94
+ super(classParam, socket, loggers, classers);
95
+ this.model = model;
96
+ }
97
+
98
+ public async loadDB() {
99
+ const docs = await this.model.find({});
100
+ for (const doc of docs) {
101
+ this.classes[(doc as any)._id] =
102
+ this.classes[(doc as any)._id] ??
103
+ (await this.handleGetMissingObject((doc as any)._id.toString()));
104
+ }
105
+ }
106
+
107
+ public registerSocket(socket: Socket) {
108
+ this.clientSockets.add(socket);
109
+
110
+ socket.on(
111
+ "startup" + this.className,
112
+ async (ack: (ids: string[]) => void) => {
113
+ const ids: string[] = [];
114
+ ack(this.objectIDs);
115
+ }
116
+ );
117
+ socket.on("delete" + this.className, async (id: string) => {
118
+ this.classes[id].destroy();
119
+ this.classesAsArray.splice(
120
+ this.classesAsArray.indexOf(this.classes[id]),
121
+ 1
122
+ );
123
+ delete this.classes[id];
124
+ });
125
+ socket.on(
126
+ "new" + this.className,
127
+ async (
128
+ data: IsData<InstanceType<T>>,
129
+ ack: (res: ServerResponse<T>) => void
130
+ ) => {
131
+ try {
132
+ const newDoc = await this.createObject(data);
133
+ ack({
134
+ data: newDoc.extractedData,
135
+ success: true,
136
+ message: "Created successfully",
137
+ });
138
+ } catch (error) {
139
+ this.loggers.error(error);
140
+ ack({ success: false, message: (error as any).message });
141
+ }
142
+ }
143
+ );
144
+
145
+ socket.onAny(
146
+ async (
147
+ event: string,
148
+ data: ServerUpdateRequest<T>,
149
+ ack: (res: ServerResponse<T>) => void
150
+ ) => {
151
+ if (
152
+ event.startsWith("update" + this.className) &&
153
+ event.replace("update" + this.className, "").length === 24
154
+ ) {
155
+ try {
156
+ const id = event.replace("update" + this.className, "");
157
+ let obj = this.classes[id];
158
+ if (typeof obj === "string")
159
+ this.classes[id] = obj = await this.handleGetMissingObject(obj);
160
+ if (typeof obj === "string") throw new Error(`Never...`);
161
+ obj.setValue(data.key as any, data.value);
162
+ ack({
163
+ data: obj.extractedData,
164
+ success: true,
165
+ message: "Updated successfully",
166
+ });
167
+ } catch (error) {
168
+ ack({ success: false, message: (error as any).message });
169
+ }
170
+ } else if (
171
+ event.startsWith("get" + this.className) &&
172
+ event.replace("get" + this.className, "").length === 24
173
+ ) {
174
+ try {
175
+ const id = event.replace("get" + this.className, "");
176
+ let obj = this.classes[id];
177
+ ack({
178
+ data: obj.extractedData,
179
+ success: true,
180
+ message: "Updated successfully",
181
+ });
182
+ } catch (error) {
183
+ ack({ success: false, message: (error as any).message });
184
+ }
185
+ }
186
+ }
187
+ );
188
+ socket.on("disconnect", () => {
189
+ this.clientSockets.delete(socket);
190
+ });
191
+ }
192
+
193
+ protected async handleGetMissingObject(_id: string) {
194
+ const document = await this.model.findById(_id);
195
+ if (!document) throw new Error(`No document with id ${_id} in DB.`);
196
+ if (!this.classers) throw new Error(`No classers.`);
197
+ return await createAutoUpdatedClass(
198
+ this.classParam,
199
+ this.socket,
200
+ document,
201
+ this.loggers,
202
+ this.classers
203
+ );
204
+ }
205
+
206
+ public async createObject(data: Omit<IsData<InstanceType<T>>, "_id">) {
207
+ if (!this.classers) throw new Error(`No classers.`);
208
+ (data as any)._id = undefined;
209
+ const entry = await this.model.create(data);
210
+ const object = await createAutoUpdatedClass(
211
+ this.classParam,
212
+ this.socket,
213
+ entry,
214
+ this.loggers,
215
+ this.classers
216
+ );
217
+ this.classes[object._id] = object;
218
+ this.classesAsArray.push(object);
219
+ this.socket.emit("new" + this.classParam.name, await object._id);
220
+ return object;
221
+ }
222
+ }