@entity-access/server-pages 1.0.8 → 1.0.11
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.
- package/dist/ServerPages.d.ts +1 -0
- package/dist/ServerPages.d.ts.map +1 -1
- package/dist/ServerPages.js +5 -0
- package/dist/ServerPages.js.map +1 -1
- package/dist/core/camelToChain.d.ts +3 -0
- package/dist/core/camelToChain.d.ts.map +1 -0
- package/dist/core/camelToChain.js +6 -0
- package/dist/core/camelToChain.js.map +1 -0
- package/dist/routes/api/entity/get.d.ts +7 -0
- package/dist/routes/api/entity/get.d.ts.map +1 -0
- package/dist/routes/api/entity/get.js +33 -0
- package/dist/routes/api/entity/get.js.map +1 -0
- package/dist/routes/api/entity/index.d.ts +11 -0
- package/dist/routes/api/entity/index.d.ts.map +1 -0
- package/dist/routes/api/entity/index.js +167 -0
- package/dist/routes/api/entity/index.js.map +1 -0
- package/dist/routes/api/entity/model/get.d.ts +7 -0
- package/dist/routes/api/entity/model/get.d.ts.map +1 -0
- package/dist/routes/api/entity/model/get.js +23 -0
- package/dist/routes/api/entity/model/get.js.map +1 -0
- package/dist/routes/api/entity/model.ts/get.d.ts +7 -0
- package/dist/routes/api/entity/model.ts/get.d.ts.map +1 -0
- package/dist/routes/api/entity/model.ts/get.js +23 -0
- package/dist/routes/api/entity/model.ts/get.js.map +1 -0
- package/dist/routes/api/entity/query/get.d.ts +6 -0
- package/dist/routes/api/entity/query/get.d.ts.map +1 -0
- package/dist/routes/api/entity/query/get.js +27 -0
- package/dist/routes/api/entity/query/get.js.map +1 -0
- package/dist/services/EntityAccessServer.d.ts +19 -0
- package/dist/services/EntityAccessServer.d.ts.map +1 -0
- package/dist/services/EntityAccessServer.js +77 -0
- package/dist/services/EntityAccessServer.js.map +1 -0
- package/dist/services/GraphService.d.ts +7 -0
- package/dist/services/GraphService.d.ts.map +1 -0
- package/dist/services/GraphService.js +72 -0
- package/dist/services/GraphService.js.map +1 -0
- package/dist/services/IndentedStringWriter.d.ts +12 -0
- package/dist/services/IndentedStringWriter.d.ts.map +1 -0
- package/dist/services/IndentedStringWriter.js +28 -0
- package/dist/services/IndentedStringWriter.js.map +1 -0
- package/dist/services/ModelService.d.ts +36 -0
- package/dist/services/ModelService.d.ts.map +1 -0
- package/dist/services/ModelService.js +188 -0
- package/dist/services/ModelService.js.map +1 -0
- package/dist/services/StringHelper.d.ts +6 -0
- package/dist/services/StringHelper.d.ts.map +1 -0
- package/dist/services/StringHelper.js +30 -0
- package/dist/services/StringHelper.js.map +1 -0
- package/dist/socket/SocketNamespace.d.ts +14 -0
- package/dist/socket/SocketNamespace.d.ts.map +1 -0
- package/dist/socket/SocketNamespace.js +76 -0
- package/dist/socket/SocketNamespace.js.map +1 -0
- package/dist/socket/SocketService.d.ts +15 -0
- package/dist/socket/SocketService.d.ts.map +1 -0
- package/dist/socket/SocketService.js +37 -0
- package/dist/socket/SocketService.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/ServerPages.ts +6 -0
- package/src/core/camelToChain.ts +9 -0
- package/src/routes/api/entity/get.tsx +32 -0
- package/src/routes/api/entity/index.tsx +178 -0
- package/src/routes/api/entity/model/get.tsx +16 -0
- package/src/routes/api/entity/model.ts/get.tsx +16 -0
- package/src/routes/api/entity/query/get.ts +19 -0
- package/src/services/EntityAccessServer.ts +121 -0
- package/src/services/GraphService.ts +86 -0
- package/src/services/IndentedStringWriter.ts +33 -0
- package/src/services/ModelService.ts +257 -0
- package/src/services/StringHelper.ts +31 -0
- package/src/socket/SocketNamespace.ts +90 -0
- package/src/socket/SocketService.ts +34 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Inject from "@entity-access/entity-access/dist/di/di.js";
|
|
2
|
+
import EntityContext from "@entity-access/entity-access/dist/model/EntityContext.js";
|
|
3
|
+
import Page from "../../../../Page.js";
|
|
4
|
+
import EntityAccessServer from "../../../../services/EntityAccessServer.js";
|
|
5
|
+
|
|
6
|
+
export default class extends Page {
|
|
7
|
+
|
|
8
|
+
@Inject
|
|
9
|
+
private db: EntityContext;
|
|
10
|
+
|
|
11
|
+
async all(params: any) {
|
|
12
|
+
const entity = this.childPath[0];
|
|
13
|
+
return this.json(await EntityAccessServer.query(this.db, {
|
|
14
|
+
entity,
|
|
15
|
+
... params
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import SchemaRegistry from "@entity-access/entity-access/dist/decorators/SchemaRegistry.js";
|
|
3
|
+
import EntityContext from "@entity-access/entity-access/dist/model/EntityContext.js";
|
|
4
|
+
import { StringHelper } from "./StringHelper.js";
|
|
5
|
+
import EntityQuery from "@entity-access/entity-access/dist/model/EntityQuery.js";
|
|
6
|
+
import GraphService from "./GraphService.js";
|
|
7
|
+
|
|
8
|
+
export type IQueryMethod = [string, string, ... any[]];
|
|
9
|
+
|
|
10
|
+
const replaceArgs = (code: string, p: any, args: any[]) => {
|
|
11
|
+
let index = 0;
|
|
12
|
+
for (const iterator of args) {
|
|
13
|
+
const name = "p" + index;
|
|
14
|
+
code = StringHelper.replaceAll(code, "@" + index, "p." + name);
|
|
15
|
+
p[name] = iterator;
|
|
16
|
+
index++;
|
|
17
|
+
}
|
|
18
|
+
return StringHelper.replaceAll(code, "Sql_1.Sql", "Sql");
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export interface IEntityQueryOptions {
|
|
22
|
+
entity: string;
|
|
23
|
+
methods: string | IQueryMethod[];
|
|
24
|
+
start: number;
|
|
25
|
+
size: number;
|
|
26
|
+
split: boolean;
|
|
27
|
+
trace: boolean;
|
|
28
|
+
cache: number;
|
|
29
|
+
count: boolean;
|
|
30
|
+
function: string;
|
|
31
|
+
args: string | any[];
|
|
32
|
+
traceFunc?(text: string);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default class EntityAccessServer {
|
|
36
|
+
|
|
37
|
+
public static async query(db: EntityContext, options: IEntityQueryOptions) {
|
|
38
|
+
|
|
39
|
+
db.verifyFilters = true;
|
|
40
|
+
db.raiseEvents = true;
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
entity: name,
|
|
44
|
+
start = 0,
|
|
45
|
+
size = 100,
|
|
46
|
+
trace,
|
|
47
|
+
function: queryFunction
|
|
48
|
+
} = options;
|
|
49
|
+
let {
|
|
50
|
+
count = false,
|
|
51
|
+
methods,
|
|
52
|
+
args = "[]"
|
|
53
|
+
} = options;
|
|
54
|
+
const entityClass = SchemaRegistry.classForName(name);
|
|
55
|
+
|
|
56
|
+
if (!entityClass) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof methods === "string") {
|
|
61
|
+
methods = JSON.parse(methods);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof args === "string") {
|
|
65
|
+
args = JSON.parse(args);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (typeof count === "string") {
|
|
69
|
+
count = count === "true";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const events = db.eventsFor(entityClass, true);
|
|
73
|
+
|
|
74
|
+
let q = queryFunction
|
|
75
|
+
? events[queryFunction](... args) as EntityQuery<any>
|
|
76
|
+
: events.filter(db.query(entityClass));
|
|
77
|
+
|
|
78
|
+
if (methods) {
|
|
79
|
+
for (const [method, code, ... methodArgs] of methods) {
|
|
80
|
+
const p = {};
|
|
81
|
+
if (method === "include") {
|
|
82
|
+
q = q[method](code);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const arrow = replaceArgs(code, p, methodArgs);
|
|
86
|
+
q = q[method](p, `(p) => ${arrow}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const oq = q;
|
|
91
|
+
|
|
92
|
+
if (start > 0) {
|
|
93
|
+
q = q.offset(start);
|
|
94
|
+
count = true;
|
|
95
|
+
}
|
|
96
|
+
if (size > 0) {
|
|
97
|
+
q = q.limit(size);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (count) {
|
|
101
|
+
const total = await oq.count();
|
|
102
|
+
if (trace) {
|
|
103
|
+
q = q.trace(console.log);
|
|
104
|
+
}
|
|
105
|
+
return GraphService.prepareGraph({
|
|
106
|
+
total,
|
|
107
|
+
items: await q.toArray()
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (trace) {
|
|
112
|
+
q = q.trace(console.log);
|
|
113
|
+
}
|
|
114
|
+
return GraphService.prepareGraph({
|
|
115
|
+
total: 0,
|
|
116
|
+
items: await q.toArray()
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import SchemaRegistry from "@entity-access/entity-access/dist/decorators/SchemaRegistry.js";
|
|
2
|
+
import ModelService from "./ModelService.js";
|
|
3
|
+
|
|
4
|
+
export default class GraphService {
|
|
5
|
+
|
|
6
|
+
static toGraph = Symbol("toGraph");
|
|
7
|
+
|
|
8
|
+
static appendToGraph = Symbol("toGraph");
|
|
9
|
+
|
|
10
|
+
static prepareGraph(body) {
|
|
11
|
+
const r = this.prepare(body, new Map());
|
|
12
|
+
return r;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private static prepare(body: any, visited: Map<any, any>) {
|
|
16
|
+
|
|
17
|
+
if (Array.isArray(body)) {
|
|
18
|
+
const r = [];
|
|
19
|
+
for (const iterator of body) {
|
|
20
|
+
r.push(this.prepare(iterator, visited));
|
|
21
|
+
}
|
|
22
|
+
return r;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if(!body) {
|
|
26
|
+
return body;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof body !== "object") {
|
|
30
|
+
return body;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (body instanceof Date) {
|
|
34
|
+
return body;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let $id = visited.get(body);
|
|
38
|
+
if ($id) {
|
|
39
|
+
return { $ref: $id };
|
|
40
|
+
}
|
|
41
|
+
$id = visited.size + 1;
|
|
42
|
+
visited.set(body, $id);
|
|
43
|
+
|
|
44
|
+
const appendToGraph = body[this.appendToGraph]?.() ?? {};
|
|
45
|
+
|
|
46
|
+
const copy = {
|
|
47
|
+
$id,
|
|
48
|
+
... appendToGraph
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// check constructor
|
|
52
|
+
const { constructor } = Object.getPrototypeOf(body);
|
|
53
|
+
if(constructor !== Object) {
|
|
54
|
+
copy["$type"] = SchemaRegistry.entityNameForClass(constructor);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
body = body[this.toGraph]?.() ?? body;
|
|
58
|
+
for (const key in body) {
|
|
59
|
+
|
|
60
|
+
if (ModelService.ignore(constructor, key)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (Object.prototype.hasOwnProperty.call(body, key)) {
|
|
65
|
+
const element = body[key];
|
|
66
|
+
copy[key] = this.prepare(element, visited);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const e = body[key];
|
|
70
|
+
switch(typeof e) {
|
|
71
|
+
case "number":
|
|
72
|
+
case "boolean":
|
|
73
|
+
case "bigint":
|
|
74
|
+
case "string":
|
|
75
|
+
copy[key] = e;
|
|
76
|
+
continue;
|
|
77
|
+
case "object":
|
|
78
|
+
if (e instanceof Date) {
|
|
79
|
+
copy[key] = e;
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return copy;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default class IndentedStringWriter {
|
|
2
|
+
|
|
3
|
+
public get indent(): number {
|
|
4
|
+
return this.indention;
|
|
5
|
+
}
|
|
6
|
+
public set indent(value: number) {
|
|
7
|
+
this.indention = value;
|
|
8
|
+
let p = "";
|
|
9
|
+
for (let index = 0; index < value; index++) {
|
|
10
|
+
p += this.indentChar;
|
|
11
|
+
}
|
|
12
|
+
this.prefix = p;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private indention: number = 0;
|
|
16
|
+
private prefix = "";
|
|
17
|
+
private content = "";
|
|
18
|
+
|
|
19
|
+
constructor(private indentChar = " ") {
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public writeLine(text: string = "") {
|
|
24
|
+
for (const iterator of text.split("\n")) {
|
|
25
|
+
this.content = this.content.concat(this.prefix, iterator.trimStart(), "\n");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public toString() {
|
|
30
|
+
return this.content;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { IColumn, IEntityRelation } from "@entity-access/entity-access/dist/decorators/IColumn.js";
|
|
2
|
+
import SchemaRegistry from "@entity-access/entity-access/dist/decorators/SchemaRegistry.js";
|
|
3
|
+
import EntityType, { addColumnSymbol } from "@entity-access/entity-access/dist/entity-query/EntityType.js";
|
|
4
|
+
import EntityContext from "@entity-access/entity-access/dist/model/EntityContext.js";
|
|
5
|
+
import IndentedStringWriter from "./IndentedStringWriter.js";
|
|
6
|
+
import DateTime from "@entity-access/entity-access/dist/types/DateTime.js";
|
|
7
|
+
import { IClassOf } from "@entity-access/entity-access/dist/decorators/IClassOf.js";
|
|
8
|
+
|
|
9
|
+
const modelProperties = Symbol("modelProperty");
|
|
10
|
+
|
|
11
|
+
interface IModelProperty {
|
|
12
|
+
type?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
enum?: readonly string[];
|
|
15
|
+
help?: string;
|
|
16
|
+
ignore?: boolean;
|
|
17
|
+
readonly?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface IModelProperties {
|
|
21
|
+
[key: string]: IModelProperty;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function JsonProperty(mp: IModelProperty = {}) {
|
|
25
|
+
return (target, name) => {
|
|
26
|
+
mp.type ??= (Reflect as any).getMetadata("design:type", target, name);
|
|
27
|
+
mp.name ??= name;
|
|
28
|
+
(target[modelProperties] ??= {})[name] = mp;;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const IgnoreJsonProperty= JsonProperty({ ignore: true });
|
|
33
|
+
|
|
34
|
+
export const ReadOnlyJsonProperty = JsonProperty({ readonly: true });
|
|
35
|
+
|
|
36
|
+
export interface IEntityKey {
|
|
37
|
+
name: string;
|
|
38
|
+
type: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface IEntityPropertyInfo extends IEntityKey {
|
|
42
|
+
isNullable?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface IEntityNavigationProperty extends IEntityKey {
|
|
46
|
+
isCollection?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface IEntityModel {
|
|
50
|
+
name?: string;
|
|
51
|
+
keys?: IEntityKey[];
|
|
52
|
+
properties?: IEntityPropertyInfo[];
|
|
53
|
+
navigationProperties?: IEntityNavigationProperty[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const columnFrom = (c: IColumn): IEntityPropertyInfo => {
|
|
57
|
+
return {
|
|
58
|
+
name: c.name,
|
|
59
|
+
type: c.type?.name ?? c.type.toString(),
|
|
60
|
+
isNullable: !!c.nullable
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const relationFrom = (r: IEntityRelation): IEntityNavigationProperty => {
|
|
65
|
+
return {
|
|
66
|
+
name: r.name,
|
|
67
|
+
isCollection: r.isCollection,
|
|
68
|
+
type: r.relatedEntity.entityName
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const modelFrom = (type: EntityType): IEntityModel => {
|
|
73
|
+
const model: IEntityModel = {
|
|
74
|
+
name: type.entityName,
|
|
75
|
+
keys: type.keys.map(columnFrom),
|
|
76
|
+
properties: type.columns.map(columnFrom),
|
|
77
|
+
navigationProperties: type.relations.map(relationFrom)
|
|
78
|
+
};
|
|
79
|
+
return model;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const getJSType = (column: { type?: any, enum?: readonly string[]}) => {
|
|
83
|
+
const { type, enum: enumValues } = column;
|
|
84
|
+
if (enumValues) {
|
|
85
|
+
return enumValues.map((x) => JSON.stringify(x)).join(" | ");
|
|
86
|
+
}
|
|
87
|
+
switch(type) {
|
|
88
|
+
case Number:
|
|
89
|
+
return "number";
|
|
90
|
+
case BigInt:
|
|
91
|
+
return "bigint";
|
|
92
|
+
case String:
|
|
93
|
+
return "string";
|
|
94
|
+
case Boolean:
|
|
95
|
+
return "boolean";
|
|
96
|
+
case Date:
|
|
97
|
+
case DateTime:
|
|
98
|
+
return "DateTime";
|
|
99
|
+
}
|
|
100
|
+
if (typeof type == "function") {
|
|
101
|
+
const mps = type.prototype[modelProperties];
|
|
102
|
+
if (mps) {
|
|
103
|
+
return `{ ${ Object.entries<IModelProperty>(mps).map(([key, p]) => `${key}?: ${getJSType(p)}`).join(";") } }`;
|
|
104
|
+
}
|
|
105
|
+
return type.name;
|
|
106
|
+
}
|
|
107
|
+
return "any";
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const getDefaults = (column: IColumn): [string, any] => {
|
|
111
|
+
if (column.generated) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (column.key) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (column.fkRelation) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const { type, enum: enumValues } = column;
|
|
121
|
+
if (enumValues) {
|
|
122
|
+
return [ column.name, JSON.stringify(enumValues[0])];
|
|
123
|
+
}
|
|
124
|
+
switch(type) {
|
|
125
|
+
case Number:
|
|
126
|
+
return [column.name, "0"];
|
|
127
|
+
case BigInt:
|
|
128
|
+
return [column.name, "0n"];
|
|
129
|
+
case String:
|
|
130
|
+
return [column.name, `""`];
|
|
131
|
+
case Boolean:
|
|
132
|
+
return [column.name, "false"];
|
|
133
|
+
case Date:
|
|
134
|
+
return [column.name, "new DefaultFactory(() => new Date())"];
|
|
135
|
+
case DateTime:
|
|
136
|
+
return [column.name, "new DefaultFactory(() => new DateTime())"];
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export default class ModelService {
|
|
142
|
+
|
|
143
|
+
public static ignore(t: IClassOf<any>, key: string) {
|
|
144
|
+
return (t.prototype?.[modelProperties] as IModelProperties)?.[key]?.ignore;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public static getModel(context: EntityContext) {
|
|
148
|
+
const model = [] as IEntityModel[];
|
|
149
|
+
|
|
150
|
+
for (const [type] of context.model.sources) {
|
|
151
|
+
const entityType = context.model.getEntityType(type);
|
|
152
|
+
model.push(modelFrom(entityType));
|
|
153
|
+
}
|
|
154
|
+
return model;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public static getModelDeclaration(context: EntityContext) {
|
|
158
|
+
const writer = new IndentedStringWriter("\t");
|
|
159
|
+
writer.writeLine(`import DateTime from "@web-atoms/date-time/dist/DateTime";
|
|
160
|
+
import type IClrEntity from "@web-atoms/entity/dist/models/IClrEntity";
|
|
161
|
+
import { ICollection, IGeometry, IModel, Model, DefaultFactory } from "@web-atoms/entity/dist/services/BaseEntityService";
|
|
162
|
+
`);
|
|
163
|
+
|
|
164
|
+
for (const [type] of context.model.sources) {
|
|
165
|
+
const entityType = context.model.getEntityType(type);
|
|
166
|
+
|
|
167
|
+
const entityName = entityType.entityName;
|
|
168
|
+
const name = entityType.typeClass.name;
|
|
169
|
+
|
|
170
|
+
const defaults = [] as string[];
|
|
171
|
+
|
|
172
|
+
writer.writeLine(`export interface I${name} extends IClrEntity {`);
|
|
173
|
+
writer.indent++;
|
|
174
|
+
|
|
175
|
+
const enums = [] as string[];
|
|
176
|
+
|
|
177
|
+
const set = new Set<string>();
|
|
178
|
+
|
|
179
|
+
const keys = [] as string[];
|
|
180
|
+
|
|
181
|
+
const mps = type.prototype[modelProperties] as IModelProperties;
|
|
182
|
+
for (const column of entityType.columns) {
|
|
183
|
+
|
|
184
|
+
if (column.key) {
|
|
185
|
+
keys.push(column.name);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
set.add(column.name);
|
|
189
|
+
|
|
190
|
+
const jsonProperties = mps?.[column.name];
|
|
191
|
+
|
|
192
|
+
if (jsonProperties?.ignore) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const isReadonly = column.generated || jsonProperties?.readonly ? " readonly " : "";
|
|
197
|
+
|
|
198
|
+
if (jsonProperties?.help) {
|
|
199
|
+
writer.writeLine(`/** ${jsonProperties.help} */`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (column.enum) {
|
|
203
|
+
enums.push(`export const ${name}${column.name[0].toUpperCase()}${column.name.substring(1)}Array = ${JSON.stringify(column.enum.map((x) => ({label: x, value: x})))};`);
|
|
204
|
+
}
|
|
205
|
+
const jsType = getJSType(column);
|
|
206
|
+
if (column.nullable) {
|
|
207
|
+
writer.writeLine(`${isReadonly}${column.name}?: ${jsType} | null;`);
|
|
208
|
+
} else {
|
|
209
|
+
writer.writeLine(`${isReadonly}${column.name}?: ${jsType};`);
|
|
210
|
+
const defs = getDefaults(column);
|
|
211
|
+
if (defs) {
|
|
212
|
+
const [k, v] = defs;
|
|
213
|
+
defaults.push(`${k}: ${v}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// additional model properties...
|
|
219
|
+
if (mps) {
|
|
220
|
+
for (const [key, property] of Object.entries(mps)) {
|
|
221
|
+
if (set.has(key)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const jsType = getJSType(property);
|
|
225
|
+
if (property.help) {
|
|
226
|
+
writer.writeLine(`/** ${property.help} */`);
|
|
227
|
+
}
|
|
228
|
+
writer.writeLine(`${key}?: ${jsType};`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (const relation of entityType.relations) {
|
|
233
|
+
if (relation.isCollection) {
|
|
234
|
+
writer.writeLine(`${relation.name}?: ICollection<I${relation.relatedTypeClass.name}>;`);
|
|
235
|
+
} else {
|
|
236
|
+
writer.writeLine(`${relation.name}?: I${relation.relatedTypeClass.name};`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
writer.indent--;
|
|
240
|
+
writer.writeLine(`}`);
|
|
241
|
+
|
|
242
|
+
writer.writeLine();
|
|
243
|
+
if (enums.length) {
|
|
244
|
+
for (const iterator of enums) {
|
|
245
|
+
writer.writeLine(iterator);
|
|
246
|
+
}
|
|
247
|
+
writer.writeLine();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
writer.writeLine(`export const ${name}: IModel<I${name}> = new Model<I${name}>("${entityName}", ${JSON.stringify(keys)}, { ${defaults.join(",")} });`);
|
|
251
|
+
writer.writeLine();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return writer.toString();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const StringHelper = {
|
|
2
|
+
replaceAll: (text: string, search: string, replace: string) => {
|
|
3
|
+
if (!text || !search) {
|
|
4
|
+
return text;
|
|
5
|
+
}
|
|
6
|
+
let index = 0;
|
|
7
|
+
do {
|
|
8
|
+
index = text.indexOf(search, index);
|
|
9
|
+
if (index === -1) {
|
|
10
|
+
break;
|
|
11
|
+
}
|
|
12
|
+
text = text.substring(0, index) + replace + text.substring(index + search.length);
|
|
13
|
+
} while(true);
|
|
14
|
+
return text;
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
remove00: (text: string) => {
|
|
18
|
+
if (!text) {
|
|
19
|
+
return text;
|
|
20
|
+
}
|
|
21
|
+
return text.replace(/\x00/g, "");
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
extractFloat: (text: string, def: number = 0) => {
|
|
25
|
+
const group = /(\d+)/.exec(text);
|
|
26
|
+
if (group?.length) {
|
|
27
|
+
return parseFloat( group[0]);
|
|
28
|
+
}
|
|
29
|
+
return def;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import Inject, { ServiceProvider, injectServiceKeysSymbol } from "@entity-access/entity-access/dist/di/di.js";
|
|
3
|
+
import type SocketService from "./SocketService.js";
|
|
4
|
+
import { parse } from "cookie";
|
|
5
|
+
import { Namespace, Server, Socket } from "socket.io";
|
|
6
|
+
import { camelToChain } from "../core/camelToChain.js";
|
|
7
|
+
import TokenService from "../services/TokenService.js";
|
|
8
|
+
import CookieService from "../services/CookieService.js";
|
|
9
|
+
import SessionUser from "../core/SessionUser.js";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export function Receive(target, key) {
|
|
13
|
+
const method = target[key] as (socket: Socket, ... a: any) => any;
|
|
14
|
+
target[key] = async function(this: SocketNamespace, ... a: any[]) {
|
|
15
|
+
const types = method[injectServiceKeysSymbol] as any[];
|
|
16
|
+
if (types) {
|
|
17
|
+
for (let index = a.length; index < types.length; index++) {
|
|
18
|
+
const element = ServiceProvider.resolve(this, types[index]);
|
|
19
|
+
a.push(element);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return method.apply(this, a);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function Send(target: typeof SocketNamespace, key) {
|
|
27
|
+
const value = function(this: SocketNamespace, room, ... args: any[]) {
|
|
28
|
+
return target.server?.to(room)?.emit(key, ... args);
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
value
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default abstract class SocketNamespace {
|
|
36
|
+
|
|
37
|
+
public static server: Namespace;
|
|
38
|
+
|
|
39
|
+
public static attach(server: Server, name?: string) {
|
|
40
|
+
if (!name) {
|
|
41
|
+
name = this.name;
|
|
42
|
+
if (name.endsWith("Namespace")) {
|
|
43
|
+
name = name.substring(0, name.length - "Namespace".length);
|
|
44
|
+
}
|
|
45
|
+
name = camelToChain(name);
|
|
46
|
+
}
|
|
47
|
+
console.log(`Listening Sockets on /${name}`);
|
|
48
|
+
const s = server.of("/" + name);
|
|
49
|
+
this.server = s;
|
|
50
|
+
const tokenService = ServiceProvider.resolve(this,TokenService);
|
|
51
|
+
const cookieService = ServiceProvider.resolve(this, CookieService);
|
|
52
|
+
s.on("connection", (socket) => {
|
|
53
|
+
socket.onAny(async (methodName, ... args: any[]) => {
|
|
54
|
+
const cookies = parse(socket.request.headers.cookie);
|
|
55
|
+
const cookie = cookies[tokenService.authCookieName];
|
|
56
|
+
const sessionUser = await cookieService.createSessionUserFromCookie(cookie, socket.handshake.address);
|
|
57
|
+
const scope = ServiceProvider.createScope(this);
|
|
58
|
+
try {
|
|
59
|
+
scope.add(SessionUser, sessionUser);
|
|
60
|
+
const socketEvent = scope.create(this as any);
|
|
61
|
+
(socketEvent as any).socket = socket;
|
|
62
|
+
const method = socketEvent[methodName];
|
|
63
|
+
const types = method[injectServiceKeysSymbol] as any[];
|
|
64
|
+
if (types) {
|
|
65
|
+
for (let index = args.length; index < types.length; index++) {
|
|
66
|
+
const element = scope.resolve(types[index]);
|
|
67
|
+
args.push(element);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
await socketEvent[methodName](... args);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(error);
|
|
73
|
+
} finally {
|
|
74
|
+
scope.dispose();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected room: string;
|
|
81
|
+
|
|
82
|
+
protected socket: Socket;
|
|
83
|
+
|
|
84
|
+
abstract join(... a: any[]);
|
|
85
|
+
|
|
86
|
+
leave() {
|
|
87
|
+
return this.socket.leave(this.room);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import Inject, { RegisterSingleton } from "@entity-access/entity-access/dist/di/di.js";
|
|
3
|
+
import { Http2SecureServer } from "http2";
|
|
4
|
+
import { Server as HttpServer } from "http";
|
|
5
|
+
import { Server as HttpsServer } from "https";
|
|
6
|
+
import { parse } from "cookie";
|
|
7
|
+
|
|
8
|
+
import { Server, Socket } from "socket.io";
|
|
9
|
+
import CookieService from "../services/CookieService.js";
|
|
10
|
+
import TokenService from "../services/TokenService.js";
|
|
11
|
+
|
|
12
|
+
@RegisterSingleton
|
|
13
|
+
export default class SocketService {
|
|
14
|
+
|
|
15
|
+
@Inject
|
|
16
|
+
private tokenService: TokenService;
|
|
17
|
+
|
|
18
|
+
@Inject
|
|
19
|
+
private cookieService: CookieService;
|
|
20
|
+
|
|
21
|
+
private server: Server;
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
this.server = new Server();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
attach(server: Http2SecureServer | HttpsServer | HttpServer): Server {
|
|
28
|
+
|
|
29
|
+
this.server.attach(server);
|
|
30
|
+
|
|
31
|
+
return this.server;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
}
|