@gzl10/nexus-backend 0.16.8 → 0.18.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/dist/cli.js +602 -7327
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +113 -7
- package/dist/index.js +567 -247
- package/dist/index.js.map +1 -1
- package/dist/main.js +577 -247
- package/dist/main.js.map +1 -1
- package/package.json +12 -19
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var init_package = __esm({
|
|
|
14
14
|
"package.json"() {
|
|
15
15
|
package_default = {
|
|
16
16
|
name: "@gzl10/nexus-backend",
|
|
17
|
-
version: "0.
|
|
17
|
+
version: "0.18.0",
|
|
18
18
|
description: "Backend as a Service (BaaS) with Express 5, Knex and CASL",
|
|
19
19
|
type: "module",
|
|
20
20
|
main: "./dist/index.js",
|
|
@@ -70,7 +70,7 @@ var init_package = __esm({
|
|
|
70
70
|
"jwt"
|
|
71
71
|
],
|
|
72
72
|
scripts: {
|
|
73
|
-
dev: "
|
|
73
|
+
dev: "node --watch-path=./src --import tsx/esm src/main.ts",
|
|
74
74
|
build: "tsup",
|
|
75
75
|
start: "node dist/main.js",
|
|
76
76
|
nexus: "tsx src/cli.ts",
|
|
@@ -126,22 +126,6 @@ var init_package = __esm({
|
|
|
126
126
|
zod: "^3.24.0"
|
|
127
127
|
},
|
|
128
128
|
devDependencies: {
|
|
129
|
-
"@gzl10/nexus-plugin-ai": "workspace:^",
|
|
130
|
-
"@gzl10/nexus-plugin-auth-providers": "workspace:^",
|
|
131
|
-
"@gzl10/nexus-plugin-charts": "workspace:^",
|
|
132
|
-
"@gzl10/nexus-plugin-cms": "workspace:^",
|
|
133
|
-
"@gzl10/nexus-plugin-compliance": "workspace:^",
|
|
134
|
-
"@gzl10/nexus-plugin-docker": "workspace:^",
|
|
135
|
-
"@gzl10/nexus-plugin-links": "workspace:*",
|
|
136
|
-
"@gzl10/nexus-plugin-notifications": "workspace:*",
|
|
137
|
-
"@gzl10/nexus-plugin-oidc-server": "workspace:^",
|
|
138
|
-
"@gzl10/nexus-plugin-plane": "workspace:^",
|
|
139
|
-
"@gzl10/nexus-plugin-prisma": "workspace:^",
|
|
140
|
-
"@gzl10/nexus-plugin-remote": "workspace:*",
|
|
141
|
-
"@gzl10/nexus-plugin-schedules": "workspace:*",
|
|
142
|
-
"@gzl10/nexus-plugin-scim": "workspace:^",
|
|
143
|
-
"@gzl10/nexus-plugin-tags": "workspace:*",
|
|
144
|
-
"@gzl10/nexus-plugin-webhooks": "workspace:*",
|
|
145
129
|
"@types/bcryptjs": "^2.4.0",
|
|
146
130
|
"@types/compression": "^1.8.1",
|
|
147
131
|
"@types/cookie-parser": "^1.4.10",
|
|
@@ -154,7 +138,16 @@ var init_package = __esm({
|
|
|
154
138
|
"pino-pretty": "^13.1.3",
|
|
155
139
|
"socket.io-client": "^4.8.3",
|
|
156
140
|
supertest: "^7.2.2",
|
|
157
|
-
tsx: "^4.21.0"
|
|
141
|
+
tsx: "^4.21.0",
|
|
142
|
+
vite: "^8.0.3"
|
|
143
|
+
},
|
|
144
|
+
peerDependencies: {
|
|
145
|
+
vite: ">=6.0.0"
|
|
146
|
+
},
|
|
147
|
+
peerDependenciesMeta: {
|
|
148
|
+
vite: {
|
|
149
|
+
optional: true
|
|
150
|
+
}
|
|
158
151
|
},
|
|
159
152
|
publishConfig: {
|
|
160
153
|
access: "public",
|
|
@@ -1055,15 +1048,17 @@ async function isWorkspacePackage(name) {
|
|
|
1055
1048
|
}
|
|
1056
1049
|
}
|
|
1057
1050
|
async function installPlugin(name, opts) {
|
|
1051
|
+
const isWorkspace = await isWorkspacePackage(name);
|
|
1058
1052
|
let pkg2;
|
|
1059
1053
|
if (opts?.version) {
|
|
1060
1054
|
pkg2 = `${name}@${opts.version}`;
|
|
1061
|
-
} else if (
|
|
1055
|
+
} else if (isWorkspace) {
|
|
1062
1056
|
pkg2 = `${name}@workspace:*`;
|
|
1063
1057
|
} else {
|
|
1064
1058
|
pkg2 = name;
|
|
1065
1059
|
}
|
|
1066
|
-
|
|
1060
|
+
const flag = isWorkspace ? "-D " : "";
|
|
1061
|
+
await execAsync(`pnpm add ${flag}${pkg2}`);
|
|
1067
1062
|
const filePath = getPluginsFilePath(opts?.projectPath);
|
|
1068
1063
|
const plugins = readPluginsFile(filePath);
|
|
1069
1064
|
plugins[name] = { enabled: true };
|
|
@@ -1154,7 +1149,7 @@ var init_table_prefix = __esm({
|
|
|
1154
1149
|
}
|
|
1155
1150
|
});
|
|
1156
1151
|
|
|
1157
|
-
// src/engine/store.ts
|
|
1152
|
+
// src/engine/module-store.ts
|
|
1158
1153
|
function resetStore() {
|
|
1159
1154
|
moduleStore.modules.length = 0;
|
|
1160
1155
|
moduleStore.plugins.clear();
|
|
@@ -1165,8 +1160,8 @@ function resetStore() {
|
|
|
1165
1160
|
moduleStore.tableToSubject.clear();
|
|
1166
1161
|
}
|
|
1167
1162
|
var moduleStore;
|
|
1168
|
-
var
|
|
1169
|
-
"src/engine/store.ts"() {
|
|
1163
|
+
var init_module_store = __esm({
|
|
1164
|
+
"src/engine/module-store.ts"() {
|
|
1170
1165
|
"use strict";
|
|
1171
1166
|
moduleStore = {
|
|
1172
1167
|
/** Registered modules with source metadata */
|
|
@@ -1219,7 +1214,7 @@ var init_id = __esm({
|
|
|
1219
1214
|
}
|
|
1220
1215
|
});
|
|
1221
1216
|
|
|
1222
|
-
// src/engine/extractors.ts
|
|
1217
|
+
// src/engine/definition-extractors.ts
|
|
1223
1218
|
function getTableAndSubject(def) {
|
|
1224
1219
|
const caslSubject = def.casl?.subject;
|
|
1225
1220
|
if (!TYPES_WITH_TABLE.has(def.type)) {
|
|
@@ -1287,8 +1282,8 @@ function validateModuleDependencies(modules) {
|
|
|
1287
1282
|
}
|
|
1288
1283
|
}
|
|
1289
1284
|
var TYPES_WITH_TABLE;
|
|
1290
|
-
var
|
|
1291
|
-
"src/engine/extractors.ts"() {
|
|
1285
|
+
var init_definition_extractors = __esm({
|
|
1286
|
+
"src/engine/definition-extractors.ts"() {
|
|
1292
1287
|
"use strict";
|
|
1293
1288
|
TYPES_WITH_TABLE = /* @__PURE__ */ new Set(["collection", "reference", "event", "config", "temp", "view", void 0]);
|
|
1294
1289
|
}
|
|
@@ -1408,9 +1403,9 @@ var init_registry = __esm({
|
|
|
1408
1403
|
"use strict";
|
|
1409
1404
|
init_plugin_ops();
|
|
1410
1405
|
init_table_prefix();
|
|
1411
|
-
|
|
1406
|
+
init_module_store();
|
|
1412
1407
|
init_id();
|
|
1413
|
-
|
|
1408
|
+
init_definition_extractors();
|
|
1414
1409
|
}
|
|
1415
1410
|
});
|
|
1416
1411
|
|
|
@@ -1473,7 +1468,28 @@ var init_paths = __esm({
|
|
|
1473
1468
|
}
|
|
1474
1469
|
});
|
|
1475
1470
|
|
|
1476
|
-
// src/engine/queries.ts
|
|
1471
|
+
// src/engine/module-queries.ts
|
|
1472
|
+
var module_queries_exports = {};
|
|
1473
|
+
__export(module_queries_exports, {
|
|
1474
|
+
getCoreManifest: () => getCoreManifest,
|
|
1475
|
+
getCoreModules: () => getCoreModules,
|
|
1476
|
+
getModule: () => getModule,
|
|
1477
|
+
getModules: () => getModules,
|
|
1478
|
+
getOrderedModules: () => getOrderedModules,
|
|
1479
|
+
getOrderedModulesInternal: () => getOrderedModulesInternal,
|
|
1480
|
+
getPlugin: () => getPlugin,
|
|
1481
|
+
getPluginByCode: () => getPluginByCode,
|
|
1482
|
+
getPlugins: () => getPlugins,
|
|
1483
|
+
getRegisteredSubjects: () => getRegisteredSubjects,
|
|
1484
|
+
getSubjectForTable: () => getSubjectForTable,
|
|
1485
|
+
getUserManifest: () => getUserManifest,
|
|
1486
|
+
getUserModules: () => getUserModules,
|
|
1487
|
+
hasModule: () => hasModule,
|
|
1488
|
+
hasPlugin: () => hasPlugin,
|
|
1489
|
+
hasPluginByCode: () => hasPluginByCode,
|
|
1490
|
+
hasUserApp: () => hasUserApp,
|
|
1491
|
+
isValidSubject: () => isValidSubject
|
|
1492
|
+
});
|
|
1477
1493
|
import { join as join4 } from "path";
|
|
1478
1494
|
import { readFileSync as readFileSync3 } from "fs";
|
|
1479
1495
|
function readPackageJson(dir) {
|
|
@@ -1569,10 +1585,10 @@ function getPluginByCode(code) {
|
|
|
1569
1585
|
function hasPluginByCode(code) {
|
|
1570
1586
|
return moduleStore.pluginsByCode.has(code);
|
|
1571
1587
|
}
|
|
1572
|
-
var
|
|
1573
|
-
"src/engine/queries.ts"() {
|
|
1588
|
+
var init_module_queries = __esm({
|
|
1589
|
+
"src/engine/module-queries.ts"() {
|
|
1574
1590
|
"use strict";
|
|
1575
|
-
|
|
1591
|
+
init_module_store();
|
|
1576
1592
|
init_paths();
|
|
1577
1593
|
}
|
|
1578
1594
|
});
|
|
@@ -2057,12 +2073,16 @@ var init_definitions = __esm({
|
|
|
2057
2073
|
labelPlural: { en: "Masters", es: "Maestros" },
|
|
2058
2074
|
labelField: "label",
|
|
2059
2075
|
timestamps: true,
|
|
2076
|
+
availableDisplayModes: ["board"],
|
|
2077
|
+
groupBy: "type",
|
|
2078
|
+
groupableFields: ["type"],
|
|
2079
|
+
//columnDragFields: ['is_active'],
|
|
2060
2080
|
fields: {
|
|
2061
2081
|
id: useTextField2({
|
|
2062
2082
|
label: { en: "ID", es: "ID" },
|
|
2063
2083
|
required: true,
|
|
2064
2084
|
size: 100,
|
|
2065
|
-
|
|
2085
|
+
hidden: true,
|
|
2066
2086
|
meta: { sortable: true }
|
|
2067
2087
|
}),
|
|
2068
2088
|
type: useTextField2({
|
|
@@ -2083,9 +2103,20 @@ var init_definitions = __esm({
|
|
|
2083
2103
|
is_active: isActiveField,
|
|
2084
2104
|
metadata: useJsonField({
|
|
2085
2105
|
label: { en: "Metadata", es: "Metadatos" },
|
|
2086
|
-
hint: {
|
|
2106
|
+
hint: {
|
|
2107
|
+
en: "Type-specific fields (symbol, flag, etc.)",
|
|
2108
|
+
es: "Campos espec\xEDficos del tipo"
|
|
2109
|
+
}
|
|
2087
2110
|
})
|
|
2088
2111
|
},
|
|
2112
|
+
hooks: () => ({
|
|
2113
|
+
beforeCreate: async (data) => {
|
|
2114
|
+
if (data["type"] && data["code"] && !data["id"]) {
|
|
2115
|
+
data["id"] = `${data["type"]}:${data["code"]}`;
|
|
2116
|
+
}
|
|
2117
|
+
return data;
|
|
2118
|
+
}
|
|
2119
|
+
}),
|
|
2089
2120
|
defaultSort: { field: "order", order: "asc" },
|
|
2090
2121
|
indexes: [{ columns: ["type", "code"], unique: true }],
|
|
2091
2122
|
casl: { subject: "Master", permissions: masterCaslPermissions }
|
|
@@ -2094,6 +2125,10 @@ var init_definitions = __esm({
|
|
|
2094
2125
|
});
|
|
2095
2126
|
|
|
2096
2127
|
// src/modules/masters/registry.ts
|
|
2128
|
+
var registry_exports = {};
|
|
2129
|
+
__export(registry_exports, {
|
|
2130
|
+
createMasterRegistry: () => createMasterRegistry
|
|
2131
|
+
});
|
|
2097
2132
|
function createMasterRegistry() {
|
|
2098
2133
|
const registrations = [];
|
|
2099
2134
|
return {
|
|
@@ -8772,15 +8807,18 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8772
8807
|
const meta = field["meta"];
|
|
8773
8808
|
return meta?.["sortable"] === true && !field["hidden"];
|
|
8774
8809
|
});
|
|
8810
|
+
const explicitGroupable = def["groupableFields"];
|
|
8775
8811
|
const groupableInputTypes = ["select", "switch", "checkbox", "radio", "tags"];
|
|
8776
|
-
const
|
|
8812
|
+
const autoGroupableFields = fieldEntries.filter(([, f]) => {
|
|
8777
8813
|
const field = f;
|
|
8778
8814
|
const inputType = field["input"];
|
|
8779
8815
|
return groupableInputTypes.includes(inputType ?? "") && !field["hidden"];
|
|
8780
8816
|
});
|
|
8817
|
+
const resolvedGroupableFields = explicitGroupable ?? (autoGroupableFields.length > 0 ? autoGroupableFields.map(([name]) => name) : void 0);
|
|
8781
8818
|
const entityType = def["type"] ?? "collection";
|
|
8782
8819
|
const explicitDisplayMode = def["displayMode"];
|
|
8783
|
-
const
|
|
8820
|
+
const explicitAvailableModes = def["availableDisplayModes"];
|
|
8821
|
+
const defaultDisplayMode = explicitDisplayMode ?? (explicitAvailableModes?.length === 1 ? explicitAvailableModes[0] : void 0) ?? (["tree", "dag"].includes(entityType) ? "tree" : "table");
|
|
8784
8822
|
const entityIdent = def["table"] ?? def["key"];
|
|
8785
8823
|
return {
|
|
8786
8824
|
id: def._id,
|
|
@@ -8800,9 +8838,11 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8800
8838
|
displayMode: explicitDisplayMode,
|
|
8801
8839
|
defaultDisplayMode,
|
|
8802
8840
|
availableDisplayModes: (() => {
|
|
8841
|
+
const explicit = def["availableDisplayModes"];
|
|
8842
|
+
if (explicit?.length) return explicit;
|
|
8803
8843
|
const modes = ["table", "list", "masonry"];
|
|
8804
8844
|
if (["tree", "dag"].includes(entityType)) modes.push("tree");
|
|
8805
|
-
if (
|
|
8845
|
+
if ((resolvedGroupableFields?.length ?? 0) > 0) modes.push("board");
|
|
8806
8846
|
if (def["calendarFrom"]) modes.push("calendar");
|
|
8807
8847
|
return modes;
|
|
8808
8848
|
})(),
|
|
@@ -8812,10 +8852,8 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8812
8852
|
value: name,
|
|
8813
8853
|
label: f["label"]
|
|
8814
8854
|
})),
|
|
8815
|
-
groupableFields:
|
|
8816
|
-
|
|
8817
|
-
label: f["label"]
|
|
8818
|
-
})) : void 0,
|
|
8855
|
+
groupableFields: resolvedGroupableFields,
|
|
8856
|
+
columnDragFields: def["columnDragFields"],
|
|
8819
8857
|
groupBy: def["groupBy"],
|
|
8820
8858
|
subgroupBy: def["subgroupBy"],
|
|
8821
8859
|
calendarFrom: def["calendarFrom"],
|
|
@@ -8845,7 +8883,7 @@ function toModuleDTO(mod, ctx) {
|
|
|
8845
8883
|
dependencies: mod.dependencies ?? [],
|
|
8846
8884
|
routePrefix: mod.routePrefix ?? `/${mod.name}`,
|
|
8847
8885
|
subjects: ctx.engine.getModuleSubjects(mod),
|
|
8848
|
-
definitions: (mod.definitions ?? []).map(
|
|
8886
|
+
definitions: (mod.definitions ?? []).filter((def) => ("expose" in def ? def.expose : true) !== false).map(
|
|
8849
8887
|
(def) => toEntityDefinitionDTO(def, ctx.engine, mod.name)
|
|
8850
8888
|
),
|
|
8851
8889
|
actions: (mod.actions ?? []).length > 0 ? (mod.actions ?? []).filter((a) => !a.hidden).map((a) => toActionDTO(a)) : void 0,
|
|
@@ -8855,21 +8893,6 @@ function toModuleDTO(mod, ctx) {
|
|
|
8855
8893
|
hasInit: !!mod.init
|
|
8856
8894
|
};
|
|
8857
8895
|
}
|
|
8858
|
-
function getOrderedModulesViaContext(ctx) {
|
|
8859
|
-
return ctx.engine.getModules();
|
|
8860
|
-
}
|
|
8861
|
-
async function runModuleSeedViaContext(mod, ctx) {
|
|
8862
|
-
if (!mod.seed) {
|
|
8863
|
-
return false;
|
|
8864
|
-
}
|
|
8865
|
-
try {
|
|
8866
|
-
await mod.seed(ctx);
|
|
8867
|
-
return true;
|
|
8868
|
-
} catch (err) {
|
|
8869
|
-
ctx.core.logger.error({ module: mod.name, err }, "Seed failed");
|
|
8870
|
-
return false;
|
|
8871
|
-
}
|
|
8872
|
-
}
|
|
8873
8896
|
var init_system_helpers = __esm({
|
|
8874
8897
|
"src/modules/system/system.helpers.ts"() {
|
|
8875
8898
|
"use strict";
|
|
@@ -9104,7 +9127,8 @@ function createSystemController(ctx) {
|
|
|
9104
9127
|
const plugins = engine.getPlugins();
|
|
9105
9128
|
const body = {
|
|
9106
9129
|
version: manifest.version,
|
|
9107
|
-
plugins: plugins.map((p) => p.code)
|
|
9130
|
+
plugins: plugins.map((p) => p.code),
|
|
9131
|
+
locales: ctx.locales
|
|
9108
9132
|
};
|
|
9109
9133
|
res.json(body);
|
|
9110
9134
|
}
|
|
@@ -9991,7 +10015,6 @@ var SYSTEM_TABLES, factoryResetAction;
|
|
|
9991
10015
|
var init_factory_reset_action = __esm({
|
|
9992
10016
|
"src/modules/system/actions/factory-reset.action.ts"() {
|
|
9993
10017
|
"use strict";
|
|
9994
|
-
init_system_helpers();
|
|
9995
10018
|
SYSTEM_TABLES = /* @__PURE__ */ new Set([
|
|
9996
10019
|
"_nexus_migrations",
|
|
9997
10020
|
"_nexus_migration_lock",
|
|
@@ -10034,8 +10057,8 @@ var init_factory_reset_action = __esm({
|
|
|
10034
10057
|
source: "core:system",
|
|
10035
10058
|
action: "factory_reset",
|
|
10036
10059
|
actorId: authReq.user?.id,
|
|
10037
|
-
ip: req
|
|
10038
|
-
userAgent: req
|
|
10060
|
+
ip: req?.ip,
|
|
10061
|
+
userAgent: req?.headers["user-agent"]
|
|
10039
10062
|
});
|
|
10040
10063
|
await new Promise((resolve2) => setImmediate(resolve2));
|
|
10041
10064
|
const allTables = await getAllTables(knex3);
|
|
@@ -10073,11 +10096,11 @@ var init_factory_reset_action = __esm({
|
|
|
10073
10096
|
} catch {
|
|
10074
10097
|
}
|
|
10075
10098
|
ctx.core.logger.info({ tables: dataTables.length }, "All data tables cleared");
|
|
10076
|
-
const modules =
|
|
10099
|
+
const modules = ctx.engine.getModules();
|
|
10077
10100
|
let modulesSeeded = 0;
|
|
10078
10101
|
for (const mod of modules) {
|
|
10079
10102
|
try {
|
|
10080
|
-
const seeded = await
|
|
10103
|
+
const seeded = await ctx.db.seedModule(mod);
|
|
10081
10104
|
if (seeded) modulesSeeded++;
|
|
10082
10105
|
} catch (err) {
|
|
10083
10106
|
ctx.core.logger.error({ module: mod.name, err }, "Seed failed during factory reset");
|
|
@@ -10131,8 +10154,8 @@ var init_restart_server_action = __esm({
|
|
|
10131
10154
|
source: "core:system",
|
|
10132
10155
|
action: "server_restart",
|
|
10133
10156
|
actorId: authReq.user?.id,
|
|
10134
|
-
ip: req
|
|
10135
|
-
userAgent: req
|
|
10157
|
+
ip: req?.ip,
|
|
10158
|
+
userAgent: req?.headers["user-agent"]
|
|
10136
10159
|
});
|
|
10137
10160
|
setTimeout(() => process.exit(0), 500);
|
|
10138
10161
|
return { success: true, message: "Server is restarting..." };
|
|
@@ -11754,7 +11777,7 @@ function createUploadMiddleware(ctx, options) {
|
|
|
11754
11777
|
const rateLimit2 = ctx.core.middleware.rateLimit({
|
|
11755
11778
|
windowMs: 60 * 1e3,
|
|
11756
11779
|
max: 20,
|
|
11757
|
-
message: "
|
|
11780
|
+
message: "Too many uploads, try again in 1 minute"
|
|
11758
11781
|
});
|
|
11759
11782
|
const upload = multer({
|
|
11760
11783
|
storage: multer.memoryStorage(),
|
|
@@ -12001,7 +12024,7 @@ function createStorageRoutes(ctx) {
|
|
|
12001
12024
|
}
|
|
12002
12025
|
res.status(201).json(results);
|
|
12003
12026
|
};
|
|
12004
|
-
const uploadRateLimit = ctx.core.middleware.rateLimit({ windowMs: 60 * 1e3, max: 20, message: "
|
|
12027
|
+
const uploadRateLimit = ctx.core.middleware.rateLimit({ windowMs: 60 * 1e3, max: 20, message: "Too many uploads, try again in 1 minute" });
|
|
12005
12028
|
if (auth) {
|
|
12006
12029
|
router.post("/upload/multiple", uploadRateLimit, auth, upload.array("files", 10), uploadMultiple);
|
|
12007
12030
|
} else {
|
|
@@ -12035,7 +12058,7 @@ function createStorageRoutes(ctx) {
|
|
|
12035
12058
|
delete filesController.create;
|
|
12036
12059
|
delete filesController.update;
|
|
12037
12060
|
filesController.delete = async (req, res) => {
|
|
12038
|
-
const id = req.params["id"];
|
|
12061
|
+
const id = String(req.params["id"] ?? "");
|
|
12039
12062
|
if (!id) throw new ValidationError2("VALIDATION_ERROR");
|
|
12040
12063
|
const storageService = getStorageService();
|
|
12041
12064
|
const file = await storageService.getById(id);
|
|
@@ -12299,7 +12322,7 @@ var init_users_entity = __esm({
|
|
|
12299
12322
|
],
|
|
12300
12323
|
nullable: true,
|
|
12301
12324
|
meta: { sortable: true },
|
|
12302
|
-
defaultValue: "
|
|
12325
|
+
defaultValue: "en"
|
|
12303
12326
|
}),
|
|
12304
12327
|
timezone: useSelectField9({
|
|
12305
12328
|
label: { en: "Timezone", es: "Zona horaria" },
|
|
@@ -12377,7 +12400,7 @@ var init_users_entity = __esm({
|
|
|
12377
12400
|
middleware: (ctx) => ctx.core.middleware.rateLimit({
|
|
12378
12401
|
windowMs: 15 * 60 * 1e3,
|
|
12379
12402
|
max: 5,
|
|
12380
|
-
message: "
|
|
12403
|
+
message: "Too many attempts, try again in 15 minutes"
|
|
12381
12404
|
}),
|
|
12382
12405
|
handler: async (ctx, input) => {
|
|
12383
12406
|
const {
|
|
@@ -12471,9 +12494,9 @@ var init_users_entity = __esm({
|
|
|
12471
12494
|
labelField: "role_id",
|
|
12472
12495
|
timestamps: true,
|
|
12473
12496
|
order: 15,
|
|
12474
|
-
routePrefix: "/user-roles",
|
|
12475
12497
|
hidden: true,
|
|
12476
12498
|
// Pivot table - managed via Users UI
|
|
12499
|
+
expose: false,
|
|
12477
12500
|
fields: {
|
|
12478
12501
|
id: useIdField4(),
|
|
12479
12502
|
user_id: useSelectField9({
|
|
@@ -12526,7 +12549,7 @@ function createUsersRoutes(ctx) {
|
|
|
12526
12549
|
if (originalUsersDelete) {
|
|
12527
12550
|
usersController.delete = async (req, res) => {
|
|
12528
12551
|
const authReq = req;
|
|
12529
|
-
const id = req.params["id"] ?? "";
|
|
12552
|
+
const id = String(req.params["id"] ?? "");
|
|
12530
12553
|
if (authReq.user?.id === id) {
|
|
12531
12554
|
throw new ctx.core.errors.ForbiddenError("No puedes eliminarte a ti mismo");
|
|
12532
12555
|
}
|
|
@@ -12601,7 +12624,7 @@ function createUsersRoutes(ctx) {
|
|
|
12601
12624
|
"/:userId/roles",
|
|
12602
12625
|
auth,
|
|
12603
12626
|
async (req, res) => {
|
|
12604
|
-
const roles = await usersService.getUserRoles(req.params["userId"]);
|
|
12627
|
+
const roles = await usersService.getUserRoles(String(req.params["userId"] ?? ""));
|
|
12605
12628
|
res.json(roles);
|
|
12606
12629
|
}
|
|
12607
12630
|
);
|
|
@@ -12611,18 +12634,19 @@ function createUsersRoutes(ctx) {
|
|
|
12611
12634
|
validate2({ body: z3.object({ roleId: z3.string().min(1) }) }),
|
|
12612
12635
|
async (req, res) => {
|
|
12613
12636
|
const { roleId } = req.body;
|
|
12614
|
-
|
|
12637
|
+
const userId = String(req.params["userId"] ?? "");
|
|
12638
|
+
await usersService.assignRole(userId, roleId);
|
|
12615
12639
|
ctx.events.notify("audit.log", {
|
|
12616
12640
|
source: "core:users",
|
|
12617
12641
|
action: "role_assigned",
|
|
12618
12642
|
actorId: req.user?.id,
|
|
12619
12643
|
resourceType: "user",
|
|
12620
|
-
resourceId:
|
|
12644
|
+
resourceId: userId,
|
|
12621
12645
|
ip: req.ip,
|
|
12622
12646
|
userAgent: req.headers["user-agent"],
|
|
12623
12647
|
metadata: { roleId }
|
|
12624
12648
|
});
|
|
12625
|
-
const roles = await usersService.getUserRoles(
|
|
12649
|
+
const roles = await usersService.getUserRoles(userId);
|
|
12626
12650
|
res.json(roles);
|
|
12627
12651
|
}
|
|
12628
12652
|
);
|
|
@@ -12632,18 +12656,19 @@ function createUsersRoutes(ctx) {
|
|
|
12632
12656
|
validate2({ body: z3.object({ roleIds: z3.array(z3.string()) }) }),
|
|
12633
12657
|
async (req, res) => {
|
|
12634
12658
|
const { roleIds } = req.body;
|
|
12635
|
-
|
|
12659
|
+
const userId = String(req.params["userId"] ?? "");
|
|
12660
|
+
await usersService.setRoles(userId, roleIds);
|
|
12636
12661
|
ctx.events.notify("audit.log", {
|
|
12637
12662
|
source: "core:users",
|
|
12638
12663
|
action: "roles_replaced",
|
|
12639
12664
|
actorId: req.user?.id,
|
|
12640
12665
|
resourceType: "user",
|
|
12641
|
-
resourceId:
|
|
12666
|
+
resourceId: userId,
|
|
12642
12667
|
ip: req.ip,
|
|
12643
12668
|
userAgent: req.headers["user-agent"],
|
|
12644
12669
|
metadata: { roleIds }
|
|
12645
12670
|
});
|
|
12646
|
-
const roles = await usersService.getUserRoles(
|
|
12671
|
+
const roles = await usersService.getUserRoles(userId);
|
|
12647
12672
|
res.json(roles);
|
|
12648
12673
|
}
|
|
12649
12674
|
);
|
|
@@ -12651,16 +12676,18 @@ function createUsersRoutes(ctx) {
|
|
|
12651
12676
|
"/:userId/roles/:roleId",
|
|
12652
12677
|
auth,
|
|
12653
12678
|
async (req, res) => {
|
|
12654
|
-
|
|
12679
|
+
const userId = String(req.params["userId"] ?? "");
|
|
12680
|
+
const roleId = String(req.params["roleId"] ?? "");
|
|
12681
|
+
await usersService.removeRole(userId, roleId);
|
|
12655
12682
|
ctx.events.notify("audit.log", {
|
|
12656
12683
|
source: "core:users",
|
|
12657
12684
|
action: "role_removed",
|
|
12658
12685
|
actorId: req.user?.id,
|
|
12659
12686
|
resourceType: "user",
|
|
12660
|
-
resourceId:
|
|
12687
|
+
resourceId: userId,
|
|
12661
12688
|
ip: req.ip,
|
|
12662
12689
|
userAgent: req.headers["user-agent"],
|
|
12663
|
-
metadata: { roleId
|
|
12690
|
+
metadata: { roleId }
|
|
12664
12691
|
});
|
|
12665
12692
|
res.json({ success: true });
|
|
12666
12693
|
}
|
|
@@ -13438,8 +13465,8 @@ var init_auth_entity = __esm({
|
|
|
13438
13465
|
label: { en: "Refresh Token", es: "Token de refresco" },
|
|
13439
13466
|
labelPlural: { en: "Refresh Tokens", es: "Tokens de refresco" },
|
|
13440
13467
|
labelField: "id",
|
|
13441
|
-
routePrefix: "/tokens",
|
|
13442
13468
|
retention: { days: 7, expiresField: "expires_at" },
|
|
13469
|
+
expose: false,
|
|
13443
13470
|
fields: {
|
|
13444
13471
|
id: useIdField5(),
|
|
13445
13472
|
token: useTextField9({
|
|
@@ -13504,8 +13531,8 @@ var init_auth_entity = __esm({
|
|
|
13504
13531
|
labelField: "provider",
|
|
13505
13532
|
timestamps: true,
|
|
13506
13533
|
hidden: true,
|
|
13534
|
+
expose: false,
|
|
13507
13535
|
order: 5,
|
|
13508
|
-
routePrefix: "/identities",
|
|
13509
13536
|
fields: {
|
|
13510
13537
|
id: useIdField5(),
|
|
13511
13538
|
user_id: useSelectField10({
|
|
@@ -16584,7 +16611,7 @@ var init_plugins_entity = __esm({
|
|
|
16584
16611
|
label: "Plugins",
|
|
16585
16612
|
icon: "mdi:puzzle",
|
|
16586
16613
|
labelField: "code",
|
|
16587
|
-
routePrefix: "/
|
|
16614
|
+
routePrefix: "/",
|
|
16588
16615
|
defaultSort: { field: "name", order: "asc" },
|
|
16589
16616
|
fields: {
|
|
16590
16617
|
name: useTextField12({
|
|
@@ -16997,12 +17024,12 @@ var init_loader = __esm({
|
|
|
16997
17024
|
"src/engine/loader.ts"() {
|
|
16998
17025
|
"use strict";
|
|
16999
17026
|
init_registry();
|
|
17000
|
-
|
|
17027
|
+
init_definition_extractors();
|
|
17001
17028
|
init_modules();
|
|
17002
17029
|
}
|
|
17003
17030
|
});
|
|
17004
17031
|
|
|
17005
|
-
// src/engine/
|
|
17032
|
+
// src/engine/subject-extractor.ts
|
|
17006
17033
|
function getModuleSubjects(mod) {
|
|
17007
17034
|
const subjects = /* @__PURE__ */ new Set();
|
|
17008
17035
|
for (const def of mod.definitions ?? []) {
|
|
@@ -17011,10 +17038,10 @@ function getModuleSubjects(mod) {
|
|
|
17011
17038
|
}
|
|
17012
17039
|
return [...subjects];
|
|
17013
17040
|
}
|
|
17014
|
-
var
|
|
17015
|
-
"src/engine/
|
|
17041
|
+
var init_subject_extractor = __esm({
|
|
17042
|
+
"src/engine/subject-extractor.ts"() {
|
|
17016
17043
|
"use strict";
|
|
17017
|
-
|
|
17044
|
+
init_definition_extractors();
|
|
17018
17045
|
}
|
|
17019
17046
|
});
|
|
17020
17047
|
|
|
@@ -17092,7 +17119,7 @@ function initSocketIO(httpServer, options) {
|
|
|
17092
17119
|
maxHttpBufferSize: 1e6
|
|
17093
17120
|
// 1MB - match Express json body limit
|
|
17094
17121
|
});
|
|
17095
|
-
logger.info({
|
|
17122
|
+
logger.info({ cors: corsOrigin }, "Socket.IO initialized");
|
|
17096
17123
|
io.use((socket, next) => {
|
|
17097
17124
|
const token = socket.handshake.auth?.["token"] || socket.handshake.query?.["token"];
|
|
17098
17125
|
if (token && typeof token === "string" && jwtSecret) {
|
|
@@ -17115,7 +17142,6 @@ function initSocketIO(httpServer, options) {
|
|
|
17115
17142
|
logger.warn({ code: err.code, message: err.message }, "Socket.IO connection error");
|
|
17116
17143
|
});
|
|
17117
17144
|
io.on("connection", handleConnection);
|
|
17118
|
-
logger.info("Socket.IO initialized");
|
|
17119
17145
|
nexusEvents.emitEvent("socket.initialized");
|
|
17120
17146
|
return io;
|
|
17121
17147
|
}
|
|
@@ -17710,6 +17736,12 @@ var init_app_error = __esm({
|
|
|
17710
17736
|
|
|
17711
17737
|
// src/core/abilities/ability.factory.ts
|
|
17712
17738
|
import { AbilityBuilder, createMongoAbility } from "@casl/ability";
|
|
17739
|
+
function setSeedPermissions(perms) {
|
|
17740
|
+
seedPermissions = perms;
|
|
17741
|
+
}
|
|
17742
|
+
function clearSeedPermissions() {
|
|
17743
|
+
seedPermissions = null;
|
|
17744
|
+
}
|
|
17713
17745
|
function setCustomCaslRules(fn) {
|
|
17714
17746
|
customCaslRules = fn;
|
|
17715
17747
|
}
|
|
@@ -17767,6 +17799,17 @@ async function defineAbilityFor(user, roleNames) {
|
|
|
17767
17799
|
if (customCaslRules) {
|
|
17768
17800
|
await customCaslRules(user, { can, cannot });
|
|
17769
17801
|
}
|
|
17802
|
+
if (seedPermissions && !isSuperuser) {
|
|
17803
|
+
for (const roleName of roleNames) {
|
|
17804
|
+
const rolePerms = seedPermissions.get(roleName);
|
|
17805
|
+
if (!rolePerms) continue;
|
|
17806
|
+
for (const [subject2, actions] of rolePerms) {
|
|
17807
|
+
for (const action of actions) {
|
|
17808
|
+
can(action, subject2);
|
|
17809
|
+
}
|
|
17810
|
+
}
|
|
17811
|
+
}
|
|
17812
|
+
}
|
|
17770
17813
|
return build();
|
|
17771
17814
|
}
|
|
17772
17815
|
function packRules(ability) {
|
|
@@ -17775,11 +17818,12 @@ function packRules(ability) {
|
|
|
17775
17818
|
function unpackRules(rules) {
|
|
17776
17819
|
return createMongoAbility(rules);
|
|
17777
17820
|
}
|
|
17778
|
-
var customCaslRules, entityDefinitionsRegistry, SUPERUSER_ROLES;
|
|
17821
|
+
var customCaslRules, seedPermissions, entityDefinitionsRegistry, SUPERUSER_ROLES;
|
|
17779
17822
|
var init_ability_factory = __esm({
|
|
17780
17823
|
"src/core/abilities/ability.factory.ts"() {
|
|
17781
17824
|
"use strict";
|
|
17782
17825
|
init_logger();
|
|
17826
|
+
seedPermissions = null;
|
|
17783
17827
|
entityDefinitionsRegistry = null;
|
|
17784
17828
|
SUPERUSER_ROLES = ["ADMIN", "OWNER"];
|
|
17785
17829
|
}
|
|
@@ -17863,7 +17907,7 @@ var init_validate_middleware = __esm({
|
|
|
17863
17907
|
import { z as z8 } from "zod";
|
|
17864
17908
|
function resolveConfig() {
|
|
17865
17909
|
env = envSchema.parse(process.env);
|
|
17866
|
-
process.env
|
|
17910
|
+
process.env["TZ"] = env.TZ;
|
|
17867
17911
|
resolvedConfig = {
|
|
17868
17912
|
nodeEnv: env.NODE_ENV,
|
|
17869
17913
|
port: env.PORT,
|
|
@@ -17918,7 +17962,7 @@ var init_env = __esm({
|
|
|
17918
17962
|
FRPC_SUBDOMAIN: z8.string().optional()
|
|
17919
17963
|
});
|
|
17920
17964
|
env = envSchema.parse(process.env);
|
|
17921
|
-
process.env
|
|
17965
|
+
process.env["TZ"] = env.TZ;
|
|
17922
17966
|
resolvedConfig = null;
|
|
17923
17967
|
}
|
|
17924
17968
|
});
|
|
@@ -18345,7 +18389,7 @@ var init_sequence = __esm({
|
|
|
18345
18389
|
}
|
|
18346
18390
|
});
|
|
18347
18391
|
|
|
18348
|
-
// src/core/utils/
|
|
18392
|
+
// src/core/utils/safe-json.ts
|
|
18349
18393
|
function safeJsonParse(logger2, jsonString, fallback, context) {
|
|
18350
18394
|
try {
|
|
18351
18395
|
return JSON.parse(jsonString);
|
|
@@ -18354,8 +18398,8 @@ function safeJsonParse(logger2, jsonString, fallback, context) {
|
|
|
18354
18398
|
return fallback;
|
|
18355
18399
|
}
|
|
18356
18400
|
}
|
|
18357
|
-
var
|
|
18358
|
-
"src/core/utils/
|
|
18401
|
+
var init_safe_json = __esm({
|
|
18402
|
+
"src/core/utils/safe-json.ts"() {
|
|
18359
18403
|
"use strict";
|
|
18360
18404
|
}
|
|
18361
18405
|
});
|
|
@@ -18364,14 +18408,15 @@ var init_error_handler = __esm({
|
|
|
18364
18408
|
import express from "express";
|
|
18365
18409
|
import { resolve, join as join9 } from "path";
|
|
18366
18410
|
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
18367
|
-
function createServeSPA(app) {
|
|
18368
|
-
return (endpoint, distPath, options = {}) => {
|
|
18411
|
+
function createServeSPA(app, httpServer) {
|
|
18412
|
+
return async (endpoint, distPath, options = {}) => {
|
|
18369
18413
|
const {
|
|
18370
18414
|
maxAge = "1d",
|
|
18371
18415
|
etag = true,
|
|
18372
18416
|
immutable = false,
|
|
18373
18417
|
index = "index.html",
|
|
18374
|
-
absolute = false
|
|
18418
|
+
absolute = false,
|
|
18419
|
+
viteSrc
|
|
18375
18420
|
} = options;
|
|
18376
18421
|
if (endpoint === "/api" || endpoint.startsWith("/api/")) {
|
|
18377
18422
|
logger.error(`Cannot mount SPA on ${endpoint} - reserved for API routes`);
|
|
@@ -18382,58 +18427,117 @@ function createServeSPA(app) {
|
|
|
18382
18427
|
return;
|
|
18383
18428
|
}
|
|
18384
18429
|
registeredEndpoints.add(endpoint);
|
|
18385
|
-
|
|
18386
|
-
|
|
18387
|
-
|
|
18388
|
-
|
|
18389
|
-
const projectPath2 = resolve(getProjectPath(), distPath);
|
|
18390
|
-
if (existsSync9(projectPath2)) {
|
|
18391
|
-
resolvedPath = projectPath2;
|
|
18430
|
+
if (env.NODE_ENV === "development" && viteSrc) {
|
|
18431
|
+
const srcPath = resolve(getProjectPath(), viteSrc);
|
|
18432
|
+
if (!existsSync9(srcPath)) {
|
|
18433
|
+
logger.warn({ endpoint, viteSrc, resolved: srcPath }, "Vite source not found \u2014 falling back to static");
|
|
18392
18434
|
} else {
|
|
18393
|
-
|
|
18435
|
+
const mounted = await mountViteDevMiddleware(app, endpoint, srcPath, httpServer);
|
|
18436
|
+
if (mounted) return;
|
|
18394
18437
|
}
|
|
18395
18438
|
}
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
|
|
18439
|
+
mountStaticSPA(app, endpoint, distPath, { maxAge, etag, immutable, index, absolute });
|
|
18440
|
+
};
|
|
18441
|
+
}
|
|
18442
|
+
async function mountViteDevMiddleware(app, endpoint, srcPath, httpServer) {
|
|
18443
|
+
try {
|
|
18444
|
+
const vite = await import("vite");
|
|
18445
|
+
const apiUrl = env.BACKEND_URL ? `${env.BACKEND_URL}/api/v1` : "/api/v1";
|
|
18446
|
+
const server2 = await vite.createServer({
|
|
18447
|
+
root: srcPath,
|
|
18448
|
+
server: {
|
|
18449
|
+
middlewareMode: true,
|
|
18450
|
+
allowedHosts: true,
|
|
18451
|
+
hmr: httpServer ? { server: httpServer } : true
|
|
18452
|
+
},
|
|
18453
|
+
plugins: [{
|
|
18454
|
+
name: "nexus-config-inject",
|
|
18455
|
+
transformIndexHtml(html) {
|
|
18456
|
+
const config3 = JSON.stringify({ apiUrl });
|
|
18457
|
+
return html.replace("</head>", `<script>window.__NEXUS__=${config3}</script>
|
|
18458
|
+
</head>`);
|
|
18459
|
+
}
|
|
18460
|
+
}],
|
|
18461
|
+
appType: "spa",
|
|
18462
|
+
clearScreen: false
|
|
18463
|
+
});
|
|
18464
|
+
viteServers.push(server2);
|
|
18465
|
+
if (endpoint === "/") {
|
|
18466
|
+
app.use(server2.middlewares);
|
|
18467
|
+
} else {
|
|
18468
|
+
app.use(endpoint, server2.middlewares);
|
|
18399
18469
|
}
|
|
18400
|
-
|
|
18401
|
-
|
|
18402
|
-
|
|
18403
|
-
|
|
18404
|
-
|
|
18405
|
-
|
|
18406
|
-
if (existsSync9(indexPath)) {
|
|
18407
|
-
const rawHtml = readFileSync5(indexPath, "utf-8");
|
|
18408
|
-
const apiUrl = env.BACKEND_URL ? `${env.BACKEND_URL}/api/v1` : "/api/v1";
|
|
18409
|
-
const nexusConfig = JSON.stringify({ apiUrl });
|
|
18410
|
-
injectedHtml = rawHtml.replace(
|
|
18411
|
-
"</head>",
|
|
18412
|
-
`<script>window.__NEXUS__=${nexusConfig}</script>
|
|
18413
|
-
</head>`
|
|
18414
|
-
);
|
|
18470
|
+
logger.info({ path: srcPath }, `Vite dev server mounted at ${endpoint} (HMR enabled)`);
|
|
18471
|
+
return true;
|
|
18472
|
+
} catch (err) {
|
|
18473
|
+
if (err.code === "ERR_MODULE_NOT_FOUND" || err.code === "MODULE_NOT_FOUND") {
|
|
18474
|
+
logger.warn(`vite not installed \u2014 falling back to static serving for ${endpoint}`);
|
|
18475
|
+
return false;
|
|
18415
18476
|
}
|
|
18416
|
-
|
|
18417
|
-
|
|
18418
|
-
|
|
18419
|
-
|
|
18420
|
-
|
|
18421
|
-
|
|
18422
|
-
|
|
18423
|
-
|
|
18424
|
-
|
|
18425
|
-
|
|
18477
|
+
logger.error({ err }, `Failed to mount Vite dev server at ${endpoint}`);
|
|
18478
|
+
return false;
|
|
18479
|
+
}
|
|
18480
|
+
}
|
|
18481
|
+
function mountStaticSPA(app, endpoint, distPath, options) {
|
|
18482
|
+
const { maxAge, etag, immutable, index, absolute } = options;
|
|
18483
|
+
let resolvedPath;
|
|
18484
|
+
if (absolute) {
|
|
18485
|
+
resolvedPath = distPath;
|
|
18486
|
+
} else {
|
|
18487
|
+
const projectPath2 = resolve(getProjectPath(), distPath);
|
|
18488
|
+
if (existsSync9(projectPath2)) {
|
|
18489
|
+
resolvedPath = projectPath2;
|
|
18426
18490
|
} else {
|
|
18427
|
-
|
|
18428
|
-
|
|
18491
|
+
resolvedPath = resolve(getLibPath(), distPath);
|
|
18492
|
+
}
|
|
18493
|
+
}
|
|
18494
|
+
if (!existsSync9(resolvedPath)) {
|
|
18495
|
+
logger.warn({ endpoint, distPath, hint: "Build the frontend first" }, `SPA directory not found: ${resolvedPath}`);
|
|
18496
|
+
return;
|
|
18497
|
+
}
|
|
18498
|
+
const indexPath = join9(resolvedPath, index);
|
|
18499
|
+
if (!existsSync9(indexPath)) {
|
|
18500
|
+
logger.warn({ endpoint, index }, `Index file not found: ${indexPath}`);
|
|
18501
|
+
}
|
|
18502
|
+
app.use(endpoint, express.static(resolvedPath, { maxAge, etag, immutable }));
|
|
18503
|
+
let injectedHtml = "";
|
|
18504
|
+
if (existsSync9(indexPath)) {
|
|
18505
|
+
const rawHtml = readFileSync5(indexPath, "utf-8");
|
|
18506
|
+
const apiUrl = env.BACKEND_URL ? `${env.BACKEND_URL}/api/v1` : "/api/v1";
|
|
18507
|
+
const nexusConfig = JSON.stringify({ apiUrl });
|
|
18508
|
+
injectedHtml = rawHtml.replace(
|
|
18509
|
+
"</head>",
|
|
18510
|
+
`<script>window.__NEXUS__=${nexusConfig}</script>
|
|
18511
|
+
</head>`
|
|
18512
|
+
);
|
|
18513
|
+
}
|
|
18514
|
+
const fallbackHandler = (_req, res) => {
|
|
18515
|
+
if (!injectedHtml) {
|
|
18516
|
+
res.status(404).send("index.html not found");
|
|
18517
|
+
return;
|
|
18429
18518
|
}
|
|
18430
|
-
|
|
18519
|
+
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
18520
|
+
res.type("html").send(injectedHtml);
|
|
18431
18521
|
};
|
|
18522
|
+
if (endpoint === "/") {
|
|
18523
|
+
app.get("{*splat}", fallbackHandler);
|
|
18524
|
+
} else {
|
|
18525
|
+
app.get(endpoint, fallbackHandler);
|
|
18526
|
+
app.get(`${endpoint}/{*splat}`, fallbackHandler);
|
|
18527
|
+
}
|
|
18528
|
+
logger.info({ path: resolvedPath }, `SPA mounted at ${endpoint}`);
|
|
18432
18529
|
}
|
|
18433
|
-
function
|
|
18530
|
+
async function resetServeSPA() {
|
|
18531
|
+
for (const server2 of viteServers) {
|
|
18532
|
+
try {
|
|
18533
|
+
await server2.close();
|
|
18534
|
+
} catch {
|
|
18535
|
+
}
|
|
18536
|
+
}
|
|
18537
|
+
viteServers.length = 0;
|
|
18434
18538
|
registeredEndpoints.clear();
|
|
18435
18539
|
}
|
|
18436
|
-
var registeredEndpoints;
|
|
18540
|
+
var registeredEndpoints, viteServers;
|
|
18437
18541
|
var init_spa_handler = __esm({
|
|
18438
18542
|
"src/core/spa-handler.ts"() {
|
|
18439
18543
|
"use strict";
|
|
@@ -18441,6 +18545,7 @@ var init_spa_handler = __esm({
|
|
|
18441
18545
|
init_logger();
|
|
18442
18546
|
init_env();
|
|
18443
18547
|
registeredEndpoints = /* @__PURE__ */ new Set();
|
|
18548
|
+
viteServers = [];
|
|
18444
18549
|
}
|
|
18445
18550
|
});
|
|
18446
18551
|
|
|
@@ -19011,7 +19116,7 @@ var init_core = __esm({
|
|
|
19011
19116
|
init_id();
|
|
19012
19117
|
init_sequence();
|
|
19013
19118
|
init_paths();
|
|
19014
|
-
|
|
19119
|
+
init_safe_json();
|
|
19015
19120
|
init_spa_handler();
|
|
19016
19121
|
init_cache();
|
|
19017
19122
|
init_jwt();
|
|
@@ -19726,7 +19831,9 @@ var init_base_service = __esm({
|
|
|
19726
19831
|
const countResult = await qb.clone().count("* as count").first();
|
|
19727
19832
|
const total = Number(countResult?.count ?? 0);
|
|
19728
19833
|
qb = this.applySortingWithDefaults(qb, query);
|
|
19729
|
-
|
|
19834
|
+
if (limit > 0) {
|
|
19835
|
+
qb = qb.limit(limit).offset(offset);
|
|
19836
|
+
}
|
|
19730
19837
|
const rawItems = await qb;
|
|
19731
19838
|
const items = this.parseJsonFieldsFromArray(rawItems);
|
|
19732
19839
|
const processedItems = await this.afterFindAll(items);
|
|
@@ -19811,7 +19918,7 @@ var init_base_service = __esm({
|
|
|
19811
19918
|
});
|
|
19812
19919
|
}
|
|
19813
19920
|
const total = result.length;
|
|
19814
|
-
const paginatedItems = result.slice(offset, offset + limit);
|
|
19921
|
+
const paginatedItems = limit === 0 ? result : result.slice(offset, offset + limit);
|
|
19815
19922
|
return this.buildPaginatedResult(paginatedItems, total, page, limit);
|
|
19816
19923
|
}
|
|
19817
19924
|
/**
|
|
@@ -20066,14 +20173,14 @@ var init_base_service = __esm({
|
|
|
20066
20173
|
* Build paginated result from items and count
|
|
20067
20174
|
*/
|
|
20068
20175
|
buildPaginatedResult(items, total, page, limit) {
|
|
20069
|
-
const totalPages = Math.ceil(total / limit);
|
|
20176
|
+
const totalPages = limit === 0 ? 1 : Math.ceil(total / limit);
|
|
20070
20177
|
return {
|
|
20071
20178
|
items,
|
|
20072
20179
|
total,
|
|
20073
|
-
page,
|
|
20074
|
-
limit,
|
|
20180
|
+
page: limit === 0 ? 1 : page,
|
|
20181
|
+
limit: limit === 0 ? total : limit,
|
|
20075
20182
|
totalPages,
|
|
20076
|
-
hasNext: page < totalPages
|
|
20183
|
+
hasNext: limit === 0 ? false : page < totalPages
|
|
20077
20184
|
};
|
|
20078
20185
|
}
|
|
20079
20186
|
/**
|
|
@@ -20082,8 +20189,8 @@ var init_base_service = __esm({
|
|
|
20082
20189
|
getPagination(query) {
|
|
20083
20190
|
const maxLimit = query?.maxLimit ?? 100;
|
|
20084
20191
|
const page = Math.max(1, query?.page ?? 1);
|
|
20085
|
-
const limit = Math.min(maxLimit, Math.max(1, query?.limit ?? 20));
|
|
20086
|
-
const offset = (page - 1) * limit;
|
|
20192
|
+
const limit = query?.limit === 0 ? 0 : Math.min(maxLimit, Math.max(1, query?.limit ?? 20));
|
|
20193
|
+
const offset = limit === 0 ? 0 : (page - 1) * limit;
|
|
20087
20194
|
return { page, limit, offset };
|
|
20088
20195
|
}
|
|
20089
20196
|
/**
|
|
@@ -21730,7 +21837,8 @@ function createEntityController(service, definition, ctx) {
|
|
|
21730
21837
|
async list(req, res) {
|
|
21731
21838
|
checkPermission(req, "read");
|
|
21732
21839
|
const page = Math.max(1, parseInt(req.query["page"]) || 1);
|
|
21733
|
-
const
|
|
21840
|
+
const rawLimit = parseInt(req.query["limit"]);
|
|
21841
|
+
const limit = rawLimit === 0 ? 0 : Math.min(100, Math.max(1, rawLimit || 20));
|
|
21734
21842
|
let filters;
|
|
21735
21843
|
if (req.query["filters"]) {
|
|
21736
21844
|
filters = parseFilters(req.query["filters"], ctx.core.errors);
|
|
@@ -21773,7 +21881,7 @@ function createEntityController(service, definition, ctx) {
|
|
|
21773
21881
|
* Get single entity
|
|
21774
21882
|
*/
|
|
21775
21883
|
async get(req, res) {
|
|
21776
|
-
const id = req.params["id"] ?? "";
|
|
21884
|
+
const id = String(req.params["id"] ?? "");
|
|
21777
21885
|
const entity = await service.findById(id);
|
|
21778
21886
|
if (!entity) {
|
|
21779
21887
|
throw new ctx.core.errors.NotFoundError(`${resolveLocalized6(definition.label, "en")} not found`);
|
|
@@ -21824,7 +21932,7 @@ function createEntityController(service, definition, ctx) {
|
|
|
21824
21932
|
if (!service.update) {
|
|
21825
21933
|
throw new ctx.core.errors.ForbiddenError(`${resolveLocalized6(definition.label, "en")} does not support update`);
|
|
21826
21934
|
}
|
|
21827
|
-
const id = req.params["id"] ?? "";
|
|
21935
|
+
const id = String(req.params["id"] ?? "");
|
|
21828
21936
|
const existing = await service.findById(id);
|
|
21829
21937
|
if (!existing) {
|
|
21830
21938
|
throw new ctx.core.errors.NotFoundError(`${resolveLocalized6(definition.label, "en")} not found`);
|
|
@@ -21856,7 +21964,7 @@ function createEntityController(service, definition, ctx) {
|
|
|
21856
21964
|
if (!service.delete) {
|
|
21857
21965
|
throw new ctx.core.errors.ForbiddenError(`${resolveLocalized6(definition.label, "en")} does not support delete`);
|
|
21858
21966
|
}
|
|
21859
|
-
const id = req.params["id"] ?? "";
|
|
21967
|
+
const id = String(req.params["id"] ?? "");
|
|
21860
21968
|
const existing = await service.findById(id);
|
|
21861
21969
|
if (!existing) {
|
|
21862
21970
|
throw new ctx.core.errors.NotFoundError(`${resolveLocalized6(definition.label, "en")} not found`);
|
|
@@ -22055,7 +22163,7 @@ function createActionHandler(action, definition, ctx, scope = "row") {
|
|
|
22055
22163
|
const caslSubject = (action.casl && "subject" in action.casl ? action.casl.subject : void 0) ?? definition.casl?.subject ?? capitalizeFirst(entityName ?? "Entity");
|
|
22056
22164
|
const hasCasl = !action.skipAuth && (!!definition.casl || !!action.casl);
|
|
22057
22165
|
return async (req, res) => {
|
|
22058
|
-
const recordId = scope === "row" ? req.params["id"] : void 0;
|
|
22166
|
+
const recordId = scope === "row" ? String(req.params["id"] ?? "") : void 0;
|
|
22059
22167
|
const authReq = req;
|
|
22060
22168
|
if (hasCasl && !authReq.ability) {
|
|
22061
22169
|
ctx.core.logger.warn({ action: "execute", subject: caslSubject, actionKey: action.key, reqId: req.requestId, decision: "deny", reason: "unauthenticated" }, "authz:deny");
|
|
@@ -22129,6 +22237,8 @@ var init_entity_controller = __esm({
|
|
|
22129
22237
|
// src/runtime/routes/entity.routes.ts
|
|
22130
22238
|
function createEntityRouter(controller, definition, ctx, service) {
|
|
22131
22239
|
const router = ctx.createRouter();
|
|
22240
|
+
const expose = "expose" in definition ? definition.expose : true;
|
|
22241
|
+
if (expose === false) return router;
|
|
22132
22242
|
const type2 = definition.type ?? "collection";
|
|
22133
22243
|
const isSingleton = (type2 === "single" || type2 === "config") && !("scopeField" in definition && definition.scopeField);
|
|
22134
22244
|
const entityDef = definition;
|
|
@@ -22223,44 +22333,44 @@ function createEntityRouter(controller, definition, ctx, service) {
|
|
|
22223
22333
|
res.json(await treeSvc.findRoots());
|
|
22224
22334
|
}));
|
|
22225
22335
|
router.post("/:id/move", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22226
|
-
const id = req.params["id"] ?? "";
|
|
22336
|
+
const id = String(req.params["id"] ?? "");
|
|
22227
22337
|
const { parentId } = req.body;
|
|
22228
22338
|
res.json(await treeSvc.move(id, parentId ?? null));
|
|
22229
22339
|
}));
|
|
22230
22340
|
router.get("/:id/ancestors", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22231
|
-
const id = req.params["id"] ?? "";
|
|
22341
|
+
const id = String(req.params["id"] ?? "");
|
|
22232
22342
|
res.json(await treeSvc.getAncestors(id));
|
|
22233
22343
|
}));
|
|
22234
22344
|
router.get("/:id/descendants", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22235
|
-
const id = req.params["id"] ?? "";
|
|
22345
|
+
const id = String(req.params["id"] ?? "");
|
|
22236
22346
|
res.json(await treeSvc.getDescendants(id));
|
|
22237
22347
|
}));
|
|
22238
22348
|
router.get("/:id/children", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22239
|
-
const id = req.params["id"] ?? "";
|
|
22349
|
+
const id = String(req.params["id"] ?? "");
|
|
22240
22350
|
res.json(await treeSvc.findChildren(id));
|
|
22241
22351
|
}));
|
|
22242
22352
|
}
|
|
22243
22353
|
if (type2 === "dag" && service) {
|
|
22244
22354
|
const dagSvc = service;
|
|
22245
22355
|
router.get("/:id/parents", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22246
|
-
const id = req.params["id"] ?? "";
|
|
22356
|
+
const id = String(req.params["id"] ?? "");
|
|
22247
22357
|
res.json(await dagSvc.getParents(id));
|
|
22248
22358
|
}));
|
|
22249
22359
|
router.put("/:id/parents", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22250
|
-
const id = req.params["id"] ?? "";
|
|
22360
|
+
const id = String(req.params["id"] ?? "");
|
|
22251
22361
|
const { parentIds } = req.body;
|
|
22252
22362
|
await dagSvc.setParents(id, parentIds);
|
|
22253
22363
|
res.status(204).send();
|
|
22254
22364
|
}));
|
|
22255
22365
|
router.post("/:id/parents", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22256
|
-
const id = req.params["id"] ?? "";
|
|
22366
|
+
const id = String(req.params["id"] ?? "");
|
|
22257
22367
|
const { parentId } = req.body;
|
|
22258
22368
|
await dagSvc.addParent(id, parentId);
|
|
22259
22369
|
res.status(204).send();
|
|
22260
22370
|
}));
|
|
22261
22371
|
router.delete("/:id/parents/:parentId", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22262
|
-
const id = req.params["id"] ?? "";
|
|
22263
|
-
const parentId = req.params["parentId"] ?? "";
|
|
22372
|
+
const id = String(req.params["id"] ?? "");
|
|
22373
|
+
const parentId = String(req.params["parentId"] ?? "");
|
|
22264
22374
|
await dagSvc.removeParent(id, parentId);
|
|
22265
22375
|
res.status(204).send();
|
|
22266
22376
|
}));
|
|
@@ -23209,17 +23319,18 @@ async function createModuleRouters(ctx, definitions, modulePrefix) {
|
|
|
23209
23319
|
const runtime = await createEntityRuntimeAsync(ctx, definition);
|
|
23210
23320
|
const route = inferEntityRoutePath(definition);
|
|
23211
23321
|
const entityLabel = resolveLocalized7(definition.label, "en");
|
|
23212
|
-
|
|
23322
|
+
const isExposed = !("expose" in definition && definition.expose === false);
|
|
23323
|
+
if (isExposed && modulePrefix && route === modulePrefix) {
|
|
23213
23324
|
ctx.core.logger.warn(
|
|
23214
23325
|
`Entity "${entityLabel}" inferred route "${route}" duplicates module prefix \u2014 add routePrefix to the entity definition to fix`
|
|
23215
23326
|
);
|
|
23216
23327
|
}
|
|
23217
|
-
if (routeMap.has(route)) {
|
|
23328
|
+
if (isExposed && routeMap.has(route)) {
|
|
23218
23329
|
ctx.core.logger.warn(
|
|
23219
23330
|
`Entity "${entityLabel}" route "${route}" collides with "${routeMap.get(route)}" in the same module`
|
|
23220
23331
|
);
|
|
23221
23332
|
}
|
|
23222
|
-
routeMap.set(route, entityLabel);
|
|
23333
|
+
if (isExposed) routeMap.set(route, entityLabel);
|
|
23223
23334
|
router.use(route, runtime.router);
|
|
23224
23335
|
const key = getServiceKey(definition);
|
|
23225
23336
|
if (ctx.services.has(key)) {
|
|
@@ -23435,7 +23546,7 @@ var init_runtime = __esm({
|
|
|
23435
23546
|
}
|
|
23436
23547
|
});
|
|
23437
23548
|
|
|
23438
|
-
// src/db/
|
|
23549
|
+
// src/db/seed-runner.ts
|
|
23439
23550
|
import { existsSync as existsSync10 } from "fs";
|
|
23440
23551
|
import { join as join10 } from "path";
|
|
23441
23552
|
import { pathToFileURL } from "url";
|
|
@@ -23503,8 +23614,8 @@ function hasSeedData(seed5) {
|
|
|
23503
23614
|
if (!Array.isArray(seed5) && "source" in seed5 && seed5.source === "url") return true;
|
|
23504
23615
|
return Array.isArray(seed5) && seed5.length > 0;
|
|
23505
23616
|
}
|
|
23506
|
-
var
|
|
23507
|
-
"src/db/
|
|
23617
|
+
var init_seed_runner = __esm({
|
|
23618
|
+
"src/db/seed-runner.ts"() {
|
|
23508
23619
|
"use strict";
|
|
23509
23620
|
init_runtime();
|
|
23510
23621
|
init_paths();
|
|
@@ -23607,7 +23718,7 @@ var init_migration_sources = __esm({
|
|
|
23607
23718
|
init_shared();
|
|
23608
23719
|
init_paths();
|
|
23609
23720
|
init_plugin_ops();
|
|
23610
|
-
|
|
23721
|
+
init_module_store();
|
|
23611
23722
|
}
|
|
23612
23723
|
});
|
|
23613
23724
|
|
|
@@ -23940,7 +24051,7 @@ async function runMigrations(knexInstance, sources) {
|
|
|
23940
24051
|
const executedMigrations = await knex3("_nexus_migrations").where({ status: "completed" }).select("name").then((rows) => new Set(rows.map((r) => r.name)));
|
|
23941
24052
|
const pendingMigrations = migrationFiles.filter((m2) => !executedMigrations.has(m2.name));
|
|
23942
24053
|
if (pendingMigrations.length === 0) {
|
|
23943
|
-
logger.
|
|
24054
|
+
logger.debug("No pending migrations");
|
|
23944
24055
|
return;
|
|
23945
24056
|
}
|
|
23946
24057
|
const batch = await getNextBatch(knex3);
|
|
@@ -24340,6 +24451,27 @@ var init_migration_helpers = __esm({
|
|
|
24340
24451
|
import path2 from "path";
|
|
24341
24452
|
import fs2 from "fs/promises";
|
|
24342
24453
|
import { readFileSync as readFileSync6, mkdirSync as mkdirSync5, realpathSync } from "fs";
|
|
24454
|
+
function getColumnIndexBytes(field) {
|
|
24455
|
+
if (!field?.db) return 255 * MYSQL_BYTES_PER_CHAR;
|
|
24456
|
+
const size = field.db.size ?? 255;
|
|
24457
|
+
if (field.db.type === "text") return 0;
|
|
24458
|
+
if (field.db.type === "string") return size * MYSQL_BYTES_PER_CHAR;
|
|
24459
|
+
if (field.db.type === "integer") return 4;
|
|
24460
|
+
if (field.db.type === "boolean") return 1;
|
|
24461
|
+
if (field.db.type === "datetime" || field.db.type === "date") return 8;
|
|
24462
|
+
if (field.db.type === "uuid") return 16;
|
|
24463
|
+
return size * MYSQL_BYTES_PER_CHAR;
|
|
24464
|
+
}
|
|
24465
|
+
function warnIfIndexExceedsMySQLLimit(table, columns, unique, fields) {
|
|
24466
|
+
if (!fields) return;
|
|
24467
|
+
const totalBytes = columns.reduce((sum, col) => sum + getColumnIndexBytes(fields[col]), 0);
|
|
24468
|
+
if (totalBytes > MYSQL_MAX_INDEX_BYTES) {
|
|
24469
|
+
const indexType = unique ? "UNIQUE" : "INDEX";
|
|
24470
|
+
logger.warn(
|
|
24471
|
+
`${indexType} on ${table}(${columns.join(", ")}) requires ${totalBytes} bytes \u2014 exceeds MySQL utf8mb4 limit of ${MYSQL_MAX_INDEX_BYTES} bytes. Reduce column sizes or restructure the index for MySQL compatibility.`
|
|
24472
|
+
);
|
|
24473
|
+
}
|
|
24474
|
+
}
|
|
24343
24475
|
async function detectSchemaDrift(knexInstance) {
|
|
24344
24476
|
const knex3 = knexInstance ?? getDb();
|
|
24345
24477
|
const entities = getAllPersistentEntities();
|
|
@@ -24447,6 +24579,7 @@ function computeSchemaDiff(entities, currentSchema, options) {
|
|
|
24447
24579
|
for (const idx of entityIndexes) {
|
|
24448
24580
|
const key = normalizeKey(idx.columns, !!idx.unique);
|
|
24449
24581
|
if (!currentKeys.has(key)) {
|
|
24582
|
+
warnIfIndexExceedsMySQLLimit(tableName, idx.columns, !!idx.unique, entity.fields);
|
|
24450
24583
|
diff.newIndexes.push({ columns: idx.columns, unique: !!idx.unique });
|
|
24451
24584
|
}
|
|
24452
24585
|
}
|
|
@@ -24618,7 +24751,7 @@ function formatDriftMessage(drift) {
|
|
|
24618
24751
|
lines.push('Run "pnpm migrate:dev" to generate and apply migrations.');
|
|
24619
24752
|
return lines.join("\n");
|
|
24620
24753
|
}
|
|
24621
|
-
var PERSISTENT_TYPES;
|
|
24754
|
+
var MYSQL_MAX_INDEX_BYTES, MYSQL_BYTES_PER_CHAR, PERSISTENT_TYPES;
|
|
24622
24755
|
var init_migration_generator = __esm({
|
|
24623
24756
|
"src/db/migration-generator.ts"() {
|
|
24624
24757
|
"use strict";
|
|
@@ -24627,9 +24760,11 @@ var init_migration_generator = __esm({
|
|
|
24627
24760
|
init_paths();
|
|
24628
24761
|
init_schema_reader();
|
|
24629
24762
|
init_engine();
|
|
24630
|
-
|
|
24763
|
+
init_module_queries();
|
|
24631
24764
|
init_migration_helpers();
|
|
24632
|
-
|
|
24765
|
+
init_module_store();
|
|
24766
|
+
MYSQL_MAX_INDEX_BYTES = 3072;
|
|
24767
|
+
MYSQL_BYTES_PER_CHAR = 4;
|
|
24633
24768
|
PERSISTENT_TYPES = /* @__PURE__ */ new Set([
|
|
24634
24769
|
"collection",
|
|
24635
24770
|
"tree",
|
|
@@ -24683,6 +24818,13 @@ async function runAllGeneratedMigrations(ctx, modules) {
|
|
|
24683
24818
|
table.string("created_by", 26).nullable();
|
|
24684
24819
|
table.string("updated_by", 26).nullable();
|
|
24685
24820
|
}
|
|
24821
|
+
const hasSoftDelete = entityDef.softDelete;
|
|
24822
|
+
if (hasSoftDelete) {
|
|
24823
|
+
table.timestamp("deleted_at").nullable();
|
|
24824
|
+
}
|
|
24825
|
+
if (entityDef.type === "tree") {
|
|
24826
|
+
table.string("parent_id", 26).nullable().references(`${tableName}.id`).onDelete("SET NULL");
|
|
24827
|
+
}
|
|
24686
24828
|
});
|
|
24687
24829
|
const entityIndexes = entityDef.indexes;
|
|
24688
24830
|
if (entityIndexes?.length) {
|
|
@@ -24799,7 +24941,16 @@ function addColumn(table, fieldName, field, db2) {
|
|
|
24799
24941
|
column.unique();
|
|
24800
24942
|
}
|
|
24801
24943
|
if (dbConfig.default !== void 0) {
|
|
24802
|
-
|
|
24944
|
+
const isJsonColumn = dbConfig.type === "json" || dbConfig.type === "array";
|
|
24945
|
+
if (isJsonColumn && typeof dbConfig.default === "string") {
|
|
24946
|
+
try {
|
|
24947
|
+
column.defaultTo(JSON.parse(dbConfig.default));
|
|
24948
|
+
} catch {
|
|
24949
|
+
column.defaultTo(dbConfig.default);
|
|
24950
|
+
}
|
|
24951
|
+
} else {
|
|
24952
|
+
column.defaultTo(dbConfig.default);
|
|
24953
|
+
}
|
|
24803
24954
|
} else if (dbConfig.defaultFn === "now") {
|
|
24804
24955
|
column.defaultTo(db2.fn.now());
|
|
24805
24956
|
}
|
|
@@ -26187,7 +26338,7 @@ var init_db = __esm({
|
|
|
26187
26338
|
init_schema_helpers();
|
|
26188
26339
|
init_sql_utils();
|
|
26189
26340
|
init_sqlite_compat();
|
|
26190
|
-
|
|
26341
|
+
init_seed_runner();
|
|
26191
26342
|
init_ensure_system_tables();
|
|
26192
26343
|
init_migration_sources();
|
|
26193
26344
|
init_migration_runner();
|
|
@@ -26277,12 +26428,20 @@ async function discoverPlugins(projectPath2) {
|
|
|
26277
26428
|
manifest.image = imagePath;
|
|
26278
26429
|
}
|
|
26279
26430
|
}
|
|
26431
|
+
if (!manifest.llms) {
|
|
26432
|
+
const llmsPath = join12(projectPath2, "node_modules", pkgName, "llms.txt");
|
|
26433
|
+
if (existsSync11(llmsPath)) {
|
|
26434
|
+
manifest.llms = readFileSync7(llmsPath, "utf-8");
|
|
26435
|
+
}
|
|
26436
|
+
}
|
|
26280
26437
|
discovered.push(manifest);
|
|
26281
26438
|
} else {
|
|
26282
26439
|
logger.warn({ plugin: pkgName }, "Plugin discovery: no PluginManifest export, skipping");
|
|
26283
26440
|
}
|
|
26284
26441
|
} catch (err) {
|
|
26285
|
-
|
|
26442
|
+
const errorMsg = err.message;
|
|
26443
|
+
logger.warn({ plugin: pkgName, err: errorMsg }, "Plugin discovery: failed to import");
|
|
26444
|
+
console.error(` \u26A0 Plugin '${pkgName}' failed to load: ${errorMsg}`);
|
|
26286
26445
|
}
|
|
26287
26446
|
}
|
|
26288
26447
|
return discovered;
|
|
@@ -26350,7 +26509,9 @@ async function discoverModules(projectPath2) {
|
|
|
26350
26509
|
discovered.push(...manifests);
|
|
26351
26510
|
}
|
|
26352
26511
|
} catch (err) {
|
|
26353
|
-
|
|
26512
|
+
const errorMsg = err.message;
|
|
26513
|
+
logger.warn({ module: entry, err: errorMsg }, "Module discovery: failed to import");
|
|
26514
|
+
console.error(` \u26A0 Module '${entry}' failed to load: ${errorMsg}`);
|
|
26354
26515
|
}
|
|
26355
26516
|
}
|
|
26356
26517
|
return discovered;
|
|
@@ -26441,8 +26602,11 @@ var init_events_api = __esm({
|
|
|
26441
26602
|
|
|
26442
26603
|
// src/engine/context.ts
|
|
26443
26604
|
import { ForbiddenError as CASLForbiddenError3, subject } from "@casl/ability";
|
|
26444
|
-
import { DEFAULT_TENANT_ID as DEFAULT_TENANT_ID2 } from "@gzl10/nexus-sdk";
|
|
26605
|
+
import { DEFAULT_TENANT_ID as DEFAULT_TENANT_ID2, DEFAULT_LOCALES } from "@gzl10/nexus-sdk";
|
|
26445
26606
|
import { Redis as Redis2 } from "ioredis";
|
|
26607
|
+
function setLocales(locales) {
|
|
26608
|
+
platformLocales = locales;
|
|
26609
|
+
}
|
|
26446
26610
|
function getSharedCacheManager() {
|
|
26447
26611
|
if (!sharedCacheManager) {
|
|
26448
26612
|
const redisUrl = env.REDIS_URL;
|
|
@@ -26518,7 +26682,7 @@ function createModuleContext() {
|
|
|
26518
26682
|
const defaultAdapter = createKnexAdapter(knex3);
|
|
26519
26683
|
const defaultSchemaAdapter = createKnexSchemaAdapter(knex3);
|
|
26520
26684
|
adaptersRegistry["temp"] = { data: getSharedTempAdapter() };
|
|
26521
|
-
logger.
|
|
26685
|
+
logger.trace(env.REDIS_URL ? "Temp adapter: Redis (shared)" : "Temp adapter: InMemory (shared)");
|
|
26522
26686
|
const middleware = {
|
|
26523
26687
|
validate,
|
|
26524
26688
|
rateLimit: createRateLimit,
|
|
@@ -26626,7 +26790,9 @@ function createModuleContext() {
|
|
|
26626
26790
|
throw new Error(`Knex connection for adapter "${adapter}" not found. Available: ${Object.keys(knexConnections).join(", ")}`);
|
|
26627
26791
|
}
|
|
26628
26792
|
return conn;
|
|
26629
|
-
}
|
|
26793
|
+
},
|
|
26794
|
+
// Placeholder — bound after ctx construction (needs full ModuleContext)
|
|
26795
|
+
seedModule: null
|
|
26630
26796
|
};
|
|
26631
26797
|
const configContext = {
|
|
26632
26798
|
env,
|
|
@@ -26719,7 +26885,8 @@ function createModuleContext() {
|
|
|
26719
26885
|
adapters: adaptersContext,
|
|
26720
26886
|
// Root-level shortcuts for frequently used utilities
|
|
26721
26887
|
events: createEventsApi(nexusEvents, logger),
|
|
26722
|
-
createRouter: () => createRouter()
|
|
26888
|
+
createRouter: () => createRouter(),
|
|
26889
|
+
locales: platformLocales
|
|
26723
26890
|
};
|
|
26724
26891
|
servicesRegistry["cacheManager"] = getSharedCacheManager();
|
|
26725
26892
|
ctx.runtime = {
|
|
@@ -26728,9 +26895,10 @@ function createModuleContext() {
|
|
|
26728
26895
|
createEntityController: (service, def) => createEntityController(service, def, ctx),
|
|
26729
26896
|
createEntityRouter: (controller, def) => createEntityRouter(controller, def, ctx)
|
|
26730
26897
|
};
|
|
26898
|
+
ctx.db.seedModule = (mod) => runModuleSeed(mod, ctx);
|
|
26731
26899
|
return ctx;
|
|
26732
26900
|
}
|
|
26733
|
-
var sharedCacheManager, sharedTempAdapter;
|
|
26901
|
+
var platformLocales, sharedCacheManager, sharedTempAdapter;
|
|
26734
26902
|
var init_context = __esm({
|
|
26735
26903
|
"src/engine/context.ts"() {
|
|
26736
26904
|
"use strict";
|
|
@@ -26743,7 +26911,9 @@ var init_context = __esm({
|
|
|
26743
26911
|
init_plugin_ops();
|
|
26744
26912
|
init_load_config();
|
|
26745
26913
|
init_events_api();
|
|
26914
|
+
init_seed_runner();
|
|
26746
26915
|
init_cache_manager();
|
|
26916
|
+
platformLocales = DEFAULT_LOCALES;
|
|
26747
26917
|
sharedCacheManager = null;
|
|
26748
26918
|
sharedTempAdapter = null;
|
|
26749
26919
|
}
|
|
@@ -26754,11 +26924,11 @@ var init_engine = __esm({
|
|
|
26754
26924
|
"src/engine/index.ts"() {
|
|
26755
26925
|
"use strict";
|
|
26756
26926
|
init_registry();
|
|
26757
|
-
|
|
26927
|
+
init_module_queries();
|
|
26758
26928
|
init_loader();
|
|
26759
|
-
|
|
26760
|
-
|
|
26761
|
-
|
|
26929
|
+
init_module_store();
|
|
26930
|
+
init_subject_extractor();
|
|
26931
|
+
init_definition_extractors();
|
|
26762
26932
|
init_context();
|
|
26763
26933
|
}
|
|
26764
26934
|
});
|
|
@@ -27300,7 +27470,7 @@ async function createApp(options = {}) {
|
|
|
27300
27470
|
// Only accept arrays and objects
|
|
27301
27471
|
}));
|
|
27302
27472
|
app.use(cookieParser());
|
|
27303
|
-
const serveSPA = createServeSPA(app);
|
|
27473
|
+
const serveSPA = createServeSPA(app, options.httpServer);
|
|
27304
27474
|
if (options.beforeRoutes) {
|
|
27305
27475
|
const result = options.beforeRoutes(app, serveSPA);
|
|
27306
27476
|
if (result instanceof Promise) {
|
|
@@ -27394,11 +27564,11 @@ async function createApp(options = {}) {
|
|
|
27394
27564
|
});
|
|
27395
27565
|
const sortedSpas = [...servedSpas].sort((a, b) => b.endpoint.length - a.endpoint.length);
|
|
27396
27566
|
for (const spa of sortedSpas) {
|
|
27397
|
-
serveSPA(spa.endpoint, spa.path, spa);
|
|
27567
|
+
await serveSPA(spa.endpoint, spa.path, { ...spa, viteSrc: spa.viteSrc });
|
|
27398
27568
|
}
|
|
27399
27569
|
const { ui } = getConfig();
|
|
27400
27570
|
if (ui.enabled) {
|
|
27401
|
-
serveSPA(ui.base, ui.path);
|
|
27571
|
+
await serveSPA(ui.base, ui.path, { viteSrc: "../ui" });
|
|
27402
27572
|
}
|
|
27403
27573
|
app.use(errorMiddleware);
|
|
27404
27574
|
return app;
|
|
@@ -27424,58 +27594,32 @@ var init_app = __esm({
|
|
|
27424
27594
|
}
|
|
27425
27595
|
});
|
|
27426
27596
|
|
|
27427
|
-
// src/core/utils/
|
|
27597
|
+
// src/core/utils/port-check.ts
|
|
27428
27598
|
import net from "net";
|
|
27429
27599
|
import { execSync } from "child_process";
|
|
27430
27600
|
function findProcessOnPort(port) {
|
|
27431
27601
|
try {
|
|
27432
27602
|
const output = execSync(`lsof -ti :${port} 2>/dev/null`, { encoding: "utf-8" });
|
|
27433
27603
|
const pid = parseInt(output.trim().split("\n")[0] ?? "", 10);
|
|
27434
|
-
|
|
27604
|
+
if (isNaN(pid)) return null;
|
|
27605
|
+
let name = "unknown";
|
|
27606
|
+
try {
|
|
27607
|
+
name = execSync(`ps -o comm= -p ${pid} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
27608
|
+
} catch {
|
|
27609
|
+
}
|
|
27610
|
+
return { pid, name };
|
|
27435
27611
|
} catch {
|
|
27436
27612
|
return null;
|
|
27437
27613
|
}
|
|
27438
27614
|
}
|
|
27439
|
-
function isSameProcessGroup(pid) {
|
|
27440
|
-
if (pid === process.pid || pid === process.ppid) return true;
|
|
27441
|
-
try {
|
|
27442
|
-
const ourPgid = execSync(`ps -o pgid= -p ${process.pid} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
27443
|
-
const targetPgid = execSync(`ps -o pgid= -p ${pid} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
27444
|
-
return ourPgid === targetPgid;
|
|
27445
|
-
} catch {
|
|
27446
|
-
return false;
|
|
27447
|
-
}
|
|
27448
|
-
}
|
|
27449
|
-
function killProcessOnPort(port) {
|
|
27450
|
-
const pid = findProcessOnPort(port);
|
|
27451
|
-
if (!pid) return false;
|
|
27452
|
-
if (isSameProcessGroup(pid)) {
|
|
27453
|
-
logger.warn({ port, pid }, `Skipping same process group (PID ${pid}) on port ${port}`);
|
|
27454
|
-
return false;
|
|
27455
|
-
}
|
|
27456
|
-
try {
|
|
27457
|
-
process.kill(pid, "SIGTERM");
|
|
27458
|
-
logger.warn({ port, pid }, `Killed process ${pid} on port ${port}`);
|
|
27459
|
-
return true;
|
|
27460
|
-
} catch {
|
|
27461
|
-
return false;
|
|
27462
|
-
}
|
|
27463
|
-
}
|
|
27464
27615
|
async function checkPortAvailable(port, host = "0.0.0.0") {
|
|
27465
27616
|
return new Promise((resolve2, reject) => {
|
|
27466
27617
|
const server2 = net.createServer();
|
|
27467
27618
|
server2.once("error", (err) => {
|
|
27468
27619
|
if (err.code === "EADDRINUSE") {
|
|
27469
|
-
|
|
27470
|
-
|
|
27471
|
-
|
|
27472
|
-
checkPortAvailable(port, host).then(resolve2).catch(reject);
|
|
27473
|
-
}, 500);
|
|
27474
|
-
return;
|
|
27475
|
-
}
|
|
27476
|
-
}
|
|
27477
|
-
logger.error({ port }, `Port ${port} is already in use`);
|
|
27478
|
-
reject(new Error(`Port ${port} is already in use`));
|
|
27620
|
+
const proc = findProcessOnPort(port);
|
|
27621
|
+
const msg = proc ? `Port ${port} is already in use by "${proc.name}" (PID ${proc.pid}). Stop that process first.` : `Port ${port} is already in use`;
|
|
27622
|
+
reject(new Error(msg));
|
|
27479
27623
|
} else {
|
|
27480
27624
|
reject(err);
|
|
27481
27625
|
}
|
|
@@ -27486,10 +27630,9 @@ async function checkPortAvailable(port, host = "0.0.0.0") {
|
|
|
27486
27630
|
server2.listen(port, host);
|
|
27487
27631
|
});
|
|
27488
27632
|
}
|
|
27489
|
-
var
|
|
27490
|
-
"src/core/utils/
|
|
27633
|
+
var init_port_check = __esm({
|
|
27634
|
+
"src/core/utils/port-check.ts"() {
|
|
27491
27635
|
"use strict";
|
|
27492
|
-
init_logger();
|
|
27493
27636
|
}
|
|
27494
27637
|
});
|
|
27495
27638
|
|
|
@@ -27584,6 +27727,150 @@ var init_tunnel = __esm({
|
|
|
27584
27727
|
}
|
|
27585
27728
|
});
|
|
27586
27729
|
|
|
27730
|
+
// src/db/seed-context.ts
|
|
27731
|
+
var seed_context_exports = {};
|
|
27732
|
+
__export(seed_context_exports, {
|
|
27733
|
+
createSeedContext: () => createSeedContext
|
|
27734
|
+
});
|
|
27735
|
+
function createSeedContext(deps) {
|
|
27736
|
+
const { knex: db2, generateId: generateId4, hashPassword: hashPassword2, pluginPrefixes, logger: logger2 } = deps;
|
|
27737
|
+
const permissionsMap = /* @__PURE__ */ new Map();
|
|
27738
|
+
const seedContext = {
|
|
27739
|
+
masters: {
|
|
27740
|
+
register(type2, entries, options) {
|
|
27741
|
+
deps.masterRegistry.register(type2, entries, options);
|
|
27742
|
+
}
|
|
27743
|
+
},
|
|
27744
|
+
roles: {
|
|
27745
|
+
async add(role) {
|
|
27746
|
+
const hasTable = await db2.schema.hasTable("roles");
|
|
27747
|
+
if (!hasTable) return;
|
|
27748
|
+
const existing = await db2("roles").where({ name: role.name }).first();
|
|
27749
|
+
if (!existing) {
|
|
27750
|
+
const description = role.description ? JSON.stringify(typeof role.description === "string" ? { en: role.description } : role.description) : null;
|
|
27751
|
+
await db2("roles").insert({
|
|
27752
|
+
id: generateId4(),
|
|
27753
|
+
name: role.name,
|
|
27754
|
+
description,
|
|
27755
|
+
is_system: role.is_system ?? false
|
|
27756
|
+
});
|
|
27757
|
+
logger2.info({ role: role.name }, "Seeded role");
|
|
27758
|
+
}
|
|
27759
|
+
if (role.permissions) {
|
|
27760
|
+
let rolePerms = permissionsMap.get(role.name);
|
|
27761
|
+
if (!rolePerms) {
|
|
27762
|
+
rolePerms = /* @__PURE__ */ new Map();
|
|
27763
|
+
permissionsMap.set(role.name, rolePerms);
|
|
27764
|
+
}
|
|
27765
|
+
for (const [subject2, actions] of Object.entries(role.permissions)) {
|
|
27766
|
+
const normalized = subject2.charAt(0).toUpperCase() + subject2.slice(1);
|
|
27767
|
+
rolePerms.set(normalized, actions);
|
|
27768
|
+
}
|
|
27769
|
+
}
|
|
27770
|
+
}
|
|
27771
|
+
},
|
|
27772
|
+
users: {
|
|
27773
|
+
async add(user) {
|
|
27774
|
+
const hasTable = await db2.schema.hasTable("users");
|
|
27775
|
+
if (!hasTable) return;
|
|
27776
|
+
const existing = await db2("users").where({ email: user.email }).first();
|
|
27777
|
+
if (existing) return;
|
|
27778
|
+
const userId = generateId4();
|
|
27779
|
+
await db2("users").insert({
|
|
27780
|
+
id: userId,
|
|
27781
|
+
type: user.type ?? "human",
|
|
27782
|
+
email: user.email,
|
|
27783
|
+
password: await hashPassword2(user.password),
|
|
27784
|
+
name: user.name ?? user.email
|
|
27785
|
+
});
|
|
27786
|
+
if (user.roles?.length) {
|
|
27787
|
+
for (const roleName of user.roles) {
|
|
27788
|
+
const role = await db2("roles").where({ name: roleName }).first();
|
|
27789
|
+
if (role) {
|
|
27790
|
+
await db2("user_roles").insert({
|
|
27791
|
+
id: generateId4(),
|
|
27792
|
+
user_id: userId,
|
|
27793
|
+
role_id: role.id
|
|
27794
|
+
});
|
|
27795
|
+
} else {
|
|
27796
|
+
logger2.warn({ role: roleName, user: user.email }, "Role not found, skipping assignment");
|
|
27797
|
+
}
|
|
27798
|
+
}
|
|
27799
|
+
}
|
|
27800
|
+
logger2.info({ email: user.email, roles: user.roles }, "Seeded user");
|
|
27801
|
+
}
|
|
27802
|
+
},
|
|
27803
|
+
plugin(pluginName) {
|
|
27804
|
+
return {
|
|
27805
|
+
entity(entityName) {
|
|
27806
|
+
return {
|
|
27807
|
+
async upsert(data, options) {
|
|
27808
|
+
let prefix = pluginPrefixes.get(pluginName) ?? "";
|
|
27809
|
+
if (!prefix) {
|
|
27810
|
+
for (const [key2, pfx] of pluginPrefixes) {
|
|
27811
|
+
if (pluginName === key2 || pluginName.endsWith(`-${key2}`)) {
|
|
27812
|
+
prefix = pfx;
|
|
27813
|
+
break;
|
|
27814
|
+
}
|
|
27815
|
+
}
|
|
27816
|
+
}
|
|
27817
|
+
const fullTable = `${prefix}${entityName}`;
|
|
27818
|
+
const hasTable = await db2.schema.hasTable(fullTable);
|
|
27819
|
+
if (!hasTable) {
|
|
27820
|
+
logger2.warn({ table: fullTable, plugin: pluginName }, "Table not found for plugin entity seed");
|
|
27821
|
+
return 0;
|
|
27822
|
+
}
|
|
27823
|
+
const key = options?.key ?? "id";
|
|
27824
|
+
let seeded = 0;
|
|
27825
|
+
for (const row of data) {
|
|
27826
|
+
const keyValue = row[key];
|
|
27827
|
+
if (keyValue != null) {
|
|
27828
|
+
const existing = await db2(fullTable).where({ [key]: keyValue }).first();
|
|
27829
|
+
if (existing) continue;
|
|
27830
|
+
}
|
|
27831
|
+
const insertData = { ...row };
|
|
27832
|
+
if (key === "id" && !insertData["id"]) {
|
|
27833
|
+
insertData["id"] = generateId4();
|
|
27834
|
+
}
|
|
27835
|
+
await db2(fullTable).insert(insertData);
|
|
27836
|
+
seeded++;
|
|
27837
|
+
}
|
|
27838
|
+
if (seeded > 0) {
|
|
27839
|
+
logger2.info({ table: fullTable, seeded }, "Seeded plugin entity data");
|
|
27840
|
+
}
|
|
27841
|
+
return seeded;
|
|
27842
|
+
}
|
|
27843
|
+
};
|
|
27844
|
+
}
|
|
27845
|
+
};
|
|
27846
|
+
},
|
|
27847
|
+
async raw(table, data) {
|
|
27848
|
+
logger2.warn({ table }, "Using seed.raw() \u2014 prefer typed helpers when available");
|
|
27849
|
+
const hasTable = await db2.schema.hasTable(table);
|
|
27850
|
+
if (!hasTable) {
|
|
27851
|
+
logger2.warn({ table }, "Table not found for raw seed");
|
|
27852
|
+
return 0;
|
|
27853
|
+
}
|
|
27854
|
+
const rows = Array.isArray(data) ? data : [data];
|
|
27855
|
+
await db2(table).insert(rows);
|
|
27856
|
+
return rows.length;
|
|
27857
|
+
}
|
|
27858
|
+
};
|
|
27859
|
+
return {
|
|
27860
|
+
ctx: seedContext,
|
|
27861
|
+
flushPermissions: () => {
|
|
27862
|
+
if (permissionsMap.size > 0 && deps.onPermissionsCollected) {
|
|
27863
|
+
deps.onPermissionsCollected(permissionsMap);
|
|
27864
|
+
}
|
|
27865
|
+
}
|
|
27866
|
+
};
|
|
27867
|
+
}
|
|
27868
|
+
var init_seed_context = __esm({
|
|
27869
|
+
"src/db/seed-context.ts"() {
|
|
27870
|
+
"use strict";
|
|
27871
|
+
}
|
|
27872
|
+
});
|
|
27873
|
+
|
|
27587
27874
|
// src/instrumentation.ts
|
|
27588
27875
|
var instrumentation_exports = {};
|
|
27589
27876
|
__export(instrumentation_exports, {
|
|
@@ -27648,7 +27935,9 @@ var init_instrumentation = __esm({
|
|
|
27648
27935
|
});
|
|
27649
27936
|
|
|
27650
27937
|
// src/core/server.ts
|
|
27938
|
+
import http from "http";
|
|
27651
27939
|
import { entityRoom as entityRoom6 } from "@gzl10/nexus-sdk";
|
|
27940
|
+
import { DEFAULT_LOCALES as DEFAULT_LOCALES2 } from "@gzl10/nexus-sdk";
|
|
27652
27941
|
async function runMigrationsAndSeeds(config3) {
|
|
27653
27942
|
initLoggerService(getLoggerConfig());
|
|
27654
27943
|
setLoggerInstance(getPinoLogger());
|
|
@@ -27701,7 +27990,7 @@ async function runMigrationsAndSeeds(config3) {
|
|
|
27701
27990
|
const sources = buildMigrationSources();
|
|
27702
27991
|
const migrationFiles = await loadAllMigrationFiles(sources);
|
|
27703
27992
|
if (migrationFiles.length > 0) {
|
|
27704
|
-
logger.info({
|
|
27993
|
+
logger.info({ sources: sources.length, files: migrationFiles.length }, "Running migration deploy...");
|
|
27705
27994
|
try {
|
|
27706
27995
|
await runMigrations(void 0, sources);
|
|
27707
27996
|
} catch (err) {
|
|
@@ -27710,7 +27999,7 @@ async function runMigrationsAndSeeds(config3) {
|
|
|
27710
27999
|
throw err;
|
|
27711
28000
|
}
|
|
27712
28001
|
}
|
|
27713
|
-
logger.
|
|
28002
|
+
logger.debug("Checking schema drift...");
|
|
27714
28003
|
const drift = await detectSchemaDrift();
|
|
27715
28004
|
if (drift && (drift.newTables.length > 0 || drift.alteredTables.length > 0)) {
|
|
27716
28005
|
const message = formatDriftMessage(drift);
|
|
@@ -27757,7 +28046,7 @@ ${dirs}`);
|
|
|
27757
28046
|
}
|
|
27758
28047
|
const allDefinitions = modules.flatMap((m2) => m2.definitions ?? []);
|
|
27759
28048
|
await createMemoryTables(allDefinitions);
|
|
27760
|
-
logger.
|
|
28049
|
+
logger.debug("Running seeds...");
|
|
27761
28050
|
for (const mod of modules) {
|
|
27762
28051
|
try {
|
|
27763
28052
|
await runModuleSeed(mod, ctx);
|
|
@@ -27765,12 +28054,39 @@ ${dirs}`);
|
|
|
27765
28054
|
logger.error({ module: mod.name, err }, "Seed failed - continuing with next module");
|
|
27766
28055
|
}
|
|
27767
28056
|
}
|
|
28057
|
+
if (config3?.onSeed) {
|
|
28058
|
+
const { getPlugins: getPlugins2 } = await Promise.resolve().then(() => (init_module_queries(), module_queries_exports));
|
|
28059
|
+
const { createMasterRegistry: createMasterRegistry2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports));
|
|
28060
|
+
const masterRegistry = ctx.services.has("masters") ? ctx.services.get("masters") : createMasterRegistry2();
|
|
28061
|
+
const pluginPrefixes = /* @__PURE__ */ new Map();
|
|
28062
|
+
for (const plugin of getPlugins2()) {
|
|
28063
|
+
pluginPrefixes.set(plugin.code, `${plugin.code}_`);
|
|
28064
|
+
const shortName = plugin.name.replace(/^@[^/]+\/nexus-plugin-/, "");
|
|
28065
|
+
if (shortName !== plugin.code) {
|
|
28066
|
+
pluginPrefixes.set(shortName, `${plugin.code}_`);
|
|
28067
|
+
}
|
|
28068
|
+
}
|
|
28069
|
+
const { createSeedContext: createSeedContext2 } = await Promise.resolve().then(() => (init_seed_context(), seed_context_exports));
|
|
28070
|
+
const { ctx: seedCtx, flushPermissions } = createSeedContext2({
|
|
28071
|
+
knex: ctx.db.knex,
|
|
28072
|
+
generateId: ctx.core.generateId,
|
|
28073
|
+
hashPassword: ctx.core.crypto.hashPassword,
|
|
28074
|
+
masterRegistry,
|
|
28075
|
+
pluginPrefixes,
|
|
28076
|
+
logger: ctx.core.logger,
|
|
28077
|
+
onPermissionsCollected: (perms) => setSeedPermissions(perms)
|
|
28078
|
+
});
|
|
28079
|
+
await config3.onSeed(seedCtx);
|
|
28080
|
+
await masterRegistry.seed(ctx);
|
|
28081
|
+
flushPermissions();
|
|
28082
|
+
}
|
|
27768
28083
|
}
|
|
27769
28084
|
async function start(config3) {
|
|
27770
28085
|
if (server) {
|
|
27771
28086
|
throw new Error("Server already running. Call stop() first.");
|
|
27772
28087
|
}
|
|
27773
28088
|
currentConfig = config3;
|
|
28089
|
+
setLocales(config3?.locales ?? DEFAULT_LOCALES2);
|
|
27774
28090
|
if (env.NODE_ENV === "development" && env.FRPC_SERVER && env.FRPC_SUBDOMAIN && !env.BACKEND_URL) {
|
|
27775
28091
|
process.env["BACKEND_URL"] = getTunnelUrl(env.FRPC_SUBDOMAIN, env.FRPC_SERVER);
|
|
27776
28092
|
}
|
|
@@ -27795,13 +28111,17 @@ async function start(config3) {
|
|
|
27795
28111
|
await initTelemetry2();
|
|
27796
28112
|
await runMigrationsAndSeeds(config3);
|
|
27797
28113
|
const effectiveCorsOrigins = buildEffectiveCorsOrigins(env.CORS_ORIGIN, config3?.spas);
|
|
28114
|
+
const httpServer = http.createServer();
|
|
27798
28115
|
const app = await createApp({
|
|
27799
28116
|
beforeRoutes: config3?.beforeRoutes,
|
|
27800
28117
|
afterRoutes: config3?.afterRoutes,
|
|
27801
|
-
spas: config3?.spas
|
|
28118
|
+
spas: config3?.spas,
|
|
28119
|
+
httpServer
|
|
27802
28120
|
});
|
|
28121
|
+
httpServer.on("request", app);
|
|
27803
28122
|
return new Promise((resolve2) => {
|
|
27804
|
-
server =
|
|
28123
|
+
server = httpServer;
|
|
28124
|
+
httpServer.listen(resolved.port, resolved.host, async () => {
|
|
27805
28125
|
const timeoutMs = parseInt(process.env["REQUEST_TIMEOUT_MS"] || "30000", 10);
|
|
27806
28126
|
if (timeoutMs > 0) {
|
|
27807
28127
|
server.setTimeout(timeoutMs);
|
|
@@ -27837,12 +28157,10 @@ async function start(config3) {
|
|
|
27837
28157
|
});
|
|
27838
28158
|
}
|
|
27839
28159
|
const baseUrl = env.BACKEND_URL || `http://localhost:${actualPort}`;
|
|
27840
|
-
logger.
|
|
27841
|
-
|
|
27842
|
-
if (resolved.ui.enabled)
|
|
27843
|
-
|
|
27844
|
-
}
|
|
27845
|
-
logger.info({ port: actualPort, mode: resolved.nodeEnv }, "Server started");
|
|
28160
|
+
logger.debug({ libPath: getLibPath(), projectPath: getProjectPath() }, "Paths");
|
|
28161
|
+
const urls = { api: `${baseUrl}/api/v1` };
|
|
28162
|
+
if (resolved.ui.enabled) urls["ui"] = baseUrl;
|
|
28163
|
+
logger.info({ port: actualPort, mode: resolved.nodeEnv, ...urls }, "Server started");
|
|
27846
28164
|
nexusEvents.emitEvent("server.started", { port: actualPort, host: resolved.host });
|
|
27847
28165
|
if (config3?.onReady) {
|
|
27848
28166
|
try {
|
|
@@ -27868,7 +28186,8 @@ async function stop() {
|
|
|
27868
28186
|
await resetSharedAdapters();
|
|
27869
28187
|
resetConfigCache();
|
|
27870
28188
|
clearCustomCaslRules();
|
|
27871
|
-
|
|
28189
|
+
clearSeedPermissions();
|
|
28190
|
+
await resetServeSPA();
|
|
27872
28191
|
return;
|
|
27873
28192
|
}
|
|
27874
28193
|
if (currentConfig?.beforeClose) {
|
|
@@ -27902,7 +28221,8 @@ async function stop() {
|
|
|
27902
28221
|
await resetSharedAdapters();
|
|
27903
28222
|
resetConfigCache();
|
|
27904
28223
|
clearCustomCaslRules();
|
|
27905
|
-
|
|
28224
|
+
clearSeedPermissions();
|
|
28225
|
+
await resetServeSPA();
|
|
27906
28226
|
currentConfig = void 0;
|
|
27907
28227
|
server = null;
|
|
27908
28228
|
nexusEvents.emitEvent("server.stopped");
|
|
@@ -27962,7 +28282,7 @@ var init_server = __esm({
|
|
|
27962
28282
|
init_cors();
|
|
27963
28283
|
init_logger();
|
|
27964
28284
|
init_error_middleware();
|
|
27965
|
-
|
|
28285
|
+
init_port_check();
|
|
27966
28286
|
init_engine();
|
|
27967
28287
|
init_context();
|
|
27968
28288
|
init_db();
|