@gzl10/nexus-backend 0.17.0 → 0.19.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/app-error-CKbYJQ9V.d.ts +136 -0
- package/dist/cli.js +2769 -9275
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +116 -142
- package/dist/index.js +1126 -359
- package/dist/index.js.map +1 -1
- package/dist/main.js +1136 -359
- package/dist/main.js.map +1 -1
- package/dist/migration-helpers/index.d.ts +63 -0
- package/dist/migration-helpers/index.js +12116 -0
- package/dist/migration-helpers/index.js.map +1 -0
- package/dist/testing/index.d.ts +81 -0
- package/dist/testing/index.js +1675 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +26 -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.19.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",
|
|
@@ -26,6 +26,14 @@ var init_package = __esm({
|
|
|
26
26
|
".": {
|
|
27
27
|
import: "./dist/index.js",
|
|
28
28
|
types: "./dist/index.d.ts"
|
|
29
|
+
},
|
|
30
|
+
"./testing": {
|
|
31
|
+
import: "./dist/testing/index.js",
|
|
32
|
+
types: "./dist/testing/index.d.ts"
|
|
33
|
+
},
|
|
34
|
+
"./migrations": {
|
|
35
|
+
import: "./dist/migration-helpers/index.js",
|
|
36
|
+
types: "./dist/migration-helpers/index.d.ts"
|
|
29
37
|
}
|
|
30
38
|
},
|
|
31
39
|
files: [
|
|
@@ -70,7 +78,7 @@ var init_package = __esm({
|
|
|
70
78
|
"jwt"
|
|
71
79
|
],
|
|
72
80
|
scripts: {
|
|
73
|
-
dev: "
|
|
81
|
+
dev: "node --watch-path=./src --import tsx/esm src/main.ts",
|
|
74
82
|
build: "tsup",
|
|
75
83
|
start: "node dist/main.js",
|
|
76
84
|
nexus: "tsx src/cli.ts",
|
|
@@ -126,39 +134,34 @@ var init_package = __esm({
|
|
|
126
134
|
zod: "^3.24.0"
|
|
127
135
|
},
|
|
128
136
|
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
137
|
"@types/bcryptjs": "^2.4.0",
|
|
150
138
|
"@types/compression": "^1.8.1",
|
|
151
139
|
"@types/cookie-parser": "^1.4.10",
|
|
152
140
|
"@types/cors": "^2.8.19",
|
|
153
141
|
"@types/express": "^5.0.6",
|
|
142
|
+
"@types/express-serve-static-core": "^5.1.1",
|
|
154
143
|
"@types/jsonwebtoken": "^9.0.10",
|
|
155
144
|
"@types/multer": "^2.1.0",
|
|
156
145
|
"@types/nodemailer": "^7.0.11",
|
|
146
|
+
"@types/qs": "^6.15.0",
|
|
157
147
|
"@types/supertest": "^6.0.3",
|
|
158
148
|
"pino-pretty": "^13.1.3",
|
|
159
149
|
"socket.io-client": "^4.8.3",
|
|
160
150
|
supertest: "^7.2.2",
|
|
161
|
-
tsx: "^4.21.0"
|
|
151
|
+
tsx: "^4.21.0",
|
|
152
|
+
vite: "^8.0.3"
|
|
153
|
+
},
|
|
154
|
+
peerDependencies: {
|
|
155
|
+
vite: ">=6.0.0",
|
|
156
|
+
vitest: ">=3.0.0"
|
|
157
|
+
},
|
|
158
|
+
peerDependenciesMeta: {
|
|
159
|
+
vite: {
|
|
160
|
+
optional: true
|
|
161
|
+
},
|
|
162
|
+
vitest: {
|
|
163
|
+
optional: true
|
|
164
|
+
}
|
|
162
165
|
},
|
|
163
166
|
publishConfig: {
|
|
164
167
|
access: "public",
|
|
@@ -1059,6 +1062,14 @@ async function isWorkspacePackage(name) {
|
|
|
1059
1062
|
}
|
|
1060
1063
|
}
|
|
1061
1064
|
async function installPlugin(name, opts) {
|
|
1065
|
+
const projectPath2 = opts?.projectPath ?? process.cwd();
|
|
1066
|
+
const pkgPath = join2(projectPath2, "package.json");
|
|
1067
|
+
if (existsSync2(pkgPath)) {
|
|
1068
|
+
const pkg3 = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1069
|
+
if (pkg3.name === "@gzl10/nexus-backend") {
|
|
1070
|
+
throw new Error("Cannot install plugins inside @gzl10/nexus-backend. Run this command from a Nexus project directory.");
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1062
1073
|
const isWorkspace = await isWorkspacePackage(name);
|
|
1063
1074
|
let pkg2;
|
|
1064
1075
|
if (opts?.version) {
|
|
@@ -1075,11 +1086,28 @@ async function installPlugin(name, opts) {
|
|
|
1075
1086
|
plugins[name] = { enabled: true };
|
|
1076
1087
|
writePluginsFile(plugins, filePath);
|
|
1077
1088
|
}
|
|
1089
|
+
function isPackageInstalled(name, projectPath2) {
|
|
1090
|
+
const base = projectPath2 || process.cwd();
|
|
1091
|
+
const pkgPath = join2(base, "package.json");
|
|
1092
|
+
try {
|
|
1093
|
+
const pkg2 = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1094
|
+
return !!(pkg2.dependencies?.[name] || pkg2.devDependencies?.[name]);
|
|
1095
|
+
} catch {
|
|
1096
|
+
return false;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1078
1099
|
async function uninstallPlugin(name, projectPath2) {
|
|
1079
|
-
await execAsync(`pnpm remove ${name}`);
|
|
1080
1100
|
const filePath = getPluginsFilePath(projectPath2);
|
|
1081
1101
|
const plugins = readPluginsFile(filePath);
|
|
1082
|
-
|
|
1102
|
+
const isRegistered = name in plugins;
|
|
1103
|
+
const isInstalled = isPackageInstalled(name, projectPath2);
|
|
1104
|
+
if (!isRegistered && !isInstalled) {
|
|
1105
|
+
throw new Error(`Plugin ${shortPluginName(name)} is not installed`);
|
|
1106
|
+
}
|
|
1107
|
+
if (isInstalled) {
|
|
1108
|
+
await execAsync(`pnpm remove ${name}`);
|
|
1109
|
+
}
|
|
1110
|
+
if (isRegistered) {
|
|
1083
1111
|
delete plugins[name];
|
|
1084
1112
|
writePluginsFile(plugins, filePath);
|
|
1085
1113
|
}
|
|
@@ -1160,7 +1188,7 @@ var init_table_prefix = __esm({
|
|
|
1160
1188
|
}
|
|
1161
1189
|
});
|
|
1162
1190
|
|
|
1163
|
-
// src/engine/store.ts
|
|
1191
|
+
// src/engine/module-store.ts
|
|
1164
1192
|
function resetStore() {
|
|
1165
1193
|
moduleStore.modules.length = 0;
|
|
1166
1194
|
moduleStore.plugins.clear();
|
|
@@ -1171,8 +1199,8 @@ function resetStore() {
|
|
|
1171
1199
|
moduleStore.tableToSubject.clear();
|
|
1172
1200
|
}
|
|
1173
1201
|
var moduleStore;
|
|
1174
|
-
var
|
|
1175
|
-
"src/engine/store.ts"() {
|
|
1202
|
+
var init_module_store = __esm({
|
|
1203
|
+
"src/engine/module-store.ts"() {
|
|
1176
1204
|
"use strict";
|
|
1177
1205
|
moduleStore = {
|
|
1178
1206
|
/** Registered modules with source metadata */
|
|
@@ -1225,7 +1253,7 @@ var init_id = __esm({
|
|
|
1225
1253
|
}
|
|
1226
1254
|
});
|
|
1227
1255
|
|
|
1228
|
-
// src/engine/extractors.ts
|
|
1256
|
+
// src/engine/definition-extractors.ts
|
|
1229
1257
|
function getTableAndSubject(def) {
|
|
1230
1258
|
const caslSubject = def.casl?.subject;
|
|
1231
1259
|
if (!TYPES_WITH_TABLE.has(def.type)) {
|
|
@@ -1293,8 +1321,8 @@ function validateModuleDependencies(modules) {
|
|
|
1293
1321
|
}
|
|
1294
1322
|
}
|
|
1295
1323
|
var TYPES_WITH_TABLE;
|
|
1296
|
-
var
|
|
1297
|
-
"src/engine/extractors.ts"() {
|
|
1324
|
+
var init_definition_extractors = __esm({
|
|
1325
|
+
"src/engine/definition-extractors.ts"() {
|
|
1298
1326
|
"use strict";
|
|
1299
1327
|
TYPES_WITH_TABLE = /* @__PURE__ */ new Set(["collection", "reference", "event", "config", "temp", "view", void 0]);
|
|
1300
1328
|
}
|
|
@@ -1414,9 +1442,9 @@ var init_registry = __esm({
|
|
|
1414
1442
|
"use strict";
|
|
1415
1443
|
init_plugin_ops();
|
|
1416
1444
|
init_table_prefix();
|
|
1417
|
-
|
|
1445
|
+
init_module_store();
|
|
1418
1446
|
init_id();
|
|
1419
|
-
|
|
1447
|
+
init_definition_extractors();
|
|
1420
1448
|
}
|
|
1421
1449
|
});
|
|
1422
1450
|
|
|
@@ -1479,7 +1507,28 @@ var init_paths = __esm({
|
|
|
1479
1507
|
}
|
|
1480
1508
|
});
|
|
1481
1509
|
|
|
1482
|
-
// src/engine/queries.ts
|
|
1510
|
+
// src/engine/module-queries.ts
|
|
1511
|
+
var module_queries_exports = {};
|
|
1512
|
+
__export(module_queries_exports, {
|
|
1513
|
+
getCoreManifest: () => getCoreManifest,
|
|
1514
|
+
getCoreModules: () => getCoreModules,
|
|
1515
|
+
getModule: () => getModule,
|
|
1516
|
+
getModules: () => getModules,
|
|
1517
|
+
getOrderedModules: () => getOrderedModules,
|
|
1518
|
+
getOrderedModulesInternal: () => getOrderedModulesInternal,
|
|
1519
|
+
getPlugin: () => getPlugin,
|
|
1520
|
+
getPluginByCode: () => getPluginByCode,
|
|
1521
|
+
getPlugins: () => getPlugins,
|
|
1522
|
+
getRegisteredSubjects: () => getRegisteredSubjects,
|
|
1523
|
+
getSubjectForTable: () => getSubjectForTable,
|
|
1524
|
+
getUserManifest: () => getUserManifest,
|
|
1525
|
+
getUserModules: () => getUserModules,
|
|
1526
|
+
hasModule: () => hasModule,
|
|
1527
|
+
hasPlugin: () => hasPlugin,
|
|
1528
|
+
hasPluginByCode: () => hasPluginByCode,
|
|
1529
|
+
hasUserApp: () => hasUserApp,
|
|
1530
|
+
isValidSubject: () => isValidSubject
|
|
1531
|
+
});
|
|
1483
1532
|
import { join as join4 } from "path";
|
|
1484
1533
|
import { readFileSync as readFileSync3 } from "fs";
|
|
1485
1534
|
function readPackageJson(dir) {
|
|
@@ -1575,10 +1624,10 @@ function getPluginByCode(code) {
|
|
|
1575
1624
|
function hasPluginByCode(code) {
|
|
1576
1625
|
return moduleStore.pluginsByCode.has(code);
|
|
1577
1626
|
}
|
|
1578
|
-
var
|
|
1579
|
-
"src/engine/queries.ts"() {
|
|
1627
|
+
var init_module_queries = __esm({
|
|
1628
|
+
"src/engine/module-queries.ts"() {
|
|
1580
1629
|
"use strict";
|
|
1581
|
-
|
|
1630
|
+
init_module_store();
|
|
1582
1631
|
init_paths();
|
|
1583
1632
|
}
|
|
1584
1633
|
});
|
|
@@ -2058,17 +2107,22 @@ var init_definitions = __esm({
|
|
|
2058
2107
|
};
|
|
2059
2108
|
mastersEntity = {
|
|
2060
2109
|
table: "masters",
|
|
2110
|
+
seedable: true,
|
|
2061
2111
|
routePrefix: "/",
|
|
2062
2112
|
label: { en: "Master", es: "Maestro" },
|
|
2063
2113
|
labelPlural: { en: "Masters", es: "Maestros" },
|
|
2064
2114
|
labelField: "label",
|
|
2065
2115
|
timestamps: true,
|
|
2116
|
+
availableDisplayModes: ["board"],
|
|
2117
|
+
groupBy: "type",
|
|
2118
|
+
groupableFields: ["type"],
|
|
2119
|
+
//columnDragFields: ['is_active'],
|
|
2066
2120
|
fields: {
|
|
2067
2121
|
id: useTextField2({
|
|
2068
2122
|
label: { en: "ID", es: "ID" },
|
|
2069
2123
|
required: true,
|
|
2070
2124
|
size: 100,
|
|
2071
|
-
|
|
2125
|
+
hidden: true,
|
|
2072
2126
|
meta: { sortable: true }
|
|
2073
2127
|
}),
|
|
2074
2128
|
type: useTextField2({
|
|
@@ -2085,13 +2139,31 @@ var init_definitions = __esm({
|
|
|
2085
2139
|
meta: { sortable: true, searchable: true }
|
|
2086
2140
|
}),
|
|
2087
2141
|
label: useLocalizedField({ label: { en: "Name", es: "Nombre" } }),
|
|
2088
|
-
order: orderField,
|
|
2142
|
+
order: { ...orderField, meta: { ...orderField.meta, showInDisplay: false } },
|
|
2089
2143
|
is_active: isActiveField,
|
|
2090
2144
|
metadata: useJsonField({
|
|
2091
2145
|
label: { en: "Metadata", es: "Metadatos" },
|
|
2092
|
-
hint: {
|
|
2146
|
+
hint: {
|
|
2147
|
+
en: "Type-specific fields (symbol, flag, etc.)",
|
|
2148
|
+
es: "Campos espec\xEDficos del tipo"
|
|
2149
|
+
},
|
|
2150
|
+
meta: { showInDisplay: false }
|
|
2093
2151
|
})
|
|
2094
2152
|
},
|
|
2153
|
+
hooks: () => ({
|
|
2154
|
+
beforeCreate: async (data) => {
|
|
2155
|
+
if (data["type"] && data["code"] && !data["id"]) {
|
|
2156
|
+
data["id"] = `${data["type"]}:${data["code"]}`;
|
|
2157
|
+
}
|
|
2158
|
+
return data;
|
|
2159
|
+
},
|
|
2160
|
+
beforeUpdate: async (_id, data) => {
|
|
2161
|
+
if ("type" in data || "code" in data) {
|
|
2162
|
+
throw new Error("Cannot update type or code of a master record. Delete and recreate instead.");
|
|
2163
|
+
}
|
|
2164
|
+
return data;
|
|
2165
|
+
}
|
|
2166
|
+
}),
|
|
2095
2167
|
defaultSort: { field: "order", order: "asc" },
|
|
2096
2168
|
indexes: [{ columns: ["type", "code"], unique: true }],
|
|
2097
2169
|
casl: { subject: "Master", permissions: masterCaslPermissions }
|
|
@@ -2100,6 +2172,10 @@ var init_definitions = __esm({
|
|
|
2100
2172
|
});
|
|
2101
2173
|
|
|
2102
2174
|
// src/modules/masters/registry.ts
|
|
2175
|
+
var registry_exports = {};
|
|
2176
|
+
__export(registry_exports, {
|
|
2177
|
+
createMasterRegistry: () => createMasterRegistry
|
|
2178
|
+
});
|
|
2103
2179
|
function createMasterRegistry() {
|
|
2104
2180
|
const registrations = [];
|
|
2105
2181
|
return {
|
|
@@ -2137,7 +2213,7 @@ function createMasterRegistry() {
|
|
|
2137
2213
|
await ctx.db.knex("masters").insert(row).onConflict(["type", "code"]).merge();
|
|
2138
2214
|
}
|
|
2139
2215
|
} else {
|
|
2140
|
-
await ctx.db.knex("masters").insert(rows);
|
|
2216
|
+
await ctx.db.knex("masters").insert(rows).onConflict(["type", "code"]).ignore();
|
|
2141
2217
|
}
|
|
2142
2218
|
}
|
|
2143
2219
|
},
|
|
@@ -8570,15 +8646,12 @@ var init_document_types = __esm({
|
|
|
8570
8646
|
}
|
|
8571
8647
|
});
|
|
8572
8648
|
|
|
8573
|
-
// src/modules/masters/
|
|
8574
|
-
var
|
|
8575
|
-
var
|
|
8576
|
-
"src/modules/masters/
|
|
8649
|
+
// src/modules/masters/constants.ts
|
|
8650
|
+
var DEFAULT_MASTER_TYPES, PREDEFINED_MASTERS;
|
|
8651
|
+
var init_constants = __esm({
|
|
8652
|
+
"src/modules/masters/constants.ts"() {
|
|
8577
8653
|
"use strict";
|
|
8578
|
-
|
|
8579
|
-
init_registry2();
|
|
8580
|
-
init_definitions();
|
|
8581
|
-
init_registry2();
|
|
8654
|
+
DEFAULT_MASTER_TYPES = ["languages", "timezones"];
|
|
8582
8655
|
PREDEFINED_MASTERS = {
|
|
8583
8656
|
currencies: () => Promise.resolve().then(() => (init_currencies(), currencies_exports)).then((m2) => m2.default),
|
|
8584
8657
|
languages: () => Promise.resolve().then(() => (init_languages(), languages_exports)).then((m2) => m2.default),
|
|
@@ -8595,15 +8668,137 @@ var init_masters = __esm({
|
|
|
8595
8668
|
"phone-prefixes": () => Promise.resolve().then(() => (init_phone_prefixes(), phone_prefixes_exports)).then((m2) => m2.default),
|
|
8596
8669
|
"document-types": () => Promise.resolve().then(() => (init_document_types(), document_types_exports)).then((m2) => m2.default)
|
|
8597
8670
|
};
|
|
8671
|
+
}
|
|
8672
|
+
});
|
|
8673
|
+
|
|
8674
|
+
// src/modules/masters/actions/install-type.ts
|
|
8675
|
+
import { useSelectField as useSelectField2 } from "@gzl10/nexus-sdk/fields";
|
|
8676
|
+
var installTypeAction;
|
|
8677
|
+
var init_install_type = __esm({
|
|
8678
|
+
"src/modules/masters/actions/install-type.ts"() {
|
|
8679
|
+
"use strict";
|
|
8680
|
+
init_constants();
|
|
8681
|
+
installTypeAction = {
|
|
8682
|
+
key: "install-type",
|
|
8683
|
+
scope: "module",
|
|
8684
|
+
label: { en: "Install Master Type", es: "Instalar Tipo de Maestro" },
|
|
8685
|
+
icon: "mdi:database-plus",
|
|
8686
|
+
variant: "primary",
|
|
8687
|
+
output: {},
|
|
8688
|
+
input: {
|
|
8689
|
+
type: useSelectField2({
|
|
8690
|
+
label: { en: "Master Type", es: "Tipo de Maestro" },
|
|
8691
|
+
required: true,
|
|
8692
|
+
options: Object.keys(PREDEFINED_MASTERS).filter((t) => !DEFAULT_MASTER_TYPES.includes(t)).map((t) => ({
|
|
8693
|
+
value: t,
|
|
8694
|
+
label: t.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
|
|
8695
|
+
}))
|
|
8696
|
+
})
|
|
8697
|
+
},
|
|
8698
|
+
handler: async (ctx, input) => {
|
|
8699
|
+
if (process.env["NODE_ENV"] === "production") {
|
|
8700
|
+
throw new ctx.core.errors.ForbiddenError("Master type management is only available in development");
|
|
8701
|
+
}
|
|
8702
|
+
const { type: type2 } = input;
|
|
8703
|
+
const loader = PREDEFINED_MASTERS[type2];
|
|
8704
|
+
if (!loader) {
|
|
8705
|
+
throw new ctx.core.errors.AppError(`Unknown predefined master type: ${type2}`, 400);
|
|
8706
|
+
}
|
|
8707
|
+
const existing = await ctx.db.knex("masters").where({ type: type2 }).first();
|
|
8708
|
+
if (existing) {
|
|
8709
|
+
throw new ctx.core.errors.ConflictError(`Master type "${type2}" is already installed`);
|
|
8710
|
+
}
|
|
8711
|
+
const entries = await loader();
|
|
8712
|
+
const rows = entries.map((entry, i) => ({
|
|
8713
|
+
id: `${type2}:${entry.code}`,
|
|
8714
|
+
type: type2,
|
|
8715
|
+
code: entry.code,
|
|
8716
|
+
label: JSON.stringify(typeof entry.label === "string" ? { en: entry.label } : entry.label),
|
|
8717
|
+
order: entry.order ?? i,
|
|
8718
|
+
is_active: entry.is_active ?? true,
|
|
8719
|
+
metadata: entry.metadata ? JSON.stringify(entry.metadata) : null
|
|
8720
|
+
}));
|
|
8721
|
+
await ctx.db.knex("masters").insert(rows).onConflict(["type", "code"]).ignore();
|
|
8722
|
+
return { installed: type2, count: rows.length };
|
|
8723
|
+
}
|
|
8724
|
+
};
|
|
8725
|
+
}
|
|
8726
|
+
});
|
|
8727
|
+
|
|
8728
|
+
// src/modules/masters/actions/uninstall-type.ts
|
|
8729
|
+
import { useTextField as useTextField3 } from "@gzl10/nexus-sdk/fields";
|
|
8730
|
+
var uninstallTypeAction;
|
|
8731
|
+
var init_uninstall_type = __esm({
|
|
8732
|
+
"src/modules/masters/actions/uninstall-type.ts"() {
|
|
8733
|
+
"use strict";
|
|
8734
|
+
init_constants();
|
|
8735
|
+
uninstallTypeAction = {
|
|
8736
|
+
key: "uninstall-type",
|
|
8737
|
+
scope: "module",
|
|
8738
|
+
label: { en: "Uninstall Master Type", es: "Desinstalar Tipo de Maestro" },
|
|
8739
|
+
icon: "mdi:database-minus",
|
|
8740
|
+
variant: "danger",
|
|
8741
|
+
output: {},
|
|
8742
|
+
confirm: {
|
|
8743
|
+
type: "simple",
|
|
8744
|
+
title: { en: "Uninstall Master Type", es: "Desinstalar Tipo de Maestro" },
|
|
8745
|
+
message: {
|
|
8746
|
+
en: "This will delete ALL records of the selected type. This action cannot be undone.",
|
|
8747
|
+
es: "Esto eliminar\xE1 TODOS los registros del tipo seleccionado. Esta acci\xF3n no se puede deshacer."
|
|
8748
|
+
}
|
|
8749
|
+
},
|
|
8750
|
+
input: {
|
|
8751
|
+
type: useTextField3({
|
|
8752
|
+
label: { en: "Type slug to uninstall", es: "Slug del tipo a desinstalar" },
|
|
8753
|
+
required: true,
|
|
8754
|
+
hint: {
|
|
8755
|
+
en: 'Enter the master type slug (e.g., "currencies", "countries")',
|
|
8756
|
+
es: 'Introduce el slug del tipo (ej: "currencies", "countries")'
|
|
8757
|
+
}
|
|
8758
|
+
})
|
|
8759
|
+
},
|
|
8760
|
+
handler: async (ctx, input) => {
|
|
8761
|
+
if (process.env["NODE_ENV"] === "production") {
|
|
8762
|
+
throw new ctx.core.errors.ForbiddenError("Master type management is only available in development");
|
|
8763
|
+
}
|
|
8764
|
+
const { type: type2 } = input;
|
|
8765
|
+
if (DEFAULT_MASTER_TYPES.includes(type2)) {
|
|
8766
|
+
throw new ctx.core.errors.AppError(`Cannot uninstall default master type "${type2}" (required by core)`, 400);
|
|
8767
|
+
}
|
|
8768
|
+
const existing = await ctx.db.knex("masters").where({ type: type2 }).first();
|
|
8769
|
+
if (!existing) {
|
|
8770
|
+
throw new ctx.core.errors.NotFoundError(`Master type "${type2}" is not installed`);
|
|
8771
|
+
}
|
|
8772
|
+
const deleted = await ctx.db.knex("masters").where({ type: type2 }).del();
|
|
8773
|
+
return { uninstalled: type2, count: deleted };
|
|
8774
|
+
}
|
|
8775
|
+
};
|
|
8776
|
+
}
|
|
8777
|
+
});
|
|
8778
|
+
|
|
8779
|
+
// src/modules/masters/index.ts
|
|
8780
|
+
var mastersModule;
|
|
8781
|
+
var init_masters = __esm({
|
|
8782
|
+
"src/modules/masters/index.ts"() {
|
|
8783
|
+
"use strict";
|
|
8784
|
+
init_definitions();
|
|
8785
|
+
init_registry2();
|
|
8786
|
+
init_constants();
|
|
8787
|
+
init_install_type();
|
|
8788
|
+
init_uninstall_type();
|
|
8789
|
+
init_definitions();
|
|
8790
|
+
init_registry2();
|
|
8791
|
+
init_constants();
|
|
8598
8792
|
mastersModule = {
|
|
8599
8793
|
name: "masters",
|
|
8600
8794
|
type: "core",
|
|
8601
|
-
label: { en: "
|
|
8795
|
+
label: { en: "Masters", es: "Maestros" },
|
|
8602
8796
|
icon: "mdi:database-outline",
|
|
8603
8797
|
description: { en: "Reference data catalogs", es: "Cat\xE1logos de datos de referencia" },
|
|
8604
|
-
category: "
|
|
8798
|
+
category: "settings",
|
|
8605
8799
|
routePrefix: "/masters",
|
|
8606
8800
|
definitions: [mastersEntity],
|
|
8801
|
+
actions: [installTypeAction, uninstallTypeAction],
|
|
8607
8802
|
init: (ctx) => {
|
|
8608
8803
|
if (!ctx.services.has("masters")) {
|
|
8609
8804
|
ctx.services.register("masters", createMasterRegistry());
|
|
@@ -8614,13 +8809,14 @@ var init_masters = __esm({
|
|
|
8614
8809
|
ctx.services.register("masters", createMasterRegistry());
|
|
8615
8810
|
}
|
|
8616
8811
|
const registry2 = ctx.services.get("masters");
|
|
8617
|
-
const
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8812
|
+
const existing = await ctx.db.knex("masters").first();
|
|
8813
|
+
if (!existing) {
|
|
8814
|
+
for (const type2 of DEFAULT_MASTER_TYPES) {
|
|
8815
|
+
const loader = PREDEFINED_MASTERS[type2];
|
|
8816
|
+
if (!loader) continue;
|
|
8817
|
+
const entries = await loader();
|
|
8818
|
+
registry2.register(type2, entries, { seed: "if-empty" });
|
|
8819
|
+
}
|
|
8624
8820
|
}
|
|
8625
8821
|
await registry2.seed(ctx);
|
|
8626
8822
|
}
|
|
@@ -8778,15 +8974,18 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8778
8974
|
const meta = field["meta"];
|
|
8779
8975
|
return meta?.["sortable"] === true && !field["hidden"];
|
|
8780
8976
|
});
|
|
8977
|
+
const explicitGroupable = def["groupableFields"];
|
|
8781
8978
|
const groupableInputTypes = ["select", "switch", "checkbox", "radio", "tags"];
|
|
8782
|
-
const
|
|
8979
|
+
const autoGroupableFields = fieldEntries.filter(([, f]) => {
|
|
8783
8980
|
const field = f;
|
|
8784
8981
|
const inputType = field["input"];
|
|
8785
8982
|
return groupableInputTypes.includes(inputType ?? "") && !field["hidden"];
|
|
8786
8983
|
});
|
|
8984
|
+
const resolvedGroupableFields = explicitGroupable ?? (autoGroupableFields.length > 0 ? autoGroupableFields.map(([name]) => name) : void 0);
|
|
8787
8985
|
const entityType = def["type"] ?? "collection";
|
|
8788
8986
|
const explicitDisplayMode = def["displayMode"];
|
|
8789
|
-
const
|
|
8987
|
+
const explicitAvailableModes = def["availableDisplayModes"];
|
|
8988
|
+
const defaultDisplayMode = explicitDisplayMode ?? (explicitAvailableModes?.length === 1 ? explicitAvailableModes[0] : void 0) ?? (["tree", "dag"].includes(entityType) ? "tree" : "table");
|
|
8790
8989
|
const entityIdent = def["table"] ?? def["key"];
|
|
8791
8990
|
return {
|
|
8792
8991
|
id: def._id,
|
|
@@ -8806,9 +9005,11 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8806
9005
|
displayMode: explicitDisplayMode,
|
|
8807
9006
|
defaultDisplayMode,
|
|
8808
9007
|
availableDisplayModes: (() => {
|
|
9008
|
+
const explicit = def["availableDisplayModes"];
|
|
9009
|
+
if (explicit?.length) return explicit;
|
|
8809
9010
|
const modes = ["table", "list", "masonry"];
|
|
8810
9011
|
if (["tree", "dag"].includes(entityType)) modes.push("tree");
|
|
8811
|
-
if (
|
|
9012
|
+
if ((resolvedGroupableFields?.length ?? 0) > 0) modes.push("board");
|
|
8812
9013
|
if (def["calendarFrom"]) modes.push("calendar");
|
|
8813
9014
|
return modes;
|
|
8814
9015
|
})(),
|
|
@@ -8818,10 +9019,8 @@ function toEntityDefinitionDTO(def, _engine, moduleName) {
|
|
|
8818
9019
|
value: name,
|
|
8819
9020
|
label: f["label"]
|
|
8820
9021
|
})),
|
|
8821
|
-
groupableFields:
|
|
8822
|
-
|
|
8823
|
-
label: f["label"]
|
|
8824
|
-
})) : void 0,
|
|
9022
|
+
groupableFields: resolvedGroupableFields,
|
|
9023
|
+
columnDragFields: def["columnDragFields"],
|
|
8825
9024
|
groupBy: def["groupBy"],
|
|
8826
9025
|
subgroupBy: def["subgroupBy"],
|
|
8827
9026
|
calendarFrom: def["calendarFrom"],
|
|
@@ -8861,21 +9060,6 @@ function toModuleDTO(mod, ctx) {
|
|
|
8861
9060
|
hasInit: !!mod.init
|
|
8862
9061
|
};
|
|
8863
9062
|
}
|
|
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
9063
|
var init_system_helpers = __esm({
|
|
8880
9064
|
"src/modules/system/system.helpers.ts"() {
|
|
8881
9065
|
"use strict";
|
|
@@ -8896,6 +9080,7 @@ function toPageDTO(page) {
|
|
|
8896
9080
|
dataSource: page.dataSource,
|
|
8897
9081
|
widgets: page.widgets,
|
|
8898
9082
|
component: page.component,
|
|
9083
|
+
contentEndpoint: page.contentEndpoint,
|
|
8899
9084
|
meta: page.meta,
|
|
8900
9085
|
layout: page.layout
|
|
8901
9086
|
};
|
|
@@ -9110,7 +9295,8 @@ function createSystemController(ctx) {
|
|
|
9110
9295
|
const plugins = engine.getPlugins();
|
|
9111
9296
|
const body = {
|
|
9112
9297
|
version: manifest.version,
|
|
9113
|
-
plugins: plugins.map((p) => p.code)
|
|
9298
|
+
plugins: plugins.map((p) => p.code),
|
|
9299
|
+
locales: ctx.locales
|
|
9114
9300
|
};
|
|
9115
9301
|
res.json(body);
|
|
9116
9302
|
}
|
|
@@ -9158,7 +9344,7 @@ var init_system_routes = __esm({
|
|
|
9158
9344
|
|
|
9159
9345
|
// src/modules/system/system.entity.ts
|
|
9160
9346
|
import * as os from "os";
|
|
9161
|
-
import { useIconField, useTextField as
|
|
9347
|
+
import { useIconField, useTextField as useTextField4, useSelectField as useSelectField3, useNumberField as useNumberField2, useCheckboxField, useTagsField, useNameField, useDescriptionField } from "@gzl10/nexus-sdk/fields";
|
|
9162
9348
|
var moduleEntity, osEntity;
|
|
9163
9349
|
var init_system_entity = __esm({
|
|
9164
9350
|
"src/modules/system/system.entity.ts"() {
|
|
@@ -9176,7 +9362,7 @@ var init_system_entity = __esm({
|
|
|
9176
9362
|
name: useNameField({
|
|
9177
9363
|
size: 50
|
|
9178
9364
|
}),
|
|
9179
|
-
label:
|
|
9365
|
+
label: useTextField4({
|
|
9180
9366
|
label: { en: "Label", es: "Etiqueta" },
|
|
9181
9367
|
size: 100,
|
|
9182
9368
|
nullable: false,
|
|
@@ -9184,7 +9370,7 @@ var init_system_entity = __esm({
|
|
|
9184
9370
|
}),
|
|
9185
9371
|
icon: useIconField({ label: { en: "Icon", es: "Icono" }, size: 50 }),
|
|
9186
9372
|
type: {
|
|
9187
|
-
...
|
|
9373
|
+
...useSelectField3({
|
|
9188
9374
|
label: { en: "Type", es: "Tipo" },
|
|
9189
9375
|
options: [
|
|
9190
9376
|
{ value: "core", label: { en: "Core", es: "Core" } },
|
|
@@ -9200,7 +9386,7 @@ var init_system_entity = __esm({
|
|
|
9200
9386
|
description: useDescriptionField({
|
|
9201
9387
|
mode: "text"
|
|
9202
9388
|
}),
|
|
9203
|
-
routePrefix:
|
|
9389
|
+
routePrefix: useTextField4({
|
|
9204
9390
|
label: { en: "Route", es: "Ruta" },
|
|
9205
9391
|
size: 50,
|
|
9206
9392
|
nullable: true
|
|
@@ -9258,14 +9444,14 @@ var init_system_entity = __esm({
|
|
|
9258
9444
|
order: 1,
|
|
9259
9445
|
refreshInterval: 5e3,
|
|
9260
9446
|
fields: {
|
|
9261
|
-
hostname:
|
|
9447
|
+
hostname: useTextField4({
|
|
9262
9448
|
label: { en: "Hostname", es: "Nombre de servidor" },
|
|
9263
9449
|
size: 100,
|
|
9264
9450
|
nullable: false,
|
|
9265
9451
|
inputProps: { order: 1 }
|
|
9266
9452
|
}),
|
|
9267
9453
|
platform: {
|
|
9268
|
-
...
|
|
9454
|
+
...useSelectField3({
|
|
9269
9455
|
label: { en: "Platform", es: "Plataforma" },
|
|
9270
9456
|
options: [
|
|
9271
9457
|
{ value: "darwin", label: { en: "macOS", es: "macOS" } },
|
|
@@ -9279,7 +9465,7 @@ var init_system_entity = __esm({
|
|
|
9279
9465
|
inputProps: { order: 2 }
|
|
9280
9466
|
},
|
|
9281
9467
|
arch: {
|
|
9282
|
-
...
|
|
9468
|
+
...useSelectField3({
|
|
9283
9469
|
label: { en: "Architecture", es: "Arquitectura" },
|
|
9284
9470
|
options: [
|
|
9285
9471
|
{ value: "x64", label: { en: "x64", es: "x64" } },
|
|
@@ -9292,7 +9478,7 @@ var init_system_entity = __esm({
|
|
|
9292
9478
|
inputProps: { order: 3 }
|
|
9293
9479
|
},
|
|
9294
9480
|
type: {
|
|
9295
|
-
...
|
|
9481
|
+
...useTextField4({
|
|
9296
9482
|
label: { en: "Type", es: "Tipo" },
|
|
9297
9483
|
size: 30,
|
|
9298
9484
|
nullable: false
|
|
@@ -9300,7 +9486,7 @@ var init_system_entity = __esm({
|
|
|
9300
9486
|
inputProps: { order: 4 }
|
|
9301
9487
|
},
|
|
9302
9488
|
release: {
|
|
9303
|
-
...
|
|
9489
|
+
...useTextField4({
|
|
9304
9490
|
label: { en: "Release", es: "Versi\xF3n" },
|
|
9305
9491
|
size: 50,
|
|
9306
9492
|
nullable: false
|
|
@@ -9323,7 +9509,7 @@ var init_system_entity = __esm({
|
|
|
9323
9509
|
inputProps: { order: 7, format: "duration" }
|
|
9324
9510
|
},
|
|
9325
9511
|
cpuModel: {
|
|
9326
|
-
...
|
|
9512
|
+
...useTextField4({
|
|
9327
9513
|
label: { en: "CPU Model", es: "Modelo de CPU" },
|
|
9328
9514
|
size: 100,
|
|
9329
9515
|
nullable: false
|
|
@@ -9389,7 +9575,7 @@ var init_system_entity = __esm({
|
|
|
9389
9575
|
});
|
|
9390
9576
|
|
|
9391
9577
|
// src/modules/system/migration-history.entity.ts
|
|
9392
|
-
import { useIdField as useIdField2, useTextField as
|
|
9578
|
+
import { useIdField as useIdField2, useTextField as useTextField5, useNumberField as useNumberField3, useSelectField as useSelectField4, useDatetimeField as useDatetimeField2, useTextareaField as useTextareaField2 } from "@gzl10/nexus-sdk/fields";
|
|
9393
9579
|
var migrationHistoryEntity;
|
|
9394
9580
|
var init_migration_history_entity = __esm({
|
|
9395
9581
|
"src/modules/system/migration-history.entity.ts"() {
|
|
@@ -9406,9 +9592,9 @@ var init_migration_history_entity = __esm({
|
|
|
9406
9592
|
calendarFrom: "executed_at",
|
|
9407
9593
|
fields: {
|
|
9408
9594
|
id: useIdField2(),
|
|
9409
|
-
name:
|
|
9595
|
+
name: useTextField5({ label: { en: "Migration Name", es: "Nombre de la Migraci\xF3n" }, required: true, size: 255, unique: true, meta: { sortable: true, searchable: true } }),
|
|
9410
9596
|
batch: useNumberField3({ label: { en: "Batch", es: "Lote" }, required: true, meta: { sortable: true } }),
|
|
9411
|
-
status:
|
|
9597
|
+
status: useSelectField4({
|
|
9412
9598
|
label: { en: "Status", es: "Estado" },
|
|
9413
9599
|
options: [
|
|
9414
9600
|
{ value: "running", label: { en: "Running", es: "Ejecutando" } },
|
|
@@ -9440,7 +9626,7 @@ var init_migration_history_entity = __esm({
|
|
|
9440
9626
|
});
|
|
9441
9627
|
|
|
9442
9628
|
// src/modules/system/env-config.entity.ts
|
|
9443
|
-
import { useTextField as
|
|
9629
|
+
import { useTextField as useTextField6, useSelectField as useSelectField5, useCheckboxField as useCheckboxField2 } from "@gzl10/nexus-sdk/fields";
|
|
9444
9630
|
function maskValue(value, sensitive) {
|
|
9445
9631
|
if (!sensitive) return value;
|
|
9446
9632
|
try {
|
|
@@ -9465,14 +9651,14 @@ var init_env_config_entity = __esm({
|
|
|
9465
9651
|
routePrefix: "/env-config",
|
|
9466
9652
|
defaultSort: { field: "category", order: "asc" },
|
|
9467
9653
|
fields: {
|
|
9468
|
-
name:
|
|
9654
|
+
name: useTextField6({
|
|
9469
9655
|
label: { en: "Variable", es: "Variable" },
|
|
9470
9656
|
size: 100,
|
|
9471
9657
|
nullable: false,
|
|
9472
9658
|
meta: { sortable: true, searchable: true }
|
|
9473
9659
|
}),
|
|
9474
9660
|
category: {
|
|
9475
|
-
...
|
|
9661
|
+
...useSelectField5({
|
|
9476
9662
|
label: { en: "Category", es: "Categor\xEDa" },
|
|
9477
9663
|
options: [
|
|
9478
9664
|
{ value: "server", label: { en: "Server", es: "Servidor" } },
|
|
@@ -9487,18 +9673,18 @@ var init_env_config_entity = __esm({
|
|
|
9487
9673
|
meta: { sortable: true }
|
|
9488
9674
|
})
|
|
9489
9675
|
},
|
|
9490
|
-
source:
|
|
9676
|
+
source: useTextField6({
|
|
9491
9677
|
label: { en: "Source", es: "Origen" },
|
|
9492
9678
|
size: 50,
|
|
9493
9679
|
nullable: false,
|
|
9494
9680
|
meta: { sortable: true }
|
|
9495
9681
|
}),
|
|
9496
|
-
value:
|
|
9682
|
+
value: useTextField6({
|
|
9497
9683
|
label: { en: "Value", es: "Valor" },
|
|
9498
9684
|
size: 255,
|
|
9499
9685
|
nullable: true
|
|
9500
9686
|
}),
|
|
9501
|
-
default:
|
|
9687
|
+
default: useTextField6({
|
|
9502
9688
|
label: { en: "Default", es: "Por defecto" },
|
|
9503
9689
|
size: 100,
|
|
9504
9690
|
nullable: true
|
|
@@ -9997,7 +10183,6 @@ var SYSTEM_TABLES, factoryResetAction;
|
|
|
9997
10183
|
var init_factory_reset_action = __esm({
|
|
9998
10184
|
"src/modules/system/actions/factory-reset.action.ts"() {
|
|
9999
10185
|
"use strict";
|
|
10000
|
-
init_system_helpers();
|
|
10001
10186
|
SYSTEM_TABLES = /* @__PURE__ */ new Set([
|
|
10002
10187
|
"_nexus_migrations",
|
|
10003
10188
|
"_nexus_migration_lock",
|
|
@@ -10040,8 +10225,8 @@ var init_factory_reset_action = __esm({
|
|
|
10040
10225
|
source: "core:system",
|
|
10041
10226
|
action: "factory_reset",
|
|
10042
10227
|
actorId: authReq.user?.id,
|
|
10043
|
-
ip: req
|
|
10044
|
-
userAgent: req
|
|
10228
|
+
ip: req?.ip,
|
|
10229
|
+
userAgent: req?.headers["user-agent"]
|
|
10045
10230
|
});
|
|
10046
10231
|
await new Promise((resolve2) => setImmediate(resolve2));
|
|
10047
10232
|
const allTables = await getAllTables(knex3);
|
|
@@ -10079,11 +10264,11 @@ var init_factory_reset_action = __esm({
|
|
|
10079
10264
|
} catch {
|
|
10080
10265
|
}
|
|
10081
10266
|
ctx.core.logger.info({ tables: dataTables.length }, "All data tables cleared");
|
|
10082
|
-
const modules =
|
|
10267
|
+
const modules = ctx.engine.getModules();
|
|
10083
10268
|
let modulesSeeded = 0;
|
|
10084
10269
|
for (const mod of modules) {
|
|
10085
10270
|
try {
|
|
10086
|
-
const seeded = await
|
|
10271
|
+
const seeded = await ctx.db.seedModule(mod);
|
|
10087
10272
|
if (seeded) modulesSeeded++;
|
|
10088
10273
|
} catch (err) {
|
|
10089
10274
|
ctx.core.logger.error({ module: mod.name, err }, "Seed failed during factory reset");
|
|
@@ -10137,8 +10322,8 @@ var init_restart_server_action = __esm({
|
|
|
10137
10322
|
source: "core:system",
|
|
10138
10323
|
action: "server_restart",
|
|
10139
10324
|
actorId: authReq.user?.id,
|
|
10140
|
-
ip: req
|
|
10141
|
-
userAgent: req
|
|
10325
|
+
ip: req?.ip,
|
|
10326
|
+
userAgent: req?.headers["user-agent"]
|
|
10142
10327
|
});
|
|
10143
10328
|
setTimeout(() => process.exit(0), 500);
|
|
10144
10329
|
return { success: true, message: "Server is restarting..." };
|
|
@@ -10205,7 +10390,7 @@ var init_system = __esm({
|
|
|
10205
10390
|
});
|
|
10206
10391
|
|
|
10207
10392
|
// src/modules/ui-settings/ui-branding.entity.ts
|
|
10208
|
-
import { useTextField as
|
|
10393
|
+
import { useTextField as useTextField7, useImageField } from "@gzl10/nexus-sdk/fields";
|
|
10209
10394
|
var uiBrandingEntity;
|
|
10210
10395
|
var init_ui_branding_entity = __esm({
|
|
10211
10396
|
"src/modules/ui-settings/ui-branding.entity.ts"() {
|
|
@@ -10225,7 +10410,7 @@ var init_ui_branding_entity = __esm({
|
|
|
10225
10410
|
favicon: null
|
|
10226
10411
|
},
|
|
10227
10412
|
fields: {
|
|
10228
|
-
appName:
|
|
10413
|
+
appName: useTextField7({
|
|
10229
10414
|
label: { en: "App Name", es: "Nombre de la App" },
|
|
10230
10415
|
hint: { en: "Displayed in the header, browser tab and emails", es: "Se muestra en el header, pesta\xF1a del navegador y emails" },
|
|
10231
10416
|
size: 100,
|
|
@@ -10266,7 +10451,7 @@ var init_ui_branding_entity = __esm({
|
|
|
10266
10451
|
});
|
|
10267
10452
|
|
|
10268
10453
|
// src/modules/ui-settings/ui-theme.entity.ts
|
|
10269
|
-
import { useSelectField as
|
|
10454
|
+
import { useSelectField as useSelectField6, useColorField } from "@gzl10/nexus-sdk/fields";
|
|
10270
10455
|
var uiThemeEntity;
|
|
10271
10456
|
var init_ui_theme_entity = __esm({
|
|
10272
10457
|
"src/modules/ui-settings/ui-theme.entity.ts"() {
|
|
@@ -10291,7 +10476,7 @@ var init_ui_theme_entity = __esm({
|
|
|
10291
10476
|
},
|
|
10292
10477
|
fields: {
|
|
10293
10478
|
// === Typography ===
|
|
10294
|
-
font:
|
|
10479
|
+
font: useSelectField6({
|
|
10295
10480
|
label: { en: "Font", es: "Fuente" },
|
|
10296
10481
|
hint: { en: "Primary font for headings and UI elements", es: "Fuente principal para t\xEDtulos y elementos de interfaz" },
|
|
10297
10482
|
options: [
|
|
@@ -10304,7 +10489,7 @@ var init_ui_theme_entity = __esm({
|
|
|
10304
10489
|
]
|
|
10305
10490
|
}),
|
|
10306
10491
|
// === Theme & Colors ===
|
|
10307
|
-
theme:
|
|
10492
|
+
theme: useSelectField6({
|
|
10308
10493
|
label: { en: "Theme", es: "Tema" },
|
|
10309
10494
|
hint: { en: "System follows your device preferences", es: "Sistema sigue las preferencias de tu dispositivo" },
|
|
10310
10495
|
options: [
|
|
@@ -10313,7 +10498,7 @@ var init_ui_theme_entity = __esm({
|
|
|
10313
10498
|
{ value: "system", label: { en: "System", es: "Sistema" } }
|
|
10314
10499
|
]
|
|
10315
10500
|
}),
|
|
10316
|
-
dopamineTheme:
|
|
10501
|
+
dopamineTheme: useSelectField6({
|
|
10317
10502
|
label: { en: "Dopamine Theme", es: "Tema Dopamina" },
|
|
10318
10503
|
hint: { en: "Vibrant color presets optimized for light and dark modes (2025/2026 trends)", es: "Presets de colores vibrantes optimizados para modo claro y oscuro (tendencias 2025/2026)" },
|
|
10319
10504
|
options: [
|
|
@@ -10331,7 +10516,7 @@ var init_ui_theme_entity = __esm({
|
|
|
10331
10516
|
]
|
|
10332
10517
|
}),
|
|
10333
10518
|
// === Login Layout ===
|
|
10334
|
-
loginLayout:
|
|
10519
|
+
loginLayout: useSelectField6({
|
|
10335
10520
|
label: { en: "Login Layout", es: "Dise\xF1o de Login" },
|
|
10336
10521
|
hint: { en: "Visual layout for authentication pages", es: "Dise\xF1o visual para p\xE1ginas de autenticaci\xF3n" },
|
|
10337
10522
|
options: [
|
|
@@ -10362,7 +10547,7 @@ var init_ui_theme_entity = __esm({
|
|
|
10362
10547
|
});
|
|
10363
10548
|
|
|
10364
10549
|
// src/modules/ui-settings/ui-effects.entity.ts
|
|
10365
|
-
import { useSelectField as
|
|
10550
|
+
import { useSelectField as useSelectField7, useSwitchField as useSwitchField2 } from "@gzl10/nexus-sdk/fields";
|
|
10366
10551
|
var uiEffectsEntity;
|
|
10367
10552
|
var init_ui_effects_entity = __esm({
|
|
10368
10553
|
"src/modules/ui-settings/ui-effects.entity.ts"() {
|
|
@@ -10382,7 +10567,7 @@ var init_ui_effects_entity = __esm({
|
|
|
10382
10567
|
enableOrganicShapes: false
|
|
10383
10568
|
},
|
|
10384
10569
|
fields: {
|
|
10385
|
-
glassIntensity:
|
|
10570
|
+
glassIntensity: useSelectField7({
|
|
10386
10571
|
label: { en: "Glass Intensity", es: "Intensidad Glass" },
|
|
10387
10572
|
hint: { en: "Glassmorphism blur effect on cards and modals", es: "Efecto de desenfoque glassmorphism en cards y modales" },
|
|
10388
10573
|
options: [
|
|
@@ -10392,7 +10577,7 @@ var init_ui_effects_entity = __esm({
|
|
|
10392
10577
|
{ value: "high", label: { en: "High", es: "Alta" } }
|
|
10393
10578
|
]
|
|
10394
10579
|
}),
|
|
10395
|
-
borderStyle:
|
|
10580
|
+
borderStyle: useSelectField7({
|
|
10396
10581
|
label: { en: "Border Style", es: "Estilo de Bordes" },
|
|
10397
10582
|
hint: { en: "Corner radius for buttons, cards and inputs", es: "Radio de esquinas para botones, cards e inputs" },
|
|
10398
10583
|
options: [
|
|
@@ -10426,7 +10611,7 @@ var init_ui_effects_entity = __esm({
|
|
|
10426
10611
|
});
|
|
10427
10612
|
|
|
10428
10613
|
// src/modules/ui-settings/ui-accessibility.entity.ts
|
|
10429
|
-
import { useSelectField as
|
|
10614
|
+
import { useSelectField as useSelectField8, useSwitchField as useSwitchField3 } from "@gzl10/nexus-sdk/fields";
|
|
10430
10615
|
var uiAccessibilityEntity;
|
|
10431
10616
|
var init_ui_accessibility_entity = __esm({
|
|
10432
10617
|
"src/modules/ui-settings/ui-accessibility.entity.ts"() {
|
|
@@ -10445,7 +10630,7 @@ var init_ui_accessibility_entity = __esm({
|
|
|
10445
10630
|
highContrast: false
|
|
10446
10631
|
},
|
|
10447
10632
|
fields: {
|
|
10448
|
-
typographyScale:
|
|
10633
|
+
typographyScale: useSelectField8({
|
|
10449
10634
|
label: { en: "Typography Scale", es: "Escala Tipogr\xE1fica" },
|
|
10450
10635
|
hint: { en: "Adjusts font sizes for better readability (WCAG 1.4.4)", es: "Ajusta tama\xF1os de fuente para mejor legibilidad (WCAG 1.4.4)" },
|
|
10451
10636
|
options: [
|
|
@@ -11436,7 +11621,7 @@ var init_storage_service = __esm({
|
|
|
11436
11621
|
});
|
|
11437
11622
|
|
|
11438
11623
|
// src/modules/storage/storage.entity.ts
|
|
11439
|
-
import { useIdField as useIdField3, useTextField as
|
|
11624
|
+
import { useIdField as useIdField3, useTextField as useTextField8, useSelectField as useSelectField9, useUrlField, useNumberField as useNumberField4, useCheckboxField as useCheckboxField3, useJsonField as useJsonField2, useMetadataField, usePublicField } from "@gzl10/nexus-sdk/fields";
|
|
11440
11625
|
var DEFAULT_MAX_SIZE2, storageConfigEntity, storageFilesEntity;
|
|
11441
11626
|
var init_storage_entity = __esm({
|
|
11442
11627
|
"src/modules/storage/storage.entity.ts"() {
|
|
@@ -11462,7 +11647,7 @@ var init_storage_entity = __esm({
|
|
|
11462
11647
|
},
|
|
11463
11648
|
fields: {
|
|
11464
11649
|
id: useIdField3(),
|
|
11465
|
-
scope:
|
|
11650
|
+
scope: useTextField8({
|
|
11466
11651
|
label: { en: "Scope", es: "\xC1mbito" },
|
|
11467
11652
|
disabled: true,
|
|
11468
11653
|
size: 100,
|
|
@@ -11471,7 +11656,7 @@ var init_storage_entity = __esm({
|
|
|
11471
11656
|
hint: { en: "Unique identifier (e.g. default_filesystem, default_s3)", es: "Identificador \xFAnico (ej: default_filesystem, default_s3)" }
|
|
11472
11657
|
}),
|
|
11473
11658
|
driver: {
|
|
11474
|
-
...
|
|
11659
|
+
...useSelectField9({
|
|
11475
11660
|
label: { en: "Driver", es: "Controlador" },
|
|
11476
11661
|
required: true,
|
|
11477
11662
|
hint: { en: "Storage backend", es: "Backend de almacenamiento" },
|
|
@@ -11494,7 +11679,7 @@ var init_storage_entity = __esm({
|
|
|
11494
11679
|
nullable: false,
|
|
11495
11680
|
displayProps: { format: "bytes" }
|
|
11496
11681
|
}),
|
|
11497
|
-
allowed_mime_types:
|
|
11682
|
+
allowed_mime_types: useTextField8({
|
|
11498
11683
|
label: { en: "Allowed MIME Types", es: "Tipos MIME permitidos" },
|
|
11499
11684
|
hint: { en: "Allowed types separated by comma (e.g. image/*,application/pdf)", es: "Tipos permitidos separados por coma (ej: image/*,application/pdf)" },
|
|
11500
11685
|
size: 1e3,
|
|
@@ -11659,21 +11844,21 @@ var init_storage_entity = __esm({
|
|
|
11659
11844
|
],
|
|
11660
11845
|
fields: {
|
|
11661
11846
|
id: useIdField3(),
|
|
11662
|
-
filename:
|
|
11847
|
+
filename: useTextField8({
|
|
11663
11848
|
label: { en: "Original Filename", es: "Nombre original" },
|
|
11664
11849
|
required: true,
|
|
11665
11850
|
size: 255,
|
|
11666
11851
|
nullable: false,
|
|
11667
11852
|
meta: { sortable: true, searchable: true }
|
|
11668
11853
|
}),
|
|
11669
|
-
disk_filename:
|
|
11854
|
+
disk_filename: useTextField8({
|
|
11670
11855
|
label: { en: "Disk Filename", es: "Nombre en disco" },
|
|
11671
11856
|
required: true,
|
|
11672
11857
|
hidden: true,
|
|
11673
11858
|
size: 255,
|
|
11674
11859
|
nullable: false
|
|
11675
11860
|
}),
|
|
11676
|
-
mimetype:
|
|
11861
|
+
mimetype: useTextField8({
|
|
11677
11862
|
label: { en: "MIME Type", es: "Tipo MIME" },
|
|
11678
11863
|
required: true,
|
|
11679
11864
|
size: 100,
|
|
@@ -11689,14 +11874,14 @@ var init_storage_entity = __esm({
|
|
|
11689
11874
|
meta: { sortable: true },
|
|
11690
11875
|
displayProps: { format: "bytes" }
|
|
11691
11876
|
}),
|
|
11692
|
-
folder:
|
|
11877
|
+
folder: useTextField8({
|
|
11693
11878
|
label: { en: "Folder", es: "Carpeta" },
|
|
11694
11879
|
size: 100,
|
|
11695
11880
|
nullable: true,
|
|
11696
11881
|
index: true,
|
|
11697
11882
|
meta: { searchable: true }
|
|
11698
11883
|
}),
|
|
11699
|
-
scope:
|
|
11884
|
+
scope: useTextField8({
|
|
11700
11885
|
label: { en: "Storage Scope", es: "\xC1mbito de almacenamiento" },
|
|
11701
11886
|
hidden: true,
|
|
11702
11887
|
size: 50,
|
|
@@ -11707,7 +11892,7 @@ var init_storage_entity = __esm({
|
|
|
11707
11892
|
es: "\xC1mbito de configuraci\xF3n de almacenamiento usado para este archivo"
|
|
11708
11893
|
}
|
|
11709
11894
|
}),
|
|
11710
|
-
path:
|
|
11895
|
+
path: useTextField8({
|
|
11711
11896
|
label: { en: "Full Path", es: "Ruta completa" },
|
|
11712
11897
|
required: true,
|
|
11713
11898
|
hidden: true,
|
|
@@ -11720,13 +11905,13 @@ var init_storage_entity = __esm({
|
|
|
11720
11905
|
nullable: true,
|
|
11721
11906
|
meta: { exportable: true, showInDisplay: false }
|
|
11722
11907
|
}),
|
|
11723
|
-
thumbnail_path:
|
|
11908
|
+
thumbnail_path: useTextField8({
|
|
11724
11909
|
label: { en: "Thumbnail Path", es: "Ruta de miniatura" },
|
|
11725
11910
|
size: 500,
|
|
11726
11911
|
nullable: true,
|
|
11727
11912
|
meta: { showInDisplay: false }
|
|
11728
11913
|
}),
|
|
11729
|
-
hash:
|
|
11914
|
+
hash: useTextField8({
|
|
11730
11915
|
label: { en: "SHA256 Hash", es: "Hash SHA256" },
|
|
11731
11916
|
hidden: true,
|
|
11732
11917
|
size: 64,
|
|
@@ -11760,7 +11945,7 @@ function createUploadMiddleware(ctx, options) {
|
|
|
11760
11945
|
const rateLimit2 = ctx.core.middleware.rateLimit({
|
|
11761
11946
|
windowMs: 60 * 1e3,
|
|
11762
11947
|
max: 20,
|
|
11763
|
-
message: "
|
|
11948
|
+
message: "Too many uploads, try again in 1 minute"
|
|
11764
11949
|
});
|
|
11765
11950
|
const upload = multer({
|
|
11766
11951
|
storage: multer.memoryStorage(),
|
|
@@ -12007,7 +12192,7 @@ function createStorageRoutes(ctx) {
|
|
|
12007
12192
|
}
|
|
12008
12193
|
res.status(201).json(results);
|
|
12009
12194
|
};
|
|
12010
|
-
const uploadRateLimit = ctx.core.middleware.rateLimit({ windowMs: 60 * 1e3, max: 20, message: "
|
|
12195
|
+
const uploadRateLimit = ctx.core.middleware.rateLimit({ windowMs: 60 * 1e3, max: 20, message: "Too many uploads, try again in 1 minute" });
|
|
12011
12196
|
if (auth) {
|
|
12012
12197
|
router.post("/upload/multiple", uploadRateLimit, auth, upload.array("files", 10), uploadMultiple);
|
|
12013
12198
|
} else {
|
|
@@ -12223,7 +12408,7 @@ var init_storage = __esm({
|
|
|
12223
12408
|
});
|
|
12224
12409
|
|
|
12225
12410
|
// src/modules/users/users.entity.ts
|
|
12226
|
-
import { useIdField as useIdField4, useSelectField as
|
|
12411
|
+
import { useIdField as useIdField4, useSelectField as useSelectField10, useEmailField, usePasswordField, useTextField as useTextField9, useDatetimeField as useDatetimeField3, useCheckboxField as useCheckboxField4, useImageField as useImageField2, useNameField as useNameField2, useMetadataField as useMetadataField2, useDescriptionField as useDescriptionField2 } from "@gzl10/nexus-sdk/fields";
|
|
12227
12412
|
import { z as z2 } from "zod";
|
|
12228
12413
|
var userEntity, roleEntity, userRoleEntity;
|
|
12229
12414
|
var init_users_entity = __esm({
|
|
@@ -12286,7 +12471,7 @@ var init_users_entity = __esm({
|
|
|
12286
12471
|
nullable: true,
|
|
12287
12472
|
meta: { exportable: true, showInForm: false, showInDisplay: false }
|
|
12288
12473
|
}),
|
|
12289
|
-
consent_version:
|
|
12474
|
+
consent_version: useTextField9({
|
|
12290
12475
|
label: { en: "Consent Version", es: "Versi\xF3n de consentimiento" },
|
|
12291
12476
|
hidden: true,
|
|
12292
12477
|
size: 20,
|
|
@@ -12297,7 +12482,7 @@ var init_users_entity = __esm({
|
|
|
12297
12482
|
label: { en: "Marketing Opt-in", es: "Aceptar marketing" },
|
|
12298
12483
|
meta: { exportable: true, showInForm: false, showInDisplay: false }
|
|
12299
12484
|
}),
|
|
12300
|
-
locale:
|
|
12485
|
+
locale: useSelectField10({
|
|
12301
12486
|
label: { en: "Language", es: "Idioma" },
|
|
12302
12487
|
options: [
|
|
12303
12488
|
{ value: "es", label: { en: "Spanish", es: "Espa\xF1ol" } },
|
|
@@ -12305,15 +12490,15 @@ var init_users_entity = __esm({
|
|
|
12305
12490
|
],
|
|
12306
12491
|
nullable: true,
|
|
12307
12492
|
meta: { sortable: true },
|
|
12308
|
-
defaultValue: "
|
|
12493
|
+
defaultValue: "en"
|
|
12309
12494
|
}),
|
|
12310
|
-
timezone:
|
|
12495
|
+
timezone: useSelectField10({
|
|
12311
12496
|
label: { en: "Timezone", es: "Zona horaria" },
|
|
12312
12497
|
master: "timezones",
|
|
12313
12498
|
meta: { sortable: true },
|
|
12314
12499
|
defaultValue: "timezones:Europe/Madrid"
|
|
12315
12500
|
}),
|
|
12316
|
-
type:
|
|
12501
|
+
type: useSelectField10({
|
|
12317
12502
|
label: { en: "Type", es: "Tipo" },
|
|
12318
12503
|
defaultValue: "human",
|
|
12319
12504
|
options: [
|
|
@@ -12383,7 +12568,7 @@ var init_users_entity = __esm({
|
|
|
12383
12568
|
middleware: (ctx) => ctx.core.middleware.rateLimit({
|
|
12384
12569
|
windowMs: 15 * 60 * 1e3,
|
|
12385
12570
|
max: 5,
|
|
12386
|
-
message: "
|
|
12571
|
+
message: "Too many attempts, try again in 15 minutes"
|
|
12387
12572
|
}),
|
|
12388
12573
|
handler: async (ctx, input) => {
|
|
12389
12574
|
const {
|
|
@@ -12482,7 +12667,7 @@ var init_users_entity = __esm({
|
|
|
12482
12667
|
expose: false,
|
|
12483
12668
|
fields: {
|
|
12484
12669
|
id: useIdField4(),
|
|
12485
|
-
user_id:
|
|
12670
|
+
user_id: useSelectField10({
|
|
12486
12671
|
label: { en: "User", es: "Usuario" },
|
|
12487
12672
|
required: true,
|
|
12488
12673
|
table: "users",
|
|
@@ -12493,7 +12678,7 @@ var init_users_entity = __esm({
|
|
|
12493
12678
|
labelField: "name",
|
|
12494
12679
|
meta: { searchable: true }
|
|
12495
12680
|
}),
|
|
12496
|
-
role_id:
|
|
12681
|
+
role_id: useSelectField10({
|
|
12497
12682
|
label: { en: "Role", es: "Rol" },
|
|
12498
12683
|
required: true,
|
|
12499
12684
|
table: "roles",
|
|
@@ -13437,7 +13622,7 @@ var init_users = __esm({
|
|
|
13437
13622
|
});
|
|
13438
13623
|
|
|
13439
13624
|
// src/modules/auth/auth.entity.ts
|
|
13440
|
-
import { useIdField as useIdField5, useTextField as
|
|
13625
|
+
import { useIdField as useIdField5, useTextField as useTextField10, useSelectField as useSelectField11, useDatetimeField as useDatetimeField4, useEmailField as useEmailField2, useMetadataField as useMetadataField3, useExpiresAtField } from "@gzl10/nexus-sdk/fields";
|
|
13441
13626
|
var refreshTokenEntity, authIdentitiesEntity;
|
|
13442
13627
|
var init_auth_entity = __esm({
|
|
13443
13628
|
"src/modules/auth/auth.entity.ts"() {
|
|
@@ -13452,7 +13637,7 @@ var init_auth_entity = __esm({
|
|
|
13452
13637
|
expose: false,
|
|
13453
13638
|
fields: {
|
|
13454
13639
|
id: useIdField5(),
|
|
13455
|
-
token:
|
|
13640
|
+
token: useTextField10({
|
|
13456
13641
|
label: { en: "Token", es: "Token" },
|
|
13457
13642
|
hidden: true,
|
|
13458
13643
|
size: 255,
|
|
@@ -13479,14 +13664,14 @@ var init_auth_entity = __esm({
|
|
|
13479
13664
|
nullable: true,
|
|
13480
13665
|
meta: { sortable: true }
|
|
13481
13666
|
}),
|
|
13482
|
-
device_id:
|
|
13667
|
+
device_id: useTextField10({
|
|
13483
13668
|
label: { en: "Device ID", es: "ID de dispositivo" },
|
|
13484
13669
|
size: 64,
|
|
13485
13670
|
index: true,
|
|
13486
13671
|
nullable: true,
|
|
13487
13672
|
meta: { searchable: true }
|
|
13488
13673
|
}),
|
|
13489
|
-
device_name:
|
|
13674
|
+
device_name: useTextField10({
|
|
13490
13675
|
label: { en: "Device", es: "Dispositivo" },
|
|
13491
13676
|
size: 100,
|
|
13492
13677
|
nullable: true,
|
|
@@ -13518,7 +13703,7 @@ var init_auth_entity = __esm({
|
|
|
13518
13703
|
order: 5,
|
|
13519
13704
|
fields: {
|
|
13520
13705
|
id: useIdField5(),
|
|
13521
|
-
user_id:
|
|
13706
|
+
user_id: useSelectField11({
|
|
13522
13707
|
label: { en: "User", es: "Usuario" },
|
|
13523
13708
|
table: "users",
|
|
13524
13709
|
column: "id",
|
|
@@ -13530,7 +13715,7 @@ var init_auth_entity = __esm({
|
|
|
13530
13715
|
labelField: "name",
|
|
13531
13716
|
meta: { searchable: true }
|
|
13532
13717
|
}),
|
|
13533
|
-
provider:
|
|
13718
|
+
provider: useTextField10({
|
|
13534
13719
|
label: { en: "Provider", es: "Proveedor" },
|
|
13535
13720
|
required: true,
|
|
13536
13721
|
size: 50,
|
|
@@ -13539,7 +13724,7 @@ var init_auth_entity = __esm({
|
|
|
13539
13724
|
hint: { en: "e.g. pocketid, google, microsoft", es: "ej. pocketid, google, microsoft" },
|
|
13540
13725
|
meta: { sortable: true, searchable: true }
|
|
13541
13726
|
}),
|
|
13542
|
-
provider_user_id:
|
|
13727
|
+
provider_user_id: useTextField10({
|
|
13543
13728
|
label: { en: "Provider User ID", es: "ID de usuario del proveedor" },
|
|
13544
13729
|
required: true,
|
|
13545
13730
|
size: 255,
|
|
@@ -13834,7 +14019,7 @@ var init_auth_middleware = __esm({
|
|
|
13834
14019
|
});
|
|
13835
14020
|
|
|
13836
14021
|
// src/modules/auth/auth.pat.entity.ts
|
|
13837
|
-
import { useIdField as useIdField6, useTextField as
|
|
14022
|
+
import { useIdField as useIdField6, useTextField as useTextField11, useSelectField as useSelectField12, useDatetimeField as useDatetimeField5, useExpiresAtField as useExpiresAtField2 } from "@gzl10/nexus-sdk/fields";
|
|
13838
14023
|
var personalTokenEntity;
|
|
13839
14024
|
var init_auth_pat_entity = __esm({
|
|
13840
14025
|
"src/modules/auth/auth.pat.entity.ts"() {
|
|
@@ -13851,7 +14036,7 @@ var init_auth_pat_entity = __esm({
|
|
|
13851
14036
|
routePrefix: "/personal-tokens",
|
|
13852
14037
|
fields: {
|
|
13853
14038
|
id: useIdField6(),
|
|
13854
|
-
user_id:
|
|
14039
|
+
user_id: useSelectField12({
|
|
13855
14040
|
label: { en: "User", es: "Usuario" },
|
|
13856
14041
|
table: "users",
|
|
13857
14042
|
column: "id",
|
|
@@ -13863,7 +14048,7 @@ var init_auth_pat_entity = __esm({
|
|
|
13863
14048
|
labelField: "name",
|
|
13864
14049
|
meta: { searchable: true }
|
|
13865
14050
|
}),
|
|
13866
|
-
name:
|
|
14051
|
+
name: useTextField11({
|
|
13867
14052
|
label: { en: "Name", es: "Nombre" },
|
|
13868
14053
|
required: true,
|
|
13869
14054
|
size: 100,
|
|
@@ -13871,14 +14056,14 @@ var init_auth_pat_entity = __esm({
|
|
|
13871
14056
|
hint: { en: "Descriptive name for this token", es: "Nombre descriptivo para este token" },
|
|
13872
14057
|
meta: { searchable: true }
|
|
13873
14058
|
}),
|
|
13874
|
-
token_prefix:
|
|
14059
|
+
token_prefix: useTextField11({
|
|
13875
14060
|
label: { en: "Token", es: "Token" },
|
|
13876
14061
|
size: 20,
|
|
13877
14062
|
disabled: true,
|
|
13878
14063
|
nullable: false,
|
|
13879
14064
|
hint: { en: "Partial token for identification", es: "Token parcial para identificaci\xF3n" }
|
|
13880
14065
|
}),
|
|
13881
|
-
token_hash:
|
|
14066
|
+
token_hash: useTextField11({
|
|
13882
14067
|
label: { en: "Token Hash", es: "Hash del token" },
|
|
13883
14068
|
size: 64,
|
|
13884
14069
|
hidden: true,
|
|
@@ -13886,7 +14071,7 @@ var init_auth_pat_entity = __esm({
|
|
|
13886
14071
|
unique: true,
|
|
13887
14072
|
meta: { exportable: false }
|
|
13888
14073
|
}),
|
|
13889
|
-
scope:
|
|
14074
|
+
scope: useSelectField12({
|
|
13890
14075
|
label: { en: "Permission", es: "Permiso" },
|
|
13891
14076
|
required: true,
|
|
13892
14077
|
options: [
|
|
@@ -15642,7 +15827,7 @@ var init_mail_service = __esm({
|
|
|
15642
15827
|
});
|
|
15643
15828
|
|
|
15644
15829
|
// src/modules/mail/mail.entity.ts
|
|
15645
|
-
import { useIdField as useIdField7, useTextField as
|
|
15830
|
+
import { useIdField as useIdField7, useTextField as useTextField12, useSelectField as useSelectField13, useNumberField as useNumberField5, useSwitchField as useSwitchField4, useEmailField as useEmailField3, usePasswordField as usePasswordField2, useTextareaField as useTextareaField3, useTagsField as useTagsField2, useDatetimeField as useDatetimeField6 } from "@gzl10/nexus-sdk/fields";
|
|
15646
15831
|
import nodemailer2 from "nodemailer";
|
|
15647
15832
|
async function getMailConfigFromDB(ctx) {
|
|
15648
15833
|
const configService = ctx.services["config"];
|
|
@@ -15705,7 +15890,7 @@ var init_mail_entity = __esm({
|
|
|
15705
15890
|
},
|
|
15706
15891
|
fields: {
|
|
15707
15892
|
id: useIdField7(),
|
|
15708
|
-
host:
|
|
15893
|
+
host: useTextField12({
|
|
15709
15894
|
label: { en: "SMTP Host", es: "Host SMTP" },
|
|
15710
15895
|
size: 255,
|
|
15711
15896
|
nullable: false,
|
|
@@ -15725,7 +15910,7 @@ var init_mail_entity = __esm({
|
|
|
15725
15910
|
nullable: false,
|
|
15726
15911
|
hint: { en: "Default: SMTP_FROM env var", es: "Por defecto: variable SMTP_FROM" }
|
|
15727
15912
|
}),
|
|
15728
|
-
auth_user:
|
|
15913
|
+
auth_user: useTextField12({
|
|
15729
15914
|
label: { en: "Auth User", es: "Usuario de autenticaci\xF3n" },
|
|
15730
15915
|
size: 255,
|
|
15731
15916
|
nullable: true,
|
|
@@ -15757,12 +15942,12 @@ var init_mail_entity = __esm({
|
|
|
15757
15942
|
required: true,
|
|
15758
15943
|
validation: { format: "email" }
|
|
15759
15944
|
}),
|
|
15760
|
-
subject:
|
|
15945
|
+
subject: useTextField12({
|
|
15761
15946
|
label: { en: "Subject", es: "Asunto" },
|
|
15762
15947
|
required: true,
|
|
15763
15948
|
validation: { min: 1, max: 255 }
|
|
15764
15949
|
}),
|
|
15765
|
-
title:
|
|
15950
|
+
title: useTextField12({
|
|
15766
15951
|
label: { en: "Title", es: "T\xEDtulo" },
|
|
15767
15952
|
hint: { en: "Large title in email header", es: "T\xEDtulo grande en la cabecera del correo" }
|
|
15768
15953
|
}),
|
|
@@ -15906,20 +16091,20 @@ var init_mail_entity = __esm({
|
|
|
15906
16091
|
nullable: false,
|
|
15907
16092
|
meta: { sortable: true }
|
|
15908
16093
|
}),
|
|
15909
|
-
to:
|
|
16094
|
+
to: useTextField12({
|
|
15910
16095
|
label: { en: "Recipient(s)", es: "Destinatario(s)" },
|
|
15911
16096
|
size: 1e3,
|
|
15912
16097
|
nullable: false,
|
|
15913
16098
|
meta: { searchable: true }
|
|
15914
16099
|
}),
|
|
15915
|
-
subject:
|
|
16100
|
+
subject: useTextField12({
|
|
15916
16101
|
label: { en: "Subject", es: "Asunto" },
|
|
15917
16102
|
size: 255,
|
|
15918
16103
|
nullable: false,
|
|
15919
16104
|
meta: { searchable: true, sortable: true }
|
|
15920
16105
|
}),
|
|
15921
16106
|
status: {
|
|
15922
|
-
...
|
|
16107
|
+
...useSelectField13({
|
|
15923
16108
|
label: { en: "Status", es: "Estado" },
|
|
15924
16109
|
options: [
|
|
15925
16110
|
{ value: "pending", label: { en: "Pending", es: "Pendiente" } },
|
|
@@ -15933,7 +16118,7 @@ var init_mail_entity = __esm({
|
|
|
15933
16118
|
}),
|
|
15934
16119
|
validation: { enum: ["pending", "sent", "failed", "bounced"] }
|
|
15935
16120
|
},
|
|
15936
|
-
message_id:
|
|
16121
|
+
message_id: useTextField12({
|
|
15937
16122
|
label: { en: "Message ID", es: "ID de mensaje" },
|
|
15938
16123
|
hidden: true,
|
|
15939
16124
|
size: 255,
|
|
@@ -15943,7 +16128,7 @@ var init_mail_entity = __esm({
|
|
|
15943
16128
|
label: { en: "Error", es: "Error" },
|
|
15944
16129
|
nullable: true
|
|
15945
16130
|
}),
|
|
15946
|
-
sent_by:
|
|
16131
|
+
sent_by: useSelectField13({
|
|
15947
16132
|
label: { en: "Sent by", es: "Enviado por" },
|
|
15948
16133
|
table: "users",
|
|
15949
16134
|
column: "id",
|
|
@@ -16578,7 +16763,7 @@ var init_toggle_plugin_action = __esm({
|
|
|
16578
16763
|
});
|
|
16579
16764
|
|
|
16580
16765
|
// src/modules/plugins/plugins.entity.ts
|
|
16581
|
-
import { useTextField as
|
|
16766
|
+
import { useTextField as useTextField13, useSelectField as useSelectField14, useCheckboxField as useCheckboxField5 } from "@gzl10/nexus-sdk/fields";
|
|
16582
16767
|
import { OFFICIAL_PLUGINS } from "@gzl10/nexus-sdk";
|
|
16583
16768
|
var allowPluginManagement, pluginsEntity;
|
|
16584
16769
|
var init_plugins_entity = __esm({
|
|
@@ -16594,33 +16779,33 @@ var init_plugins_entity = __esm({
|
|
|
16594
16779
|
label: "Plugins",
|
|
16595
16780
|
icon: "mdi:puzzle",
|
|
16596
16781
|
labelField: "code",
|
|
16597
|
-
routePrefix: "/
|
|
16782
|
+
routePrefix: "/",
|
|
16598
16783
|
defaultSort: { field: "name", order: "asc" },
|
|
16599
16784
|
fields: {
|
|
16600
|
-
name:
|
|
16785
|
+
name: useTextField13({
|
|
16601
16786
|
label: { en: "Name", es: "Nombre" },
|
|
16602
16787
|
size: 50,
|
|
16603
16788
|
nullable: false,
|
|
16604
16789
|
meta: { sortable: true, searchable: true }
|
|
16605
16790
|
}),
|
|
16606
|
-
code:
|
|
16791
|
+
code: useTextField13({
|
|
16607
16792
|
label: { en: "Code", es: "C\xF3digo" },
|
|
16608
16793
|
size: 10,
|
|
16609
16794
|
nullable: false,
|
|
16610
16795
|
meta: { sortable: true }
|
|
16611
16796
|
}),
|
|
16612
|
-
label:
|
|
16797
|
+
label: useTextField13({
|
|
16613
16798
|
label: { en: "Label", es: "Etiqueta" },
|
|
16614
16799
|
size: 100,
|
|
16615
16800
|
nullable: false,
|
|
16616
16801
|
meta: { sortable: true }
|
|
16617
16802
|
}),
|
|
16618
|
-
version:
|
|
16803
|
+
version: useTextField13({
|
|
16619
16804
|
label: { en: "Version", es: "Versi\xF3n" },
|
|
16620
16805
|
size: 20,
|
|
16621
16806
|
nullable: false
|
|
16622
16807
|
}),
|
|
16623
|
-
category:
|
|
16808
|
+
category: useSelectField14({
|
|
16624
16809
|
label: { en: "Category", es: "Categor\xEDa" },
|
|
16625
16810
|
options: [
|
|
16626
16811
|
{ value: "content", label: { en: "Content", es: "Contenido" } },
|
|
@@ -16761,8 +16946,8 @@ var init_plugins = __esm({
|
|
|
16761
16946
|
// src/modules/audit/audit.entity.ts
|
|
16762
16947
|
import {
|
|
16763
16948
|
useIdField as useIdField8,
|
|
16764
|
-
useTextField as
|
|
16765
|
-
useSelectField as
|
|
16949
|
+
useTextField as useTextField14,
|
|
16950
|
+
useSelectField as useSelectField15,
|
|
16766
16951
|
useTextareaField as useTextareaField4,
|
|
16767
16952
|
useJsonField as useJsonField3,
|
|
16768
16953
|
useDatetimeField as useDatetimeField7,
|
|
@@ -16785,7 +16970,7 @@ var init_audit_entity = __esm({
|
|
|
16785
16970
|
fields: {
|
|
16786
16971
|
id: useIdField8(),
|
|
16787
16972
|
source: {
|
|
16788
|
-
...
|
|
16973
|
+
...useTextField14({
|
|
16789
16974
|
label: { en: "Source", es: "Origen" },
|
|
16790
16975
|
size: 100,
|
|
16791
16976
|
nullable: false,
|
|
@@ -16795,7 +16980,7 @@ var init_audit_entity = __esm({
|
|
|
16795
16980
|
validation: { min: 1, max: 100 }
|
|
16796
16981
|
},
|
|
16797
16982
|
action: {
|
|
16798
|
-
...
|
|
16983
|
+
...useTextField14({
|
|
16799
16984
|
label: { en: "Action", es: "Acci\xF3n" },
|
|
16800
16985
|
size: 100,
|
|
16801
16986
|
nullable: false,
|
|
@@ -16804,7 +16989,7 @@ var init_audit_entity = __esm({
|
|
|
16804
16989
|
}),
|
|
16805
16990
|
validation: { min: 1, max: 100 }
|
|
16806
16991
|
},
|
|
16807
|
-
actor_id:
|
|
16992
|
+
actor_id: useSelectField15({
|
|
16808
16993
|
label: { en: "Actor", es: "Actor" },
|
|
16809
16994
|
table: "users",
|
|
16810
16995
|
column: "id",
|
|
@@ -16821,20 +17006,20 @@ var init_audit_entity = __esm({
|
|
|
16821
17006
|
nullable: true,
|
|
16822
17007
|
meta: { searchable: true }
|
|
16823
17008
|
}),
|
|
16824
|
-
resource_type:
|
|
17009
|
+
resource_type: useTextField14({
|
|
16825
17010
|
label: { en: "Resource Type", es: "Tipo de recurso" },
|
|
16826
17011
|
size: 100,
|
|
16827
17012
|
nullable: true,
|
|
16828
17013
|
index: true,
|
|
16829
17014
|
meta: { searchable: true }
|
|
16830
17015
|
}),
|
|
16831
|
-
resource_id:
|
|
17016
|
+
resource_id: useTextField14({
|
|
16832
17017
|
label: { en: "Resource ID", es: "ID del recurso" },
|
|
16833
17018
|
size: 100,
|
|
16834
17019
|
nullable: true,
|
|
16835
17020
|
meta: { searchable: true }
|
|
16836
17021
|
}),
|
|
16837
|
-
ip_address:
|
|
17022
|
+
ip_address: useTextField14({
|
|
16838
17023
|
label: { en: "IP Address", es: "Direcci\xF3n IP" },
|
|
16839
17024
|
size: 45,
|
|
16840
17025
|
nullable: true,
|
|
@@ -17007,12 +17192,12 @@ var init_loader = __esm({
|
|
|
17007
17192
|
"src/engine/loader.ts"() {
|
|
17008
17193
|
"use strict";
|
|
17009
17194
|
init_registry();
|
|
17010
|
-
|
|
17195
|
+
init_definition_extractors();
|
|
17011
17196
|
init_modules();
|
|
17012
17197
|
}
|
|
17013
17198
|
});
|
|
17014
17199
|
|
|
17015
|
-
// src/engine/
|
|
17200
|
+
// src/engine/subject-extractor.ts
|
|
17016
17201
|
function getModuleSubjects(mod) {
|
|
17017
17202
|
const subjects = /* @__PURE__ */ new Set();
|
|
17018
17203
|
for (const def of mod.definitions ?? []) {
|
|
@@ -17021,10 +17206,10 @@ function getModuleSubjects(mod) {
|
|
|
17021
17206
|
}
|
|
17022
17207
|
return [...subjects];
|
|
17023
17208
|
}
|
|
17024
|
-
var
|
|
17025
|
-
"src/engine/
|
|
17209
|
+
var init_subject_extractor = __esm({
|
|
17210
|
+
"src/engine/subject-extractor.ts"() {
|
|
17026
17211
|
"use strict";
|
|
17027
|
-
|
|
17212
|
+
init_definition_extractors();
|
|
17028
17213
|
}
|
|
17029
17214
|
});
|
|
17030
17215
|
|
|
@@ -17102,7 +17287,7 @@ function initSocketIO(httpServer, options) {
|
|
|
17102
17287
|
maxHttpBufferSize: 1e6
|
|
17103
17288
|
// 1MB - match Express json body limit
|
|
17104
17289
|
});
|
|
17105
|
-
logger.info({
|
|
17290
|
+
logger.info({ cors: corsOrigin }, "Socket.IO initialized");
|
|
17106
17291
|
io.use((socket, next) => {
|
|
17107
17292
|
const token = socket.handshake.auth?.["token"] || socket.handshake.query?.["token"];
|
|
17108
17293
|
if (token && typeof token === "string" && jwtSecret) {
|
|
@@ -17125,7 +17310,6 @@ function initSocketIO(httpServer, options) {
|
|
|
17125
17310
|
logger.warn({ code: err.code, message: err.message }, "Socket.IO connection error");
|
|
17126
17311
|
});
|
|
17127
17312
|
io.on("connection", handleConnection);
|
|
17128
|
-
logger.info("Socket.IO initialized");
|
|
17129
17313
|
nexusEvents.emitEvent("socket.initialized");
|
|
17130
17314
|
return io;
|
|
17131
17315
|
}
|
|
@@ -17602,6 +17786,7 @@ var init_error_codes = __esm({
|
|
|
17602
17786
|
DB_CONSTRAINT_UNIQUE: "DB_CONSTRAINT_UNIQUE",
|
|
17603
17787
|
DB_CONSTRAINT_FK: "DB_CONSTRAINT_FK",
|
|
17604
17788
|
DB_CONNECTION_ERROR: "DB_CONNECTION_ERROR",
|
|
17789
|
+
DATABASE_NOT_READY: "DATABASE_NOT_READY",
|
|
17605
17790
|
// System
|
|
17606
17791
|
SYSTEM_INTERNAL_ERROR: "SYSTEM_INTERNAL_ERROR"
|
|
17607
17792
|
};
|
|
@@ -17720,6 +17905,12 @@ var init_app_error = __esm({
|
|
|
17720
17905
|
|
|
17721
17906
|
// src/core/abilities/ability.factory.ts
|
|
17722
17907
|
import { AbilityBuilder, createMongoAbility } from "@casl/ability";
|
|
17908
|
+
function setSeedPermissions(perms) {
|
|
17909
|
+
seedPermissions = perms;
|
|
17910
|
+
}
|
|
17911
|
+
function clearSeedPermissions() {
|
|
17912
|
+
seedPermissions = null;
|
|
17913
|
+
}
|
|
17723
17914
|
function setCustomCaslRules(fn) {
|
|
17724
17915
|
customCaslRules = fn;
|
|
17725
17916
|
}
|
|
@@ -17777,6 +17968,17 @@ async function defineAbilityFor(user, roleNames) {
|
|
|
17777
17968
|
if (customCaslRules) {
|
|
17778
17969
|
await customCaslRules(user, { can, cannot });
|
|
17779
17970
|
}
|
|
17971
|
+
if (seedPermissions && !isSuperuser) {
|
|
17972
|
+
for (const roleName of roleNames) {
|
|
17973
|
+
const rolePerms = seedPermissions.get(roleName);
|
|
17974
|
+
if (!rolePerms) continue;
|
|
17975
|
+
for (const [subject2, actions] of rolePerms) {
|
|
17976
|
+
for (const action of actions) {
|
|
17977
|
+
can(action, subject2);
|
|
17978
|
+
}
|
|
17979
|
+
}
|
|
17980
|
+
}
|
|
17981
|
+
}
|
|
17780
17982
|
return build();
|
|
17781
17983
|
}
|
|
17782
17984
|
function packRules(ability) {
|
|
@@ -17785,11 +17987,12 @@ function packRules(ability) {
|
|
|
17785
17987
|
function unpackRules(rules) {
|
|
17786
17988
|
return createMongoAbility(rules);
|
|
17787
17989
|
}
|
|
17788
|
-
var customCaslRules, entityDefinitionsRegistry, SUPERUSER_ROLES;
|
|
17990
|
+
var customCaslRules, seedPermissions, entityDefinitionsRegistry, SUPERUSER_ROLES;
|
|
17789
17991
|
var init_ability_factory = __esm({
|
|
17790
17992
|
"src/core/abilities/ability.factory.ts"() {
|
|
17791
17993
|
"use strict";
|
|
17792
17994
|
init_logger();
|
|
17995
|
+
seedPermissions = null;
|
|
17793
17996
|
entityDefinitionsRegistry = null;
|
|
17794
17997
|
SUPERUSER_ROLES = ["ADMIN", "OWNER"];
|
|
17795
17998
|
}
|
|
@@ -17907,7 +18110,7 @@ var init_env = __esm({
|
|
|
17907
18110
|
envSchema = z8.object({
|
|
17908
18111
|
NODE_ENV: z8.enum(["development", "production", "test"]).default("development"),
|
|
17909
18112
|
PORT: z8.coerce.number().default(3e3),
|
|
17910
|
-
CORS_ORIGIN: z8.string().default("
|
|
18113
|
+
CORS_ORIGIN: z8.string().default("*"),
|
|
17911
18114
|
BACKEND_URL: z8.string().optional(),
|
|
17912
18115
|
DATABASE_URL: z8.string().default("file:./dev.db"),
|
|
17913
18116
|
REDIS_URL: z8.string().url().optional(),
|
|
@@ -18355,7 +18558,7 @@ var init_sequence = __esm({
|
|
|
18355
18558
|
}
|
|
18356
18559
|
});
|
|
18357
18560
|
|
|
18358
|
-
// src/core/utils/
|
|
18561
|
+
// src/core/utils/safe-json.ts
|
|
18359
18562
|
function safeJsonParse(logger2, jsonString, fallback, context) {
|
|
18360
18563
|
try {
|
|
18361
18564
|
return JSON.parse(jsonString);
|
|
@@ -18364,8 +18567,8 @@ function safeJsonParse(logger2, jsonString, fallback, context) {
|
|
|
18364
18567
|
return fallback;
|
|
18365
18568
|
}
|
|
18366
18569
|
}
|
|
18367
|
-
var
|
|
18368
|
-
"src/core/utils/
|
|
18570
|
+
var init_safe_json = __esm({
|
|
18571
|
+
"src/core/utils/safe-json.ts"() {
|
|
18369
18572
|
"use strict";
|
|
18370
18573
|
}
|
|
18371
18574
|
});
|
|
@@ -18374,14 +18577,15 @@ var init_error_handler = __esm({
|
|
|
18374
18577
|
import express from "express";
|
|
18375
18578
|
import { resolve, join as join9 } from "path";
|
|
18376
18579
|
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
18377
|
-
function createServeSPA(app) {
|
|
18378
|
-
return (endpoint, distPath, options = {}) => {
|
|
18580
|
+
function createServeSPA(app, httpServer) {
|
|
18581
|
+
return async (endpoint, distPath, options = {}) => {
|
|
18379
18582
|
const {
|
|
18380
18583
|
maxAge = "1d",
|
|
18381
18584
|
etag = true,
|
|
18382
18585
|
immutable = false,
|
|
18383
18586
|
index = "index.html",
|
|
18384
|
-
absolute = false
|
|
18587
|
+
absolute = false,
|
|
18588
|
+
viteSrc
|
|
18385
18589
|
} = options;
|
|
18386
18590
|
if (endpoint === "/api" || endpoint.startsWith("/api/")) {
|
|
18387
18591
|
logger.error(`Cannot mount SPA on ${endpoint} - reserved for API routes`);
|
|
@@ -18392,58 +18596,117 @@ function createServeSPA(app) {
|
|
|
18392
18596
|
return;
|
|
18393
18597
|
}
|
|
18394
18598
|
registeredEndpoints.add(endpoint);
|
|
18395
|
-
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
|
|
18399
|
-
const projectPath2 = resolve(getProjectPath(), distPath);
|
|
18400
|
-
if (existsSync9(projectPath2)) {
|
|
18401
|
-
resolvedPath = projectPath2;
|
|
18599
|
+
if (env.NODE_ENV === "development" && viteSrc) {
|
|
18600
|
+
const srcPath = resolve(getProjectPath(), viteSrc);
|
|
18601
|
+
if (!existsSync9(srcPath)) {
|
|
18602
|
+
logger.warn({ endpoint, viteSrc, resolved: srcPath }, "Vite source not found \u2014 falling back to static");
|
|
18402
18603
|
} else {
|
|
18403
|
-
|
|
18604
|
+
const mounted = await mountViteDevMiddleware(app, endpoint, srcPath, httpServer);
|
|
18605
|
+
if (mounted) return;
|
|
18404
18606
|
}
|
|
18405
18607
|
}
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
18608
|
+
mountStaticSPA(app, endpoint, distPath, { maxAge, etag, immutable, index, absolute });
|
|
18609
|
+
};
|
|
18610
|
+
}
|
|
18611
|
+
async function mountViteDevMiddleware(app, endpoint, srcPath, httpServer) {
|
|
18612
|
+
try {
|
|
18613
|
+
const vite = await import("vite");
|
|
18614
|
+
const apiUrl = env.BACKEND_URL ? `${env.BACKEND_URL}/api/v1` : "/api/v1";
|
|
18615
|
+
const server2 = await vite.createServer({
|
|
18616
|
+
root: srcPath,
|
|
18617
|
+
server: {
|
|
18618
|
+
middlewareMode: true,
|
|
18619
|
+
allowedHosts: true,
|
|
18620
|
+
hmr: httpServer ? { server: httpServer } : true
|
|
18621
|
+
},
|
|
18622
|
+
plugins: [{
|
|
18623
|
+
name: "nexus-config-inject",
|
|
18624
|
+
transformIndexHtml(html) {
|
|
18625
|
+
const config3 = JSON.stringify({ apiUrl });
|
|
18626
|
+
return html.replace("</head>", `<script>window.__NEXUS__=${config3}</script>
|
|
18627
|
+
</head>`);
|
|
18628
|
+
}
|
|
18629
|
+
}],
|
|
18630
|
+
appType: "spa",
|
|
18631
|
+
clearScreen: false
|
|
18632
|
+
});
|
|
18633
|
+
viteServers.push(server2);
|
|
18634
|
+
if (endpoint === "/") {
|
|
18635
|
+
app.use(server2.middlewares);
|
|
18636
|
+
} else {
|
|
18637
|
+
app.use(endpoint, server2.middlewares);
|
|
18409
18638
|
}
|
|
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
|
-
);
|
|
18639
|
+
logger.info({ path: srcPath }, `Vite dev server mounted at ${endpoint} (HMR enabled)`);
|
|
18640
|
+
return true;
|
|
18641
|
+
} catch (err) {
|
|
18642
|
+
if (err.code === "ERR_MODULE_NOT_FOUND" || err.code === "MODULE_NOT_FOUND") {
|
|
18643
|
+
logger.warn(`vite not installed \u2014 falling back to static serving for ${endpoint}`);
|
|
18644
|
+
return false;
|
|
18425
18645
|
}
|
|
18426
|
-
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18430
|
-
|
|
18431
|
-
|
|
18432
|
-
|
|
18433
|
-
|
|
18434
|
-
|
|
18435
|
-
|
|
18646
|
+
logger.error({ err }, `Failed to mount Vite dev server at ${endpoint}`);
|
|
18647
|
+
return false;
|
|
18648
|
+
}
|
|
18649
|
+
}
|
|
18650
|
+
function mountStaticSPA(app, endpoint, distPath, options) {
|
|
18651
|
+
const { maxAge, etag, immutable, index, absolute } = options;
|
|
18652
|
+
let resolvedPath;
|
|
18653
|
+
if (absolute) {
|
|
18654
|
+
resolvedPath = distPath;
|
|
18655
|
+
} else {
|
|
18656
|
+
const projectPath2 = resolve(getProjectPath(), distPath);
|
|
18657
|
+
if (existsSync9(projectPath2)) {
|
|
18658
|
+
resolvedPath = projectPath2;
|
|
18436
18659
|
} else {
|
|
18437
|
-
|
|
18438
|
-
app.get(`${endpoint}/{*splat}`, fallbackHandler);
|
|
18660
|
+
resolvedPath = resolve(getLibPath(), distPath);
|
|
18439
18661
|
}
|
|
18440
|
-
|
|
18662
|
+
}
|
|
18663
|
+
if (!existsSync9(resolvedPath)) {
|
|
18664
|
+
logger.warn({ endpoint, distPath, hint: "Build the frontend first" }, `SPA directory not found: ${resolvedPath}`);
|
|
18665
|
+
return;
|
|
18666
|
+
}
|
|
18667
|
+
const indexPath = join9(resolvedPath, index);
|
|
18668
|
+
if (!existsSync9(indexPath)) {
|
|
18669
|
+
logger.warn({ endpoint, index }, `Index file not found: ${indexPath}`);
|
|
18670
|
+
}
|
|
18671
|
+
app.use(endpoint, express.static(resolvedPath, { maxAge, etag, immutable }));
|
|
18672
|
+
let injectedHtml = "";
|
|
18673
|
+
if (existsSync9(indexPath)) {
|
|
18674
|
+
const rawHtml = readFileSync5(indexPath, "utf-8");
|
|
18675
|
+
const apiUrl = env.BACKEND_URL ? `${env.BACKEND_URL}/api/v1` : "/api/v1";
|
|
18676
|
+
const nexusConfig = JSON.stringify({ apiUrl });
|
|
18677
|
+
injectedHtml = rawHtml.replace(
|
|
18678
|
+
"</head>",
|
|
18679
|
+
`<script>window.__NEXUS__=${nexusConfig}</script>
|
|
18680
|
+
</head>`
|
|
18681
|
+
);
|
|
18682
|
+
}
|
|
18683
|
+
const fallbackHandler = (_req, res) => {
|
|
18684
|
+
if (!injectedHtml) {
|
|
18685
|
+
res.status(404).send("index.html not found");
|
|
18686
|
+
return;
|
|
18687
|
+
}
|
|
18688
|
+
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
18689
|
+
res.type("html").send(injectedHtml);
|
|
18441
18690
|
};
|
|
18691
|
+
if (endpoint === "/") {
|
|
18692
|
+
app.get("{*splat}", fallbackHandler);
|
|
18693
|
+
} else {
|
|
18694
|
+
app.get(endpoint, fallbackHandler);
|
|
18695
|
+
app.get(`${endpoint}/{*splat}`, fallbackHandler);
|
|
18696
|
+
}
|
|
18697
|
+
logger.info({ path: resolvedPath }, `SPA mounted at ${endpoint}`);
|
|
18442
18698
|
}
|
|
18443
|
-
function
|
|
18699
|
+
async function resetServeSPA() {
|
|
18700
|
+
for (const server2 of viteServers) {
|
|
18701
|
+
try {
|
|
18702
|
+
await server2.close();
|
|
18703
|
+
} catch {
|
|
18704
|
+
}
|
|
18705
|
+
}
|
|
18706
|
+
viteServers.length = 0;
|
|
18444
18707
|
registeredEndpoints.clear();
|
|
18445
18708
|
}
|
|
18446
|
-
var registeredEndpoints;
|
|
18709
|
+
var registeredEndpoints, viteServers;
|
|
18447
18710
|
var init_spa_handler = __esm({
|
|
18448
18711
|
"src/core/spa-handler.ts"() {
|
|
18449
18712
|
"use strict";
|
|
@@ -18451,6 +18714,7 @@ var init_spa_handler = __esm({
|
|
|
18451
18714
|
init_logger();
|
|
18452
18715
|
init_env();
|
|
18453
18716
|
registeredEndpoints = /* @__PURE__ */ new Set();
|
|
18717
|
+
viteServers = [];
|
|
18454
18718
|
}
|
|
18455
18719
|
});
|
|
18456
18720
|
|
|
@@ -19021,7 +19285,7 @@ var init_core = __esm({
|
|
|
19021
19285
|
init_id();
|
|
19022
19286
|
init_sequence();
|
|
19023
19287
|
init_paths();
|
|
19024
|
-
|
|
19288
|
+
init_safe_json();
|
|
19025
19289
|
init_spa_handler();
|
|
19026
19290
|
init_cache();
|
|
19027
19291
|
init_jwt();
|
|
@@ -19736,7 +20000,9 @@ var init_base_service = __esm({
|
|
|
19736
20000
|
const countResult = await qb.clone().count("* as count").first();
|
|
19737
20001
|
const total = Number(countResult?.count ?? 0);
|
|
19738
20002
|
qb = this.applySortingWithDefaults(qb, query);
|
|
19739
|
-
|
|
20003
|
+
if (limit > 0) {
|
|
20004
|
+
qb = qb.limit(limit).offset(offset);
|
|
20005
|
+
}
|
|
19740
20006
|
const rawItems = await qb;
|
|
19741
20007
|
const items = this.parseJsonFieldsFromArray(rawItems);
|
|
19742
20008
|
const processedItems = await this.afterFindAll(items);
|
|
@@ -19821,7 +20087,7 @@ var init_base_service = __esm({
|
|
|
19821
20087
|
});
|
|
19822
20088
|
}
|
|
19823
20089
|
const total = result.length;
|
|
19824
|
-
const paginatedItems = result.slice(offset, offset + limit);
|
|
20090
|
+
const paginatedItems = limit === 0 ? result : result.slice(offset, offset + limit);
|
|
19825
20091
|
return this.buildPaginatedResult(paginatedItems, total, page, limit);
|
|
19826
20092
|
}
|
|
19827
20093
|
/**
|
|
@@ -20076,14 +20342,14 @@ var init_base_service = __esm({
|
|
|
20076
20342
|
* Build paginated result from items and count
|
|
20077
20343
|
*/
|
|
20078
20344
|
buildPaginatedResult(items, total, page, limit) {
|
|
20079
|
-
const totalPages = Math.ceil(total / limit);
|
|
20345
|
+
const totalPages = limit === 0 ? 1 : Math.ceil(total / limit);
|
|
20080
20346
|
return {
|
|
20081
20347
|
items,
|
|
20082
20348
|
total,
|
|
20083
|
-
page,
|
|
20084
|
-
limit,
|
|
20349
|
+
page: limit === 0 ? 1 : page,
|
|
20350
|
+
limit: limit === 0 ? total : limit,
|
|
20085
20351
|
totalPages,
|
|
20086
|
-
hasNext: page < totalPages
|
|
20352
|
+
hasNext: limit === 0 ? false : page < totalPages
|
|
20087
20353
|
};
|
|
20088
20354
|
}
|
|
20089
20355
|
/**
|
|
@@ -20092,8 +20358,8 @@ var init_base_service = __esm({
|
|
|
20092
20358
|
getPagination(query) {
|
|
20093
20359
|
const maxLimit = query?.maxLimit ?? 100;
|
|
20094
20360
|
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;
|
|
20361
|
+
const limit = query?.limit === 0 ? 0 : Math.min(maxLimit, Math.max(1, query?.limit ?? 20));
|
|
20362
|
+
const offset = limit === 0 ? 0 : (page - 1) * limit;
|
|
20097
20363
|
return { page, limit, offset };
|
|
20098
20364
|
}
|
|
20099
20365
|
/**
|
|
@@ -20113,10 +20379,31 @@ var init_base_service = __esm({
|
|
|
20113
20379
|
* Override in subclasses for entity-specific search
|
|
20114
20380
|
*/
|
|
20115
20381
|
applySearch(qb, search) {
|
|
20382
|
+
const searchTerm = `%${search}%`;
|
|
20383
|
+
const searchableFields = [];
|
|
20384
|
+
const fields = "fields" in this.definition ? this.definition.fields : {};
|
|
20385
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
20386
|
+
if (field.meta?.searchable) {
|
|
20387
|
+
searchableFields.push(name);
|
|
20388
|
+
}
|
|
20389
|
+
}
|
|
20116
20390
|
if ("labelField" in this.definition && this.definition.labelField) {
|
|
20117
|
-
const
|
|
20118
|
-
|
|
20391
|
+
const lf = this.definition.labelField;
|
|
20392
|
+
if (!searchableFields.includes(lf)) {
|
|
20393
|
+
searchableFields.unshift(lf);
|
|
20394
|
+
}
|
|
20119
20395
|
}
|
|
20396
|
+
if (searchableFields.length === 0) return qb;
|
|
20397
|
+
qb.where(function() {
|
|
20398
|
+
for (const fieldName of searchableFields) {
|
|
20399
|
+
const field = fields[fieldName];
|
|
20400
|
+
if (field?.db?.type === "json") {
|
|
20401
|
+
this.orWhereRaw(`CAST(?? AS TEXT) LIKE ?`, [fieldName, searchTerm]);
|
|
20402
|
+
} else {
|
|
20403
|
+
this.orWhere(fieldName, "like", searchTerm);
|
|
20404
|
+
}
|
|
20405
|
+
}
|
|
20406
|
+
});
|
|
20120
20407
|
return qb;
|
|
20121
20408
|
}
|
|
20122
20409
|
/**
|
|
@@ -21740,7 +22027,8 @@ function createEntityController(service, definition, ctx) {
|
|
|
21740
22027
|
async list(req, res) {
|
|
21741
22028
|
checkPermission(req, "read");
|
|
21742
22029
|
const page = Math.max(1, parseInt(req.query["page"]) || 1);
|
|
21743
|
-
const
|
|
22030
|
+
const rawLimit = parseInt(req.query["limit"]);
|
|
22031
|
+
const limit = rawLimit === 0 ? 0 : Math.min(100, Math.max(1, rawLimit || 20));
|
|
21744
22032
|
let filters;
|
|
21745
22033
|
if (req.query["filters"]) {
|
|
21746
22034
|
filters = parseFilters(req.query["filters"], ctx.core.errors);
|
|
@@ -23221,17 +23509,18 @@ async function createModuleRouters(ctx, definitions, modulePrefix) {
|
|
|
23221
23509
|
const runtime = await createEntityRuntimeAsync(ctx, definition);
|
|
23222
23510
|
const route = inferEntityRoutePath(definition);
|
|
23223
23511
|
const entityLabel = resolveLocalized7(definition.label, "en");
|
|
23224
|
-
|
|
23512
|
+
const isExposed = !("expose" in definition && definition.expose === false);
|
|
23513
|
+
if (isExposed && modulePrefix && route === modulePrefix) {
|
|
23225
23514
|
ctx.core.logger.warn(
|
|
23226
23515
|
`Entity "${entityLabel}" inferred route "${route}" duplicates module prefix \u2014 add routePrefix to the entity definition to fix`
|
|
23227
23516
|
);
|
|
23228
23517
|
}
|
|
23229
|
-
if (routeMap.has(route)) {
|
|
23518
|
+
if (isExposed && routeMap.has(route)) {
|
|
23230
23519
|
ctx.core.logger.warn(
|
|
23231
23520
|
`Entity "${entityLabel}" route "${route}" collides with "${routeMap.get(route)}" in the same module`
|
|
23232
23521
|
);
|
|
23233
23522
|
}
|
|
23234
|
-
routeMap.set(route, entityLabel);
|
|
23523
|
+
if (isExposed) routeMap.set(route, entityLabel);
|
|
23235
23524
|
router.use(route, runtime.router);
|
|
23236
23525
|
const key = getServiceKey(definition);
|
|
23237
23526
|
if (ctx.services.has(key)) {
|
|
@@ -23447,7 +23736,7 @@ var init_runtime = __esm({
|
|
|
23447
23736
|
}
|
|
23448
23737
|
});
|
|
23449
23738
|
|
|
23450
|
-
// src/db/
|
|
23739
|
+
// src/db/seed-runner.ts
|
|
23451
23740
|
import { existsSync as existsSync10 } from "fs";
|
|
23452
23741
|
import { join as join10 } from "path";
|
|
23453
23742
|
import { pathToFileURL } from "url";
|
|
@@ -23515,8 +23804,8 @@ function hasSeedData(seed5) {
|
|
|
23515
23804
|
if (!Array.isArray(seed5) && "source" in seed5 && seed5.source === "url") return true;
|
|
23516
23805
|
return Array.isArray(seed5) && seed5.length > 0;
|
|
23517
23806
|
}
|
|
23518
|
-
var
|
|
23519
|
-
"src/db/
|
|
23807
|
+
var init_seed_runner = __esm({
|
|
23808
|
+
"src/db/seed-runner.ts"() {
|
|
23520
23809
|
"use strict";
|
|
23521
23810
|
init_runtime();
|
|
23522
23811
|
init_paths();
|
|
@@ -23588,15 +23877,6 @@ var init_ensure_system_tables = __esm({
|
|
|
23588
23877
|
}
|
|
23589
23878
|
});
|
|
23590
23879
|
|
|
23591
|
-
// src/cli/shared.ts
|
|
23592
|
-
import { consola } from "consola";
|
|
23593
|
-
var init_shared = __esm({
|
|
23594
|
-
"src/cli/shared.ts"() {
|
|
23595
|
-
"use strict";
|
|
23596
|
-
init_logger();
|
|
23597
|
-
}
|
|
23598
|
-
});
|
|
23599
|
-
|
|
23600
23880
|
// src/db/migration-sources.ts
|
|
23601
23881
|
function buildMigrationSources() {
|
|
23602
23882
|
const sources = [
|
|
@@ -23616,10 +23896,8 @@ function buildMigrationSources() {
|
|
|
23616
23896
|
var init_migration_sources = __esm({
|
|
23617
23897
|
"src/db/migration-sources.ts"() {
|
|
23618
23898
|
"use strict";
|
|
23619
|
-
init_shared();
|
|
23620
23899
|
init_paths();
|
|
23621
|
-
|
|
23622
|
-
init_store();
|
|
23900
|
+
init_module_store();
|
|
23623
23901
|
}
|
|
23624
23902
|
});
|
|
23625
23903
|
|
|
@@ -23952,7 +24230,7 @@ async function runMigrations(knexInstance, sources) {
|
|
|
23952
24230
|
const executedMigrations = await knex3("_nexus_migrations").where({ status: "completed" }).select("name").then((rows) => new Set(rows.map((r) => r.name)));
|
|
23953
24231
|
const pendingMigrations = migrationFiles.filter((m2) => !executedMigrations.has(m2.name));
|
|
23954
24232
|
if (pendingMigrations.length === 0) {
|
|
23955
|
-
logger.
|
|
24233
|
+
logger.debug("No pending migrations");
|
|
23956
24234
|
return;
|
|
23957
24235
|
}
|
|
23958
24236
|
const batch = await getNextBatch(knex3);
|
|
@@ -24352,6 +24630,27 @@ var init_migration_helpers = __esm({
|
|
|
24352
24630
|
import path2 from "path";
|
|
24353
24631
|
import fs2 from "fs/promises";
|
|
24354
24632
|
import { readFileSync as readFileSync6, mkdirSync as mkdirSync5, realpathSync } from "fs";
|
|
24633
|
+
function getColumnIndexBytes(field) {
|
|
24634
|
+
if (!field?.db) return 255 * MYSQL_BYTES_PER_CHAR;
|
|
24635
|
+
const size = field.db.size ?? 255;
|
|
24636
|
+
if (field.db.type === "text") return 0;
|
|
24637
|
+
if (field.db.type === "string") return size * MYSQL_BYTES_PER_CHAR;
|
|
24638
|
+
if (field.db.type === "integer") return 4;
|
|
24639
|
+
if (field.db.type === "boolean") return 1;
|
|
24640
|
+
if (field.db.type === "datetime" || field.db.type === "date") return 8;
|
|
24641
|
+
if (field.db.type === "uuid") return 16;
|
|
24642
|
+
return size * MYSQL_BYTES_PER_CHAR;
|
|
24643
|
+
}
|
|
24644
|
+
function warnIfIndexExceedsMySQLLimit(table, columns, unique, fields) {
|
|
24645
|
+
if (!fields) return;
|
|
24646
|
+
const totalBytes = columns.reduce((sum, col) => sum + getColumnIndexBytes(fields[col]), 0);
|
|
24647
|
+
if (totalBytes > MYSQL_MAX_INDEX_BYTES) {
|
|
24648
|
+
const indexType = unique ? "UNIQUE" : "INDEX";
|
|
24649
|
+
logger.warn(
|
|
24650
|
+
`${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.`
|
|
24651
|
+
);
|
|
24652
|
+
}
|
|
24653
|
+
}
|
|
24355
24654
|
async function detectSchemaDrift(knexInstance) {
|
|
24356
24655
|
const knex3 = knexInstance ?? getDb();
|
|
24357
24656
|
const entities = getAllPersistentEntities();
|
|
@@ -24459,6 +24758,7 @@ function computeSchemaDiff(entities, currentSchema, options) {
|
|
|
24459
24758
|
for (const idx of entityIndexes) {
|
|
24460
24759
|
const key = normalizeKey(idx.columns, !!idx.unique);
|
|
24461
24760
|
if (!currentKeys.has(key)) {
|
|
24761
|
+
warnIfIndexExceedsMySQLLimit(tableName, idx.columns, !!idx.unique, entity.fields);
|
|
24462
24762
|
diff.newIndexes.push({ columns: idx.columns, unique: !!idx.unique });
|
|
24463
24763
|
}
|
|
24464
24764
|
}
|
|
@@ -24630,7 +24930,7 @@ function formatDriftMessage(drift) {
|
|
|
24630
24930
|
lines.push('Run "pnpm migrate:dev" to generate and apply migrations.');
|
|
24631
24931
|
return lines.join("\n");
|
|
24632
24932
|
}
|
|
24633
|
-
var PERSISTENT_TYPES;
|
|
24933
|
+
var MYSQL_MAX_INDEX_BYTES, MYSQL_BYTES_PER_CHAR, PERSISTENT_TYPES;
|
|
24634
24934
|
var init_migration_generator = __esm({
|
|
24635
24935
|
"src/db/migration-generator.ts"() {
|
|
24636
24936
|
"use strict";
|
|
@@ -24639,9 +24939,11 @@ var init_migration_generator = __esm({
|
|
|
24639
24939
|
init_paths();
|
|
24640
24940
|
init_schema_reader();
|
|
24641
24941
|
init_engine();
|
|
24642
|
-
|
|
24942
|
+
init_module_queries();
|
|
24643
24943
|
init_migration_helpers();
|
|
24644
|
-
|
|
24944
|
+
init_module_store();
|
|
24945
|
+
MYSQL_MAX_INDEX_BYTES = 3072;
|
|
24946
|
+
MYSQL_BYTES_PER_CHAR = 4;
|
|
24645
24947
|
PERSISTENT_TYPES = /* @__PURE__ */ new Set([
|
|
24646
24948
|
"collection",
|
|
24647
24949
|
"tree",
|
|
@@ -24872,6 +25174,10 @@ function handleDbError(err) {
|
|
|
24872
25174
|
{ path: "foreignKey", message: "Foreign key constraint violation" }
|
|
24873
25175
|
]);
|
|
24874
25176
|
}
|
|
25177
|
+
if (code === "42P01" || code === "ER_NO_SUCH_TABLE" || dbErr.errno === 1146 || msg.includes("no such table")) {
|
|
25178
|
+
logger.error({ err }, "Database table not found \u2014 database may need migration or was wiped");
|
|
25179
|
+
throw new AppError({ code: ErrorCodes.DATABASE_NOT_READY, message: "Database not ready" }, 503);
|
|
25180
|
+
}
|
|
24875
25181
|
logger.error({ err }, "Unexpected database error");
|
|
24876
25182
|
throw new AppError({ code: ErrorCodes.SYSTEM_INTERNAL_ERROR, message: "Internal database error" }, 500);
|
|
24877
25183
|
}
|
|
@@ -26215,7 +26521,7 @@ var init_db = __esm({
|
|
|
26215
26521
|
init_schema_helpers();
|
|
26216
26522
|
init_sql_utils();
|
|
26217
26523
|
init_sqlite_compat();
|
|
26218
|
-
|
|
26524
|
+
init_seed_runner();
|
|
26219
26525
|
init_ensure_system_tables();
|
|
26220
26526
|
init_migration_sources();
|
|
26221
26527
|
init_migration_runner();
|
|
@@ -26479,8 +26785,11 @@ var init_events_api = __esm({
|
|
|
26479
26785
|
|
|
26480
26786
|
// src/engine/context.ts
|
|
26481
26787
|
import { ForbiddenError as CASLForbiddenError3, subject } from "@casl/ability";
|
|
26482
|
-
import { DEFAULT_TENANT_ID as DEFAULT_TENANT_ID2 } from "@gzl10/nexus-sdk";
|
|
26788
|
+
import { DEFAULT_TENANT_ID as DEFAULT_TENANT_ID2, DEFAULT_LOCALES } from "@gzl10/nexus-sdk";
|
|
26483
26789
|
import { Redis as Redis2 } from "ioredis";
|
|
26790
|
+
function setLocales(locales) {
|
|
26791
|
+
platformLocales = locales;
|
|
26792
|
+
}
|
|
26484
26793
|
function getSharedCacheManager() {
|
|
26485
26794
|
if (!sharedCacheManager) {
|
|
26486
26795
|
const redisUrl = env.REDIS_URL;
|
|
@@ -26556,7 +26865,7 @@ function createModuleContext() {
|
|
|
26556
26865
|
const defaultAdapter = createKnexAdapter(knex3);
|
|
26557
26866
|
const defaultSchemaAdapter = createKnexSchemaAdapter(knex3);
|
|
26558
26867
|
adaptersRegistry["temp"] = { data: getSharedTempAdapter() };
|
|
26559
|
-
logger.
|
|
26868
|
+
logger.trace(env.REDIS_URL ? "Temp adapter: Redis (shared)" : "Temp adapter: InMemory (shared)");
|
|
26560
26869
|
const middleware = {
|
|
26561
26870
|
validate,
|
|
26562
26871
|
rateLimit: createRateLimit,
|
|
@@ -26664,7 +26973,9 @@ function createModuleContext() {
|
|
|
26664
26973
|
throw new Error(`Knex connection for adapter "${adapter}" not found. Available: ${Object.keys(knexConnections).join(", ")}`);
|
|
26665
26974
|
}
|
|
26666
26975
|
return conn;
|
|
26667
|
-
}
|
|
26976
|
+
},
|
|
26977
|
+
// Placeholder — bound after ctx construction (needs full ModuleContext)
|
|
26978
|
+
seedModule: null
|
|
26668
26979
|
};
|
|
26669
26980
|
const configContext = {
|
|
26670
26981
|
env,
|
|
@@ -26757,7 +27068,8 @@ function createModuleContext() {
|
|
|
26757
27068
|
adapters: adaptersContext,
|
|
26758
27069
|
// Root-level shortcuts for frequently used utilities
|
|
26759
27070
|
events: createEventsApi(nexusEvents, logger),
|
|
26760
|
-
createRouter: () => createRouter()
|
|
27071
|
+
createRouter: () => createRouter(),
|
|
27072
|
+
locales: platformLocales
|
|
26761
27073
|
};
|
|
26762
27074
|
servicesRegistry["cacheManager"] = getSharedCacheManager();
|
|
26763
27075
|
ctx.runtime = {
|
|
@@ -26766,9 +27078,10 @@ function createModuleContext() {
|
|
|
26766
27078
|
createEntityController: (service, def) => createEntityController(service, def, ctx),
|
|
26767
27079
|
createEntityRouter: (controller, def) => createEntityRouter(controller, def, ctx)
|
|
26768
27080
|
};
|
|
27081
|
+
ctx.db.seedModule = (mod) => runModuleSeed(mod, ctx);
|
|
26769
27082
|
return ctx;
|
|
26770
27083
|
}
|
|
26771
|
-
var sharedCacheManager, sharedTempAdapter;
|
|
27084
|
+
var platformLocales, sharedCacheManager, sharedTempAdapter;
|
|
26772
27085
|
var init_context = __esm({
|
|
26773
27086
|
"src/engine/context.ts"() {
|
|
26774
27087
|
"use strict";
|
|
@@ -26781,7 +27094,9 @@ var init_context = __esm({
|
|
|
26781
27094
|
init_plugin_ops();
|
|
26782
27095
|
init_load_config();
|
|
26783
27096
|
init_events_api();
|
|
27097
|
+
init_seed_runner();
|
|
26784
27098
|
init_cache_manager();
|
|
27099
|
+
platformLocales = DEFAULT_LOCALES;
|
|
26785
27100
|
sharedCacheManager = null;
|
|
26786
27101
|
sharedTempAdapter = null;
|
|
26787
27102
|
}
|
|
@@ -26792,11 +27107,11 @@ var init_engine = __esm({
|
|
|
26792
27107
|
"src/engine/index.ts"() {
|
|
26793
27108
|
"use strict";
|
|
26794
27109
|
init_registry();
|
|
26795
|
-
|
|
27110
|
+
init_module_queries();
|
|
26796
27111
|
init_loader();
|
|
26797
|
-
|
|
26798
|
-
|
|
26799
|
-
|
|
27112
|
+
init_module_store();
|
|
27113
|
+
init_subject_extractor();
|
|
27114
|
+
init_definition_extractors();
|
|
26800
27115
|
init_context();
|
|
26801
27116
|
}
|
|
26802
27117
|
});
|
|
@@ -27145,6 +27460,33 @@ async function setupModuleRoutes(app) {
|
|
|
27145
27460
|
}
|
|
27146
27461
|
}
|
|
27147
27462
|
const caslRegistry = /* @__PURE__ */ new Map();
|
|
27463
|
+
function mergeActionPermissions(existing, incoming) {
|
|
27464
|
+
const merged = { ...existing };
|
|
27465
|
+
for (const [role, perm] of Object.entries(incoming)) {
|
|
27466
|
+
if (!merged[role]) {
|
|
27467
|
+
merged[role] = perm;
|
|
27468
|
+
} else {
|
|
27469
|
+
const existingArr = Array.isArray(merged[role]) ? merged[role] : [merged[role]];
|
|
27470
|
+
const incomingArr = Array.isArray(perm) ? perm : [perm];
|
|
27471
|
+
merged[role] = [...existingArr, ...incomingArr];
|
|
27472
|
+
}
|
|
27473
|
+
}
|
|
27474
|
+
return merged;
|
|
27475
|
+
}
|
|
27476
|
+
function registerActionCasl(action) {
|
|
27477
|
+
const casl = action.casl;
|
|
27478
|
+
if (!casl || !("subject" in casl) || !casl.subject || !("permissions" in casl) || !casl.permissions) return;
|
|
27479
|
+
const subject2 = casl.subject;
|
|
27480
|
+
const existing = caslRegistry.get(subject2);
|
|
27481
|
+
if (existing) {
|
|
27482
|
+
existing.permissions = mergeActionPermissions(existing.permissions, casl.permissions);
|
|
27483
|
+
} else {
|
|
27484
|
+
caslRegistry.set(subject2, {
|
|
27485
|
+
subject: subject2,
|
|
27486
|
+
permissions: casl.permissions ?? {}
|
|
27487
|
+
});
|
|
27488
|
+
}
|
|
27489
|
+
}
|
|
27148
27490
|
for (const mod of modules) {
|
|
27149
27491
|
for (const def of mod.definitions ?? []) {
|
|
27150
27492
|
const casl = def.casl;
|
|
@@ -27154,6 +27496,12 @@ async function setupModuleRoutes(app) {
|
|
|
27154
27496
|
subject: casl.subject,
|
|
27155
27497
|
permissions: casl.permissions ?? {}
|
|
27156
27498
|
});
|
|
27499
|
+
for (const action of def.actions ?? []) {
|
|
27500
|
+
registerActionCasl(action);
|
|
27501
|
+
}
|
|
27502
|
+
}
|
|
27503
|
+
for (const action of mod.actions ?? []) {
|
|
27504
|
+
registerActionCasl(action);
|
|
27157
27505
|
}
|
|
27158
27506
|
}
|
|
27159
27507
|
setEntityDefinitions(caslRegistry);
|
|
@@ -27338,7 +27686,7 @@ async function createApp(options = {}) {
|
|
|
27338
27686
|
// Only accept arrays and objects
|
|
27339
27687
|
}));
|
|
27340
27688
|
app.use(cookieParser());
|
|
27341
|
-
const serveSPA = createServeSPA(app);
|
|
27689
|
+
const serveSPA = createServeSPA(app, options.httpServer);
|
|
27342
27690
|
if (options.beforeRoutes) {
|
|
27343
27691
|
const result = options.beforeRoutes(app, serveSPA);
|
|
27344
27692
|
if (result instanceof Promise) {
|
|
@@ -27432,11 +27780,11 @@ async function createApp(options = {}) {
|
|
|
27432
27780
|
});
|
|
27433
27781
|
const sortedSpas = [...servedSpas].sort((a, b) => b.endpoint.length - a.endpoint.length);
|
|
27434
27782
|
for (const spa of sortedSpas) {
|
|
27435
|
-
serveSPA(spa.endpoint, spa.path, spa);
|
|
27783
|
+
await serveSPA(spa.endpoint, spa.path, { ...spa, viteSrc: spa.viteSrc });
|
|
27436
27784
|
}
|
|
27437
27785
|
const { ui } = getConfig();
|
|
27438
27786
|
if (ui.enabled) {
|
|
27439
|
-
serveSPA(ui.base, ui.path);
|
|
27787
|
+
await serveSPA(ui.base, ui.path, { viteSrc: "../ui" });
|
|
27440
27788
|
}
|
|
27441
27789
|
app.use(errorMiddleware);
|
|
27442
27790
|
return app;
|
|
@@ -27462,58 +27810,32 @@ var init_app = __esm({
|
|
|
27462
27810
|
}
|
|
27463
27811
|
});
|
|
27464
27812
|
|
|
27465
|
-
// src/core/utils/
|
|
27813
|
+
// src/core/utils/port-check.ts
|
|
27466
27814
|
import net from "net";
|
|
27467
27815
|
import { execSync } from "child_process";
|
|
27468
27816
|
function findProcessOnPort(port) {
|
|
27469
27817
|
try {
|
|
27470
27818
|
const output = execSync(`lsof -ti :${port} 2>/dev/null`, { encoding: "utf-8" });
|
|
27471
27819
|
const pid = parseInt(output.trim().split("\n")[0] ?? "", 10);
|
|
27472
|
-
|
|
27820
|
+
if (isNaN(pid)) return null;
|
|
27821
|
+
let name = "unknown";
|
|
27822
|
+
try {
|
|
27823
|
+
name = execSync(`ps -o comm= -p ${pid} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
27824
|
+
} catch {
|
|
27825
|
+
}
|
|
27826
|
+
return { pid, name };
|
|
27473
27827
|
} catch {
|
|
27474
27828
|
return null;
|
|
27475
27829
|
}
|
|
27476
27830
|
}
|
|
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
27831
|
async function checkPortAvailable(port, host = "0.0.0.0") {
|
|
27503
27832
|
return new Promise((resolve2, reject) => {
|
|
27504
27833
|
const server2 = net.createServer();
|
|
27505
27834
|
server2.once("error", (err) => {
|
|
27506
27835
|
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`));
|
|
27836
|
+
const proc = findProcessOnPort(port);
|
|
27837
|
+
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`;
|
|
27838
|
+
reject(new Error(msg));
|
|
27517
27839
|
} else {
|
|
27518
27840
|
reject(err);
|
|
27519
27841
|
}
|
|
@@ -27524,10 +27846,9 @@ async function checkPortAvailable(port, host = "0.0.0.0") {
|
|
|
27524
27846
|
server2.listen(port, host);
|
|
27525
27847
|
});
|
|
27526
27848
|
}
|
|
27527
|
-
var
|
|
27528
|
-
"src/core/utils/
|
|
27849
|
+
var init_port_check = __esm({
|
|
27850
|
+
"src/core/utils/port-check.ts"() {
|
|
27529
27851
|
"use strict";
|
|
27530
|
-
init_logger();
|
|
27531
27852
|
}
|
|
27532
27853
|
});
|
|
27533
27854
|
|
|
@@ -27565,7 +27886,7 @@ function isFrpcInstalled() {
|
|
|
27565
27886
|
return false;
|
|
27566
27887
|
}
|
|
27567
27888
|
}
|
|
27568
|
-
function startTunnel(config3) {
|
|
27889
|
+
async function startTunnel(config3) {
|
|
27569
27890
|
if (tunnelProcess) return true;
|
|
27570
27891
|
if (!isFrpcInstalled()) {
|
|
27571
27892
|
logger.warn("frpc binary not found \u2014 tunnel disabled. Install with: brew install frp");
|
|
@@ -27577,24 +27898,49 @@ function startTunnel(config3) {
|
|
|
27577
27898
|
tunnelProcess = spawn("frpc", ["-c", tmpConfigPath], {
|
|
27578
27899
|
stdio: ["ignore", "pipe", "pipe"]
|
|
27579
27900
|
});
|
|
27580
|
-
|
|
27581
|
-
|
|
27582
|
-
|
|
27583
|
-
|
|
27584
|
-
|
|
27585
|
-
|
|
27586
|
-
|
|
27587
|
-
|
|
27588
|
-
|
|
27589
|
-
|
|
27590
|
-
|
|
27591
|
-
|
|
27592
|
-
|
|
27593
|
-
|
|
27901
|
+
outputBuffer = "";
|
|
27902
|
+
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
27903
|
+
return new Promise((resolve2) => {
|
|
27904
|
+
let settled = false;
|
|
27905
|
+
const collectOutput = (data) => {
|
|
27906
|
+
const msg = stripAnsi(data.toString()).trim();
|
|
27907
|
+
if (!msg) return;
|
|
27908
|
+
outputBuffer += msg + "\n";
|
|
27909
|
+
logger.debug({ component: "tunnel" }, msg);
|
|
27910
|
+
if (!settled && msg.includes("start proxy success")) {
|
|
27911
|
+
settled = true;
|
|
27912
|
+
const url = getTunnelUrl(config3.subdomain, config3.server);
|
|
27913
|
+
logger.info({ url, component: "tunnel" }, `Tunnel active \u2192 ${url}`);
|
|
27914
|
+
resolve2(true);
|
|
27915
|
+
}
|
|
27916
|
+
};
|
|
27917
|
+
tunnelProcess.stdout?.on("data", collectOutput);
|
|
27918
|
+
tunnelProcess.stderr?.on("data", collectOutput);
|
|
27919
|
+
tunnelProcess.on("exit", (code) => {
|
|
27920
|
+
if (code !== 0 && code !== null) {
|
|
27921
|
+
const output = outputBuffer.trim();
|
|
27922
|
+
const hint = output.includes("authorization failed") || output.includes("auth failed") || output.includes("invalid token") ? " \u2014 check FRPC_TOKEN is correct" : output.includes("login to the server failed") ? ` \u2014 ${output.split("\n").pop()}` : "";
|
|
27923
|
+
logger.error(
|
|
27924
|
+
{ code, reason: output || void 0, component: "tunnel" },
|
|
27925
|
+
`frpc failed to start${hint}`
|
|
27926
|
+
);
|
|
27927
|
+
}
|
|
27928
|
+
tunnelProcess = null;
|
|
27929
|
+
outputBuffer = "";
|
|
27930
|
+
cleanupConfig();
|
|
27931
|
+
if (!settled) {
|
|
27932
|
+
settled = true;
|
|
27933
|
+
resolve2(false);
|
|
27934
|
+
}
|
|
27935
|
+
});
|
|
27936
|
+
setTimeout(() => {
|
|
27937
|
+
if (!settled) {
|
|
27938
|
+
settled = true;
|
|
27939
|
+
logger.warn({ component: "tunnel" }, "frpc connection timeout (5s) \u2014 tunnel may not be active");
|
|
27940
|
+
resolve2(false);
|
|
27941
|
+
}
|
|
27942
|
+
}, 5e3);
|
|
27594
27943
|
});
|
|
27595
|
-
const url = getTunnelUrl(config3.subdomain, config3.server);
|
|
27596
|
-
logger.info({ url, component: "tunnel" }, `Tunnel enabled \u2192 ${url}`);
|
|
27597
|
-
return true;
|
|
27598
27944
|
}
|
|
27599
27945
|
function stopTunnel() {
|
|
27600
27946
|
if (tunnelProcess) {
|
|
@@ -27612,13 +27958,388 @@ function cleanupConfig() {
|
|
|
27612
27958
|
tmpConfigPath = null;
|
|
27613
27959
|
}
|
|
27614
27960
|
}
|
|
27615
|
-
var tunnelProcess, tmpConfigPath;
|
|
27961
|
+
var tunnelProcess, tmpConfigPath, outputBuffer;
|
|
27616
27962
|
var init_tunnel = __esm({
|
|
27617
27963
|
"src/core/tunnel.ts"() {
|
|
27618
27964
|
"use strict";
|
|
27619
27965
|
init_core();
|
|
27620
27966
|
tunnelProcess = null;
|
|
27621
27967
|
tmpConfigPath = null;
|
|
27968
|
+
outputBuffer = "";
|
|
27969
|
+
}
|
|
27970
|
+
});
|
|
27971
|
+
|
|
27972
|
+
// src/cli/shared.ts
|
|
27973
|
+
import { consola } from "consola";
|
|
27974
|
+
function initCli() {
|
|
27975
|
+
const config3 = getLoggerConfig();
|
|
27976
|
+
initLoggerService({ ...config3, level: "error", sentry: void 0 });
|
|
27977
|
+
setLoggerInstance(getPinoLogger());
|
|
27978
|
+
}
|
|
27979
|
+
var init_shared = __esm({
|
|
27980
|
+
"src/cli/shared.ts"() {
|
|
27981
|
+
"use strict";
|
|
27982
|
+
init_logger();
|
|
27983
|
+
}
|
|
27984
|
+
});
|
|
27985
|
+
|
|
27986
|
+
// src/cli/migrate-commands.ts
|
|
27987
|
+
import { readFileSync as readFileSync8, existsSync as existsSync12 } from "fs";
|
|
27988
|
+
import { join as join14 } from "path";
|
|
27989
|
+
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
27990
|
+
import Table from "cli-table3";
|
|
27991
|
+
import { consola as consola2 } from "consola";
|
|
27992
|
+
async function loadSelfPlugin() {
|
|
27993
|
+
const projectPath2 = getProjectPath();
|
|
27994
|
+
const pkgPath = join14(projectPath2, "package.json");
|
|
27995
|
+
if (!existsSync12(pkgPath)) return;
|
|
27996
|
+
try {
|
|
27997
|
+
const pkg2 = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
27998
|
+
const pkgName = pkg2?.name;
|
|
27999
|
+
if (!pkgName || !/nexus-plugin-/.test(pkgName)) return;
|
|
28000
|
+
const srcEntry = join14(projectPath2, "src", "index.ts");
|
|
28001
|
+
const distEntry = join14(projectPath2, "dist", "index.js");
|
|
28002
|
+
if (existsSync12(srcEntry)) {
|
|
28003
|
+
try {
|
|
28004
|
+
const { tsImport } = await import("tsx/esm/api");
|
|
28005
|
+
const mod = await tsImport(
|
|
28006
|
+
pathToFileURL3(srcEntry).href,
|
|
28007
|
+
import.meta.url
|
|
28008
|
+
);
|
|
28009
|
+
const manifest = extractPluginManifest(mod);
|
|
28010
|
+
if (manifest) {
|
|
28011
|
+
if (!manifest.migrationsDir) {
|
|
28012
|
+
manifest.migrationsDir = join14(projectPath2, "migrations");
|
|
28013
|
+
}
|
|
28014
|
+
registerPlugin(manifest);
|
|
28015
|
+
return;
|
|
28016
|
+
}
|
|
28017
|
+
} catch (err) {
|
|
28018
|
+
console.error(` \u26A0 Failed to load plugin src/index.ts: ${err.message}`);
|
|
28019
|
+
}
|
|
28020
|
+
}
|
|
28021
|
+
if (existsSync12(distEntry)) {
|
|
28022
|
+
try {
|
|
28023
|
+
const mod = await import(pathToFileURL3(distEntry).href);
|
|
28024
|
+
const manifest = extractPluginManifest(mod);
|
|
28025
|
+
if (manifest) {
|
|
28026
|
+
if (!manifest.migrationsDir) {
|
|
28027
|
+
manifest.migrationsDir = join14(projectPath2, "migrations");
|
|
28028
|
+
}
|
|
28029
|
+
registerPlugin(manifest);
|
|
28030
|
+
return;
|
|
28031
|
+
}
|
|
28032
|
+
} catch (err) {
|
|
28033
|
+
console.error(` \u26A0 Failed to load plugin dist/index.js: ${err.message}`);
|
|
28034
|
+
}
|
|
28035
|
+
}
|
|
28036
|
+
} catch {
|
|
28037
|
+
}
|
|
28038
|
+
}
|
|
28039
|
+
async function loadModulesForMigration() {
|
|
28040
|
+
loadCoreModules();
|
|
28041
|
+
await loadSelfPlugin();
|
|
28042
|
+
const config3 = await loadNexusConfig();
|
|
28043
|
+
if (config3.plugins?.length) {
|
|
28044
|
+
const sorted = topologicalSortPlugins(config3.plugins);
|
|
28045
|
+
for (const plugin of sorted) {
|
|
28046
|
+
registerPlugin(plugin);
|
|
28047
|
+
}
|
|
28048
|
+
}
|
|
28049
|
+
const coreNames = new Set(getOrderedModules().map((m2) => m2.name));
|
|
28050
|
+
for (const mod of config3.modules ?? []) {
|
|
28051
|
+
if (!coreNames.has(mod.name)) {
|
|
28052
|
+
registerModule(mod, { source: "standalone" });
|
|
28053
|
+
}
|
|
28054
|
+
}
|
|
28055
|
+
}
|
|
28056
|
+
var init_migrate_commands = __esm({
|
|
28057
|
+
"src/cli/migrate-commands.ts"() {
|
|
28058
|
+
"use strict";
|
|
28059
|
+
init_database();
|
|
28060
|
+
init_engine();
|
|
28061
|
+
init_load_config();
|
|
28062
|
+
init_shared();
|
|
28063
|
+
init_paths();
|
|
28064
|
+
init_connection();
|
|
28065
|
+
init_ensure_system_tables();
|
|
28066
|
+
init_migration_generator();
|
|
28067
|
+
init_migration_runner();
|
|
28068
|
+
}
|
|
28069
|
+
});
|
|
28070
|
+
|
|
28071
|
+
// src/cli/seed-commands.ts
|
|
28072
|
+
var seed_commands_exports = {};
|
|
28073
|
+
__export(seed_commands_exports, {
|
|
28074
|
+
handleSeedExport: () => handleSeedExport,
|
|
28075
|
+
importSeedFiles: () => importSeedFiles
|
|
28076
|
+
});
|
|
28077
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, writeFileSync as writeFileSync3, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
|
|
28078
|
+
import { join as join15, basename as basename4 } from "path";
|
|
28079
|
+
import { consola as consola3 } from "consola";
|
|
28080
|
+
function deserializeJsonFields(record, fields) {
|
|
28081
|
+
const result = { ...record };
|
|
28082
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
28083
|
+
if (field.db?.type === "json" && typeof result[name] === "string") {
|
|
28084
|
+
try {
|
|
28085
|
+
result[name] = JSON.parse(result[name]);
|
|
28086
|
+
} catch {
|
|
28087
|
+
}
|
|
28088
|
+
}
|
|
28089
|
+
}
|
|
28090
|
+
return result;
|
|
28091
|
+
}
|
|
28092
|
+
function serializeJsonFields(record, fields) {
|
|
28093
|
+
const result = { ...record };
|
|
28094
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
28095
|
+
if (field.db?.type === "json" && typeof result[name] === "object" && result[name] !== null) {
|
|
28096
|
+
result[name] = JSON.stringify(result[name]);
|
|
28097
|
+
}
|
|
28098
|
+
}
|
|
28099
|
+
return result;
|
|
28100
|
+
}
|
|
28101
|
+
async function handleSeedExport(entity) {
|
|
28102
|
+
initCli();
|
|
28103
|
+
await loadModulesForMigration();
|
|
28104
|
+
const db2 = getDb();
|
|
28105
|
+
try {
|
|
28106
|
+
const modules = getOrderedModules();
|
|
28107
|
+
const seedDir = join15(getProjectPath(), "data", "seeds");
|
|
28108
|
+
const seedableEntities = [];
|
|
28109
|
+
for (const mod of modules) {
|
|
28110
|
+
for (const def of mod.definitions ?? []) {
|
|
28111
|
+
if (!("seedable" in def) || !def.seedable) continue;
|
|
28112
|
+
if (entity && def.table !== entity) continue;
|
|
28113
|
+
seedableEntities.push({
|
|
28114
|
+
module: mod.name,
|
|
28115
|
+
table: def.table,
|
|
28116
|
+
fields: def.fields
|
|
28117
|
+
});
|
|
28118
|
+
}
|
|
28119
|
+
}
|
|
28120
|
+
if (seedableEntities.length === 0) {
|
|
28121
|
+
if (entity) {
|
|
28122
|
+
consola3.warn(`No seedable entity found with table "${entity}"`);
|
|
28123
|
+
} else {
|
|
28124
|
+
consola3.warn("No seedable entities found");
|
|
28125
|
+
}
|
|
28126
|
+
return;
|
|
28127
|
+
}
|
|
28128
|
+
if (!existsSync13(seedDir)) {
|
|
28129
|
+
mkdirSync6(seedDir, { recursive: true });
|
|
28130
|
+
}
|
|
28131
|
+
for (const { module: modName, table, fields } of seedableEntities) {
|
|
28132
|
+
const rows = await db2(table).orderBy("id");
|
|
28133
|
+
if (rows.length === 0) {
|
|
28134
|
+
consola3.info(`${table}: no records, skipping`);
|
|
28135
|
+
continue;
|
|
28136
|
+
}
|
|
28137
|
+
const exported = rows.map(
|
|
28138
|
+
(row) => deserializeJsonFields(row, fields)
|
|
28139
|
+
);
|
|
28140
|
+
const filePath = join15(seedDir, `${table}.json`);
|
|
28141
|
+
writeFileSync3(filePath, JSON.stringify(exported, null, 2) + "\n", "utf-8");
|
|
28142
|
+
consola3.success(`${table}: exported ${rows.length} records to data/seeds/${table}.json (module: ${modName})`);
|
|
28143
|
+
}
|
|
28144
|
+
} catch (err) {
|
|
28145
|
+
consola3.error("Seed export failed:", err);
|
|
28146
|
+
} finally {
|
|
28147
|
+
await db2.destroy();
|
|
28148
|
+
process.exit(0);
|
|
28149
|
+
}
|
|
28150
|
+
}
|
|
28151
|
+
async function importSeedFiles(db2, modules, logger2) {
|
|
28152
|
+
const seedDir = join15(getProjectPath(), "data", "seeds");
|
|
28153
|
+
if (!existsSync13(seedDir)) return;
|
|
28154
|
+
const files = readdirSync2(seedDir).filter((f) => f.endsWith(".json"));
|
|
28155
|
+
if (files.length === 0) return;
|
|
28156
|
+
const seedableDefs = /* @__PURE__ */ new Map();
|
|
28157
|
+
for (const mod of modules) {
|
|
28158
|
+
for (const rawDef of mod.definitions ?? []) {
|
|
28159
|
+
const def = rawDef;
|
|
28160
|
+
if (!def["seedable"] || !def["table"] || !def["fields"]) continue;
|
|
28161
|
+
seedableDefs.set(
|
|
28162
|
+
def["table"],
|
|
28163
|
+
{ fields: def["fields"], module: mod.name }
|
|
28164
|
+
);
|
|
28165
|
+
}
|
|
28166
|
+
}
|
|
28167
|
+
for (const file of files) {
|
|
28168
|
+
const table = basename4(file, ".json");
|
|
28169
|
+
const defInfo = seedableDefs.get(table);
|
|
28170
|
+
if (!defInfo) {
|
|
28171
|
+
logger2.debug(`data/seeds/${file}: skipped (entity "${table}" is not seedable)`);
|
|
28172
|
+
continue;
|
|
28173
|
+
}
|
|
28174
|
+
const filePath = join15(seedDir, file);
|
|
28175
|
+
const raw = readFileSync9(filePath, "utf-8");
|
|
28176
|
+
let records;
|
|
28177
|
+
try {
|
|
28178
|
+
records = JSON.parse(raw);
|
|
28179
|
+
} catch {
|
|
28180
|
+
logger2.info(`data/seeds/${file}: skipped (invalid JSON)`);
|
|
28181
|
+
continue;
|
|
28182
|
+
}
|
|
28183
|
+
if (!Array.isArray(records) || records.length === 0) continue;
|
|
28184
|
+
const rows = records.map((r) => serializeJsonFields(r, defInfo.fields));
|
|
28185
|
+
for (const row of rows) {
|
|
28186
|
+
await db2(table).insert(row).onConflict("id").merge();
|
|
28187
|
+
}
|
|
28188
|
+
logger2.info(`Seeded ${rows.length} records into ${table} from data/seeds/${file}`);
|
|
28189
|
+
}
|
|
28190
|
+
}
|
|
28191
|
+
var init_seed_commands = __esm({
|
|
28192
|
+
"src/cli/seed-commands.ts"() {
|
|
28193
|
+
"use strict";
|
|
28194
|
+
init_engine();
|
|
28195
|
+
init_paths();
|
|
28196
|
+
init_connection();
|
|
28197
|
+
init_shared();
|
|
28198
|
+
init_migrate_commands();
|
|
28199
|
+
}
|
|
28200
|
+
});
|
|
28201
|
+
|
|
28202
|
+
// src/db/seed-context.ts
|
|
28203
|
+
var seed_context_exports = {};
|
|
28204
|
+
__export(seed_context_exports, {
|
|
28205
|
+
createSeedContext: () => createSeedContext
|
|
28206
|
+
});
|
|
28207
|
+
function createSeedContext(deps) {
|
|
28208
|
+
const { knex: db2, generateId: generateId4, hashPassword: hashPassword2, pluginPrefixes, logger: logger2 } = deps;
|
|
28209
|
+
const permissionsMap = /* @__PURE__ */ new Map();
|
|
28210
|
+
const seedContext = {
|
|
28211
|
+
masters: {
|
|
28212
|
+
register(type2, entries, options) {
|
|
28213
|
+
deps.masterRegistry.register(type2, entries, options);
|
|
28214
|
+
}
|
|
28215
|
+
},
|
|
28216
|
+
roles: {
|
|
28217
|
+
async add(role) {
|
|
28218
|
+
const hasTable = await db2.schema.hasTable("roles");
|
|
28219
|
+
if (!hasTable) return;
|
|
28220
|
+
const existing = await db2("roles").where({ name: role.name }).first();
|
|
28221
|
+
if (!existing) {
|
|
28222
|
+
const description = role.description ? JSON.stringify(typeof role.description === "string" ? { en: role.description } : role.description) : null;
|
|
28223
|
+
await db2("roles").insert({
|
|
28224
|
+
id: generateId4(),
|
|
28225
|
+
name: role.name,
|
|
28226
|
+
description,
|
|
28227
|
+
is_system: role.is_system ?? false
|
|
28228
|
+
});
|
|
28229
|
+
logger2.info({ role: role.name }, "Seeded role");
|
|
28230
|
+
}
|
|
28231
|
+
if (role.permissions) {
|
|
28232
|
+
let rolePerms = permissionsMap.get(role.name);
|
|
28233
|
+
if (!rolePerms) {
|
|
28234
|
+
rolePerms = /* @__PURE__ */ new Map();
|
|
28235
|
+
permissionsMap.set(role.name, rolePerms);
|
|
28236
|
+
}
|
|
28237
|
+
for (const [subject2, actions] of Object.entries(role.permissions)) {
|
|
28238
|
+
const normalized = subject2.charAt(0).toUpperCase() + subject2.slice(1);
|
|
28239
|
+
rolePerms.set(normalized, actions);
|
|
28240
|
+
}
|
|
28241
|
+
}
|
|
28242
|
+
}
|
|
28243
|
+
},
|
|
28244
|
+
users: {
|
|
28245
|
+
async add(user) {
|
|
28246
|
+
const hasTable = await db2.schema.hasTable("users");
|
|
28247
|
+
if (!hasTable) return;
|
|
28248
|
+
const existing = await db2("users").where({ email: user.email }).first();
|
|
28249
|
+
if (existing) return;
|
|
28250
|
+
const userId = generateId4();
|
|
28251
|
+
await db2("users").insert({
|
|
28252
|
+
id: userId,
|
|
28253
|
+
type: user.type ?? "human",
|
|
28254
|
+
email: user.email,
|
|
28255
|
+
password: await hashPassword2(user.password),
|
|
28256
|
+
name: user.name ?? user.email
|
|
28257
|
+
});
|
|
28258
|
+
if (user.roles?.length) {
|
|
28259
|
+
for (const roleName of user.roles) {
|
|
28260
|
+
const role = await db2("roles").where({ name: roleName }).first();
|
|
28261
|
+
if (role) {
|
|
28262
|
+
await db2("user_roles").insert({
|
|
28263
|
+
id: generateId4(),
|
|
28264
|
+
user_id: userId,
|
|
28265
|
+
role_id: role.id
|
|
28266
|
+
});
|
|
28267
|
+
} else {
|
|
28268
|
+
logger2.warn({ role: roleName, user: user.email }, "Role not found, skipping assignment");
|
|
28269
|
+
}
|
|
28270
|
+
}
|
|
28271
|
+
}
|
|
28272
|
+
logger2.info({ email: user.email, roles: user.roles }, "Seeded user");
|
|
28273
|
+
}
|
|
28274
|
+
},
|
|
28275
|
+
plugin(pluginName) {
|
|
28276
|
+
return {
|
|
28277
|
+
entity(entityName) {
|
|
28278
|
+
return {
|
|
28279
|
+
async upsert(data, options) {
|
|
28280
|
+
let prefix = pluginPrefixes.get(pluginName) ?? "";
|
|
28281
|
+
if (!prefix) {
|
|
28282
|
+
for (const [key2, pfx] of pluginPrefixes) {
|
|
28283
|
+
if (pluginName === key2 || pluginName.endsWith(`-${key2}`)) {
|
|
28284
|
+
prefix = pfx;
|
|
28285
|
+
break;
|
|
28286
|
+
}
|
|
28287
|
+
}
|
|
28288
|
+
}
|
|
28289
|
+
const fullTable = `${prefix}${entityName}`;
|
|
28290
|
+
const hasTable = await db2.schema.hasTable(fullTable);
|
|
28291
|
+
if (!hasTable) {
|
|
28292
|
+
logger2.warn({ table: fullTable, plugin: pluginName }, "Table not found for plugin entity seed");
|
|
28293
|
+
return 0;
|
|
28294
|
+
}
|
|
28295
|
+
const key = options?.key ?? "id";
|
|
28296
|
+
let seeded = 0;
|
|
28297
|
+
for (const row of data) {
|
|
28298
|
+
const keyValue = row[key];
|
|
28299
|
+
if (keyValue != null) {
|
|
28300
|
+
const existing = await db2(fullTable).where({ [key]: keyValue }).first();
|
|
28301
|
+
if (existing) continue;
|
|
28302
|
+
}
|
|
28303
|
+
const insertData = { ...row };
|
|
28304
|
+
if (key === "id" && !insertData["id"]) {
|
|
28305
|
+
insertData["id"] = generateId4();
|
|
28306
|
+
}
|
|
28307
|
+
await db2(fullTable).insert(insertData);
|
|
28308
|
+
seeded++;
|
|
28309
|
+
}
|
|
28310
|
+
if (seeded > 0) {
|
|
28311
|
+
logger2.info({ table: fullTable, seeded }, "Seeded plugin entity data");
|
|
28312
|
+
}
|
|
28313
|
+
return seeded;
|
|
28314
|
+
}
|
|
28315
|
+
};
|
|
28316
|
+
}
|
|
28317
|
+
};
|
|
28318
|
+
},
|
|
28319
|
+
async raw(table, data) {
|
|
28320
|
+
logger2.warn({ table }, "Using seed.raw() \u2014 prefer typed helpers when available");
|
|
28321
|
+
const hasTable = await db2.schema.hasTable(table);
|
|
28322
|
+
if (!hasTable) {
|
|
28323
|
+
logger2.warn({ table }, "Table not found for raw seed");
|
|
28324
|
+
return 0;
|
|
28325
|
+
}
|
|
28326
|
+
const rows = Array.isArray(data) ? data : [data];
|
|
28327
|
+
await db2(table).insert(rows);
|
|
28328
|
+
return rows.length;
|
|
28329
|
+
}
|
|
28330
|
+
};
|
|
28331
|
+
return {
|
|
28332
|
+
ctx: seedContext,
|
|
28333
|
+
flushPermissions: () => {
|
|
28334
|
+
if (permissionsMap.size > 0 && deps.onPermissionsCollected) {
|
|
28335
|
+
deps.onPermissionsCollected(permissionsMap);
|
|
28336
|
+
}
|
|
28337
|
+
}
|
|
28338
|
+
};
|
|
28339
|
+
}
|
|
28340
|
+
var init_seed_context = __esm({
|
|
28341
|
+
"src/db/seed-context.ts"() {
|
|
28342
|
+
"use strict";
|
|
27622
28343
|
}
|
|
27623
28344
|
});
|
|
27624
28345
|
|
|
@@ -27686,7 +28407,9 @@ var init_instrumentation = __esm({
|
|
|
27686
28407
|
});
|
|
27687
28408
|
|
|
27688
28409
|
// src/core/server.ts
|
|
28410
|
+
import http from "http";
|
|
27689
28411
|
import { entityRoom as entityRoom6 } from "@gzl10/nexus-sdk";
|
|
28412
|
+
import { DEFAULT_LOCALES as DEFAULT_LOCALES2 } from "@gzl10/nexus-sdk";
|
|
27690
28413
|
async function runMigrationsAndSeeds(config3) {
|
|
27691
28414
|
initLoggerService(getLoggerConfig());
|
|
27692
28415
|
setLoggerInstance(getPinoLogger());
|
|
@@ -27739,7 +28462,7 @@ async function runMigrationsAndSeeds(config3) {
|
|
|
27739
28462
|
const sources = buildMigrationSources();
|
|
27740
28463
|
const migrationFiles = await loadAllMigrationFiles(sources);
|
|
27741
28464
|
if (migrationFiles.length > 0) {
|
|
27742
|
-
logger.info({
|
|
28465
|
+
logger.info({ sources: sources.length, files: migrationFiles.length }, "Running migration deploy...");
|
|
27743
28466
|
try {
|
|
27744
28467
|
await runMigrations(void 0, sources);
|
|
27745
28468
|
} catch (err) {
|
|
@@ -27748,7 +28471,7 @@ async function runMigrationsAndSeeds(config3) {
|
|
|
27748
28471
|
throw err;
|
|
27749
28472
|
}
|
|
27750
28473
|
}
|
|
27751
|
-
logger.
|
|
28474
|
+
logger.debug("Checking schema drift...");
|
|
27752
28475
|
const drift = await detectSchemaDrift();
|
|
27753
28476
|
if (drift && (drift.newTables.length > 0 || drift.alteredTables.length > 0)) {
|
|
27754
28477
|
const message = formatDriftMessage(drift);
|
|
@@ -27795,7 +28518,16 @@ ${dirs}`);
|
|
|
27795
28518
|
}
|
|
27796
28519
|
const allDefinitions = modules.flatMap((m2) => m2.definitions ?? []);
|
|
27797
28520
|
await createMemoryTables(allDefinitions);
|
|
27798
|
-
|
|
28521
|
+
try {
|
|
28522
|
+
const { importSeedFiles: importSeedFiles2 } = await Promise.resolve().then(() => (init_seed_commands(), seed_commands_exports));
|
|
28523
|
+
await importSeedFiles2(ctx.db.knex, modules, {
|
|
28524
|
+
info: (msg) => logger.info(msg),
|
|
28525
|
+
debug: (msg) => logger.debug(msg)
|
|
28526
|
+
});
|
|
28527
|
+
} catch (err) {
|
|
28528
|
+
logger.debug({ err }, "Seed file import skipped (no data/seeds/ or error)");
|
|
28529
|
+
}
|
|
28530
|
+
logger.debug("Running seeds...");
|
|
27799
28531
|
for (const mod of modules) {
|
|
27800
28532
|
try {
|
|
27801
28533
|
await runModuleSeed(mod, ctx);
|
|
@@ -27803,14 +28535,41 @@ ${dirs}`);
|
|
|
27803
28535
|
logger.error({ module: mod.name, err }, "Seed failed - continuing with next module");
|
|
27804
28536
|
}
|
|
27805
28537
|
}
|
|
28538
|
+
if (config3?.onSeed) {
|
|
28539
|
+
const { getPlugins: getPlugins2 } = await Promise.resolve().then(() => (init_module_queries(), module_queries_exports));
|
|
28540
|
+
const { createMasterRegistry: createMasterRegistry2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports));
|
|
28541
|
+
const masterRegistry = ctx.services.has("masters") ? ctx.services.get("masters") : createMasterRegistry2();
|
|
28542
|
+
const pluginPrefixes = /* @__PURE__ */ new Map();
|
|
28543
|
+
for (const plugin of getPlugins2()) {
|
|
28544
|
+
pluginPrefixes.set(plugin.code, `${plugin.code}_`);
|
|
28545
|
+
const shortName = plugin.name.replace(/^@[^/]+\/nexus-plugin-/, "");
|
|
28546
|
+
if (shortName !== plugin.code) {
|
|
28547
|
+
pluginPrefixes.set(shortName, `${plugin.code}_`);
|
|
28548
|
+
}
|
|
28549
|
+
}
|
|
28550
|
+
const { createSeedContext: createSeedContext2 } = await Promise.resolve().then(() => (init_seed_context(), seed_context_exports));
|
|
28551
|
+
const { ctx: seedCtx, flushPermissions } = createSeedContext2({
|
|
28552
|
+
knex: ctx.db.knex,
|
|
28553
|
+
generateId: ctx.core.generateId,
|
|
28554
|
+
hashPassword: ctx.core.crypto.hashPassword,
|
|
28555
|
+
masterRegistry,
|
|
28556
|
+
pluginPrefixes,
|
|
28557
|
+
logger: ctx.core.logger,
|
|
28558
|
+
onPermissionsCollected: (perms) => setSeedPermissions(perms)
|
|
28559
|
+
});
|
|
28560
|
+
await config3.onSeed(seedCtx);
|
|
28561
|
+
await masterRegistry.seed(ctx);
|
|
28562
|
+
flushPermissions();
|
|
28563
|
+
}
|
|
27806
28564
|
}
|
|
27807
28565
|
async function start(config3) {
|
|
27808
28566
|
if (server) {
|
|
27809
28567
|
throw new Error("Server already running. Call stop() first.");
|
|
27810
28568
|
}
|
|
27811
28569
|
currentConfig = config3;
|
|
27812
|
-
|
|
27813
|
-
|
|
28570
|
+
setLocales(config3?.locales ?? DEFAULT_LOCALES2);
|
|
28571
|
+
if (env.NODE_ENV === "development" && env.FRPC_SERVER && !env.TRUST_PROXY) {
|
|
28572
|
+
process.env["TRUST_PROXY"] = "true";
|
|
27814
28573
|
}
|
|
27815
28574
|
const resolved = resolveConfig();
|
|
27816
28575
|
if (resolved.port > 0) {
|
|
@@ -27833,13 +28592,17 @@ async function start(config3) {
|
|
|
27833
28592
|
await initTelemetry2();
|
|
27834
28593
|
await runMigrationsAndSeeds(config3);
|
|
27835
28594
|
const effectiveCorsOrigins = buildEffectiveCorsOrigins(env.CORS_ORIGIN, config3?.spas);
|
|
28595
|
+
const httpServer = http.createServer();
|
|
27836
28596
|
const app = await createApp({
|
|
27837
28597
|
beforeRoutes: config3?.beforeRoutes,
|
|
27838
28598
|
afterRoutes: config3?.afterRoutes,
|
|
27839
|
-
spas: config3?.spas
|
|
28599
|
+
spas: config3?.spas,
|
|
28600
|
+
httpServer
|
|
27840
28601
|
});
|
|
28602
|
+
httpServer.on("request", app);
|
|
27841
28603
|
return new Promise((resolve2) => {
|
|
27842
|
-
server =
|
|
28604
|
+
server = httpServer;
|
|
28605
|
+
httpServer.listen(resolved.port, resolved.host, async () => {
|
|
27843
28606
|
const timeoutMs = parseInt(process.env["REQUEST_TIMEOUT_MS"] || "30000", 10);
|
|
27844
28607
|
if (timeoutMs > 0) {
|
|
27845
28608
|
server.setTimeout(timeoutMs);
|
|
@@ -27865,22 +28628,24 @@ async function start(config3) {
|
|
|
27865
28628
|
eventBridge.init();
|
|
27866
28629
|
const addr = server.address();
|
|
27867
28630
|
const actualPort = typeof addr === "object" && addr ? addr.port : resolved.port;
|
|
28631
|
+
let tunnelActive = false;
|
|
27868
28632
|
if (env.NODE_ENV === "development" && env.FRPC_SERVER && env.FRPC_SUBDOMAIN) {
|
|
27869
|
-
startTunnel({
|
|
28633
|
+
tunnelActive = await startTunnel({
|
|
27870
28634
|
server: env.FRPC_SERVER,
|
|
27871
28635
|
serverPort: env.FRPC_SERVER_PORT,
|
|
27872
28636
|
token: env.FRPC_TOKEN,
|
|
27873
28637
|
subdomain: env.FRPC_SUBDOMAIN,
|
|
27874
28638
|
localPort: actualPort
|
|
27875
28639
|
});
|
|
28640
|
+
if (tunnelActive && !env.BACKEND_URL) {
|
|
28641
|
+
process.env["BACKEND_URL"] = getTunnelUrl(env.FRPC_SUBDOMAIN, env.FRPC_SERVER);
|
|
28642
|
+
}
|
|
27876
28643
|
}
|
|
27877
28644
|
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");
|
|
28645
|
+
logger.debug({ libPath: getLibPath(), projectPath: getProjectPath() }, "Paths");
|
|
28646
|
+
const urls = { api: `${baseUrl}/api/v1` };
|
|
28647
|
+
if (resolved.ui.enabled) urls["ui"] = baseUrl;
|
|
28648
|
+
logger.info({ port: actualPort, mode: resolved.nodeEnv, ...urls }, "Server started");
|
|
27884
28649
|
nexusEvents.emitEvent("server.started", { port: actualPort, host: resolved.host });
|
|
27885
28650
|
if (config3?.onReady) {
|
|
27886
28651
|
try {
|
|
@@ -27906,7 +28671,8 @@ async function stop() {
|
|
|
27906
28671
|
await resetSharedAdapters();
|
|
27907
28672
|
resetConfigCache();
|
|
27908
28673
|
clearCustomCaslRules();
|
|
27909
|
-
|
|
28674
|
+
clearSeedPermissions();
|
|
28675
|
+
await resetServeSPA();
|
|
27910
28676
|
return;
|
|
27911
28677
|
}
|
|
27912
28678
|
if (currentConfig?.beforeClose) {
|
|
@@ -27940,7 +28706,8 @@ async function stop() {
|
|
|
27940
28706
|
await resetSharedAdapters();
|
|
27941
28707
|
resetConfigCache();
|
|
27942
28708
|
clearCustomCaslRules();
|
|
27943
|
-
|
|
28709
|
+
clearSeedPermissions();
|
|
28710
|
+
await resetServeSPA();
|
|
27944
28711
|
currentConfig = void 0;
|
|
27945
28712
|
server = null;
|
|
27946
28713
|
nexusEvents.emitEvent("server.stopped");
|
|
@@ -28000,7 +28767,7 @@ var init_server = __esm({
|
|
|
28000
28767
|
init_cors();
|
|
28001
28768
|
init_logger();
|
|
28002
28769
|
init_error_middleware();
|
|
28003
|
-
|
|
28770
|
+
init_port_check();
|
|
28004
28771
|
init_engine();
|
|
28005
28772
|
init_context();
|
|
28006
28773
|
init_db();
|