@airoom/nextmin-node 0.1.0
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/LICENSE +49 -0
- package/README.md +178 -0
- package/dist/api/apiRouter.d.ts +65 -0
- package/dist/api/apiRouter.js +1548 -0
- package/dist/database/DatabaseAdapter.d.ts +14 -0
- package/dist/database/DatabaseAdapter.js +2 -0
- package/dist/database/InMemoryAdapter.d.ts +15 -0
- package/dist/database/InMemoryAdapter.js +71 -0
- package/dist/database/MongoAdapter.d.ts +52 -0
- package/dist/database/MongoAdapter.js +409 -0
- package/dist/files/FileStorageAdapter.d.ts +35 -0
- package/dist/files/FileStorageAdapter.js +2 -0
- package/dist/files/S3FileStorageAdapter.d.ts +30 -0
- package/dist/files/S3FileStorageAdapter.js +84 -0
- package/dist/files/filename.d.ts +5 -0
- package/dist/files/filename.js +40 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +87 -0
- package/dist/models/BaseModel.d.ts +44 -0
- package/dist/models/BaseModel.js +31 -0
- package/dist/policy/authorize.d.ts +25 -0
- package/dist/policy/authorize.js +305 -0
- package/dist/policy/conditions.d.ts +14 -0
- package/dist/policy/conditions.js +30 -0
- package/dist/policy/types.d.ts +53 -0
- package/dist/policy/types.js +2 -0
- package/dist/policy/utils.d.ts +9 -0
- package/dist/policy/utils.js +118 -0
- package/dist/schemas/Roles.json +64 -0
- package/dist/schemas/Settings.json +62 -0
- package/dist/schemas/Users.json +123 -0
- package/dist/services/SchemaService.d.ts +10 -0
- package/dist/services/SchemaService.js +46 -0
- package/dist/utils/DefaultDataInitializer.d.ts +21 -0
- package/dist/utils/DefaultDataInitializer.js +269 -0
- package/dist/utils/Logger.d.ts +12 -0
- package/dist/utils/Logger.js +79 -0
- package/dist/utils/SchemaLoader.d.ts +51 -0
- package/dist/utils/SchemaLoader.js +323 -0
- package/dist/utils/apiKey.d.ts +5 -0
- package/dist/utils/apiKey.js +14 -0
- package/dist/utils/fieldCodecs.d.ts +13 -0
- package/dist/utils/fieldCodecs.js +133 -0
- package/package.json +45 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildConditionFilter = buildConditionFilter;
|
|
4
|
+
exports.checkRecordCondition = checkRecordCondition;
|
|
5
|
+
function buildConditionFilter(condition, opts) {
|
|
6
|
+
const by = opts.by || (condition === 'owner' ? 'createdBy' : condition === 'sameTenant' ? 'tenantId' : undefined);
|
|
7
|
+
if (!by)
|
|
8
|
+
return {};
|
|
9
|
+
if (condition === 'owner') {
|
|
10
|
+
return { [by]: opts.context.userId || null };
|
|
11
|
+
}
|
|
12
|
+
if (condition === 'sameTenant') {
|
|
13
|
+
return { [by]: opts.context.tenantId || null };
|
|
14
|
+
}
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
function checkRecordCondition(condition, record, opts) {
|
|
18
|
+
const by = opts.by || (condition === 'owner' ? 'createdBy' : condition === 'sameTenant' ? 'tenantId' : undefined);
|
|
19
|
+
if (!by)
|
|
20
|
+
return true;
|
|
21
|
+
if (!record || typeof record !== 'object')
|
|
22
|
+
return false;
|
|
23
|
+
if (condition === 'owner') {
|
|
24
|
+
return String(record[by] ?? '') === String(opts.context.userId ?? '');
|
|
25
|
+
}
|
|
26
|
+
if (condition === 'sameTenant') {
|
|
27
|
+
return String(record[by] ?? '') === String(opts.context.tenantId ?? '');
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type Action = 'create' | 'read' | 'update' | 'delete';
|
|
2
|
+
export type Rule = boolean | 'owner';
|
|
3
|
+
export type AccessMatrix = {
|
|
4
|
+
public?: Partial<Record<Action, Rule>>;
|
|
5
|
+
authenticated?: Partial<Record<Action, Rule>>;
|
|
6
|
+
roles?: Record<string, Partial<Record<Action, Rule>>>;
|
|
7
|
+
conditions?: Record<string, {
|
|
8
|
+
by: string;
|
|
9
|
+
}>;
|
|
10
|
+
filters?: Record<string, any>;
|
|
11
|
+
readMask?: string[];
|
|
12
|
+
writeDeny?: string[];
|
|
13
|
+
restrictions?: Record<string, any>;
|
|
14
|
+
createDefaults?: Record<string, Record<string, any>>;
|
|
15
|
+
};
|
|
16
|
+
export type SchemaPolicy = {
|
|
17
|
+
allowedMethods?: Partial<Record<Action, boolean>>;
|
|
18
|
+
access?: AccessMatrix;
|
|
19
|
+
modelName?: string;
|
|
20
|
+
};
|
|
21
|
+
export type Context = {
|
|
22
|
+
isAuthenticated: boolean;
|
|
23
|
+
role?: string | null;
|
|
24
|
+
userId?: string | null;
|
|
25
|
+
tenantId?: string | null;
|
|
26
|
+
isSuperadmin?: boolean;
|
|
27
|
+
apiKeyOk?: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type Decision = {
|
|
30
|
+
allow: boolean;
|
|
31
|
+
reason?: string;
|
|
32
|
+
queryFilter?: Record<string, any>;
|
|
33
|
+
readMask?: string[];
|
|
34
|
+
writeDeny?: string[];
|
|
35
|
+
createDefaults?: Record<string, any>;
|
|
36
|
+
restrictions?: Record<string, any>;
|
|
37
|
+
};
|
|
38
|
+
export type DynamicGrant = {
|
|
39
|
+
id: string;
|
|
40
|
+
subject: {
|
|
41
|
+
userId?: string;
|
|
42
|
+
role?: string;
|
|
43
|
+
};
|
|
44
|
+
resource: {
|
|
45
|
+
model: string;
|
|
46
|
+
recordId?: string;
|
|
47
|
+
query?: any;
|
|
48
|
+
};
|
|
49
|
+
actions: Action[];
|
|
50
|
+
conditions?: string[];
|
|
51
|
+
expiresAt?: string | Date | null;
|
|
52
|
+
metadata?: Record<string, any>;
|
|
53
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function applyReadMaskOne<T extends Record<string, any>>(doc: T, mask?: string[]): T;
|
|
2
|
+
export declare function applyReadMaskMany<T extends Record<string, any>>(docs: T[], mask?: string[]): T[];
|
|
3
|
+
export declare function stripWriteDeny(payload: any, deny?: string[]): any;
|
|
4
|
+
export declare function mergeCreateDefaults(payload: any, defaults?: Record<string, any>): any;
|
|
5
|
+
export declare function enforceRestrictions(payload: any, restrictions: Record<string, any> | undefined, context: {
|
|
6
|
+
role?: string;
|
|
7
|
+
isSuperadmin?: boolean;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare function andFilter(a?: Record<string, any>, b?: Record<string, any>): Record<string, any>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyReadMaskOne = applyReadMaskOne;
|
|
4
|
+
exports.applyReadMaskMany = applyReadMaskMany;
|
|
5
|
+
exports.stripWriteDeny = stripWriteDeny;
|
|
6
|
+
exports.mergeCreateDefaults = mergeCreateDefaults;
|
|
7
|
+
exports.enforceRestrictions = enforceRestrictions;
|
|
8
|
+
exports.andFilter = andFilter;
|
|
9
|
+
function deleteDeep(obj, path) {
|
|
10
|
+
let cur = obj;
|
|
11
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
12
|
+
const k = path[i];
|
|
13
|
+
if (!cur || typeof cur !== 'object')
|
|
14
|
+
return;
|
|
15
|
+
cur = cur[k];
|
|
16
|
+
}
|
|
17
|
+
const last = path[path.length - 1];
|
|
18
|
+
if (cur && typeof cur === 'object') {
|
|
19
|
+
delete cur[last];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function matchesWildcardPath(path, pattern) {
|
|
23
|
+
if (pattern.endsWith(".*")) {
|
|
24
|
+
const prefix = pattern.slice(0, -2);
|
|
25
|
+
return path === prefix || path.startsWith(prefix + ".");
|
|
26
|
+
}
|
|
27
|
+
return path === pattern || path === "-" + pattern || path === "+" + pattern;
|
|
28
|
+
}
|
|
29
|
+
function applyReadMaskOne(doc, mask) {
|
|
30
|
+
if (!mask || mask.length === 0)
|
|
31
|
+
return doc;
|
|
32
|
+
const clone = JSON.parse(JSON.stringify(doc));
|
|
33
|
+
const pathsToRemove = [];
|
|
34
|
+
for (const m of mask) {
|
|
35
|
+
const clean = m.startsWith('-') ? m.slice(1) : (m.startsWith('+') ? m.slice(1) : m);
|
|
36
|
+
pathsToRemove.push(clean);
|
|
37
|
+
}
|
|
38
|
+
function walk(current, pathPrefix) {
|
|
39
|
+
if (!current || typeof current !== 'object')
|
|
40
|
+
return;
|
|
41
|
+
for (const key of Object.keys(current)) {
|
|
42
|
+
const path = [...pathPrefix, key].join('.');
|
|
43
|
+
if (pathsToRemove.some(p => matchesWildcardPath(path, p))) {
|
|
44
|
+
delete current[key];
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
walk(current[key], [...pathPrefix, key]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
walk(clone, []);
|
|
52
|
+
return clone;
|
|
53
|
+
}
|
|
54
|
+
function applyReadMaskMany(docs, mask) {
|
|
55
|
+
if (!mask || mask.length === 0)
|
|
56
|
+
return docs;
|
|
57
|
+
return docs.map(d => applyReadMaskOne(d, mask));
|
|
58
|
+
}
|
|
59
|
+
function stripWriteDeny(payload, deny) {
|
|
60
|
+
if (!payload || !deny || deny.length === 0)
|
|
61
|
+
return payload;
|
|
62
|
+
const clone = JSON.parse(JSON.stringify(payload));
|
|
63
|
+
for (const d of deny) {
|
|
64
|
+
const p = d.startsWith('-') ? d.slice(1) : d;
|
|
65
|
+
const parts = p.split('.');
|
|
66
|
+
deleteDeep(clone, parts);
|
|
67
|
+
}
|
|
68
|
+
return clone;
|
|
69
|
+
}
|
|
70
|
+
function mergeCreateDefaults(payload, defaults) {
|
|
71
|
+
if (!defaults)
|
|
72
|
+
return payload;
|
|
73
|
+
const clone = { ...payload };
|
|
74
|
+
for (const [k, v] of Object.entries(defaults)) {
|
|
75
|
+
if (clone[k] === undefined)
|
|
76
|
+
clone[k] = v;
|
|
77
|
+
}
|
|
78
|
+
return clone;
|
|
79
|
+
}
|
|
80
|
+
function enforceRestrictions(payload, restrictions, context) {
|
|
81
|
+
if (!restrictions)
|
|
82
|
+
return;
|
|
83
|
+
if (context.isSuperadmin)
|
|
84
|
+
return;
|
|
85
|
+
const role = context.role || '';
|
|
86
|
+
const key = `roles.${role}.cannotAssign`;
|
|
87
|
+
const rule = restrictions[key];
|
|
88
|
+
if (rule && typeof rule === 'object') {
|
|
89
|
+
for (const field of Object.keys(rule)) {
|
|
90
|
+
const forbiddenVals = rule[field] || [];
|
|
91
|
+
const val = payload[field];
|
|
92
|
+
if (val != null) {
|
|
93
|
+
if (Array.isArray(val)) {
|
|
94
|
+
const set = new Set(val.map(String));
|
|
95
|
+
for (const f of forbiddenVals.map(String)) {
|
|
96
|
+
if (set.has(f)) {
|
|
97
|
+
const err = new Error(`Forbidden to assign ${field}=${f}`);
|
|
98
|
+
err.status = 403;
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (forbiddenVals.map(String).includes(String(val))) {
|
|
104
|
+
const err = new Error(`Forbidden to assign ${field}=${val}`);
|
|
105
|
+
err.status = 403;
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function andFilter(a, b) {
|
|
113
|
+
if (!a)
|
|
114
|
+
return b || {};
|
|
115
|
+
if (!b)
|
|
116
|
+
return a;
|
|
117
|
+
return { $and: [a, b] };
|
|
118
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"modelName": "Roles",
|
|
3
|
+
"attributes": {
|
|
4
|
+
"name": { "type": "string", "required": true, "unique": true },
|
|
5
|
+
"description": { "type": "string" },
|
|
6
|
+
"type": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"enum": ["system", "default", "user"],
|
|
9
|
+
"default": "user",
|
|
10
|
+
"required": true
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"allowedMethods": {
|
|
15
|
+
"create": true,
|
|
16
|
+
"read": true,
|
|
17
|
+
"update": true,
|
|
18
|
+
"delete": true
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
"access": {
|
|
22
|
+
"public": {
|
|
23
|
+
"create": true,
|
|
24
|
+
"read": false,
|
|
25
|
+
"update": false,
|
|
26
|
+
"delete": false
|
|
27
|
+
},
|
|
28
|
+
"authenticated": {
|
|
29
|
+
"create": false,
|
|
30
|
+
"read": "owner",
|
|
31
|
+
"update": false,
|
|
32
|
+
"delete": false
|
|
33
|
+
},
|
|
34
|
+
"roles": {
|
|
35
|
+
"admin": {
|
|
36
|
+
"create": true,
|
|
37
|
+
"read": true,
|
|
38
|
+
"update": true,
|
|
39
|
+
"delete": false
|
|
40
|
+
},
|
|
41
|
+
"superadmin": {
|
|
42
|
+
"create": true,
|
|
43
|
+
"read": true,
|
|
44
|
+
"update": true,
|
|
45
|
+
"delete": true
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
"conditions": { "owner": { "by": "id" } },
|
|
50
|
+
"readMask": ["-password"],
|
|
51
|
+
"writeDeny": ["password"],
|
|
52
|
+
"bypassPrivacy": { "roles": ["admin", "superadmin"] },
|
|
53
|
+
|
|
54
|
+
"createDefaults": {
|
|
55
|
+
"public": { "role": "user", "status": "pending" },
|
|
56
|
+
"roles.admin": { "status": "active" },
|
|
57
|
+
"roles.superadmin": { "status": "active" }
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
"restrictions": {
|
|
61
|
+
"roles.admin.cannotAssign": { "role": ["superadmin"] }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"modelName": "Settings",
|
|
3
|
+
"attributes": {
|
|
4
|
+
"apiKey": {
|
|
5
|
+
"type": "string",
|
|
6
|
+
"required": true,
|
|
7
|
+
"readOnly": true
|
|
8
|
+
},
|
|
9
|
+
"siteName": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"required": true
|
|
12
|
+
},
|
|
13
|
+
"googleMapsKey": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
},
|
|
16
|
+
"siteLogo": [
|
|
17
|
+
{
|
|
18
|
+
"type": "string",
|
|
19
|
+
"fileTypes": "images/*",
|
|
20
|
+
"maxFilesCount": 1,
|
|
21
|
+
"maxFileSize": 2097152,
|
|
22
|
+
"required": true
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
"allowedMethods": {
|
|
28
|
+
"read": true,
|
|
29
|
+
"update": true,
|
|
30
|
+
"create": false,
|
|
31
|
+
"delete": false
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
"access": {
|
|
35
|
+
"public": {
|
|
36
|
+
"read": true,
|
|
37
|
+
"update": false,
|
|
38
|
+
"create": false,
|
|
39
|
+
"delete": false
|
|
40
|
+
},
|
|
41
|
+
"authenticated": {
|
|
42
|
+
"read": true,
|
|
43
|
+
"update": false,
|
|
44
|
+
"create": false,
|
|
45
|
+
"delete": false
|
|
46
|
+
},
|
|
47
|
+
"roles": {
|
|
48
|
+
"admin": {
|
|
49
|
+
"read": true,
|
|
50
|
+
"update": true,
|
|
51
|
+
"create": false,
|
|
52
|
+
"delete": false
|
|
53
|
+
},
|
|
54
|
+
"superadmin": {
|
|
55
|
+
"read": true,
|
|
56
|
+
"update": true,
|
|
57
|
+
"create": false,
|
|
58
|
+
"delete": false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"modelName": "Users",
|
|
3
|
+
"attributes": {
|
|
4
|
+
"username": {
|
|
5
|
+
"type": "string",
|
|
6
|
+
"required": true,
|
|
7
|
+
"unique": true
|
|
8
|
+
},
|
|
9
|
+
"email": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"required": true,
|
|
12
|
+
"unique": true
|
|
13
|
+
},
|
|
14
|
+
"firstName": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"required": true
|
|
17
|
+
},
|
|
18
|
+
"lastName": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"required": true
|
|
21
|
+
},
|
|
22
|
+
"password": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"required": true,
|
|
25
|
+
"private": true,
|
|
26
|
+
"writeOnly": true
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
"profilePicture": [
|
|
30
|
+
{
|
|
31
|
+
"type": "string",
|
|
32
|
+
"fileTypes": "images/*",
|
|
33
|
+
"multiple": true,
|
|
34
|
+
"maxFilesCount": 1,
|
|
35
|
+
"maxFileSize": 1000000,
|
|
36
|
+
"required": true
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
|
|
40
|
+
"role": [
|
|
41
|
+
{
|
|
42
|
+
"type": "ObjectId",
|
|
43
|
+
"ref": "Roles",
|
|
44
|
+
"default": "user",
|
|
45
|
+
"show": "name",
|
|
46
|
+
"required": true
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
|
|
50
|
+
"status": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"enum": ["pending", "active", "suspended"],
|
|
53
|
+
"default": "pending",
|
|
54
|
+
"required": true
|
|
55
|
+
},
|
|
56
|
+
"type": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"enum": ["system", "default", "user"],
|
|
59
|
+
"default": "user",
|
|
60
|
+
"required": true
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
"allowedMethods": {
|
|
65
|
+
"create": true,
|
|
66
|
+
"read": true,
|
|
67
|
+
"update": true,
|
|
68
|
+
"delete": false
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
"access": {
|
|
72
|
+
"public": {
|
|
73
|
+
"create": true,
|
|
74
|
+
"read": false,
|
|
75
|
+
"update": false,
|
|
76
|
+
"delete": false
|
|
77
|
+
},
|
|
78
|
+
"authenticated": {
|
|
79
|
+
"create": false,
|
|
80
|
+
"read": "owner",
|
|
81
|
+
"update": false,
|
|
82
|
+
"delete": false
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
"roles": {
|
|
86
|
+
"admin": {
|
|
87
|
+
"create": true,
|
|
88
|
+
"read": true,
|
|
89
|
+
"update": true,
|
|
90
|
+
"delete": false
|
|
91
|
+
},
|
|
92
|
+
"superadmin": {
|
|
93
|
+
"create": true,
|
|
94
|
+
"read": true,
|
|
95
|
+
"update": true,
|
|
96
|
+
"delete": true
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
"queryFilter": {
|
|
101
|
+
"authenticated": { "id": { "ne": "$CTX.userId" } },
|
|
102
|
+
"roles": {
|
|
103
|
+
"admin": { "id": { "ne": "$CTX.userId" } },
|
|
104
|
+
"superadmin": {}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
"conditions": { "owner": { "by": "id" } },
|
|
109
|
+
"readMask": ["-password"],
|
|
110
|
+
"writeDeny": ["password"],
|
|
111
|
+
"bypassPrivacy": { "roles": ["admin", "superadmin"] },
|
|
112
|
+
|
|
113
|
+
"createDefaults": {
|
|
114
|
+
"public": { "role": "user", "status": "pending" },
|
|
115
|
+
"roles.admin": { "status": "active" },
|
|
116
|
+
"roles.superadmin": { "status": "active" }
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
"restrictions": {
|
|
120
|
+
"roles.admin.cannotAssign": { "role": ["superadmin"] }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Server as HTTPServer } from 'http';
|
|
2
|
+
export declare const SOCKET_PATH = "/__nextmin__/schema";
|
|
3
|
+
export declare const NAMESPACE = "/schema";
|
|
4
|
+
export interface SchemaServiceOptions {
|
|
5
|
+
/** Return the trusted API key used to authorize socket clients */
|
|
6
|
+
getApiKey?: () => string | undefined;
|
|
7
|
+
/** Optional CORS origin override (defaults to "*") */
|
|
8
|
+
corsOrigin?: string | RegExp | (string | RegExp)[];
|
|
9
|
+
}
|
|
10
|
+
export declare function startSchemaService(server: HTTPServer, opts?: SchemaServiceOptions): void;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NAMESPACE = exports.SOCKET_PATH = void 0;
|
|
7
|
+
exports.startSchemaService = startSchemaService;
|
|
8
|
+
const socket_io_1 = require("socket.io");
|
|
9
|
+
const SchemaLoader_1 = require("../utils/SchemaLoader");
|
|
10
|
+
const Logger_1 = __importDefault(require("../utils/Logger"));
|
|
11
|
+
exports.SOCKET_PATH = '/__nextmin__/schema';
|
|
12
|
+
exports.NAMESPACE = '/schema';
|
|
13
|
+
let started = false;
|
|
14
|
+
let io = null;
|
|
15
|
+
let nsp = null;
|
|
16
|
+
function startSchemaService(server, opts = {}) {
|
|
17
|
+
if (started)
|
|
18
|
+
return;
|
|
19
|
+
started = true;
|
|
20
|
+
Logger_1.default.info('SchemaService:', `Starting schema service path:${exports.SOCKET_PATH} namespace:${exports.NAMESPACE}`);
|
|
21
|
+
io = new socket_io_1.Server(server, {
|
|
22
|
+
path: exports.SOCKET_PATH,
|
|
23
|
+
cors: { origin: opts.corsOrigin ?? '*' },
|
|
24
|
+
});
|
|
25
|
+
nsp = io.of(exports.NAMESPACE);
|
|
26
|
+
// --- auth gate ---
|
|
27
|
+
nsp.use((socket, next) => {
|
|
28
|
+
const provided = socket.handshake.auth?.apiKey;
|
|
29
|
+
const trusted = opts.getApiKey?.();
|
|
30
|
+
if (trusted && provided === trusted)
|
|
31
|
+
return next();
|
|
32
|
+
next(new Error('unauthorized'));
|
|
33
|
+
});
|
|
34
|
+
const loader = SchemaLoader_1.SchemaLoader.getInstance?.() ?? new SchemaLoader_1.SchemaLoader();
|
|
35
|
+
nsp.on('connection', (socket) => {
|
|
36
|
+
Logger_1.default.info('SchemaService:', socket.id);
|
|
37
|
+
// Send full snapshot on connect
|
|
38
|
+
socket.emit('schemasData', loader.getPublicSchemaList());
|
|
39
|
+
});
|
|
40
|
+
// Broadcast updates on hot-reload / schema changes
|
|
41
|
+
if (typeof loader.on === 'function') {
|
|
42
|
+
loader.on('schemasChanged', (schemas) => {
|
|
43
|
+
nsp?.emit('schemasUpdated', schemas);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BaseModel } from '../models/BaseModel';
|
|
2
|
+
import { DatabaseAdapter } from '../database/DatabaseAdapter';
|
|
3
|
+
type Models = Record<string, BaseModel>;
|
|
4
|
+
export declare class DefaultDataInitializer {
|
|
5
|
+
private dbAdapter;
|
|
6
|
+
private models;
|
|
7
|
+
private apiKey;
|
|
8
|
+
private jwtSecret;
|
|
9
|
+
constructor(dbAdapter: DatabaseAdapter, models: Models, opts?: {
|
|
10
|
+
jwtSecret?: string;
|
|
11
|
+
});
|
|
12
|
+
getApiKey(): string | null;
|
|
13
|
+
initialize(): Promise<void>;
|
|
14
|
+
private getModel;
|
|
15
|
+
private inferUserRoleStorage;
|
|
16
|
+
private ensureHash;
|
|
17
|
+
private ensureRoles;
|
|
18
|
+
private ensureSuperAdminUser;
|
|
19
|
+
private ensureSettingsDocument;
|
|
20
|
+
}
|
|
21
|
+
export {};
|