@gzl10/nexus-backend 0.17.0 → 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 +551 -7296
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +113 -7
- package/dist/index.js +497 -215
- package/dist/index.js.map +1 -1
- package/dist/main.js +507 -215
- package/dist/main.js.map +1 -1
- package/package.json +12 -23
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,26 +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-feeds": "workspace:^",
|
|
136
|
-
"@gzl10/nexus-plugin-importer": "workspace:^",
|
|
137
|
-
"@gzl10/nexus-plugin-links": "workspace:*",
|
|
138
|
-
"@gzl10/nexus-plugin-notifications": "workspace:*",
|
|
139
|
-
"@gzl10/nexus-plugin-oidc-server": "workspace:^",
|
|
140
|
-
"@gzl10/nexus-plugin-plane": "workspace:^",
|
|
141
|
-
"@gzl10/nexus-plugin-prisma": "workspace:^",
|
|
142
|
-
"@gzl10/nexus-plugin-remote": "workspace:*",
|
|
143
|
-
"@gzl10/nexus-plugin-schedules": "workspace:*",
|
|
144
|
-
"@gzl10/nexus-plugin-scim": "workspace:^",
|
|
145
|
-
"@gzl10/nexus-plugin-scraper": "workspace:^",
|
|
146
|
-
"@gzl10/nexus-plugin-secrets": "workspace:^",
|
|
147
|
-
"@gzl10/nexus-plugin-tags": "workspace:*",
|
|
148
|
-
"@gzl10/nexus-plugin-webhooks": "workspace:*",
|
|
149
129
|
"@types/bcryptjs": "^2.4.0",
|
|
150
130
|
"@types/compression": "^1.8.1",
|
|
151
131
|
"@types/cookie-parser": "^1.4.10",
|
|
@@ -158,7 +138,16 @@ var init_package = __esm({
|
|
|
158
138
|
"pino-pretty": "^13.1.3",
|
|
159
139
|
"socket.io-client": "^4.8.3",
|
|
160
140
|
supertest: "^7.2.2",
|
|
161
|
-
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
|
+
}
|
|
162
151
|
},
|
|
163
152
|
publishConfig: {
|
|
164
153
|
access: "public",
|
|
@@ -1160,7 +1149,7 @@ var init_table_prefix = __esm({
|
|
|
1160
1149
|
}
|
|
1161
1150
|
});
|
|
1162
1151
|
|
|
1163
|
-
// src/engine/store.ts
|
|
1152
|
+
// src/engine/module-store.ts
|
|
1164
1153
|
function resetStore() {
|
|
1165
1154
|
moduleStore.modules.length = 0;
|
|
1166
1155
|
moduleStore.plugins.clear();
|
|
@@ -1171,8 +1160,8 @@ function resetStore() {
|
|
|
1171
1160
|
moduleStore.tableToSubject.clear();
|
|
1172
1161
|
}
|
|
1173
1162
|
var moduleStore;
|
|
1174
|
-
var
|
|
1175
|
-
"src/engine/store.ts"() {
|
|
1163
|
+
var init_module_store = __esm({
|
|
1164
|
+
"src/engine/module-store.ts"() {
|
|
1176
1165
|
"use strict";
|
|
1177
1166
|
moduleStore = {
|
|
1178
1167
|
/** Registered modules with source metadata */
|
|
@@ -1225,7 +1214,7 @@ var init_id = __esm({
|
|
|
1225
1214
|
}
|
|
1226
1215
|
});
|
|
1227
1216
|
|
|
1228
|
-
// src/engine/extractors.ts
|
|
1217
|
+
// src/engine/definition-extractors.ts
|
|
1229
1218
|
function getTableAndSubject(def) {
|
|
1230
1219
|
const caslSubject = def.casl?.subject;
|
|
1231
1220
|
if (!TYPES_WITH_TABLE.has(def.type)) {
|
|
@@ -1293,8 +1282,8 @@ function validateModuleDependencies(modules) {
|
|
|
1293
1282
|
}
|
|
1294
1283
|
}
|
|
1295
1284
|
var TYPES_WITH_TABLE;
|
|
1296
|
-
var
|
|
1297
|
-
"src/engine/extractors.ts"() {
|
|
1285
|
+
var init_definition_extractors = __esm({
|
|
1286
|
+
"src/engine/definition-extractors.ts"() {
|
|
1298
1287
|
"use strict";
|
|
1299
1288
|
TYPES_WITH_TABLE = /* @__PURE__ */ new Set(["collection", "reference", "event", "config", "temp", "view", void 0]);
|
|
1300
1289
|
}
|
|
@@ -1414,9 +1403,9 @@ var init_registry = __esm({
|
|
|
1414
1403
|
"use strict";
|
|
1415
1404
|
init_plugin_ops();
|
|
1416
1405
|
init_table_prefix();
|
|
1417
|
-
|
|
1406
|
+
init_module_store();
|
|
1418
1407
|
init_id();
|
|
1419
|
-
|
|
1408
|
+
init_definition_extractors();
|
|
1420
1409
|
}
|
|
1421
1410
|
});
|
|
1422
1411
|
|
|
@@ -1479,7 +1468,28 @@ var init_paths = __esm({
|
|
|
1479
1468
|
}
|
|
1480
1469
|
});
|
|
1481
1470
|
|
|
1482
|
-
// 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
|
+
});
|
|
1483
1493
|
import { join as join4 } from "path";
|
|
1484
1494
|
import { readFileSync as readFileSync3 } from "fs";
|
|
1485
1495
|
function readPackageJson(dir) {
|
|
@@ -1575,10 +1585,10 @@ function getPluginByCode(code) {
|
|
|
1575
1585
|
function hasPluginByCode(code) {
|
|
1576
1586
|
return moduleStore.pluginsByCode.has(code);
|
|
1577
1587
|
}
|
|
1578
|
-
var
|
|
1579
|
-
"src/engine/queries.ts"() {
|
|
1588
|
+
var init_module_queries = __esm({
|
|
1589
|
+
"src/engine/module-queries.ts"() {
|
|
1580
1590
|
"use strict";
|
|
1581
|
-
|
|
1591
|
+
init_module_store();
|
|
1582
1592
|
init_paths();
|
|
1583
1593
|
}
|
|
1584
1594
|
});
|
|
@@ -2063,12 +2073,16 @@ var init_definitions = __esm({
|
|
|
2063
2073
|
labelPlural: { en: "Masters", es: "Maestros" },
|
|
2064
2074
|
labelField: "label",
|
|
2065
2075
|
timestamps: true,
|
|
2076
|
+
availableDisplayModes: ["board"],
|
|
2077
|
+
groupBy: "type",
|
|
2078
|
+
groupableFields: ["type"],
|
|
2079
|
+
//columnDragFields: ['is_active'],
|
|
2066
2080
|
fields: {
|
|
2067
2081
|
id: useTextField2({
|
|
2068
2082
|
label: { en: "ID", es: "ID" },
|
|
2069
2083
|
required: true,
|
|
2070
2084
|
size: 100,
|
|
2071
|
-
|
|
2085
|
+
hidden: true,
|
|
2072
2086
|
meta: { sortable: true }
|
|
2073
2087
|
}),
|
|
2074
2088
|
type: useTextField2({
|
|
@@ -2089,9 +2103,20 @@ var init_definitions = __esm({
|
|
|
2089
2103
|
is_active: isActiveField,
|
|
2090
2104
|
metadata: useJsonField({
|
|
2091
2105
|
label: { en: "Metadata", es: "Metadatos" },
|
|
2092
|
-
hint: {
|
|
2106
|
+
hint: {
|
|
2107
|
+
en: "Type-specific fields (symbol, flag, etc.)",
|
|
2108
|
+
es: "Campos espec\xEDficos del tipo"
|
|
2109
|
+
}
|
|
2093
2110
|
})
|
|
2094
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
|
+
}),
|
|
2095
2120
|
defaultSort: { field: "order", order: "asc" },
|
|
2096
2121
|
indexes: [{ columns: ["type", "code"], unique: true }],
|
|
2097
2122
|
casl: { subject: "Master", permissions: masterCaslPermissions }
|
|
@@ -2100,6 +2125,10 @@ var init_definitions = __esm({
|
|
|
2100
2125
|
});
|
|
2101
2126
|
|
|
2102
2127
|
// src/modules/masters/registry.ts
|
|
2128
|
+
var registry_exports = {};
|
|
2129
|
+
__export(registry_exports, {
|
|
2130
|
+
createMasterRegistry: () => createMasterRegistry
|
|
2131
|
+
});
|
|
2103
2132
|
function createMasterRegistry() {
|
|
2104
2133
|
const registrations = [];
|
|
2105
2134
|
return {
|
|
@@ -8778,15 +8807,18 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8778
8807
|
const meta = field["meta"];
|
|
8779
8808
|
return meta?.["sortable"] === true && !field["hidden"];
|
|
8780
8809
|
});
|
|
8810
|
+
const explicitGroupable = def["groupableFields"];
|
|
8781
8811
|
const groupableInputTypes = ["select", "switch", "checkbox", "radio", "tags"];
|
|
8782
|
-
const
|
|
8812
|
+
const autoGroupableFields = fieldEntries.filter(([, f]) => {
|
|
8783
8813
|
const field = f;
|
|
8784
8814
|
const inputType = field["input"];
|
|
8785
8815
|
return groupableInputTypes.includes(inputType ?? "") && !field["hidden"];
|
|
8786
8816
|
});
|
|
8817
|
+
const resolvedGroupableFields = explicitGroupable ?? (autoGroupableFields.length > 0 ? autoGroupableFields.map(([name]) => name) : void 0);
|
|
8787
8818
|
const entityType = def["type"] ?? "collection";
|
|
8788
8819
|
const explicitDisplayMode = def["displayMode"];
|
|
8789
|
-
const
|
|
8820
|
+
const explicitAvailableModes = def["availableDisplayModes"];
|
|
8821
|
+
const defaultDisplayMode = explicitDisplayMode ?? (explicitAvailableModes?.length === 1 ? explicitAvailableModes[0] : void 0) ?? (["tree", "dag"].includes(entityType) ? "tree" : "table");
|
|
8790
8822
|
const entityIdent = def["table"] ?? def["key"];
|
|
8791
8823
|
return {
|
|
8792
8824
|
id: def._id,
|
|
@@ -8806,9 +8838,11 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8806
8838
|
displayMode: explicitDisplayMode,
|
|
8807
8839
|
defaultDisplayMode,
|
|
8808
8840
|
availableDisplayModes: (() => {
|
|
8841
|
+
const explicit = def["availableDisplayModes"];
|
|
8842
|
+
if (explicit?.length) return explicit;
|
|
8809
8843
|
const modes = ["table", "list", "masonry"];
|
|
8810
8844
|
if (["tree", "dag"].includes(entityType)) modes.push("tree");
|
|
8811
|
-
if (
|
|
8845
|
+
if ((resolvedGroupableFields?.length ?? 0) > 0) modes.push("board");
|
|
8812
8846
|
if (def["calendarFrom"]) modes.push("calendar");
|
|
8813
8847
|
return modes;
|
|
8814
8848
|
})(),
|
|
@@ -8818,10 +8852,8 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8818
8852
|
value: name,
|
|
8819
8853
|
label: f["label"]
|
|
8820
8854
|
})),
|
|
8821
|
-
groupableFields:
|
|
8822
|
-
|
|
8823
|
-
label: f["label"]
|
|
8824
|
-
})) : void 0,
|
|
8855
|
+
groupableFields: resolvedGroupableFields,
|
|
8856
|
+
columnDragFields: def["columnDragFields"],
|
|
8825
8857
|
groupBy: def["groupBy"],
|
|
8826
8858
|
subgroupBy: def["subgroupBy"],
|
|
8827
8859
|
calendarFrom: def["calendarFrom"],
|
|
@@ -8861,21 +8893,6 @@ function toModuleDTO(mod, ctx) {
|
|
|
8861
8893
|
hasInit: !!mod.init
|
|
8862
8894
|
};
|
|
8863
8895
|
}
|
|
8864
|
-
function getOrderedModulesViaContext(ctx) {
|
|
8865
|
-
return ctx.engine.getModules();
|
|
8866
|
-
}
|
|
8867
|
-
async function runModuleSeedViaContext(mod, ctx) {
|
|
8868
|
-
if (!mod.seed) {
|
|
8869
|
-
return false;
|
|
8870
|
-
}
|
|
8871
|
-
try {
|
|
8872
|
-
await mod.seed(ctx);
|
|
8873
|
-
return true;
|
|
8874
|
-
} catch (err) {
|
|
8875
|
-
ctx.core.logger.error({ module: mod.name, err }, "Seed failed");
|
|
8876
|
-
return false;
|
|
8877
|
-
}
|
|
8878
|
-
}
|
|
8879
8896
|
var init_system_helpers = __esm({
|
|
8880
8897
|
"src/modules/system/system.helpers.ts"() {
|
|
8881
8898
|
"use strict";
|
|
@@ -9110,7 +9127,8 @@ function createSystemController(ctx) {
|
|
|
9110
9127
|
const plugins = engine.getPlugins();
|
|
9111
9128
|
const body = {
|
|
9112
9129
|
version: manifest.version,
|
|
9113
|
-
plugins: plugins.map((p) => p.code)
|
|
9130
|
+
plugins: plugins.map((p) => p.code),
|
|
9131
|
+
locales: ctx.locales
|
|
9114
9132
|
};
|
|
9115
9133
|
res.json(body);
|
|
9116
9134
|
}
|
|
@@ -9997,7 +10015,6 @@ var SYSTEM_TABLES, factoryResetAction;
|
|
|
9997
10015
|
var init_factory_reset_action = __esm({
|
|
9998
10016
|
"src/modules/system/actions/factory-reset.action.ts"() {
|
|
9999
10017
|
"use strict";
|
|
10000
|
-
init_system_helpers();
|
|
10001
10018
|
SYSTEM_TABLES = /* @__PURE__ */ new Set([
|
|
10002
10019
|
"_nexus_migrations",
|
|
10003
10020
|
"_nexus_migration_lock",
|
|
@@ -10040,8 +10057,8 @@ var init_factory_reset_action = __esm({
|
|
|
10040
10057
|
source: "core:system",
|
|
10041
10058
|
action: "factory_reset",
|
|
10042
10059
|
actorId: authReq.user?.id,
|
|
10043
|
-
ip: req
|
|
10044
|
-
userAgent: req
|
|
10060
|
+
ip: req?.ip,
|
|
10061
|
+
userAgent: req?.headers["user-agent"]
|
|
10045
10062
|
});
|
|
10046
10063
|
await new Promise((resolve2) => setImmediate(resolve2));
|
|
10047
10064
|
const allTables = await getAllTables(knex3);
|
|
@@ -10079,11 +10096,11 @@ var init_factory_reset_action = __esm({
|
|
|
10079
10096
|
} catch {
|
|
10080
10097
|
}
|
|
10081
10098
|
ctx.core.logger.info({ tables: dataTables.length }, "All data tables cleared");
|
|
10082
|
-
const modules =
|
|
10099
|
+
const modules = ctx.engine.getModules();
|
|
10083
10100
|
let modulesSeeded = 0;
|
|
10084
10101
|
for (const mod of modules) {
|
|
10085
10102
|
try {
|
|
10086
|
-
const seeded = await
|
|
10103
|
+
const seeded = await ctx.db.seedModule(mod);
|
|
10087
10104
|
if (seeded) modulesSeeded++;
|
|
10088
10105
|
} catch (err) {
|
|
10089
10106
|
ctx.core.logger.error({ module: mod.name, err }, "Seed failed during factory reset");
|
|
@@ -10137,8 +10154,8 @@ var init_restart_server_action = __esm({
|
|
|
10137
10154
|
source: "core:system",
|
|
10138
10155
|
action: "server_restart",
|
|
10139
10156
|
actorId: authReq.user?.id,
|
|
10140
|
-
ip: req
|
|
10141
|
-
userAgent: req
|
|
10157
|
+
ip: req?.ip,
|
|
10158
|
+
userAgent: req?.headers["user-agent"]
|
|
10142
10159
|
});
|
|
10143
10160
|
setTimeout(() => process.exit(0), 500);
|
|
10144
10161
|
return { success: true, message: "Server is restarting..." };
|
|
@@ -11760,7 +11777,7 @@ function createUploadMiddleware(ctx, options) {
|
|
|
11760
11777
|
const rateLimit2 = ctx.core.middleware.rateLimit({
|
|
11761
11778
|
windowMs: 60 * 1e3,
|
|
11762
11779
|
max: 20,
|
|
11763
|
-
message: "
|
|
11780
|
+
message: "Too many uploads, try again in 1 minute"
|
|
11764
11781
|
});
|
|
11765
11782
|
const upload = multer({
|
|
11766
11783
|
storage: multer.memoryStorage(),
|
|
@@ -12007,7 +12024,7 @@ function createStorageRoutes(ctx) {
|
|
|
12007
12024
|
}
|
|
12008
12025
|
res.status(201).json(results);
|
|
12009
12026
|
};
|
|
12010
|
-
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" });
|
|
12011
12028
|
if (auth) {
|
|
12012
12029
|
router.post("/upload/multiple", uploadRateLimit, auth, upload.array("files", 10), uploadMultiple);
|
|
12013
12030
|
} else {
|
|
@@ -12305,7 +12322,7 @@ var init_users_entity = __esm({
|
|
|
12305
12322
|
],
|
|
12306
12323
|
nullable: true,
|
|
12307
12324
|
meta: { sortable: true },
|
|
12308
|
-
defaultValue: "
|
|
12325
|
+
defaultValue: "en"
|
|
12309
12326
|
}),
|
|
12310
12327
|
timezone: useSelectField9({
|
|
12311
12328
|
label: { en: "Timezone", es: "Zona horaria" },
|
|
@@ -12383,7 +12400,7 @@ var init_users_entity = __esm({
|
|
|
12383
12400
|
middleware: (ctx) => ctx.core.middleware.rateLimit({
|
|
12384
12401
|
windowMs: 15 * 60 * 1e3,
|
|
12385
12402
|
max: 5,
|
|
12386
|
-
message: "
|
|
12403
|
+
message: "Too many attempts, try again in 15 minutes"
|
|
12387
12404
|
}),
|
|
12388
12405
|
handler: async (ctx, input) => {
|
|
12389
12406
|
const {
|
|
@@ -16594,7 +16611,7 @@ var init_plugins_entity = __esm({
|
|
|
16594
16611
|
label: "Plugins",
|
|
16595
16612
|
icon: "mdi:puzzle",
|
|
16596
16613
|
labelField: "code",
|
|
16597
|
-
routePrefix: "/
|
|
16614
|
+
routePrefix: "/",
|
|
16598
16615
|
defaultSort: { field: "name", order: "asc" },
|
|
16599
16616
|
fields: {
|
|
16600
16617
|
name: useTextField12({
|
|
@@ -17007,12 +17024,12 @@ var init_loader = __esm({
|
|
|
17007
17024
|
"src/engine/loader.ts"() {
|
|
17008
17025
|
"use strict";
|
|
17009
17026
|
init_registry();
|
|
17010
|
-
|
|
17027
|
+
init_definition_extractors();
|
|
17011
17028
|
init_modules();
|
|
17012
17029
|
}
|
|
17013
17030
|
});
|
|
17014
17031
|
|
|
17015
|
-
// src/engine/
|
|
17032
|
+
// src/engine/subject-extractor.ts
|
|
17016
17033
|
function getModuleSubjects(mod) {
|
|
17017
17034
|
const subjects = /* @__PURE__ */ new Set();
|
|
17018
17035
|
for (const def of mod.definitions ?? []) {
|
|
@@ -17021,10 +17038,10 @@ function getModuleSubjects(mod) {
|
|
|
17021
17038
|
}
|
|
17022
17039
|
return [...subjects];
|
|
17023
17040
|
}
|
|
17024
|
-
var
|
|
17025
|
-
"src/engine/
|
|
17041
|
+
var init_subject_extractor = __esm({
|
|
17042
|
+
"src/engine/subject-extractor.ts"() {
|
|
17026
17043
|
"use strict";
|
|
17027
|
-
|
|
17044
|
+
init_definition_extractors();
|
|
17028
17045
|
}
|
|
17029
17046
|
});
|
|
17030
17047
|
|
|
@@ -17102,7 +17119,7 @@ function initSocketIO(httpServer, options) {
|
|
|
17102
17119
|
maxHttpBufferSize: 1e6
|
|
17103
17120
|
// 1MB - match Express json body limit
|
|
17104
17121
|
});
|
|
17105
|
-
logger.info({
|
|
17122
|
+
logger.info({ cors: corsOrigin }, "Socket.IO initialized");
|
|
17106
17123
|
io.use((socket, next) => {
|
|
17107
17124
|
const token = socket.handshake.auth?.["token"] || socket.handshake.query?.["token"];
|
|
17108
17125
|
if (token && typeof token === "string" && jwtSecret) {
|
|
@@ -17125,7 +17142,6 @@ function initSocketIO(httpServer, options) {
|
|
|
17125
17142
|
logger.warn({ code: err.code, message: err.message }, "Socket.IO connection error");
|
|
17126
17143
|
});
|
|
17127
17144
|
io.on("connection", handleConnection);
|
|
17128
|
-
logger.info("Socket.IO initialized");
|
|
17129
17145
|
nexusEvents.emitEvent("socket.initialized");
|
|
17130
17146
|
return io;
|
|
17131
17147
|
}
|
|
@@ -17720,6 +17736,12 @@ var init_app_error = __esm({
|
|
|
17720
17736
|
|
|
17721
17737
|
// src/core/abilities/ability.factory.ts
|
|
17722
17738
|
import { AbilityBuilder, createMongoAbility } from "@casl/ability";
|
|
17739
|
+
function setSeedPermissions(perms) {
|
|
17740
|
+
seedPermissions = perms;
|
|
17741
|
+
}
|
|
17742
|
+
function clearSeedPermissions() {
|
|
17743
|
+
seedPermissions = null;
|
|
17744
|
+
}
|
|
17723
17745
|
function setCustomCaslRules(fn) {
|
|
17724
17746
|
customCaslRules = fn;
|
|
17725
17747
|
}
|
|
@@ -17777,6 +17799,17 @@ async function defineAbilityFor(user, roleNames) {
|
|
|
17777
17799
|
if (customCaslRules) {
|
|
17778
17800
|
await customCaslRules(user, { can, cannot });
|
|
17779
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
|
+
}
|
|
17780
17813
|
return build();
|
|
17781
17814
|
}
|
|
17782
17815
|
function packRules(ability) {
|
|
@@ -17785,11 +17818,12 @@ function packRules(ability) {
|
|
|
17785
17818
|
function unpackRules(rules) {
|
|
17786
17819
|
return createMongoAbility(rules);
|
|
17787
17820
|
}
|
|
17788
|
-
var customCaslRules, entityDefinitionsRegistry, SUPERUSER_ROLES;
|
|
17821
|
+
var customCaslRules, seedPermissions, entityDefinitionsRegistry, SUPERUSER_ROLES;
|
|
17789
17822
|
var init_ability_factory = __esm({
|
|
17790
17823
|
"src/core/abilities/ability.factory.ts"() {
|
|
17791
17824
|
"use strict";
|
|
17792
17825
|
init_logger();
|
|
17826
|
+
seedPermissions = null;
|
|
17793
17827
|
entityDefinitionsRegistry = null;
|
|
17794
17828
|
SUPERUSER_ROLES = ["ADMIN", "OWNER"];
|
|
17795
17829
|
}
|
|
@@ -18355,7 +18389,7 @@ var init_sequence = __esm({
|
|
|
18355
18389
|
}
|
|
18356
18390
|
});
|
|
18357
18391
|
|
|
18358
|
-
// src/core/utils/
|
|
18392
|
+
// src/core/utils/safe-json.ts
|
|
18359
18393
|
function safeJsonParse(logger2, jsonString, fallback, context) {
|
|
18360
18394
|
try {
|
|
18361
18395
|
return JSON.parse(jsonString);
|
|
@@ -18364,8 +18398,8 @@ function safeJsonParse(logger2, jsonString, fallback, context) {
|
|
|
18364
18398
|
return fallback;
|
|
18365
18399
|
}
|
|
18366
18400
|
}
|
|
18367
|
-
var
|
|
18368
|
-
"src/core/utils/
|
|
18401
|
+
var init_safe_json = __esm({
|
|
18402
|
+
"src/core/utils/safe-json.ts"() {
|
|
18369
18403
|
"use strict";
|
|
18370
18404
|
}
|
|
18371
18405
|
});
|
|
@@ -18374,14 +18408,15 @@ var init_error_handler = __esm({
|
|
|
18374
18408
|
import express from "express";
|
|
18375
18409
|
import { resolve, join as join9 } from "path";
|
|
18376
18410
|
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
18377
|
-
function createServeSPA(app) {
|
|
18378
|
-
return (endpoint, distPath, options = {}) => {
|
|
18411
|
+
function createServeSPA(app, httpServer) {
|
|
18412
|
+
return async (endpoint, distPath, options = {}) => {
|
|
18379
18413
|
const {
|
|
18380
18414
|
maxAge = "1d",
|
|
18381
18415
|
etag = true,
|
|
18382
18416
|
immutable = false,
|
|
18383
18417
|
index = "index.html",
|
|
18384
|
-
absolute = false
|
|
18418
|
+
absolute = false,
|
|
18419
|
+
viteSrc
|
|
18385
18420
|
} = options;
|
|
18386
18421
|
if (endpoint === "/api" || endpoint.startsWith("/api/")) {
|
|
18387
18422
|
logger.error(`Cannot mount SPA on ${endpoint} - reserved for API routes`);
|
|
@@ -18392,58 +18427,117 @@ function createServeSPA(app) {
|
|
|
18392
18427
|
return;
|
|
18393
18428
|
}
|
|
18394
18429
|
registeredEndpoints.add(endpoint);
|
|
18395
|
-
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
|
|
18399
|
-
const projectPath2 = resolve(getProjectPath(), distPath);
|
|
18400
|
-
if (existsSync9(projectPath2)) {
|
|
18401
|
-
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");
|
|
18402
18434
|
} else {
|
|
18403
|
-
|
|
18435
|
+
const mounted = await mountViteDevMiddleware(app, endpoint, srcPath, httpServer);
|
|
18436
|
+
if (mounted) return;
|
|
18404
18437
|
}
|
|
18405
18438
|
}
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
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);
|
|
18409
18469
|
}
|
|
18410
|
-
|
|
18411
|
-
|
|
18412
|
-
|
|
18413
|
-
|
|
18414
|
-
|
|
18415
|
-
|
|
18416
|
-
if (existsSync9(indexPath)) {
|
|
18417
|
-
const rawHtml = readFileSync5(indexPath, "utf-8");
|
|
18418
|
-
const apiUrl = env.BACKEND_URL ? `${env.BACKEND_URL}/api/v1` : "/api/v1";
|
|
18419
|
-
const nexusConfig = JSON.stringify({ apiUrl });
|
|
18420
|
-
injectedHtml = rawHtml.replace(
|
|
18421
|
-
"</head>",
|
|
18422
|
-
`<script>window.__NEXUS__=${nexusConfig}</script>
|
|
18423
|
-
</head>`
|
|
18424
|
-
);
|
|
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;
|
|
18425
18476
|
}
|
|
18426
|
-
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18430
|
-
|
|
18431
|
-
|
|
18432
|
-
|
|
18433
|
-
|
|
18434
|
-
|
|
18435
|
-
|
|
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;
|
|
18436
18490
|
} else {
|
|
18437
|
-
|
|
18438
|
-
|
|
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;
|
|
18439
18518
|
}
|
|
18440
|
-
|
|
18519
|
+
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
18520
|
+
res.type("html").send(injectedHtml);
|
|
18441
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}`);
|
|
18442
18529
|
}
|
|
18443
|
-
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;
|
|
18444
18538
|
registeredEndpoints.clear();
|
|
18445
18539
|
}
|
|
18446
|
-
var registeredEndpoints;
|
|
18540
|
+
var registeredEndpoints, viteServers;
|
|
18447
18541
|
var init_spa_handler = __esm({
|
|
18448
18542
|
"src/core/spa-handler.ts"() {
|
|
18449
18543
|
"use strict";
|
|
@@ -18451,6 +18545,7 @@ var init_spa_handler = __esm({
|
|
|
18451
18545
|
init_logger();
|
|
18452
18546
|
init_env();
|
|
18453
18547
|
registeredEndpoints = /* @__PURE__ */ new Set();
|
|
18548
|
+
viteServers = [];
|
|
18454
18549
|
}
|
|
18455
18550
|
});
|
|
18456
18551
|
|
|
@@ -19021,7 +19116,7 @@ var init_core = __esm({
|
|
|
19021
19116
|
init_id();
|
|
19022
19117
|
init_sequence();
|
|
19023
19118
|
init_paths();
|
|
19024
|
-
|
|
19119
|
+
init_safe_json();
|
|
19025
19120
|
init_spa_handler();
|
|
19026
19121
|
init_cache();
|
|
19027
19122
|
init_jwt();
|
|
@@ -19736,7 +19831,9 @@ var init_base_service = __esm({
|
|
|
19736
19831
|
const countResult = await qb.clone().count("* as count").first();
|
|
19737
19832
|
const total = Number(countResult?.count ?? 0);
|
|
19738
19833
|
qb = this.applySortingWithDefaults(qb, query);
|
|
19739
|
-
|
|
19834
|
+
if (limit > 0) {
|
|
19835
|
+
qb = qb.limit(limit).offset(offset);
|
|
19836
|
+
}
|
|
19740
19837
|
const rawItems = await qb;
|
|
19741
19838
|
const items = this.parseJsonFieldsFromArray(rawItems);
|
|
19742
19839
|
const processedItems = await this.afterFindAll(items);
|
|
@@ -19821,7 +19918,7 @@ var init_base_service = __esm({
|
|
|
19821
19918
|
});
|
|
19822
19919
|
}
|
|
19823
19920
|
const total = result.length;
|
|
19824
|
-
const paginatedItems = result.slice(offset, offset + limit);
|
|
19921
|
+
const paginatedItems = limit === 0 ? result : result.slice(offset, offset + limit);
|
|
19825
19922
|
return this.buildPaginatedResult(paginatedItems, total, page, limit);
|
|
19826
19923
|
}
|
|
19827
19924
|
/**
|
|
@@ -20076,14 +20173,14 @@ var init_base_service = __esm({
|
|
|
20076
20173
|
* Build paginated result from items and count
|
|
20077
20174
|
*/
|
|
20078
20175
|
buildPaginatedResult(items, total, page, limit) {
|
|
20079
|
-
const totalPages = Math.ceil(total / limit);
|
|
20176
|
+
const totalPages = limit === 0 ? 1 : Math.ceil(total / limit);
|
|
20080
20177
|
return {
|
|
20081
20178
|
items,
|
|
20082
20179
|
total,
|
|
20083
|
-
page,
|
|
20084
|
-
limit,
|
|
20180
|
+
page: limit === 0 ? 1 : page,
|
|
20181
|
+
limit: limit === 0 ? total : limit,
|
|
20085
20182
|
totalPages,
|
|
20086
|
-
hasNext: page < totalPages
|
|
20183
|
+
hasNext: limit === 0 ? false : page < totalPages
|
|
20087
20184
|
};
|
|
20088
20185
|
}
|
|
20089
20186
|
/**
|
|
@@ -20092,8 +20189,8 @@ var init_base_service = __esm({
|
|
|
20092
20189
|
getPagination(query) {
|
|
20093
20190
|
const maxLimit = query?.maxLimit ?? 100;
|
|
20094
20191
|
const page = Math.max(1, query?.page ?? 1);
|
|
20095
|
-
const limit = Math.min(maxLimit, Math.max(1, query?.limit ?? 20));
|
|
20096
|
-
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;
|
|
20097
20194
|
return { page, limit, offset };
|
|
20098
20195
|
}
|
|
20099
20196
|
/**
|
|
@@ -21740,7 +21837,8 @@ function createEntityController(service, definition, ctx) {
|
|
|
21740
21837
|
async list(req, res) {
|
|
21741
21838
|
checkPermission(req, "read");
|
|
21742
21839
|
const page = Math.max(1, parseInt(req.query["page"]) || 1);
|
|
21743
|
-
const
|
|
21840
|
+
const rawLimit = parseInt(req.query["limit"]);
|
|
21841
|
+
const limit = rawLimit === 0 ? 0 : Math.min(100, Math.max(1, rawLimit || 20));
|
|
21744
21842
|
let filters;
|
|
21745
21843
|
if (req.query["filters"]) {
|
|
21746
21844
|
filters = parseFilters(req.query["filters"], ctx.core.errors);
|
|
@@ -23221,17 +23319,18 @@ async function createModuleRouters(ctx, definitions, modulePrefix) {
|
|
|
23221
23319
|
const runtime = await createEntityRuntimeAsync(ctx, definition);
|
|
23222
23320
|
const route = inferEntityRoutePath(definition);
|
|
23223
23321
|
const entityLabel = resolveLocalized7(definition.label, "en");
|
|
23224
|
-
|
|
23322
|
+
const isExposed = !("expose" in definition && definition.expose === false);
|
|
23323
|
+
if (isExposed && modulePrefix && route === modulePrefix) {
|
|
23225
23324
|
ctx.core.logger.warn(
|
|
23226
23325
|
`Entity "${entityLabel}" inferred route "${route}" duplicates module prefix \u2014 add routePrefix to the entity definition to fix`
|
|
23227
23326
|
);
|
|
23228
23327
|
}
|
|
23229
|
-
if (routeMap.has(route)) {
|
|
23328
|
+
if (isExposed && routeMap.has(route)) {
|
|
23230
23329
|
ctx.core.logger.warn(
|
|
23231
23330
|
`Entity "${entityLabel}" route "${route}" collides with "${routeMap.get(route)}" in the same module`
|
|
23232
23331
|
);
|
|
23233
23332
|
}
|
|
23234
|
-
routeMap.set(route, entityLabel);
|
|
23333
|
+
if (isExposed) routeMap.set(route, entityLabel);
|
|
23235
23334
|
router.use(route, runtime.router);
|
|
23236
23335
|
const key = getServiceKey(definition);
|
|
23237
23336
|
if (ctx.services.has(key)) {
|
|
@@ -23447,7 +23546,7 @@ var init_runtime = __esm({
|
|
|
23447
23546
|
}
|
|
23448
23547
|
});
|
|
23449
23548
|
|
|
23450
|
-
// src/db/
|
|
23549
|
+
// src/db/seed-runner.ts
|
|
23451
23550
|
import { existsSync as existsSync10 } from "fs";
|
|
23452
23551
|
import { join as join10 } from "path";
|
|
23453
23552
|
import { pathToFileURL } from "url";
|
|
@@ -23515,8 +23614,8 @@ function hasSeedData(seed5) {
|
|
|
23515
23614
|
if (!Array.isArray(seed5) && "source" in seed5 && seed5.source === "url") return true;
|
|
23516
23615
|
return Array.isArray(seed5) && seed5.length > 0;
|
|
23517
23616
|
}
|
|
23518
|
-
var
|
|
23519
|
-
"src/db/
|
|
23617
|
+
var init_seed_runner = __esm({
|
|
23618
|
+
"src/db/seed-runner.ts"() {
|
|
23520
23619
|
"use strict";
|
|
23521
23620
|
init_runtime();
|
|
23522
23621
|
init_paths();
|
|
@@ -23619,7 +23718,7 @@ var init_migration_sources = __esm({
|
|
|
23619
23718
|
init_shared();
|
|
23620
23719
|
init_paths();
|
|
23621
23720
|
init_plugin_ops();
|
|
23622
|
-
|
|
23721
|
+
init_module_store();
|
|
23623
23722
|
}
|
|
23624
23723
|
});
|
|
23625
23724
|
|
|
@@ -23952,7 +24051,7 @@ async function runMigrations(knexInstance, sources) {
|
|
|
23952
24051
|
const executedMigrations = await knex3("_nexus_migrations").where({ status: "completed" }).select("name").then((rows) => new Set(rows.map((r) => r.name)));
|
|
23953
24052
|
const pendingMigrations = migrationFiles.filter((m2) => !executedMigrations.has(m2.name));
|
|
23954
24053
|
if (pendingMigrations.length === 0) {
|
|
23955
|
-
logger.
|
|
24054
|
+
logger.debug("No pending migrations");
|
|
23956
24055
|
return;
|
|
23957
24056
|
}
|
|
23958
24057
|
const batch = await getNextBatch(knex3);
|
|
@@ -24352,6 +24451,27 @@ var init_migration_helpers = __esm({
|
|
|
24352
24451
|
import path2 from "path";
|
|
24353
24452
|
import fs2 from "fs/promises";
|
|
24354
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
|
+
}
|
|
24355
24475
|
async function detectSchemaDrift(knexInstance) {
|
|
24356
24476
|
const knex3 = knexInstance ?? getDb();
|
|
24357
24477
|
const entities = getAllPersistentEntities();
|
|
@@ -24459,6 +24579,7 @@ function computeSchemaDiff(entities, currentSchema, options) {
|
|
|
24459
24579
|
for (const idx of entityIndexes) {
|
|
24460
24580
|
const key = normalizeKey(idx.columns, !!idx.unique);
|
|
24461
24581
|
if (!currentKeys.has(key)) {
|
|
24582
|
+
warnIfIndexExceedsMySQLLimit(tableName, idx.columns, !!idx.unique, entity.fields);
|
|
24462
24583
|
diff.newIndexes.push({ columns: idx.columns, unique: !!idx.unique });
|
|
24463
24584
|
}
|
|
24464
24585
|
}
|
|
@@ -24630,7 +24751,7 @@ function formatDriftMessage(drift) {
|
|
|
24630
24751
|
lines.push('Run "pnpm migrate:dev" to generate and apply migrations.');
|
|
24631
24752
|
return lines.join("\n");
|
|
24632
24753
|
}
|
|
24633
|
-
var PERSISTENT_TYPES;
|
|
24754
|
+
var MYSQL_MAX_INDEX_BYTES, MYSQL_BYTES_PER_CHAR, PERSISTENT_TYPES;
|
|
24634
24755
|
var init_migration_generator = __esm({
|
|
24635
24756
|
"src/db/migration-generator.ts"() {
|
|
24636
24757
|
"use strict";
|
|
@@ -24639,9 +24760,11 @@ var init_migration_generator = __esm({
|
|
|
24639
24760
|
init_paths();
|
|
24640
24761
|
init_schema_reader();
|
|
24641
24762
|
init_engine();
|
|
24642
|
-
|
|
24763
|
+
init_module_queries();
|
|
24643
24764
|
init_migration_helpers();
|
|
24644
|
-
|
|
24765
|
+
init_module_store();
|
|
24766
|
+
MYSQL_MAX_INDEX_BYTES = 3072;
|
|
24767
|
+
MYSQL_BYTES_PER_CHAR = 4;
|
|
24645
24768
|
PERSISTENT_TYPES = /* @__PURE__ */ new Set([
|
|
24646
24769
|
"collection",
|
|
24647
24770
|
"tree",
|
|
@@ -26215,7 +26338,7 @@ var init_db = __esm({
|
|
|
26215
26338
|
init_schema_helpers();
|
|
26216
26339
|
init_sql_utils();
|
|
26217
26340
|
init_sqlite_compat();
|
|
26218
|
-
|
|
26341
|
+
init_seed_runner();
|
|
26219
26342
|
init_ensure_system_tables();
|
|
26220
26343
|
init_migration_sources();
|
|
26221
26344
|
init_migration_runner();
|
|
@@ -26479,8 +26602,11 @@ var init_events_api = __esm({
|
|
|
26479
26602
|
|
|
26480
26603
|
// src/engine/context.ts
|
|
26481
26604
|
import { ForbiddenError as CASLForbiddenError3, subject } from "@casl/ability";
|
|
26482
|
-
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";
|
|
26483
26606
|
import { Redis as Redis2 } from "ioredis";
|
|
26607
|
+
function setLocales(locales) {
|
|
26608
|
+
platformLocales = locales;
|
|
26609
|
+
}
|
|
26484
26610
|
function getSharedCacheManager() {
|
|
26485
26611
|
if (!sharedCacheManager) {
|
|
26486
26612
|
const redisUrl = env.REDIS_URL;
|
|
@@ -26556,7 +26682,7 @@ function createModuleContext() {
|
|
|
26556
26682
|
const defaultAdapter = createKnexAdapter(knex3);
|
|
26557
26683
|
const defaultSchemaAdapter = createKnexSchemaAdapter(knex3);
|
|
26558
26684
|
adaptersRegistry["temp"] = { data: getSharedTempAdapter() };
|
|
26559
|
-
logger.
|
|
26685
|
+
logger.trace(env.REDIS_URL ? "Temp adapter: Redis (shared)" : "Temp adapter: InMemory (shared)");
|
|
26560
26686
|
const middleware = {
|
|
26561
26687
|
validate,
|
|
26562
26688
|
rateLimit: createRateLimit,
|
|
@@ -26664,7 +26790,9 @@ function createModuleContext() {
|
|
|
26664
26790
|
throw new Error(`Knex connection for adapter "${adapter}" not found. Available: ${Object.keys(knexConnections).join(", ")}`);
|
|
26665
26791
|
}
|
|
26666
26792
|
return conn;
|
|
26667
|
-
}
|
|
26793
|
+
},
|
|
26794
|
+
// Placeholder — bound after ctx construction (needs full ModuleContext)
|
|
26795
|
+
seedModule: null
|
|
26668
26796
|
};
|
|
26669
26797
|
const configContext = {
|
|
26670
26798
|
env,
|
|
@@ -26757,7 +26885,8 @@ function createModuleContext() {
|
|
|
26757
26885
|
adapters: adaptersContext,
|
|
26758
26886
|
// Root-level shortcuts for frequently used utilities
|
|
26759
26887
|
events: createEventsApi(nexusEvents, logger),
|
|
26760
|
-
createRouter: () => createRouter()
|
|
26888
|
+
createRouter: () => createRouter(),
|
|
26889
|
+
locales: platformLocales
|
|
26761
26890
|
};
|
|
26762
26891
|
servicesRegistry["cacheManager"] = getSharedCacheManager();
|
|
26763
26892
|
ctx.runtime = {
|
|
@@ -26766,9 +26895,10 @@ function createModuleContext() {
|
|
|
26766
26895
|
createEntityController: (service, def) => createEntityController(service, def, ctx),
|
|
26767
26896
|
createEntityRouter: (controller, def) => createEntityRouter(controller, def, ctx)
|
|
26768
26897
|
};
|
|
26898
|
+
ctx.db.seedModule = (mod) => runModuleSeed(mod, ctx);
|
|
26769
26899
|
return ctx;
|
|
26770
26900
|
}
|
|
26771
|
-
var sharedCacheManager, sharedTempAdapter;
|
|
26901
|
+
var platformLocales, sharedCacheManager, sharedTempAdapter;
|
|
26772
26902
|
var init_context = __esm({
|
|
26773
26903
|
"src/engine/context.ts"() {
|
|
26774
26904
|
"use strict";
|
|
@@ -26781,7 +26911,9 @@ var init_context = __esm({
|
|
|
26781
26911
|
init_plugin_ops();
|
|
26782
26912
|
init_load_config();
|
|
26783
26913
|
init_events_api();
|
|
26914
|
+
init_seed_runner();
|
|
26784
26915
|
init_cache_manager();
|
|
26916
|
+
platformLocales = DEFAULT_LOCALES;
|
|
26785
26917
|
sharedCacheManager = null;
|
|
26786
26918
|
sharedTempAdapter = null;
|
|
26787
26919
|
}
|
|
@@ -26792,11 +26924,11 @@ var init_engine = __esm({
|
|
|
26792
26924
|
"src/engine/index.ts"() {
|
|
26793
26925
|
"use strict";
|
|
26794
26926
|
init_registry();
|
|
26795
|
-
|
|
26927
|
+
init_module_queries();
|
|
26796
26928
|
init_loader();
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
|
|
26929
|
+
init_module_store();
|
|
26930
|
+
init_subject_extractor();
|
|
26931
|
+
init_definition_extractors();
|
|
26800
26932
|
init_context();
|
|
26801
26933
|
}
|
|
26802
26934
|
});
|
|
@@ -27338,7 +27470,7 @@ async function createApp(options = {}) {
|
|
|
27338
27470
|
// Only accept arrays and objects
|
|
27339
27471
|
}));
|
|
27340
27472
|
app.use(cookieParser());
|
|
27341
|
-
const serveSPA = createServeSPA(app);
|
|
27473
|
+
const serveSPA = createServeSPA(app, options.httpServer);
|
|
27342
27474
|
if (options.beforeRoutes) {
|
|
27343
27475
|
const result = options.beforeRoutes(app, serveSPA);
|
|
27344
27476
|
if (result instanceof Promise) {
|
|
@@ -27432,11 +27564,11 @@ async function createApp(options = {}) {
|
|
|
27432
27564
|
});
|
|
27433
27565
|
const sortedSpas = [...servedSpas].sort((a, b) => b.endpoint.length - a.endpoint.length);
|
|
27434
27566
|
for (const spa of sortedSpas) {
|
|
27435
|
-
serveSPA(spa.endpoint, spa.path, spa);
|
|
27567
|
+
await serveSPA(spa.endpoint, spa.path, { ...spa, viteSrc: spa.viteSrc });
|
|
27436
27568
|
}
|
|
27437
27569
|
const { ui } = getConfig();
|
|
27438
27570
|
if (ui.enabled) {
|
|
27439
|
-
serveSPA(ui.base, ui.path);
|
|
27571
|
+
await serveSPA(ui.base, ui.path, { viteSrc: "../ui" });
|
|
27440
27572
|
}
|
|
27441
27573
|
app.use(errorMiddleware);
|
|
27442
27574
|
return app;
|
|
@@ -27462,58 +27594,32 @@ var init_app = __esm({
|
|
|
27462
27594
|
}
|
|
27463
27595
|
});
|
|
27464
27596
|
|
|
27465
|
-
// src/core/utils/
|
|
27597
|
+
// src/core/utils/port-check.ts
|
|
27466
27598
|
import net from "net";
|
|
27467
27599
|
import { execSync } from "child_process";
|
|
27468
27600
|
function findProcessOnPort(port) {
|
|
27469
27601
|
try {
|
|
27470
27602
|
const output = execSync(`lsof -ti :${port} 2>/dev/null`, { encoding: "utf-8" });
|
|
27471
27603
|
const pid = parseInt(output.trim().split("\n")[0] ?? "", 10);
|
|
27472
|
-
|
|
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 };
|
|
27473
27611
|
} catch {
|
|
27474
27612
|
return null;
|
|
27475
27613
|
}
|
|
27476
27614
|
}
|
|
27477
|
-
function isSameProcessGroup(pid) {
|
|
27478
|
-
if (pid === process.pid || pid === process.ppid) return true;
|
|
27479
|
-
try {
|
|
27480
|
-
const ourPgid = execSync(`ps -o pgid= -p ${process.pid} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
27481
|
-
const targetPgid = execSync(`ps -o pgid= -p ${pid} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
27482
|
-
return ourPgid === targetPgid;
|
|
27483
|
-
} catch {
|
|
27484
|
-
return false;
|
|
27485
|
-
}
|
|
27486
|
-
}
|
|
27487
|
-
function killProcessOnPort(port) {
|
|
27488
|
-
const pid = findProcessOnPort(port);
|
|
27489
|
-
if (!pid) return false;
|
|
27490
|
-
if (isSameProcessGroup(pid)) {
|
|
27491
|
-
logger.warn({ port, pid }, `Skipping same process group (PID ${pid}) on port ${port}`);
|
|
27492
|
-
return false;
|
|
27493
|
-
}
|
|
27494
|
-
try {
|
|
27495
|
-
process.kill(pid, "SIGTERM");
|
|
27496
|
-
logger.warn({ port, pid }, `Killed process ${pid} on port ${port}`);
|
|
27497
|
-
return true;
|
|
27498
|
-
} catch {
|
|
27499
|
-
return false;
|
|
27500
|
-
}
|
|
27501
|
-
}
|
|
27502
27615
|
async function checkPortAvailable(port, host = "0.0.0.0") {
|
|
27503
27616
|
return new Promise((resolve2, reject) => {
|
|
27504
27617
|
const server2 = net.createServer();
|
|
27505
27618
|
server2.once("error", (err) => {
|
|
27506
27619
|
if (err.code === "EADDRINUSE") {
|
|
27507
|
-
|
|
27508
|
-
|
|
27509
|
-
|
|
27510
|
-
checkPortAvailable(port, host).then(resolve2).catch(reject);
|
|
27511
|
-
}, 500);
|
|
27512
|
-
return;
|
|
27513
|
-
}
|
|
27514
|
-
}
|
|
27515
|
-
logger.error({ port }, `Port ${port} is already in use`);
|
|
27516
|
-
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));
|
|
27517
27623
|
} else {
|
|
27518
27624
|
reject(err);
|
|
27519
27625
|
}
|
|
@@ -27524,10 +27630,9 @@ async function checkPortAvailable(port, host = "0.0.0.0") {
|
|
|
27524
27630
|
server2.listen(port, host);
|
|
27525
27631
|
});
|
|
27526
27632
|
}
|
|
27527
|
-
var
|
|
27528
|
-
"src/core/utils/
|
|
27633
|
+
var init_port_check = __esm({
|
|
27634
|
+
"src/core/utils/port-check.ts"() {
|
|
27529
27635
|
"use strict";
|
|
27530
|
-
init_logger();
|
|
27531
27636
|
}
|
|
27532
27637
|
});
|
|
27533
27638
|
|
|
@@ -27622,6 +27727,150 @@ var init_tunnel = __esm({
|
|
|
27622
27727
|
}
|
|
27623
27728
|
});
|
|
27624
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
|
+
|
|
27625
27874
|
// src/instrumentation.ts
|
|
27626
27875
|
var instrumentation_exports = {};
|
|
27627
27876
|
__export(instrumentation_exports, {
|
|
@@ -27686,7 +27935,9 @@ var init_instrumentation = __esm({
|
|
|
27686
27935
|
});
|
|
27687
27936
|
|
|
27688
27937
|
// src/core/server.ts
|
|
27938
|
+
import http from "http";
|
|
27689
27939
|
import { entityRoom as entityRoom6 } from "@gzl10/nexus-sdk";
|
|
27940
|
+
import { DEFAULT_LOCALES as DEFAULT_LOCALES2 } from "@gzl10/nexus-sdk";
|
|
27690
27941
|
async function runMigrationsAndSeeds(config3) {
|
|
27691
27942
|
initLoggerService(getLoggerConfig());
|
|
27692
27943
|
setLoggerInstance(getPinoLogger());
|
|
@@ -27739,7 +27990,7 @@ async function runMigrationsAndSeeds(config3) {
|
|
|
27739
27990
|
const sources = buildMigrationSources();
|
|
27740
27991
|
const migrationFiles = await loadAllMigrationFiles(sources);
|
|
27741
27992
|
if (migrationFiles.length > 0) {
|
|
27742
|
-
logger.info({
|
|
27993
|
+
logger.info({ sources: sources.length, files: migrationFiles.length }, "Running migration deploy...");
|
|
27743
27994
|
try {
|
|
27744
27995
|
await runMigrations(void 0, sources);
|
|
27745
27996
|
} catch (err) {
|
|
@@ -27748,7 +27999,7 @@ async function runMigrationsAndSeeds(config3) {
|
|
|
27748
27999
|
throw err;
|
|
27749
28000
|
}
|
|
27750
28001
|
}
|
|
27751
|
-
logger.
|
|
28002
|
+
logger.debug("Checking schema drift...");
|
|
27752
28003
|
const drift = await detectSchemaDrift();
|
|
27753
28004
|
if (drift && (drift.newTables.length > 0 || drift.alteredTables.length > 0)) {
|
|
27754
28005
|
const message = formatDriftMessage(drift);
|
|
@@ -27795,7 +28046,7 @@ ${dirs}`);
|
|
|
27795
28046
|
}
|
|
27796
28047
|
const allDefinitions = modules.flatMap((m2) => m2.definitions ?? []);
|
|
27797
28048
|
await createMemoryTables(allDefinitions);
|
|
27798
|
-
logger.
|
|
28049
|
+
logger.debug("Running seeds...");
|
|
27799
28050
|
for (const mod of modules) {
|
|
27800
28051
|
try {
|
|
27801
28052
|
await runModuleSeed(mod, ctx);
|
|
@@ -27803,12 +28054,39 @@ ${dirs}`);
|
|
|
27803
28054
|
logger.error({ module: mod.name, err }, "Seed failed - continuing with next module");
|
|
27804
28055
|
}
|
|
27805
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
|
+
}
|
|
27806
28083
|
}
|
|
27807
28084
|
async function start(config3) {
|
|
27808
28085
|
if (server) {
|
|
27809
28086
|
throw new Error("Server already running. Call stop() first.");
|
|
27810
28087
|
}
|
|
27811
28088
|
currentConfig = config3;
|
|
28089
|
+
setLocales(config3?.locales ?? DEFAULT_LOCALES2);
|
|
27812
28090
|
if (env.NODE_ENV === "development" && env.FRPC_SERVER && env.FRPC_SUBDOMAIN && !env.BACKEND_URL) {
|
|
27813
28091
|
process.env["BACKEND_URL"] = getTunnelUrl(env.FRPC_SUBDOMAIN, env.FRPC_SERVER);
|
|
27814
28092
|
}
|
|
@@ -27833,13 +28111,17 @@ async function start(config3) {
|
|
|
27833
28111
|
await initTelemetry2();
|
|
27834
28112
|
await runMigrationsAndSeeds(config3);
|
|
27835
28113
|
const effectiveCorsOrigins = buildEffectiveCorsOrigins(env.CORS_ORIGIN, config3?.spas);
|
|
28114
|
+
const httpServer = http.createServer();
|
|
27836
28115
|
const app = await createApp({
|
|
27837
28116
|
beforeRoutes: config3?.beforeRoutes,
|
|
27838
28117
|
afterRoutes: config3?.afterRoutes,
|
|
27839
|
-
spas: config3?.spas
|
|
28118
|
+
spas: config3?.spas,
|
|
28119
|
+
httpServer
|
|
27840
28120
|
});
|
|
28121
|
+
httpServer.on("request", app);
|
|
27841
28122
|
return new Promise((resolve2) => {
|
|
27842
|
-
server =
|
|
28123
|
+
server = httpServer;
|
|
28124
|
+
httpServer.listen(resolved.port, resolved.host, async () => {
|
|
27843
28125
|
const timeoutMs = parseInt(process.env["REQUEST_TIMEOUT_MS"] || "30000", 10);
|
|
27844
28126
|
if (timeoutMs > 0) {
|
|
27845
28127
|
server.setTimeout(timeoutMs);
|
|
@@ -27875,12 +28157,10 @@ async function start(config3) {
|
|
|
27875
28157
|
});
|
|
27876
28158
|
}
|
|
27877
28159
|
const baseUrl = env.BACKEND_URL || `http://localhost:${actualPort}`;
|
|
27878
|
-
logger.
|
|
27879
|
-
|
|
27880
|
-
if (resolved.ui.enabled)
|
|
27881
|
-
|
|
27882
|
-
}
|
|
27883
|
-
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");
|
|
27884
28164
|
nexusEvents.emitEvent("server.started", { port: actualPort, host: resolved.host });
|
|
27885
28165
|
if (config3?.onReady) {
|
|
27886
28166
|
try {
|
|
@@ -27906,7 +28186,8 @@ async function stop() {
|
|
|
27906
28186
|
await resetSharedAdapters();
|
|
27907
28187
|
resetConfigCache();
|
|
27908
28188
|
clearCustomCaslRules();
|
|
27909
|
-
|
|
28189
|
+
clearSeedPermissions();
|
|
28190
|
+
await resetServeSPA();
|
|
27910
28191
|
return;
|
|
27911
28192
|
}
|
|
27912
28193
|
if (currentConfig?.beforeClose) {
|
|
@@ -27940,7 +28221,8 @@ async function stop() {
|
|
|
27940
28221
|
await resetSharedAdapters();
|
|
27941
28222
|
resetConfigCache();
|
|
27942
28223
|
clearCustomCaslRules();
|
|
27943
|
-
|
|
28224
|
+
clearSeedPermissions();
|
|
28225
|
+
await resetServeSPA();
|
|
27944
28226
|
currentConfig = void 0;
|
|
27945
28227
|
server = null;
|
|
27946
28228
|
nexusEvents.emitEvent("server.stopped");
|
|
@@ -28000,7 +28282,7 @@ var init_server = __esm({
|
|
|
28000
28282
|
init_cors();
|
|
28001
28283
|
init_logger();
|
|
28002
28284
|
init_error_middleware();
|
|
28003
|
-
|
|
28285
|
+
init_port_check();
|
|
28004
28286
|
init_engine();
|
|
28005
28287
|
init_context();
|
|
28006
28288
|
init_db();
|