@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/main.js
CHANGED
|
@@ -106,7 +106,7 @@ var init_package = __esm({
|
|
|
106
106
|
"package.json"() {
|
|
107
107
|
package_default = {
|
|
108
108
|
name: "@gzl10/nexus-backend",
|
|
109
|
-
version: "0.
|
|
109
|
+
version: "0.18.0",
|
|
110
110
|
description: "Backend as a Service (BaaS) with Express 5, Knex and CASL",
|
|
111
111
|
type: "module",
|
|
112
112
|
main: "./dist/index.js",
|
|
@@ -162,7 +162,7 @@ var init_package = __esm({
|
|
|
162
162
|
"jwt"
|
|
163
163
|
],
|
|
164
164
|
scripts: {
|
|
165
|
-
dev: "
|
|
165
|
+
dev: "node --watch-path=./src --import tsx/esm src/main.ts",
|
|
166
166
|
build: "tsup",
|
|
167
167
|
start: "node dist/main.js",
|
|
168
168
|
nexus: "tsx src/cli.ts",
|
|
@@ -218,22 +218,6 @@ var init_package = __esm({
|
|
|
218
218
|
zod: "^3.24.0"
|
|
219
219
|
},
|
|
220
220
|
devDependencies: {
|
|
221
|
-
"@gzl10/nexus-plugin-ai": "workspace:^",
|
|
222
|
-
"@gzl10/nexus-plugin-auth-providers": "workspace:^",
|
|
223
|
-
"@gzl10/nexus-plugin-charts": "workspace:^",
|
|
224
|
-
"@gzl10/nexus-plugin-cms": "workspace:^",
|
|
225
|
-
"@gzl10/nexus-plugin-compliance": "workspace:^",
|
|
226
|
-
"@gzl10/nexus-plugin-docker": "workspace:^",
|
|
227
|
-
"@gzl10/nexus-plugin-links": "workspace:*",
|
|
228
|
-
"@gzl10/nexus-plugin-notifications": "workspace:*",
|
|
229
|
-
"@gzl10/nexus-plugin-oidc-server": "workspace:^",
|
|
230
|
-
"@gzl10/nexus-plugin-plane": "workspace:^",
|
|
231
|
-
"@gzl10/nexus-plugin-prisma": "workspace:^",
|
|
232
|
-
"@gzl10/nexus-plugin-remote": "workspace:*",
|
|
233
|
-
"@gzl10/nexus-plugin-schedules": "workspace:*",
|
|
234
|
-
"@gzl10/nexus-plugin-scim": "workspace:^",
|
|
235
|
-
"@gzl10/nexus-plugin-tags": "workspace:*",
|
|
236
|
-
"@gzl10/nexus-plugin-webhooks": "workspace:*",
|
|
237
221
|
"@types/bcryptjs": "^2.4.0",
|
|
238
222
|
"@types/compression": "^1.8.1",
|
|
239
223
|
"@types/cookie-parser": "^1.4.10",
|
|
@@ -246,7 +230,16 @@ var init_package = __esm({
|
|
|
246
230
|
"pino-pretty": "^13.1.3",
|
|
247
231
|
"socket.io-client": "^4.8.3",
|
|
248
232
|
supertest: "^7.2.2",
|
|
249
|
-
tsx: "^4.21.0"
|
|
233
|
+
tsx: "^4.21.0",
|
|
234
|
+
vite: "^8.0.3"
|
|
235
|
+
},
|
|
236
|
+
peerDependencies: {
|
|
237
|
+
vite: ">=6.0.0"
|
|
238
|
+
},
|
|
239
|
+
peerDependenciesMeta: {
|
|
240
|
+
vite: {
|
|
241
|
+
optional: true
|
|
242
|
+
}
|
|
250
243
|
},
|
|
251
244
|
publishConfig: {
|
|
252
245
|
access: "public",
|
|
@@ -1147,15 +1140,17 @@ async function isWorkspacePackage(name) {
|
|
|
1147
1140
|
}
|
|
1148
1141
|
}
|
|
1149
1142
|
async function installPlugin(name, opts) {
|
|
1143
|
+
const isWorkspace = await isWorkspacePackage(name);
|
|
1150
1144
|
let pkg2;
|
|
1151
1145
|
if (opts?.version) {
|
|
1152
1146
|
pkg2 = `${name}@${opts.version}`;
|
|
1153
|
-
} else if (
|
|
1147
|
+
} else if (isWorkspace) {
|
|
1154
1148
|
pkg2 = `${name}@workspace:*`;
|
|
1155
1149
|
} else {
|
|
1156
1150
|
pkg2 = name;
|
|
1157
1151
|
}
|
|
1158
|
-
|
|
1152
|
+
const flag = isWorkspace ? "-D " : "";
|
|
1153
|
+
await execAsync(`pnpm add ${flag}${pkg2}`);
|
|
1159
1154
|
const filePath = getPluginsFilePath(opts?.projectPath);
|
|
1160
1155
|
const plugins = readPluginsFile(filePath);
|
|
1161
1156
|
plugins[name] = { enabled: true };
|
|
@@ -1246,7 +1241,7 @@ var init_table_prefix = __esm({
|
|
|
1246
1241
|
}
|
|
1247
1242
|
});
|
|
1248
1243
|
|
|
1249
|
-
// src/engine/store.ts
|
|
1244
|
+
// src/engine/module-store.ts
|
|
1250
1245
|
function resetStore() {
|
|
1251
1246
|
moduleStore.modules.length = 0;
|
|
1252
1247
|
moduleStore.plugins.clear();
|
|
@@ -1257,8 +1252,8 @@ function resetStore() {
|
|
|
1257
1252
|
moduleStore.tableToSubject.clear();
|
|
1258
1253
|
}
|
|
1259
1254
|
var moduleStore;
|
|
1260
|
-
var
|
|
1261
|
-
"src/engine/store.ts"() {
|
|
1255
|
+
var init_module_store = __esm({
|
|
1256
|
+
"src/engine/module-store.ts"() {
|
|
1262
1257
|
"use strict";
|
|
1263
1258
|
moduleStore = {
|
|
1264
1259
|
/** Registered modules with source metadata */
|
|
@@ -1311,7 +1306,7 @@ var init_id = __esm({
|
|
|
1311
1306
|
}
|
|
1312
1307
|
});
|
|
1313
1308
|
|
|
1314
|
-
// src/engine/extractors.ts
|
|
1309
|
+
// src/engine/definition-extractors.ts
|
|
1315
1310
|
function getTableAndSubject(def) {
|
|
1316
1311
|
const caslSubject = def.casl?.subject;
|
|
1317
1312
|
if (!TYPES_WITH_TABLE.has(def.type)) {
|
|
@@ -1379,8 +1374,8 @@ function validateModuleDependencies(modules) {
|
|
|
1379
1374
|
}
|
|
1380
1375
|
}
|
|
1381
1376
|
var TYPES_WITH_TABLE;
|
|
1382
|
-
var
|
|
1383
|
-
"src/engine/extractors.ts"() {
|
|
1377
|
+
var init_definition_extractors = __esm({
|
|
1378
|
+
"src/engine/definition-extractors.ts"() {
|
|
1384
1379
|
"use strict";
|
|
1385
1380
|
TYPES_WITH_TABLE = /* @__PURE__ */ new Set(["collection", "reference", "event", "config", "temp", "view", void 0]);
|
|
1386
1381
|
}
|
|
@@ -1500,9 +1495,9 @@ var init_registry = __esm({
|
|
|
1500
1495
|
"use strict";
|
|
1501
1496
|
init_plugin_ops();
|
|
1502
1497
|
init_table_prefix();
|
|
1503
|
-
|
|
1498
|
+
init_module_store();
|
|
1504
1499
|
init_id();
|
|
1505
|
-
|
|
1500
|
+
init_definition_extractors();
|
|
1506
1501
|
}
|
|
1507
1502
|
});
|
|
1508
1503
|
|
|
@@ -1554,7 +1549,28 @@ var init_paths = __esm({
|
|
|
1554
1549
|
}
|
|
1555
1550
|
});
|
|
1556
1551
|
|
|
1557
|
-
// src/engine/queries.ts
|
|
1552
|
+
// src/engine/module-queries.ts
|
|
1553
|
+
var module_queries_exports = {};
|
|
1554
|
+
__export(module_queries_exports, {
|
|
1555
|
+
getCoreManifest: () => getCoreManifest,
|
|
1556
|
+
getCoreModules: () => getCoreModules,
|
|
1557
|
+
getModule: () => getModule,
|
|
1558
|
+
getModules: () => getModules,
|
|
1559
|
+
getOrderedModules: () => getOrderedModules,
|
|
1560
|
+
getOrderedModulesInternal: () => getOrderedModulesInternal,
|
|
1561
|
+
getPlugin: () => getPlugin,
|
|
1562
|
+
getPluginByCode: () => getPluginByCode,
|
|
1563
|
+
getPlugins: () => getPlugins,
|
|
1564
|
+
getRegisteredSubjects: () => getRegisteredSubjects,
|
|
1565
|
+
getSubjectForTable: () => getSubjectForTable,
|
|
1566
|
+
getUserManifest: () => getUserManifest,
|
|
1567
|
+
getUserModules: () => getUserModules,
|
|
1568
|
+
hasModule: () => hasModule,
|
|
1569
|
+
hasPlugin: () => hasPlugin,
|
|
1570
|
+
hasPluginByCode: () => hasPluginByCode,
|
|
1571
|
+
hasUserApp: () => hasUserApp,
|
|
1572
|
+
isValidSubject: () => isValidSubject
|
|
1573
|
+
});
|
|
1558
1574
|
import { join as join4 } from "path";
|
|
1559
1575
|
import { readFileSync as readFileSync3 } from "fs";
|
|
1560
1576
|
function readPackageJson(dir) {
|
|
@@ -1588,12 +1604,22 @@ function getOrderedModulesInternal() {
|
|
|
1588
1604
|
moduleStore.modules.forEach(visit);
|
|
1589
1605
|
return sorted;
|
|
1590
1606
|
}
|
|
1607
|
+
function getModule(name) {
|
|
1608
|
+
const mod = moduleStore.modules.find((m) => m.name === name);
|
|
1609
|
+
return mod ? toModuleManifest(mod) : void 0;
|
|
1610
|
+
}
|
|
1611
|
+
function getPlugin(name) {
|
|
1612
|
+
return moduleStore.plugins.get(name);
|
|
1613
|
+
}
|
|
1591
1614
|
function getPlugins() {
|
|
1592
1615
|
return [...moduleStore.plugins.values()];
|
|
1593
1616
|
}
|
|
1594
1617
|
function getRegisteredSubjects() {
|
|
1595
1618
|
return [...moduleStore.subjects];
|
|
1596
1619
|
}
|
|
1620
|
+
function isValidSubject(subject2) {
|
|
1621
|
+
return moduleStore.subjects.has(subject2);
|
|
1622
|
+
}
|
|
1597
1623
|
function getSubjectForTable(table) {
|
|
1598
1624
|
return moduleStore.tableToSubject.get(table);
|
|
1599
1625
|
}
|
|
@@ -1640,10 +1666,10 @@ function getPluginByCode(code) {
|
|
|
1640
1666
|
function hasPluginByCode(code) {
|
|
1641
1667
|
return moduleStore.pluginsByCode.has(code);
|
|
1642
1668
|
}
|
|
1643
|
-
var
|
|
1644
|
-
"src/engine/queries.ts"() {
|
|
1669
|
+
var init_module_queries = __esm({
|
|
1670
|
+
"src/engine/module-queries.ts"() {
|
|
1645
1671
|
"use strict";
|
|
1646
|
-
|
|
1672
|
+
init_module_store();
|
|
1647
1673
|
init_paths();
|
|
1648
1674
|
}
|
|
1649
1675
|
});
|
|
@@ -2127,12 +2153,16 @@ var init_definitions = __esm({
|
|
|
2127
2153
|
labelPlural: { en: "Masters", es: "Maestros" },
|
|
2128
2154
|
labelField: "label",
|
|
2129
2155
|
timestamps: true,
|
|
2156
|
+
availableDisplayModes: ["board"],
|
|
2157
|
+
groupBy: "type",
|
|
2158
|
+
groupableFields: ["type"],
|
|
2159
|
+
//columnDragFields: ['is_active'],
|
|
2130
2160
|
fields: {
|
|
2131
2161
|
id: useTextField2({
|
|
2132
2162
|
label: { en: "ID", es: "ID" },
|
|
2133
2163
|
required: true,
|
|
2134
2164
|
size: 100,
|
|
2135
|
-
|
|
2165
|
+
hidden: true,
|
|
2136
2166
|
meta: { sortable: true }
|
|
2137
2167
|
}),
|
|
2138
2168
|
type: useTextField2({
|
|
@@ -2153,9 +2183,20 @@ var init_definitions = __esm({
|
|
|
2153
2183
|
is_active: isActiveField,
|
|
2154
2184
|
metadata: useJsonField({
|
|
2155
2185
|
label: { en: "Metadata", es: "Metadatos" },
|
|
2156
|
-
hint: {
|
|
2186
|
+
hint: {
|
|
2187
|
+
en: "Type-specific fields (symbol, flag, etc.)",
|
|
2188
|
+
es: "Campos espec\xEDficos del tipo"
|
|
2189
|
+
}
|
|
2157
2190
|
})
|
|
2158
2191
|
},
|
|
2192
|
+
hooks: () => ({
|
|
2193
|
+
beforeCreate: async (data) => {
|
|
2194
|
+
if (data["type"] && data["code"] && !data["id"]) {
|
|
2195
|
+
data["id"] = `${data["type"]}:${data["code"]}`;
|
|
2196
|
+
}
|
|
2197
|
+
return data;
|
|
2198
|
+
}
|
|
2199
|
+
}),
|
|
2159
2200
|
defaultSort: { field: "order", order: "asc" },
|
|
2160
2201
|
indexes: [{ columns: ["type", "code"], unique: true }],
|
|
2161
2202
|
casl: { subject: "Master", permissions: masterCaslPermissions }
|
|
@@ -2164,6 +2205,10 @@ var init_definitions = __esm({
|
|
|
2164
2205
|
});
|
|
2165
2206
|
|
|
2166
2207
|
// src/modules/masters/registry.ts
|
|
2208
|
+
var registry_exports = {};
|
|
2209
|
+
__export(registry_exports, {
|
|
2210
|
+
createMasterRegistry: () => createMasterRegistry
|
|
2211
|
+
});
|
|
2167
2212
|
function createMasterRegistry() {
|
|
2168
2213
|
const registrations = [];
|
|
2169
2214
|
return {
|
|
@@ -8842,15 +8887,18 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8842
8887
|
const meta = field["meta"];
|
|
8843
8888
|
return meta?.["sortable"] === true && !field["hidden"];
|
|
8844
8889
|
});
|
|
8890
|
+
const explicitGroupable = def["groupableFields"];
|
|
8845
8891
|
const groupableInputTypes = ["select", "switch", "checkbox", "radio", "tags"];
|
|
8846
|
-
const
|
|
8892
|
+
const autoGroupableFields = fieldEntries.filter(([, f]) => {
|
|
8847
8893
|
const field = f;
|
|
8848
8894
|
const inputType = field["input"];
|
|
8849
8895
|
return groupableInputTypes.includes(inputType ?? "") && !field["hidden"];
|
|
8850
8896
|
});
|
|
8897
|
+
const resolvedGroupableFields = explicitGroupable ?? (autoGroupableFields.length > 0 ? autoGroupableFields.map(([name]) => name) : void 0);
|
|
8851
8898
|
const entityType = def["type"] ?? "collection";
|
|
8852
8899
|
const explicitDisplayMode = def["displayMode"];
|
|
8853
|
-
const
|
|
8900
|
+
const explicitAvailableModes = def["availableDisplayModes"];
|
|
8901
|
+
const defaultDisplayMode = explicitDisplayMode ?? (explicitAvailableModes?.length === 1 ? explicitAvailableModes[0] : void 0) ?? (["tree", "dag"].includes(entityType) ? "tree" : "table");
|
|
8854
8902
|
const entityIdent = def["table"] ?? def["key"];
|
|
8855
8903
|
return {
|
|
8856
8904
|
id: def._id,
|
|
@@ -8870,9 +8918,11 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8870
8918
|
displayMode: explicitDisplayMode,
|
|
8871
8919
|
defaultDisplayMode,
|
|
8872
8920
|
availableDisplayModes: (() => {
|
|
8921
|
+
const explicit = def["availableDisplayModes"];
|
|
8922
|
+
if (explicit?.length) return explicit;
|
|
8873
8923
|
const modes = ["table", "list", "masonry"];
|
|
8874
8924
|
if (["tree", "dag"].includes(entityType)) modes.push("tree");
|
|
8875
|
-
if (
|
|
8925
|
+
if ((resolvedGroupableFields?.length ?? 0) > 0) modes.push("board");
|
|
8876
8926
|
if (def["calendarFrom"]) modes.push("calendar");
|
|
8877
8927
|
return modes;
|
|
8878
8928
|
})(),
|
|
@@ -8882,10 +8932,8 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8882
8932
|
value: name,
|
|
8883
8933
|
label: f["label"]
|
|
8884
8934
|
})),
|
|
8885
|
-
groupableFields:
|
|
8886
|
-
|
|
8887
|
-
label: f["label"]
|
|
8888
|
-
})) : void 0,
|
|
8935
|
+
groupableFields: resolvedGroupableFields,
|
|
8936
|
+
columnDragFields: def["columnDragFields"],
|
|
8889
8937
|
groupBy: def["groupBy"],
|
|
8890
8938
|
subgroupBy: def["subgroupBy"],
|
|
8891
8939
|
calendarFrom: def["calendarFrom"],
|
|
@@ -8915,7 +8963,7 @@ function toModuleDTO(mod, ctx) {
|
|
|
8915
8963
|
dependencies: mod.dependencies ?? [],
|
|
8916
8964
|
routePrefix: mod.routePrefix ?? `/${mod.name}`,
|
|
8917
8965
|
subjects: ctx.engine.getModuleSubjects(mod),
|
|
8918
|
-
definitions: (mod.definitions ?? []).map(
|
|
8966
|
+
definitions: (mod.definitions ?? []).filter((def) => ("expose" in def ? def.expose : true) !== false).map(
|
|
8919
8967
|
(def) => toEntityDefinitionDTO(def, ctx.engine, mod.name)
|
|
8920
8968
|
),
|
|
8921
8969
|
actions: (mod.actions ?? []).length > 0 ? (mod.actions ?? []).filter((a) => !a.hidden).map((a) => toActionDTO(a)) : void 0,
|
|
@@ -8925,21 +8973,6 @@ function toModuleDTO(mod, ctx) {
|
|
|
8925
8973
|
hasInit: !!mod.init
|
|
8926
8974
|
};
|
|
8927
8975
|
}
|
|
8928
|
-
function getOrderedModulesViaContext(ctx) {
|
|
8929
|
-
return ctx.engine.getModules();
|
|
8930
|
-
}
|
|
8931
|
-
async function runModuleSeedViaContext(mod, ctx) {
|
|
8932
|
-
if (!mod.seed) {
|
|
8933
|
-
return false;
|
|
8934
|
-
}
|
|
8935
|
-
try {
|
|
8936
|
-
await mod.seed(ctx);
|
|
8937
|
-
return true;
|
|
8938
|
-
} catch (err) {
|
|
8939
|
-
ctx.core.logger.error({ module: mod.name, err }, "Seed failed");
|
|
8940
|
-
return false;
|
|
8941
|
-
}
|
|
8942
|
-
}
|
|
8943
8976
|
var init_system_helpers = __esm({
|
|
8944
8977
|
"src/modules/system/system.helpers.ts"() {
|
|
8945
8978
|
"use strict";
|
|
@@ -9174,7 +9207,8 @@ function createSystemController(ctx) {
|
|
|
9174
9207
|
const plugins = engine.getPlugins();
|
|
9175
9208
|
const body = {
|
|
9176
9209
|
version: manifest.version,
|
|
9177
|
-
plugins: plugins.map((p) => p.code)
|
|
9210
|
+
plugins: plugins.map((p) => p.code),
|
|
9211
|
+
locales: ctx.locales
|
|
9178
9212
|
};
|
|
9179
9213
|
res.json(body);
|
|
9180
9214
|
}
|
|
@@ -10061,7 +10095,6 @@ var SYSTEM_TABLES, factoryResetAction;
|
|
|
10061
10095
|
var init_factory_reset_action = __esm({
|
|
10062
10096
|
"src/modules/system/actions/factory-reset.action.ts"() {
|
|
10063
10097
|
"use strict";
|
|
10064
|
-
init_system_helpers();
|
|
10065
10098
|
SYSTEM_TABLES = /* @__PURE__ */ new Set([
|
|
10066
10099
|
"_nexus_migrations",
|
|
10067
10100
|
"_nexus_migration_lock",
|
|
@@ -10104,8 +10137,8 @@ var init_factory_reset_action = __esm({
|
|
|
10104
10137
|
source: "core:system",
|
|
10105
10138
|
action: "factory_reset",
|
|
10106
10139
|
actorId: authReq.user?.id,
|
|
10107
|
-
ip: req
|
|
10108
|
-
userAgent: req
|
|
10140
|
+
ip: req?.ip,
|
|
10141
|
+
userAgent: req?.headers["user-agent"]
|
|
10109
10142
|
});
|
|
10110
10143
|
await new Promise((resolve2) => setImmediate(resolve2));
|
|
10111
10144
|
const allTables = await getAllTables(knex3);
|
|
@@ -10143,11 +10176,11 @@ var init_factory_reset_action = __esm({
|
|
|
10143
10176
|
} catch {
|
|
10144
10177
|
}
|
|
10145
10178
|
ctx.core.logger.info({ tables: dataTables.length }, "All data tables cleared");
|
|
10146
|
-
const modules =
|
|
10179
|
+
const modules = ctx.engine.getModules();
|
|
10147
10180
|
let modulesSeeded = 0;
|
|
10148
10181
|
for (const mod of modules) {
|
|
10149
10182
|
try {
|
|
10150
|
-
const seeded = await
|
|
10183
|
+
const seeded = await ctx.db.seedModule(mod);
|
|
10151
10184
|
if (seeded) modulesSeeded++;
|
|
10152
10185
|
} catch (err) {
|
|
10153
10186
|
ctx.core.logger.error({ module: mod.name, err }, "Seed failed during factory reset");
|
|
@@ -10201,8 +10234,8 @@ var init_restart_server_action = __esm({
|
|
|
10201
10234
|
source: "core:system",
|
|
10202
10235
|
action: "server_restart",
|
|
10203
10236
|
actorId: authReq.user?.id,
|
|
10204
|
-
ip: req
|
|
10205
|
-
userAgent: req
|
|
10237
|
+
ip: req?.ip,
|
|
10238
|
+
userAgent: req?.headers["user-agent"]
|
|
10206
10239
|
});
|
|
10207
10240
|
setTimeout(() => process.exit(0), 500);
|
|
10208
10241
|
return { success: true, message: "Server is restarting..." };
|
|
@@ -11824,7 +11857,7 @@ function createUploadMiddleware(ctx, options) {
|
|
|
11824
11857
|
const rateLimit2 = ctx.core.middleware.rateLimit({
|
|
11825
11858
|
windowMs: 60 * 1e3,
|
|
11826
11859
|
max: 20,
|
|
11827
|
-
message: "
|
|
11860
|
+
message: "Too many uploads, try again in 1 minute"
|
|
11828
11861
|
});
|
|
11829
11862
|
const upload = multer({
|
|
11830
11863
|
storage: multer.memoryStorage(),
|
|
@@ -12071,7 +12104,7 @@ function createStorageRoutes(ctx) {
|
|
|
12071
12104
|
}
|
|
12072
12105
|
res.status(201).json(results);
|
|
12073
12106
|
};
|
|
12074
|
-
const uploadRateLimit = ctx.core.middleware.rateLimit({ windowMs: 60 * 1e3, max: 20, message: "
|
|
12107
|
+
const uploadRateLimit = ctx.core.middleware.rateLimit({ windowMs: 60 * 1e3, max: 20, message: "Too many uploads, try again in 1 minute" });
|
|
12075
12108
|
if (auth) {
|
|
12076
12109
|
router.post("/upload/multiple", uploadRateLimit, auth, upload.array("files", 10), uploadMultiple);
|
|
12077
12110
|
} else {
|
|
@@ -12105,7 +12138,7 @@ function createStorageRoutes(ctx) {
|
|
|
12105
12138
|
delete filesController.create;
|
|
12106
12139
|
delete filesController.update;
|
|
12107
12140
|
filesController.delete = async (req, res) => {
|
|
12108
|
-
const id = req.params["id"];
|
|
12141
|
+
const id = String(req.params["id"] ?? "");
|
|
12109
12142
|
if (!id) throw new ValidationError2("VALIDATION_ERROR");
|
|
12110
12143
|
const storageService = getStorageService();
|
|
12111
12144
|
const file = await storageService.getById(id);
|
|
@@ -12369,7 +12402,7 @@ var init_users_entity = __esm({
|
|
|
12369
12402
|
],
|
|
12370
12403
|
nullable: true,
|
|
12371
12404
|
meta: { sortable: true },
|
|
12372
|
-
defaultValue: "
|
|
12405
|
+
defaultValue: "en"
|
|
12373
12406
|
}),
|
|
12374
12407
|
timezone: useSelectField9({
|
|
12375
12408
|
label: { en: "Timezone", es: "Zona horaria" },
|
|
@@ -12447,7 +12480,7 @@ var init_users_entity = __esm({
|
|
|
12447
12480
|
middleware: (ctx) => ctx.core.middleware.rateLimit({
|
|
12448
12481
|
windowMs: 15 * 60 * 1e3,
|
|
12449
12482
|
max: 5,
|
|
12450
|
-
message: "
|
|
12483
|
+
message: "Too many attempts, try again in 15 minutes"
|
|
12451
12484
|
}),
|
|
12452
12485
|
handler: async (ctx, input) => {
|
|
12453
12486
|
const {
|
|
@@ -12541,9 +12574,9 @@ var init_users_entity = __esm({
|
|
|
12541
12574
|
labelField: "role_id",
|
|
12542
12575
|
timestamps: true,
|
|
12543
12576
|
order: 15,
|
|
12544
|
-
routePrefix: "/user-roles",
|
|
12545
12577
|
hidden: true,
|
|
12546
12578
|
// Pivot table - managed via Users UI
|
|
12579
|
+
expose: false,
|
|
12547
12580
|
fields: {
|
|
12548
12581
|
id: useIdField4(),
|
|
12549
12582
|
user_id: useSelectField9({
|
|
@@ -12596,7 +12629,7 @@ function createUsersRoutes(ctx) {
|
|
|
12596
12629
|
if (originalUsersDelete) {
|
|
12597
12630
|
usersController.delete = async (req, res) => {
|
|
12598
12631
|
const authReq = req;
|
|
12599
|
-
const id = req.params["id"] ?? "";
|
|
12632
|
+
const id = String(req.params["id"] ?? "");
|
|
12600
12633
|
if (authReq.user?.id === id) {
|
|
12601
12634
|
throw new ctx.core.errors.ForbiddenError("No puedes eliminarte a ti mismo");
|
|
12602
12635
|
}
|
|
@@ -12671,7 +12704,7 @@ function createUsersRoutes(ctx) {
|
|
|
12671
12704
|
"/:userId/roles",
|
|
12672
12705
|
auth,
|
|
12673
12706
|
async (req, res) => {
|
|
12674
|
-
const roles = await usersService.getUserRoles(req.params["userId"]);
|
|
12707
|
+
const roles = await usersService.getUserRoles(String(req.params["userId"] ?? ""));
|
|
12675
12708
|
res.json(roles);
|
|
12676
12709
|
}
|
|
12677
12710
|
);
|
|
@@ -12681,18 +12714,19 @@ function createUsersRoutes(ctx) {
|
|
|
12681
12714
|
validate2({ body: z4.object({ roleId: z4.string().min(1) }) }),
|
|
12682
12715
|
async (req, res) => {
|
|
12683
12716
|
const { roleId } = req.body;
|
|
12684
|
-
|
|
12717
|
+
const userId = String(req.params["userId"] ?? "");
|
|
12718
|
+
await usersService.assignRole(userId, roleId);
|
|
12685
12719
|
ctx.events.notify("audit.log", {
|
|
12686
12720
|
source: "core:users",
|
|
12687
12721
|
action: "role_assigned",
|
|
12688
12722
|
actorId: req.user?.id,
|
|
12689
12723
|
resourceType: "user",
|
|
12690
|
-
resourceId:
|
|
12724
|
+
resourceId: userId,
|
|
12691
12725
|
ip: req.ip,
|
|
12692
12726
|
userAgent: req.headers["user-agent"],
|
|
12693
12727
|
metadata: { roleId }
|
|
12694
12728
|
});
|
|
12695
|
-
const roles = await usersService.getUserRoles(
|
|
12729
|
+
const roles = await usersService.getUserRoles(userId);
|
|
12696
12730
|
res.json(roles);
|
|
12697
12731
|
}
|
|
12698
12732
|
);
|
|
@@ -12702,18 +12736,19 @@ function createUsersRoutes(ctx) {
|
|
|
12702
12736
|
validate2({ body: z4.object({ roleIds: z4.array(z4.string()) }) }),
|
|
12703
12737
|
async (req, res) => {
|
|
12704
12738
|
const { roleIds } = req.body;
|
|
12705
|
-
|
|
12739
|
+
const userId = String(req.params["userId"] ?? "");
|
|
12740
|
+
await usersService.setRoles(userId, roleIds);
|
|
12706
12741
|
ctx.events.notify("audit.log", {
|
|
12707
12742
|
source: "core:users",
|
|
12708
12743
|
action: "roles_replaced",
|
|
12709
12744
|
actorId: req.user?.id,
|
|
12710
12745
|
resourceType: "user",
|
|
12711
|
-
resourceId:
|
|
12746
|
+
resourceId: userId,
|
|
12712
12747
|
ip: req.ip,
|
|
12713
12748
|
userAgent: req.headers["user-agent"],
|
|
12714
12749
|
metadata: { roleIds }
|
|
12715
12750
|
});
|
|
12716
|
-
const roles = await usersService.getUserRoles(
|
|
12751
|
+
const roles = await usersService.getUserRoles(userId);
|
|
12717
12752
|
res.json(roles);
|
|
12718
12753
|
}
|
|
12719
12754
|
);
|
|
@@ -12721,16 +12756,18 @@ function createUsersRoutes(ctx) {
|
|
|
12721
12756
|
"/:userId/roles/:roleId",
|
|
12722
12757
|
auth,
|
|
12723
12758
|
async (req, res) => {
|
|
12724
|
-
|
|
12759
|
+
const userId = String(req.params["userId"] ?? "");
|
|
12760
|
+
const roleId = String(req.params["roleId"] ?? "");
|
|
12761
|
+
await usersService.removeRole(userId, roleId);
|
|
12725
12762
|
ctx.events.notify("audit.log", {
|
|
12726
12763
|
source: "core:users",
|
|
12727
12764
|
action: "role_removed",
|
|
12728
12765
|
actorId: req.user?.id,
|
|
12729
12766
|
resourceType: "user",
|
|
12730
|
-
resourceId:
|
|
12767
|
+
resourceId: userId,
|
|
12731
12768
|
ip: req.ip,
|
|
12732
12769
|
userAgent: req.headers["user-agent"],
|
|
12733
|
-
metadata: { roleId
|
|
12770
|
+
metadata: { roleId }
|
|
12734
12771
|
});
|
|
12735
12772
|
res.json({ success: true });
|
|
12736
12773
|
}
|
|
@@ -13508,8 +13545,8 @@ var init_auth_entity = __esm({
|
|
|
13508
13545
|
label: { en: "Refresh Token", es: "Token de refresco" },
|
|
13509
13546
|
labelPlural: { en: "Refresh Tokens", es: "Tokens de refresco" },
|
|
13510
13547
|
labelField: "id",
|
|
13511
|
-
routePrefix: "/tokens",
|
|
13512
13548
|
retention: { days: 7, expiresField: "expires_at" },
|
|
13549
|
+
expose: false,
|
|
13513
13550
|
fields: {
|
|
13514
13551
|
id: useIdField5(),
|
|
13515
13552
|
token: useTextField9({
|
|
@@ -13574,8 +13611,8 @@ var init_auth_entity = __esm({
|
|
|
13574
13611
|
labelField: "provider",
|
|
13575
13612
|
timestamps: true,
|
|
13576
13613
|
hidden: true,
|
|
13614
|
+
expose: false,
|
|
13577
13615
|
order: 5,
|
|
13578
|
-
routePrefix: "/identities",
|
|
13579
13616
|
fields: {
|
|
13580
13617
|
id: useIdField5(),
|
|
13581
13618
|
user_id: useSelectField10({
|
|
@@ -16625,7 +16662,7 @@ var init_plugins_entity = __esm({
|
|
|
16625
16662
|
label: "Plugins",
|
|
16626
16663
|
icon: "mdi:puzzle",
|
|
16627
16664
|
labelField: "code",
|
|
16628
|
-
routePrefix: "/
|
|
16665
|
+
routePrefix: "/",
|
|
16629
16666
|
defaultSort: { field: "name", order: "asc" },
|
|
16630
16667
|
fields: {
|
|
16631
16668
|
name: useTextField12({
|
|
@@ -17038,12 +17075,12 @@ var init_loader = __esm({
|
|
|
17038
17075
|
"src/engine/loader.ts"() {
|
|
17039
17076
|
"use strict";
|
|
17040
17077
|
init_registry();
|
|
17041
|
-
|
|
17078
|
+
init_definition_extractors();
|
|
17042
17079
|
init_modules();
|
|
17043
17080
|
}
|
|
17044
17081
|
});
|
|
17045
17082
|
|
|
17046
|
-
// src/engine/
|
|
17083
|
+
// src/engine/subject-extractor.ts
|
|
17047
17084
|
function getModuleSubjects(mod) {
|
|
17048
17085
|
const subjects = /* @__PURE__ */ new Set();
|
|
17049
17086
|
for (const def of mod.definitions ?? []) {
|
|
@@ -17052,10 +17089,10 @@ function getModuleSubjects(mod) {
|
|
|
17052
17089
|
}
|
|
17053
17090
|
return [...subjects];
|
|
17054
17091
|
}
|
|
17055
|
-
var
|
|
17056
|
-
"src/engine/
|
|
17092
|
+
var init_subject_extractor = __esm({
|
|
17093
|
+
"src/engine/subject-extractor.ts"() {
|
|
17057
17094
|
"use strict";
|
|
17058
|
-
|
|
17095
|
+
init_definition_extractors();
|
|
17059
17096
|
}
|
|
17060
17097
|
});
|
|
17061
17098
|
|
|
@@ -17133,7 +17170,7 @@ function initSocketIO(httpServer, options) {
|
|
|
17133
17170
|
maxHttpBufferSize: 1e6
|
|
17134
17171
|
// 1MB - match Express json body limit
|
|
17135
17172
|
});
|
|
17136
|
-
logger.info({
|
|
17173
|
+
logger.info({ cors: corsOrigin }, "Socket.IO initialized");
|
|
17137
17174
|
io.use((socket, next) => {
|
|
17138
17175
|
const token = socket.handshake.auth?.["token"] || socket.handshake.query?.["token"];
|
|
17139
17176
|
if (token && typeof token === "string" && jwtSecret) {
|
|
@@ -17156,7 +17193,6 @@ function initSocketIO(httpServer, options) {
|
|
|
17156
17193
|
logger.warn({ code: err.code, message: err.message }, "Socket.IO connection error");
|
|
17157
17194
|
});
|
|
17158
17195
|
io.on("connection", handleConnection);
|
|
17159
|
-
logger.info("Socket.IO initialized");
|
|
17160
17196
|
nexusEvents.emitEvent("socket.initialized");
|
|
17161
17197
|
return io;
|
|
17162
17198
|
}
|
|
@@ -17737,6 +17773,12 @@ var init_app_error = __esm({
|
|
|
17737
17773
|
|
|
17738
17774
|
// src/core/abilities/ability.factory.ts
|
|
17739
17775
|
import { AbilityBuilder, createMongoAbility } from "@casl/ability";
|
|
17776
|
+
function setSeedPermissions(perms) {
|
|
17777
|
+
seedPermissions = perms;
|
|
17778
|
+
}
|
|
17779
|
+
function clearSeedPermissions() {
|
|
17780
|
+
seedPermissions = null;
|
|
17781
|
+
}
|
|
17740
17782
|
function setCustomCaslRules(fn) {
|
|
17741
17783
|
customCaslRules = fn;
|
|
17742
17784
|
}
|
|
@@ -17794,16 +17836,28 @@ async function defineAbilityFor(user, roleNames) {
|
|
|
17794
17836
|
if (customCaslRules) {
|
|
17795
17837
|
await customCaslRules(user, { can, cannot });
|
|
17796
17838
|
}
|
|
17839
|
+
if (seedPermissions && !isSuperuser) {
|
|
17840
|
+
for (const roleName of roleNames) {
|
|
17841
|
+
const rolePerms = seedPermissions.get(roleName);
|
|
17842
|
+
if (!rolePerms) continue;
|
|
17843
|
+
for (const [subject2, actions] of rolePerms) {
|
|
17844
|
+
for (const action of actions) {
|
|
17845
|
+
can(action, subject2);
|
|
17846
|
+
}
|
|
17847
|
+
}
|
|
17848
|
+
}
|
|
17849
|
+
}
|
|
17797
17850
|
return build();
|
|
17798
17851
|
}
|
|
17799
17852
|
function packRules(ability) {
|
|
17800
17853
|
return ability.rules;
|
|
17801
17854
|
}
|
|
17802
|
-
var customCaslRules, entityDefinitionsRegistry, SUPERUSER_ROLES;
|
|
17855
|
+
var customCaslRules, seedPermissions, entityDefinitionsRegistry, SUPERUSER_ROLES;
|
|
17803
17856
|
var init_ability_factory = __esm({
|
|
17804
17857
|
"src/core/abilities/ability.factory.ts"() {
|
|
17805
17858
|
"use strict";
|
|
17806
17859
|
init_logger();
|
|
17860
|
+
seedPermissions = null;
|
|
17807
17861
|
entityDefinitionsRegistry = null;
|
|
17808
17862
|
SUPERUSER_ROLES = ["ADMIN", "OWNER"];
|
|
17809
17863
|
}
|
|
@@ -17874,7 +17928,7 @@ var init_validate_middleware = __esm({
|
|
|
17874
17928
|
import { z as z8 } from "zod";
|
|
17875
17929
|
function resolveConfig() {
|
|
17876
17930
|
env = envSchema.parse(process.env);
|
|
17877
|
-
process.env
|
|
17931
|
+
process.env["TZ"] = env.TZ;
|
|
17878
17932
|
resolvedConfig = {
|
|
17879
17933
|
nodeEnv: env.NODE_ENV,
|
|
17880
17934
|
port: env.PORT,
|
|
@@ -17929,7 +17983,7 @@ var init_env = __esm({
|
|
|
17929
17983
|
FRPC_SUBDOMAIN: z8.string().optional()
|
|
17930
17984
|
});
|
|
17931
17985
|
env = envSchema.parse(process.env);
|
|
17932
|
-
process.env
|
|
17986
|
+
process.env["TZ"] = env.TZ;
|
|
17933
17987
|
resolvedConfig = null;
|
|
17934
17988
|
}
|
|
17935
17989
|
});
|
|
@@ -18356,7 +18410,7 @@ var init_sequence = __esm({
|
|
|
18356
18410
|
}
|
|
18357
18411
|
});
|
|
18358
18412
|
|
|
18359
|
-
// src/core/utils/
|
|
18413
|
+
// src/core/utils/safe-json.ts
|
|
18360
18414
|
function safeJsonParse(logger2, jsonString, fallback, context) {
|
|
18361
18415
|
try {
|
|
18362
18416
|
return JSON.parse(jsonString);
|
|
@@ -18365,8 +18419,8 @@ function safeJsonParse(logger2, jsonString, fallback, context) {
|
|
|
18365
18419
|
return fallback;
|
|
18366
18420
|
}
|
|
18367
18421
|
}
|
|
18368
|
-
var
|
|
18369
|
-
"src/core/utils/
|
|
18422
|
+
var init_safe_json = __esm({
|
|
18423
|
+
"src/core/utils/safe-json.ts"() {
|
|
18370
18424
|
"use strict";
|
|
18371
18425
|
}
|
|
18372
18426
|
});
|
|
@@ -18375,14 +18429,15 @@ var init_error_handler = __esm({
|
|
|
18375
18429
|
import express from "express";
|
|
18376
18430
|
import { resolve, join as join9 } from "path";
|
|
18377
18431
|
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
18378
|
-
function createServeSPA(app) {
|
|
18379
|
-
return (endpoint, distPath, options = {}) => {
|
|
18432
|
+
function createServeSPA(app, httpServer) {
|
|
18433
|
+
return async (endpoint, distPath, options = {}) => {
|
|
18380
18434
|
const {
|
|
18381
18435
|
maxAge = "1d",
|
|
18382
18436
|
etag = true,
|
|
18383
18437
|
immutable = false,
|
|
18384
18438
|
index = "index.html",
|
|
18385
|
-
absolute = false
|
|
18439
|
+
absolute = false,
|
|
18440
|
+
viteSrc
|
|
18386
18441
|
} = options;
|
|
18387
18442
|
if (endpoint === "/api" || endpoint.startsWith("/api/")) {
|
|
18388
18443
|
logger.error(`Cannot mount SPA on ${endpoint} - reserved for API routes`);
|
|
@@ -18393,58 +18448,117 @@ function createServeSPA(app) {
|
|
|
18393
18448
|
return;
|
|
18394
18449
|
}
|
|
18395
18450
|
registeredEndpoints.add(endpoint);
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
|
|
18399
|
-
|
|
18400
|
-
const projectPath2 = resolve(getProjectPath(), distPath);
|
|
18401
|
-
if (existsSync9(projectPath2)) {
|
|
18402
|
-
resolvedPath = projectPath2;
|
|
18451
|
+
if (env.NODE_ENV === "development" && viteSrc) {
|
|
18452
|
+
const srcPath = resolve(getProjectPath(), viteSrc);
|
|
18453
|
+
if (!existsSync9(srcPath)) {
|
|
18454
|
+
logger.warn({ endpoint, viteSrc, resolved: srcPath }, "Vite source not found \u2014 falling back to static");
|
|
18403
18455
|
} else {
|
|
18404
|
-
|
|
18456
|
+
const mounted = await mountViteDevMiddleware(app, endpoint, srcPath, httpServer);
|
|
18457
|
+
if (mounted) return;
|
|
18405
18458
|
}
|
|
18406
18459
|
}
|
|
18407
|
-
|
|
18408
|
-
|
|
18409
|
-
|
|
18460
|
+
mountStaticSPA(app, endpoint, distPath, { maxAge, etag, immutable, index, absolute });
|
|
18461
|
+
};
|
|
18462
|
+
}
|
|
18463
|
+
async function mountViteDevMiddleware(app, endpoint, srcPath, httpServer) {
|
|
18464
|
+
try {
|
|
18465
|
+
const vite = await import("vite");
|
|
18466
|
+
const apiUrl = env.BACKEND_URL ? `${env.BACKEND_URL}/api/v1` : "/api/v1";
|
|
18467
|
+
const server2 = await vite.createServer({
|
|
18468
|
+
root: srcPath,
|
|
18469
|
+
server: {
|
|
18470
|
+
middlewareMode: true,
|
|
18471
|
+
allowedHosts: true,
|
|
18472
|
+
hmr: httpServer ? { server: httpServer } : true
|
|
18473
|
+
},
|
|
18474
|
+
plugins: [{
|
|
18475
|
+
name: "nexus-config-inject",
|
|
18476
|
+
transformIndexHtml(html) {
|
|
18477
|
+
const config3 = JSON.stringify({ apiUrl });
|
|
18478
|
+
return html.replace("</head>", `<script>window.__NEXUS__=${config3}</script>
|
|
18479
|
+
</head>`);
|
|
18480
|
+
}
|
|
18481
|
+
}],
|
|
18482
|
+
appType: "spa",
|
|
18483
|
+
clearScreen: false
|
|
18484
|
+
});
|
|
18485
|
+
viteServers.push(server2);
|
|
18486
|
+
if (endpoint === "/") {
|
|
18487
|
+
app.use(server2.middlewares);
|
|
18488
|
+
} else {
|
|
18489
|
+
app.use(endpoint, server2.middlewares);
|
|
18410
18490
|
}
|
|
18411
|
-
|
|
18412
|
-
|
|
18413
|
-
|
|
18414
|
-
|
|
18415
|
-
|
|
18416
|
-
|
|
18417
|
-
if (existsSync9(indexPath)) {
|
|
18418
|
-
const rawHtml = readFileSync5(indexPath, "utf-8");
|
|
18419
|
-
const apiUrl = env.BACKEND_URL ? `${env.BACKEND_URL}/api/v1` : "/api/v1";
|
|
18420
|
-
const nexusConfig = JSON.stringify({ apiUrl });
|
|
18421
|
-
injectedHtml = rawHtml.replace(
|
|
18422
|
-
"</head>",
|
|
18423
|
-
`<script>window.__NEXUS__=${nexusConfig}</script>
|
|
18424
|
-
</head>`
|
|
18425
|
-
);
|
|
18491
|
+
logger.info({ path: srcPath }, `Vite dev server mounted at ${endpoint} (HMR enabled)`);
|
|
18492
|
+
return true;
|
|
18493
|
+
} catch (err) {
|
|
18494
|
+
if (err.code === "ERR_MODULE_NOT_FOUND" || err.code === "MODULE_NOT_FOUND") {
|
|
18495
|
+
logger.warn(`vite not installed \u2014 falling back to static serving for ${endpoint}`);
|
|
18496
|
+
return false;
|
|
18426
18497
|
}
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18430
|
-
|
|
18431
|
-
|
|
18432
|
-
|
|
18433
|
-
|
|
18434
|
-
|
|
18435
|
-
|
|
18436
|
-
|
|
18498
|
+
logger.error({ err }, `Failed to mount Vite dev server at ${endpoint}`);
|
|
18499
|
+
return false;
|
|
18500
|
+
}
|
|
18501
|
+
}
|
|
18502
|
+
function mountStaticSPA(app, endpoint, distPath, options) {
|
|
18503
|
+
const { maxAge, etag, immutable, index, absolute } = options;
|
|
18504
|
+
let resolvedPath;
|
|
18505
|
+
if (absolute) {
|
|
18506
|
+
resolvedPath = distPath;
|
|
18507
|
+
} else {
|
|
18508
|
+
const projectPath2 = resolve(getProjectPath(), distPath);
|
|
18509
|
+
if (existsSync9(projectPath2)) {
|
|
18510
|
+
resolvedPath = projectPath2;
|
|
18437
18511
|
} else {
|
|
18438
|
-
|
|
18439
|
-
app.get(`${endpoint}/{*splat}`, fallbackHandler);
|
|
18512
|
+
resolvedPath = resolve(getLibPath(), distPath);
|
|
18440
18513
|
}
|
|
18441
|
-
|
|
18514
|
+
}
|
|
18515
|
+
if (!existsSync9(resolvedPath)) {
|
|
18516
|
+
logger.warn({ endpoint, distPath, hint: "Build the frontend first" }, `SPA directory not found: ${resolvedPath}`);
|
|
18517
|
+
return;
|
|
18518
|
+
}
|
|
18519
|
+
const indexPath = join9(resolvedPath, index);
|
|
18520
|
+
if (!existsSync9(indexPath)) {
|
|
18521
|
+
logger.warn({ endpoint, index }, `Index file not found: ${indexPath}`);
|
|
18522
|
+
}
|
|
18523
|
+
app.use(endpoint, express.static(resolvedPath, { maxAge, etag, immutable }));
|
|
18524
|
+
let injectedHtml = "";
|
|
18525
|
+
if (existsSync9(indexPath)) {
|
|
18526
|
+
const rawHtml = readFileSync5(indexPath, "utf-8");
|
|
18527
|
+
const apiUrl = env.BACKEND_URL ? `${env.BACKEND_URL}/api/v1` : "/api/v1";
|
|
18528
|
+
const nexusConfig = JSON.stringify({ apiUrl });
|
|
18529
|
+
injectedHtml = rawHtml.replace(
|
|
18530
|
+
"</head>",
|
|
18531
|
+
`<script>window.__NEXUS__=${nexusConfig}</script>
|
|
18532
|
+
</head>`
|
|
18533
|
+
);
|
|
18534
|
+
}
|
|
18535
|
+
const fallbackHandler = (_req, res) => {
|
|
18536
|
+
if (!injectedHtml) {
|
|
18537
|
+
res.status(404).send("index.html not found");
|
|
18538
|
+
return;
|
|
18539
|
+
}
|
|
18540
|
+
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
18541
|
+
res.type("html").send(injectedHtml);
|
|
18442
18542
|
};
|
|
18543
|
+
if (endpoint === "/") {
|
|
18544
|
+
app.get("{*splat}", fallbackHandler);
|
|
18545
|
+
} else {
|
|
18546
|
+
app.get(endpoint, fallbackHandler);
|
|
18547
|
+
app.get(`${endpoint}/{*splat}`, fallbackHandler);
|
|
18548
|
+
}
|
|
18549
|
+
logger.info({ path: resolvedPath }, `SPA mounted at ${endpoint}`);
|
|
18443
18550
|
}
|
|
18444
|
-
function
|
|
18551
|
+
async function resetServeSPA() {
|
|
18552
|
+
for (const server2 of viteServers) {
|
|
18553
|
+
try {
|
|
18554
|
+
await server2.close();
|
|
18555
|
+
} catch {
|
|
18556
|
+
}
|
|
18557
|
+
}
|
|
18558
|
+
viteServers.length = 0;
|
|
18445
18559
|
registeredEndpoints.clear();
|
|
18446
18560
|
}
|
|
18447
|
-
var registeredEndpoints;
|
|
18561
|
+
var registeredEndpoints, viteServers;
|
|
18448
18562
|
var init_spa_handler = __esm({
|
|
18449
18563
|
"src/core/spa-handler.ts"() {
|
|
18450
18564
|
"use strict";
|
|
@@ -18452,6 +18566,7 @@ var init_spa_handler = __esm({
|
|
|
18452
18566
|
init_logger();
|
|
18453
18567
|
init_env();
|
|
18454
18568
|
registeredEndpoints = /* @__PURE__ */ new Set();
|
|
18569
|
+
viteServers = [];
|
|
18455
18570
|
}
|
|
18456
18571
|
});
|
|
18457
18572
|
|
|
@@ -19022,7 +19137,7 @@ var init_core = __esm({
|
|
|
19022
19137
|
init_id();
|
|
19023
19138
|
init_sequence();
|
|
19024
19139
|
init_paths();
|
|
19025
|
-
|
|
19140
|
+
init_safe_json();
|
|
19026
19141
|
init_spa_handler();
|
|
19027
19142
|
init_cache();
|
|
19028
19143
|
init_jwt();
|
|
@@ -19737,7 +19852,9 @@ var init_base_service = __esm({
|
|
|
19737
19852
|
const countResult = await qb.clone().count("* as count").first();
|
|
19738
19853
|
const total = Number(countResult?.count ?? 0);
|
|
19739
19854
|
qb = this.applySortingWithDefaults(qb, query);
|
|
19740
|
-
|
|
19855
|
+
if (limit > 0) {
|
|
19856
|
+
qb = qb.limit(limit).offset(offset);
|
|
19857
|
+
}
|
|
19741
19858
|
const rawItems = await qb;
|
|
19742
19859
|
const items = this.parseJsonFieldsFromArray(rawItems);
|
|
19743
19860
|
const processedItems = await this.afterFindAll(items);
|
|
@@ -19822,7 +19939,7 @@ var init_base_service = __esm({
|
|
|
19822
19939
|
});
|
|
19823
19940
|
}
|
|
19824
19941
|
const total = result.length;
|
|
19825
|
-
const paginatedItems = result.slice(offset, offset + limit);
|
|
19942
|
+
const paginatedItems = limit === 0 ? result : result.slice(offset, offset + limit);
|
|
19826
19943
|
return this.buildPaginatedResult(paginatedItems, total, page, limit);
|
|
19827
19944
|
}
|
|
19828
19945
|
/**
|
|
@@ -20077,14 +20194,14 @@ var init_base_service = __esm({
|
|
|
20077
20194
|
* Build paginated result from items and count
|
|
20078
20195
|
*/
|
|
20079
20196
|
buildPaginatedResult(items, total, page, limit) {
|
|
20080
|
-
const totalPages = Math.ceil(total / limit);
|
|
20197
|
+
const totalPages = limit === 0 ? 1 : Math.ceil(total / limit);
|
|
20081
20198
|
return {
|
|
20082
20199
|
items,
|
|
20083
20200
|
total,
|
|
20084
|
-
page,
|
|
20085
|
-
limit,
|
|
20201
|
+
page: limit === 0 ? 1 : page,
|
|
20202
|
+
limit: limit === 0 ? total : limit,
|
|
20086
20203
|
totalPages,
|
|
20087
|
-
hasNext: page < totalPages
|
|
20204
|
+
hasNext: limit === 0 ? false : page < totalPages
|
|
20088
20205
|
};
|
|
20089
20206
|
}
|
|
20090
20207
|
/**
|
|
@@ -20093,8 +20210,8 @@ var init_base_service = __esm({
|
|
|
20093
20210
|
getPagination(query) {
|
|
20094
20211
|
const maxLimit = query?.maxLimit ?? 100;
|
|
20095
20212
|
const page = Math.max(1, query?.page ?? 1);
|
|
20096
|
-
const limit = Math.min(maxLimit, Math.max(1, query?.limit ?? 20));
|
|
20097
|
-
const offset = (page - 1) * limit;
|
|
20213
|
+
const limit = query?.limit === 0 ? 0 : Math.min(maxLimit, Math.max(1, query?.limit ?? 20));
|
|
20214
|
+
const offset = limit === 0 ? 0 : (page - 1) * limit;
|
|
20098
20215
|
return { page, limit, offset };
|
|
20099
20216
|
}
|
|
20100
20217
|
/**
|
|
@@ -21741,7 +21858,8 @@ function createEntityController(service, definition, ctx) {
|
|
|
21741
21858
|
async list(req, res) {
|
|
21742
21859
|
checkPermission(req, "read");
|
|
21743
21860
|
const page = Math.max(1, parseInt(req.query["page"]) || 1);
|
|
21744
|
-
const
|
|
21861
|
+
const rawLimit = parseInt(req.query["limit"]);
|
|
21862
|
+
const limit = rawLimit === 0 ? 0 : Math.min(100, Math.max(1, rawLimit || 20));
|
|
21745
21863
|
let filters;
|
|
21746
21864
|
if (req.query["filters"]) {
|
|
21747
21865
|
filters = parseFilters(req.query["filters"], ctx.core.errors);
|
|
@@ -21784,7 +21902,7 @@ function createEntityController(service, definition, ctx) {
|
|
|
21784
21902
|
* Get single entity
|
|
21785
21903
|
*/
|
|
21786
21904
|
async get(req, res) {
|
|
21787
|
-
const id = req.params["id"] ?? "";
|
|
21905
|
+
const id = String(req.params["id"] ?? "");
|
|
21788
21906
|
const entity = await service.findById(id);
|
|
21789
21907
|
if (!entity) {
|
|
21790
21908
|
throw new ctx.core.errors.NotFoundError(`${resolveLocalized6(definition.label, "en")} not found`);
|
|
@@ -21835,7 +21953,7 @@ function createEntityController(service, definition, ctx) {
|
|
|
21835
21953
|
if (!service.update) {
|
|
21836
21954
|
throw new ctx.core.errors.ForbiddenError(`${resolveLocalized6(definition.label, "en")} does not support update`);
|
|
21837
21955
|
}
|
|
21838
|
-
const id = req.params["id"] ?? "";
|
|
21956
|
+
const id = String(req.params["id"] ?? "");
|
|
21839
21957
|
const existing = await service.findById(id);
|
|
21840
21958
|
if (!existing) {
|
|
21841
21959
|
throw new ctx.core.errors.NotFoundError(`${resolveLocalized6(definition.label, "en")} not found`);
|
|
@@ -21867,7 +21985,7 @@ function createEntityController(service, definition, ctx) {
|
|
|
21867
21985
|
if (!service.delete) {
|
|
21868
21986
|
throw new ctx.core.errors.ForbiddenError(`${resolveLocalized6(definition.label, "en")} does not support delete`);
|
|
21869
21987
|
}
|
|
21870
|
-
const id = req.params["id"] ?? "";
|
|
21988
|
+
const id = String(req.params["id"] ?? "");
|
|
21871
21989
|
const existing = await service.findById(id);
|
|
21872
21990
|
if (!existing) {
|
|
21873
21991
|
throw new ctx.core.errors.NotFoundError(`${resolveLocalized6(definition.label, "en")} not found`);
|
|
@@ -22066,7 +22184,7 @@ function createActionHandler(action, definition, ctx, scope = "row") {
|
|
|
22066
22184
|
const caslSubject = (action.casl && "subject" in action.casl ? action.casl.subject : void 0) ?? definition.casl?.subject ?? capitalizeFirst(entityName ?? "Entity");
|
|
22067
22185
|
const hasCasl = !action.skipAuth && (!!definition.casl || !!action.casl);
|
|
22068
22186
|
return async (req, res) => {
|
|
22069
|
-
const recordId = scope === "row" ? req.params["id"] : void 0;
|
|
22187
|
+
const recordId = scope === "row" ? String(req.params["id"] ?? "") : void 0;
|
|
22070
22188
|
const authReq = req;
|
|
22071
22189
|
if (hasCasl && !authReq.ability) {
|
|
22072
22190
|
ctx.core.logger.warn({ action: "execute", subject: caslSubject, actionKey: action.key, reqId: req.requestId, decision: "deny", reason: "unauthenticated" }, "authz:deny");
|
|
@@ -22140,6 +22258,8 @@ var init_entity_controller = __esm({
|
|
|
22140
22258
|
// src/runtime/routes/entity.routes.ts
|
|
22141
22259
|
function createEntityRouter(controller, definition, ctx, service) {
|
|
22142
22260
|
const router = ctx.createRouter();
|
|
22261
|
+
const expose = "expose" in definition ? definition.expose : true;
|
|
22262
|
+
if (expose === false) return router;
|
|
22143
22263
|
const type2 = definition.type ?? "collection";
|
|
22144
22264
|
const isSingleton = (type2 === "single" || type2 === "config") && !("scopeField" in definition && definition.scopeField);
|
|
22145
22265
|
const entityDef = definition;
|
|
@@ -22234,44 +22354,44 @@ function createEntityRouter(controller, definition, ctx, service) {
|
|
|
22234
22354
|
res.json(await treeSvc.findRoots());
|
|
22235
22355
|
}));
|
|
22236
22356
|
router.post("/:id/move", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22237
|
-
const id = req.params["id"] ?? "";
|
|
22357
|
+
const id = String(req.params["id"] ?? "");
|
|
22238
22358
|
const { parentId } = req.body;
|
|
22239
22359
|
res.json(await treeSvc.move(id, parentId ?? null));
|
|
22240
22360
|
}));
|
|
22241
22361
|
router.get("/:id/ancestors", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22242
|
-
const id = req.params["id"] ?? "";
|
|
22362
|
+
const id = String(req.params["id"] ?? "");
|
|
22243
22363
|
res.json(await treeSvc.getAncestors(id));
|
|
22244
22364
|
}));
|
|
22245
22365
|
router.get("/:id/descendants", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22246
|
-
const id = req.params["id"] ?? "";
|
|
22366
|
+
const id = String(req.params["id"] ?? "");
|
|
22247
22367
|
res.json(await treeSvc.getDescendants(id));
|
|
22248
22368
|
}));
|
|
22249
22369
|
router.get("/:id/children", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22250
|
-
const id = req.params["id"] ?? "";
|
|
22370
|
+
const id = String(req.params["id"] ?? "");
|
|
22251
22371
|
res.json(await treeSvc.findChildren(id));
|
|
22252
22372
|
}));
|
|
22253
22373
|
}
|
|
22254
22374
|
if (type2 === "dag" && service) {
|
|
22255
22375
|
const dagSvc = service;
|
|
22256
22376
|
router.get("/:id/parents", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22257
|
-
const id = req.params["id"] ?? "";
|
|
22377
|
+
const id = String(req.params["id"] ?? "");
|
|
22258
22378
|
res.json(await dagSvc.getParents(id));
|
|
22259
22379
|
}));
|
|
22260
22380
|
router.put("/:id/parents", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22261
|
-
const id = req.params["id"] ?? "";
|
|
22381
|
+
const id = String(req.params["id"] ?? "");
|
|
22262
22382
|
const { parentIds } = req.body;
|
|
22263
22383
|
await dagSvc.setParents(id, parentIds);
|
|
22264
22384
|
res.status(204).send();
|
|
22265
22385
|
}));
|
|
22266
22386
|
router.post("/:id/parents", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22267
|
-
const id = req.params["id"] ?? "";
|
|
22387
|
+
const id = String(req.params["id"] ?? "");
|
|
22268
22388
|
const { parentId } = req.body;
|
|
22269
22389
|
await dagSvc.addParent(id, parentId);
|
|
22270
22390
|
res.status(204).send();
|
|
22271
22391
|
}));
|
|
22272
22392
|
router.delete("/:id/parents/:parentId", ...entityMiddleware, ...authMiddleware, asyncHandler(async (req, res) => {
|
|
22273
|
-
const id = req.params["id"] ?? "";
|
|
22274
|
-
const parentId = req.params["parentId"] ?? "";
|
|
22393
|
+
const id = String(req.params["id"] ?? "");
|
|
22394
|
+
const parentId = String(req.params["parentId"] ?? "");
|
|
22275
22395
|
await dagSvc.removeParent(id, parentId);
|
|
22276
22396
|
res.status(204).send();
|
|
22277
22397
|
}));
|
|
@@ -23205,17 +23325,18 @@ async function createModuleRouters(ctx, definitions, modulePrefix) {
|
|
|
23205
23325
|
const runtime = await createEntityRuntimeAsync(ctx, definition);
|
|
23206
23326
|
const route = inferEntityRoutePath(definition);
|
|
23207
23327
|
const entityLabel = resolveLocalized7(definition.label, "en");
|
|
23208
|
-
|
|
23328
|
+
const isExposed = !("expose" in definition && definition.expose === false);
|
|
23329
|
+
if (isExposed && modulePrefix && route === modulePrefix) {
|
|
23209
23330
|
ctx.core.logger.warn(
|
|
23210
23331
|
`Entity "${entityLabel}" inferred route "${route}" duplicates module prefix \u2014 add routePrefix to the entity definition to fix`
|
|
23211
23332
|
);
|
|
23212
23333
|
}
|
|
23213
|
-
if (routeMap.has(route)) {
|
|
23334
|
+
if (isExposed && routeMap.has(route)) {
|
|
23214
23335
|
ctx.core.logger.warn(
|
|
23215
23336
|
`Entity "${entityLabel}" route "${route}" collides with "${routeMap.get(route)}" in the same module`
|
|
23216
23337
|
);
|
|
23217
23338
|
}
|
|
23218
|
-
routeMap.set(route, entityLabel);
|
|
23339
|
+
if (isExposed) routeMap.set(route, entityLabel);
|
|
23219
23340
|
router.use(route, runtime.router);
|
|
23220
23341
|
const key = getServiceKey(definition);
|
|
23221
23342
|
if (ctx.services.has(key)) {
|
|
@@ -23431,7 +23552,7 @@ var init_runtime = __esm({
|
|
|
23431
23552
|
}
|
|
23432
23553
|
});
|
|
23433
23554
|
|
|
23434
|
-
// src/db/
|
|
23555
|
+
// src/db/seed-runner.ts
|
|
23435
23556
|
import { existsSync as existsSync10 } from "fs";
|
|
23436
23557
|
import { join as join10 } from "path";
|
|
23437
23558
|
import { pathToFileURL } from "url";
|
|
@@ -23499,8 +23620,8 @@ function hasSeedData(seed5) {
|
|
|
23499
23620
|
if (!Array.isArray(seed5) && "source" in seed5 && seed5.source === "url") return true;
|
|
23500
23621
|
return Array.isArray(seed5) && seed5.length > 0;
|
|
23501
23622
|
}
|
|
23502
|
-
var
|
|
23503
|
-
"src/db/
|
|
23623
|
+
var init_seed_runner = __esm({
|
|
23624
|
+
"src/db/seed-runner.ts"() {
|
|
23504
23625
|
"use strict";
|
|
23505
23626
|
init_runtime();
|
|
23506
23627
|
init_paths();
|
|
@@ -23603,7 +23724,7 @@ var init_migration_sources = __esm({
|
|
|
23603
23724
|
init_shared();
|
|
23604
23725
|
init_paths();
|
|
23605
23726
|
init_plugin_ops();
|
|
23606
|
-
|
|
23727
|
+
init_module_store();
|
|
23607
23728
|
}
|
|
23608
23729
|
});
|
|
23609
23730
|
|
|
@@ -23936,7 +24057,7 @@ async function runMigrations(knexInstance, sources) {
|
|
|
23936
24057
|
const executedMigrations = await knex3("_nexus_migrations").where({ status: "completed" }).select("name").then((rows) => new Set(rows.map((r) => r.name)));
|
|
23937
24058
|
const pendingMigrations = migrationFiles.filter((m) => !executedMigrations.has(m.name));
|
|
23938
24059
|
if (pendingMigrations.length === 0) {
|
|
23939
|
-
logger.
|
|
24060
|
+
logger.debug("No pending migrations");
|
|
23940
24061
|
return;
|
|
23941
24062
|
}
|
|
23942
24063
|
const batch = await getNextBatch(knex3);
|
|
@@ -24336,6 +24457,27 @@ var init_migration_helpers = __esm({
|
|
|
24336
24457
|
import path2 from "path";
|
|
24337
24458
|
import fs2 from "fs/promises";
|
|
24338
24459
|
import { readFileSync as readFileSync6, mkdirSync as mkdirSync5, realpathSync } from "fs";
|
|
24460
|
+
function getColumnIndexBytes(field) {
|
|
24461
|
+
if (!field?.db) return 255 * MYSQL_BYTES_PER_CHAR;
|
|
24462
|
+
const size = field.db.size ?? 255;
|
|
24463
|
+
if (field.db.type === "text") return 0;
|
|
24464
|
+
if (field.db.type === "string") return size * MYSQL_BYTES_PER_CHAR;
|
|
24465
|
+
if (field.db.type === "integer") return 4;
|
|
24466
|
+
if (field.db.type === "boolean") return 1;
|
|
24467
|
+
if (field.db.type === "datetime" || field.db.type === "date") return 8;
|
|
24468
|
+
if (field.db.type === "uuid") return 16;
|
|
24469
|
+
return size * MYSQL_BYTES_PER_CHAR;
|
|
24470
|
+
}
|
|
24471
|
+
function warnIfIndexExceedsMySQLLimit(table, columns, unique, fields) {
|
|
24472
|
+
if (!fields) return;
|
|
24473
|
+
const totalBytes = columns.reduce((sum, col) => sum + getColumnIndexBytes(fields[col]), 0);
|
|
24474
|
+
if (totalBytes > MYSQL_MAX_INDEX_BYTES) {
|
|
24475
|
+
const indexType = unique ? "UNIQUE" : "INDEX";
|
|
24476
|
+
logger.warn(
|
|
24477
|
+
`${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.`
|
|
24478
|
+
);
|
|
24479
|
+
}
|
|
24480
|
+
}
|
|
24339
24481
|
async function detectSchemaDrift(knexInstance) {
|
|
24340
24482
|
const knex3 = knexInstance ?? getDb();
|
|
24341
24483
|
const entities = getAllPersistentEntities();
|
|
@@ -24443,6 +24585,7 @@ function computeSchemaDiff(entities, currentSchema, options) {
|
|
|
24443
24585
|
for (const idx of entityIndexes) {
|
|
24444
24586
|
const key = normalizeKey(idx.columns, !!idx.unique);
|
|
24445
24587
|
if (!currentKeys.has(key)) {
|
|
24588
|
+
warnIfIndexExceedsMySQLLimit(tableName, idx.columns, !!idx.unique, entity.fields);
|
|
24446
24589
|
diff.newIndexes.push({ columns: idx.columns, unique: !!idx.unique });
|
|
24447
24590
|
}
|
|
24448
24591
|
}
|
|
@@ -24614,7 +24757,7 @@ function formatDriftMessage(drift) {
|
|
|
24614
24757
|
lines.push('Run "pnpm migrate:dev" to generate and apply migrations.');
|
|
24615
24758
|
return lines.join("\n");
|
|
24616
24759
|
}
|
|
24617
|
-
var PERSISTENT_TYPES;
|
|
24760
|
+
var MYSQL_MAX_INDEX_BYTES, MYSQL_BYTES_PER_CHAR, PERSISTENT_TYPES;
|
|
24618
24761
|
var init_migration_generator = __esm({
|
|
24619
24762
|
"src/db/migration-generator.ts"() {
|
|
24620
24763
|
"use strict";
|
|
@@ -24623,9 +24766,11 @@ var init_migration_generator = __esm({
|
|
|
24623
24766
|
init_paths();
|
|
24624
24767
|
init_schema_reader();
|
|
24625
24768
|
init_engine();
|
|
24626
|
-
|
|
24769
|
+
init_module_queries();
|
|
24627
24770
|
init_migration_helpers();
|
|
24628
|
-
|
|
24771
|
+
init_module_store();
|
|
24772
|
+
MYSQL_MAX_INDEX_BYTES = 3072;
|
|
24773
|
+
MYSQL_BYTES_PER_CHAR = 4;
|
|
24629
24774
|
PERSISTENT_TYPES = /* @__PURE__ */ new Set([
|
|
24630
24775
|
"collection",
|
|
24631
24776
|
"tree",
|
|
@@ -24679,6 +24824,13 @@ async function runAllGeneratedMigrations(ctx, modules) {
|
|
|
24679
24824
|
table.string("created_by", 26).nullable();
|
|
24680
24825
|
table.string("updated_by", 26).nullable();
|
|
24681
24826
|
}
|
|
24827
|
+
const hasSoftDelete = entityDef.softDelete;
|
|
24828
|
+
if (hasSoftDelete) {
|
|
24829
|
+
table.timestamp("deleted_at").nullable();
|
|
24830
|
+
}
|
|
24831
|
+
if (entityDef.type === "tree") {
|
|
24832
|
+
table.string("parent_id", 26).nullable().references(`${tableName}.id`).onDelete("SET NULL");
|
|
24833
|
+
}
|
|
24682
24834
|
});
|
|
24683
24835
|
const entityIndexes = entityDef.indexes;
|
|
24684
24836
|
if (entityIndexes?.length) {
|
|
@@ -24795,7 +24947,16 @@ function addColumn(table, fieldName, field, db2) {
|
|
|
24795
24947
|
column.unique();
|
|
24796
24948
|
}
|
|
24797
24949
|
if (dbConfig.default !== void 0) {
|
|
24798
|
-
|
|
24950
|
+
const isJsonColumn = dbConfig.type === "json" || dbConfig.type === "array";
|
|
24951
|
+
if (isJsonColumn && typeof dbConfig.default === "string") {
|
|
24952
|
+
try {
|
|
24953
|
+
column.defaultTo(JSON.parse(dbConfig.default));
|
|
24954
|
+
} catch {
|
|
24955
|
+
column.defaultTo(dbConfig.default);
|
|
24956
|
+
}
|
|
24957
|
+
} else {
|
|
24958
|
+
column.defaultTo(dbConfig.default);
|
|
24959
|
+
}
|
|
24799
24960
|
} else if (dbConfig.defaultFn === "now") {
|
|
24800
24961
|
column.defaultTo(db2.fn.now());
|
|
24801
24962
|
}
|
|
@@ -26183,7 +26344,7 @@ var init_db = __esm({
|
|
|
26183
26344
|
init_schema_helpers();
|
|
26184
26345
|
init_sql_utils();
|
|
26185
26346
|
init_sqlite_compat();
|
|
26186
|
-
|
|
26347
|
+
init_seed_runner();
|
|
26187
26348
|
init_ensure_system_tables();
|
|
26188
26349
|
init_migration_sources();
|
|
26189
26350
|
init_migration_runner();
|
|
@@ -26273,12 +26434,20 @@ async function discoverPlugins(projectPath2) {
|
|
|
26273
26434
|
manifest.image = imagePath;
|
|
26274
26435
|
}
|
|
26275
26436
|
}
|
|
26437
|
+
if (!manifest.llms) {
|
|
26438
|
+
const llmsPath = join12(projectPath2, "node_modules", pkgName, "llms.txt");
|
|
26439
|
+
if (existsSync11(llmsPath)) {
|
|
26440
|
+
manifest.llms = readFileSync7(llmsPath, "utf-8");
|
|
26441
|
+
}
|
|
26442
|
+
}
|
|
26276
26443
|
discovered.push(manifest);
|
|
26277
26444
|
} else {
|
|
26278
26445
|
logger.warn({ plugin: pkgName }, "Plugin discovery: no PluginManifest export, skipping");
|
|
26279
26446
|
}
|
|
26280
26447
|
} catch (err) {
|
|
26281
|
-
|
|
26448
|
+
const errorMsg = err.message;
|
|
26449
|
+
logger.warn({ plugin: pkgName, err: errorMsg }, "Plugin discovery: failed to import");
|
|
26450
|
+
console.error(` \u26A0 Plugin '${pkgName}' failed to load: ${errorMsg}`);
|
|
26282
26451
|
}
|
|
26283
26452
|
}
|
|
26284
26453
|
return discovered;
|
|
@@ -26346,7 +26515,9 @@ async function discoverModules(projectPath2) {
|
|
|
26346
26515
|
discovered.push(...manifests);
|
|
26347
26516
|
}
|
|
26348
26517
|
} catch (err) {
|
|
26349
|
-
|
|
26518
|
+
const errorMsg = err.message;
|
|
26519
|
+
logger.warn({ module: entry, err: errorMsg }, "Module discovery: failed to import");
|
|
26520
|
+
console.error(` \u26A0 Module '${entry}' failed to load: ${errorMsg}`);
|
|
26350
26521
|
}
|
|
26351
26522
|
}
|
|
26352
26523
|
return discovered;
|
|
@@ -26437,8 +26608,11 @@ var init_events_api = __esm({
|
|
|
26437
26608
|
|
|
26438
26609
|
// src/engine/context.ts
|
|
26439
26610
|
import { ForbiddenError as CASLForbiddenError3, subject } from "@casl/ability";
|
|
26440
|
-
import { DEFAULT_TENANT_ID as DEFAULT_TENANT_ID2 } from "@gzl10/nexus-sdk";
|
|
26611
|
+
import { DEFAULT_TENANT_ID as DEFAULT_TENANT_ID2, DEFAULT_LOCALES } from "@gzl10/nexus-sdk";
|
|
26441
26612
|
import { Redis as Redis2 } from "ioredis";
|
|
26613
|
+
function setLocales(locales) {
|
|
26614
|
+
platformLocales = locales;
|
|
26615
|
+
}
|
|
26442
26616
|
function getSharedCacheManager() {
|
|
26443
26617
|
if (!sharedCacheManager) {
|
|
26444
26618
|
const redisUrl = env.REDIS_URL;
|
|
@@ -26514,7 +26688,7 @@ function createModuleContext() {
|
|
|
26514
26688
|
const defaultAdapter = createKnexAdapter(knex3);
|
|
26515
26689
|
const defaultSchemaAdapter = createKnexSchemaAdapter(knex3);
|
|
26516
26690
|
adaptersRegistry["temp"] = { data: getSharedTempAdapter() };
|
|
26517
|
-
logger.
|
|
26691
|
+
logger.trace(env.REDIS_URL ? "Temp adapter: Redis (shared)" : "Temp adapter: InMemory (shared)");
|
|
26518
26692
|
const middleware = {
|
|
26519
26693
|
validate,
|
|
26520
26694
|
rateLimit: createRateLimit,
|
|
@@ -26622,7 +26796,9 @@ function createModuleContext() {
|
|
|
26622
26796
|
throw new Error(`Knex connection for adapter "${adapter}" not found. Available: ${Object.keys(knexConnections).join(", ")}`);
|
|
26623
26797
|
}
|
|
26624
26798
|
return conn;
|
|
26625
|
-
}
|
|
26799
|
+
},
|
|
26800
|
+
// Placeholder — bound after ctx construction (needs full ModuleContext)
|
|
26801
|
+
seedModule: null
|
|
26626
26802
|
};
|
|
26627
26803
|
const configContext = {
|
|
26628
26804
|
env,
|
|
@@ -26715,7 +26891,8 @@ function createModuleContext() {
|
|
|
26715
26891
|
adapters: adaptersContext,
|
|
26716
26892
|
// Root-level shortcuts for frequently used utilities
|
|
26717
26893
|
events: createEventsApi(nexusEvents, logger),
|
|
26718
|
-
createRouter: () => createRouter()
|
|
26894
|
+
createRouter: () => createRouter(),
|
|
26895
|
+
locales: platformLocales
|
|
26719
26896
|
};
|
|
26720
26897
|
servicesRegistry["cacheManager"] = getSharedCacheManager();
|
|
26721
26898
|
ctx.runtime = {
|
|
@@ -26724,9 +26901,10 @@ function createModuleContext() {
|
|
|
26724
26901
|
createEntityController: (service, def) => createEntityController(service, def, ctx),
|
|
26725
26902
|
createEntityRouter: (controller, def) => createEntityRouter(controller, def, ctx)
|
|
26726
26903
|
};
|
|
26904
|
+
ctx.db.seedModule = (mod) => runModuleSeed(mod, ctx);
|
|
26727
26905
|
return ctx;
|
|
26728
26906
|
}
|
|
26729
|
-
var sharedCacheManager, sharedTempAdapter;
|
|
26907
|
+
var platformLocales, sharedCacheManager, sharedTempAdapter;
|
|
26730
26908
|
var init_context = __esm({
|
|
26731
26909
|
"src/engine/context.ts"() {
|
|
26732
26910
|
"use strict";
|
|
@@ -26739,7 +26917,9 @@ var init_context = __esm({
|
|
|
26739
26917
|
init_plugin_ops();
|
|
26740
26918
|
init_load_config();
|
|
26741
26919
|
init_events_api();
|
|
26920
|
+
init_seed_runner();
|
|
26742
26921
|
init_cache_manager();
|
|
26922
|
+
platformLocales = DEFAULT_LOCALES;
|
|
26743
26923
|
sharedCacheManager = null;
|
|
26744
26924
|
sharedTempAdapter = null;
|
|
26745
26925
|
}
|
|
@@ -26750,11 +26930,11 @@ var init_engine = __esm({
|
|
|
26750
26930
|
"src/engine/index.ts"() {
|
|
26751
26931
|
"use strict";
|
|
26752
26932
|
init_registry();
|
|
26753
|
-
|
|
26933
|
+
init_module_queries();
|
|
26754
26934
|
init_loader();
|
|
26755
|
-
|
|
26756
|
-
|
|
26757
|
-
|
|
26935
|
+
init_module_store();
|
|
26936
|
+
init_subject_extractor();
|
|
26937
|
+
init_definition_extractors();
|
|
26758
26938
|
init_context();
|
|
26759
26939
|
}
|
|
26760
26940
|
});
|
|
@@ -27290,7 +27470,7 @@ async function createApp(options = {}) {
|
|
|
27290
27470
|
// Only accept arrays and objects
|
|
27291
27471
|
}));
|
|
27292
27472
|
app.use(cookieParser());
|
|
27293
|
-
const serveSPA = createServeSPA(app);
|
|
27473
|
+
const serveSPA = createServeSPA(app, options.httpServer);
|
|
27294
27474
|
if (options.beforeRoutes) {
|
|
27295
27475
|
const result = options.beforeRoutes(app, serveSPA);
|
|
27296
27476
|
if (result instanceof Promise) {
|
|
@@ -27384,11 +27564,11 @@ async function createApp(options = {}) {
|
|
|
27384
27564
|
});
|
|
27385
27565
|
const sortedSpas = [...servedSpas].sort((a, b) => b.endpoint.length - a.endpoint.length);
|
|
27386
27566
|
for (const spa of sortedSpas) {
|
|
27387
|
-
serveSPA(spa.endpoint, spa.path, spa);
|
|
27567
|
+
await serveSPA(spa.endpoint, spa.path, { ...spa, viteSrc: spa.viteSrc });
|
|
27388
27568
|
}
|
|
27389
27569
|
const { ui } = getConfig();
|
|
27390
27570
|
if (ui.enabled) {
|
|
27391
|
-
serveSPA(ui.base, ui.path);
|
|
27571
|
+
await serveSPA(ui.base, ui.path, { viteSrc: "../ui" });
|
|
27392
27572
|
}
|
|
27393
27573
|
app.use(errorMiddleware);
|
|
27394
27574
|
return app;
|
|
@@ -27414,58 +27594,32 @@ var init_app = __esm({
|
|
|
27414
27594
|
}
|
|
27415
27595
|
});
|
|
27416
27596
|
|
|
27417
|
-
// src/core/utils/
|
|
27597
|
+
// src/core/utils/port-check.ts
|
|
27418
27598
|
import net from "net";
|
|
27419
27599
|
import { execSync } from "child_process";
|
|
27420
27600
|
function findProcessOnPort(port) {
|
|
27421
27601
|
try {
|
|
27422
27602
|
const output = execSync(`lsof -ti :${port} 2>/dev/null`, { encoding: "utf-8" });
|
|
27423
27603
|
const pid = parseInt(output.trim().split("\n")[0] ?? "", 10);
|
|
27424
|
-
|
|
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 };
|
|
27425
27611
|
} catch {
|
|
27426
27612
|
return null;
|
|
27427
27613
|
}
|
|
27428
27614
|
}
|
|
27429
|
-
function isSameProcessGroup(pid) {
|
|
27430
|
-
if (pid === process.pid || pid === process.ppid) return true;
|
|
27431
|
-
try {
|
|
27432
|
-
const ourPgid = execSync(`ps -o pgid= -p ${process.pid} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
27433
|
-
const targetPgid = execSync(`ps -o pgid= -p ${pid} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
27434
|
-
return ourPgid === targetPgid;
|
|
27435
|
-
} catch {
|
|
27436
|
-
return false;
|
|
27437
|
-
}
|
|
27438
|
-
}
|
|
27439
|
-
function killProcessOnPort(port) {
|
|
27440
|
-
const pid = findProcessOnPort(port);
|
|
27441
|
-
if (!pid) return false;
|
|
27442
|
-
if (isSameProcessGroup(pid)) {
|
|
27443
|
-
logger.warn({ port, pid }, `Skipping same process group (PID ${pid}) on port ${port}`);
|
|
27444
|
-
return false;
|
|
27445
|
-
}
|
|
27446
|
-
try {
|
|
27447
|
-
process.kill(pid, "SIGTERM");
|
|
27448
|
-
logger.warn({ port, pid }, `Killed process ${pid} on port ${port}`);
|
|
27449
|
-
return true;
|
|
27450
|
-
} catch {
|
|
27451
|
-
return false;
|
|
27452
|
-
}
|
|
27453
|
-
}
|
|
27454
27615
|
async function checkPortAvailable(port, host = "0.0.0.0") {
|
|
27455
27616
|
return new Promise((resolve2, reject) => {
|
|
27456
27617
|
const server2 = net.createServer();
|
|
27457
27618
|
server2.once("error", (err) => {
|
|
27458
27619
|
if (err.code === "EADDRINUSE") {
|
|
27459
|
-
|
|
27460
|
-
|
|
27461
|
-
|
|
27462
|
-
checkPortAvailable(port, host).then(resolve2).catch(reject);
|
|
27463
|
-
}, 500);
|
|
27464
|
-
return;
|
|
27465
|
-
}
|
|
27466
|
-
}
|
|
27467
|
-
logger.error({ port }, `Port ${port} is already in use`);
|
|
27468
|
-
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));
|
|
27469
27623
|
} else {
|
|
27470
27624
|
reject(err);
|
|
27471
27625
|
}
|
|
@@ -27476,10 +27630,9 @@ async function checkPortAvailable(port, host = "0.0.0.0") {
|
|
|
27476
27630
|
server2.listen(port, host);
|
|
27477
27631
|
});
|
|
27478
27632
|
}
|
|
27479
|
-
var
|
|
27480
|
-
"src/core/utils/
|
|
27633
|
+
var init_port_check = __esm({
|
|
27634
|
+
"src/core/utils/port-check.ts"() {
|
|
27481
27635
|
"use strict";
|
|
27482
|
-
init_logger();
|
|
27483
27636
|
}
|
|
27484
27637
|
});
|
|
27485
27638
|
|
|
@@ -27574,6 +27727,150 @@ var init_tunnel = __esm({
|
|
|
27574
27727
|
}
|
|
27575
27728
|
});
|
|
27576
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
|
+
|
|
27577
27874
|
// src/core/server.ts
|
|
27578
27875
|
var server_exports = {};
|
|
27579
27876
|
__export(server_exports, {
|
|
@@ -27582,7 +27879,9 @@ __export(server_exports, {
|
|
|
27582
27879
|
start: () => start,
|
|
27583
27880
|
stop: () => stop
|
|
27584
27881
|
});
|
|
27882
|
+
import http from "http";
|
|
27585
27883
|
import { entityRoom as entityRoom6 } from "@gzl10/nexus-sdk";
|
|
27884
|
+
import { DEFAULT_LOCALES as DEFAULT_LOCALES2 } from "@gzl10/nexus-sdk";
|
|
27586
27885
|
async function runMigrationsAndSeeds(config3) {
|
|
27587
27886
|
initLoggerService(getLoggerConfig());
|
|
27588
27887
|
setLoggerInstance(getPinoLogger());
|
|
@@ -27635,7 +27934,7 @@ async function runMigrationsAndSeeds(config3) {
|
|
|
27635
27934
|
const sources = buildMigrationSources();
|
|
27636
27935
|
const migrationFiles = await loadAllMigrationFiles(sources);
|
|
27637
27936
|
if (migrationFiles.length > 0) {
|
|
27638
|
-
logger.info({
|
|
27937
|
+
logger.info({ sources: sources.length, files: migrationFiles.length }, "Running migration deploy...");
|
|
27639
27938
|
try {
|
|
27640
27939
|
await runMigrations(void 0, sources);
|
|
27641
27940
|
} catch (err) {
|
|
@@ -27644,7 +27943,7 @@ async function runMigrationsAndSeeds(config3) {
|
|
|
27644
27943
|
throw err;
|
|
27645
27944
|
}
|
|
27646
27945
|
}
|
|
27647
|
-
logger.
|
|
27946
|
+
logger.debug("Checking schema drift...");
|
|
27648
27947
|
const drift = await detectSchemaDrift();
|
|
27649
27948
|
if (drift && (drift.newTables.length > 0 || drift.alteredTables.length > 0)) {
|
|
27650
27949
|
const message = formatDriftMessage(drift);
|
|
@@ -27691,7 +27990,7 @@ ${dirs}`);
|
|
|
27691
27990
|
}
|
|
27692
27991
|
const allDefinitions = modules.flatMap((m) => m.definitions ?? []);
|
|
27693
27992
|
await createMemoryTables(allDefinitions);
|
|
27694
|
-
logger.
|
|
27993
|
+
logger.debug("Running seeds...");
|
|
27695
27994
|
for (const mod of modules) {
|
|
27696
27995
|
try {
|
|
27697
27996
|
await runModuleSeed(mod, ctx);
|
|
@@ -27699,12 +27998,39 @@ ${dirs}`);
|
|
|
27699
27998
|
logger.error({ module: mod.name, err }, "Seed failed - continuing with next module");
|
|
27700
27999
|
}
|
|
27701
28000
|
}
|
|
28001
|
+
if (config3?.onSeed) {
|
|
28002
|
+
const { getPlugins: getPlugins2 } = await Promise.resolve().then(() => (init_module_queries(), module_queries_exports));
|
|
28003
|
+
const { createMasterRegistry: createMasterRegistry2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports));
|
|
28004
|
+
const masterRegistry = ctx.services.has("masters") ? ctx.services.get("masters") : createMasterRegistry2();
|
|
28005
|
+
const pluginPrefixes = /* @__PURE__ */ new Map();
|
|
28006
|
+
for (const plugin of getPlugins2()) {
|
|
28007
|
+
pluginPrefixes.set(plugin.code, `${plugin.code}_`);
|
|
28008
|
+
const shortName = plugin.name.replace(/^@[^/]+\/nexus-plugin-/, "");
|
|
28009
|
+
if (shortName !== plugin.code) {
|
|
28010
|
+
pluginPrefixes.set(shortName, `${plugin.code}_`);
|
|
28011
|
+
}
|
|
28012
|
+
}
|
|
28013
|
+
const { createSeedContext: createSeedContext2 } = await Promise.resolve().then(() => (init_seed_context(), seed_context_exports));
|
|
28014
|
+
const { ctx: seedCtx, flushPermissions } = createSeedContext2({
|
|
28015
|
+
knex: ctx.db.knex,
|
|
28016
|
+
generateId: ctx.core.generateId,
|
|
28017
|
+
hashPassword: ctx.core.crypto.hashPassword,
|
|
28018
|
+
masterRegistry,
|
|
28019
|
+
pluginPrefixes,
|
|
28020
|
+
logger: ctx.core.logger,
|
|
28021
|
+
onPermissionsCollected: (perms) => setSeedPermissions(perms)
|
|
28022
|
+
});
|
|
28023
|
+
await config3.onSeed(seedCtx);
|
|
28024
|
+
await masterRegistry.seed(ctx);
|
|
28025
|
+
flushPermissions();
|
|
28026
|
+
}
|
|
27702
28027
|
}
|
|
27703
28028
|
async function start(config3) {
|
|
27704
28029
|
if (server) {
|
|
27705
28030
|
throw new Error("Server already running. Call stop() first.");
|
|
27706
28031
|
}
|
|
27707
28032
|
currentConfig = config3;
|
|
28033
|
+
setLocales(config3?.locales ?? DEFAULT_LOCALES2);
|
|
27708
28034
|
if (env.NODE_ENV === "development" && env.FRPC_SERVER && env.FRPC_SUBDOMAIN && !env.BACKEND_URL) {
|
|
27709
28035
|
process.env["BACKEND_URL"] = getTunnelUrl(env.FRPC_SUBDOMAIN, env.FRPC_SERVER);
|
|
27710
28036
|
}
|
|
@@ -27729,13 +28055,17 @@ async function start(config3) {
|
|
|
27729
28055
|
await initTelemetry2();
|
|
27730
28056
|
await runMigrationsAndSeeds(config3);
|
|
27731
28057
|
const effectiveCorsOrigins = buildEffectiveCorsOrigins(env.CORS_ORIGIN, config3?.spas);
|
|
28058
|
+
const httpServer = http.createServer();
|
|
27732
28059
|
const app = await createApp({
|
|
27733
28060
|
beforeRoutes: config3?.beforeRoutes,
|
|
27734
28061
|
afterRoutes: config3?.afterRoutes,
|
|
27735
|
-
spas: config3?.spas
|
|
28062
|
+
spas: config3?.spas,
|
|
28063
|
+
httpServer
|
|
27736
28064
|
});
|
|
28065
|
+
httpServer.on("request", app);
|
|
27737
28066
|
return new Promise((resolve2) => {
|
|
27738
|
-
server =
|
|
28067
|
+
server = httpServer;
|
|
28068
|
+
httpServer.listen(resolved.port, resolved.host, async () => {
|
|
27739
28069
|
const timeoutMs = parseInt(process.env["REQUEST_TIMEOUT_MS"] || "30000", 10);
|
|
27740
28070
|
if (timeoutMs > 0) {
|
|
27741
28071
|
server.setTimeout(timeoutMs);
|
|
@@ -27771,12 +28101,10 @@ async function start(config3) {
|
|
|
27771
28101
|
});
|
|
27772
28102
|
}
|
|
27773
28103
|
const baseUrl = env.BACKEND_URL || `http://localhost:${actualPort}`;
|
|
27774
|
-
logger.
|
|
27775
|
-
|
|
27776
|
-
if (resolved.ui.enabled)
|
|
27777
|
-
|
|
27778
|
-
}
|
|
27779
|
-
logger.info({ port: actualPort, mode: resolved.nodeEnv }, "Server started");
|
|
28104
|
+
logger.debug({ libPath: getLibPath(), projectPath: getProjectPath() }, "Paths");
|
|
28105
|
+
const urls = { api: `${baseUrl}/api/v1` };
|
|
28106
|
+
if (resolved.ui.enabled) urls["ui"] = baseUrl;
|
|
28107
|
+
logger.info({ port: actualPort, mode: resolved.nodeEnv, ...urls }, "Server started");
|
|
27780
28108
|
nexusEvents.emitEvent("server.started", { port: actualPort, host: resolved.host });
|
|
27781
28109
|
if (config3?.onReady) {
|
|
27782
28110
|
try {
|
|
@@ -27802,7 +28130,8 @@ async function stop() {
|
|
|
27802
28130
|
await resetSharedAdapters();
|
|
27803
28131
|
resetConfigCache();
|
|
27804
28132
|
clearCustomCaslRules();
|
|
27805
|
-
|
|
28133
|
+
clearSeedPermissions();
|
|
28134
|
+
await resetServeSPA();
|
|
27806
28135
|
return;
|
|
27807
28136
|
}
|
|
27808
28137
|
if (currentConfig?.beforeClose) {
|
|
@@ -27836,7 +28165,8 @@ async function stop() {
|
|
|
27836
28165
|
await resetSharedAdapters();
|
|
27837
28166
|
resetConfigCache();
|
|
27838
28167
|
clearCustomCaslRules();
|
|
27839
|
-
|
|
28168
|
+
clearSeedPermissions();
|
|
28169
|
+
await resetServeSPA();
|
|
27840
28170
|
currentConfig = void 0;
|
|
27841
28171
|
server = null;
|
|
27842
28172
|
nexusEvents.emitEvent("server.stopped");
|
|
@@ -27896,7 +28226,7 @@ var init_server = __esm({
|
|
|
27896
28226
|
init_cors();
|
|
27897
28227
|
init_logger();
|
|
27898
28228
|
init_error_middleware();
|
|
27899
|
-
|
|
28229
|
+
init_port_check();
|
|
27900
28230
|
init_engine();
|
|
27901
28231
|
init_context();
|
|
27902
28232
|
init_db();
|