@naisys/erp 3.0.0-beta.22 → 3.0.0-beta.23
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/client-dist/assets/{index-BJzK1WXg.js → index-DycEn-R_.js} +53 -25
- package/client-dist/assets/{vendor-DlmcA82d.js → vendor-MNFI7PUp.js} +1339 -1
- package/client-dist/index.html +2 -2
- package/dist/erpServer.js +21 -45
- package/dist/userService.js +15 -42
- package/npm-shrinkwrap.json +4537 -0
- package/package.json +7 -7
package/client-dist/index.html
CHANGED
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
<meta name="format-detection" content="telephone=no" />
|
|
34
34
|
|
|
35
35
|
<title>NAISYS ERP</title>
|
|
36
|
-
<script type="module" crossorigin src="/erp/assets/index-
|
|
36
|
+
<script type="module" crossorigin src="/erp/assets/index-DycEn-R_.js"></script>
|
|
37
37
|
<link rel="modulepreload" crossorigin href="/erp/assets/rolldown-runtime-CvHMtSRF.js">
|
|
38
|
-
<link rel="modulepreload" crossorigin href="/erp/assets/vendor-
|
|
38
|
+
<link rel="modulepreload" crossorigin href="/erp/assets/vendor-MNFI7PUp.js">
|
|
39
39
|
<link rel="stylesheet" crossorigin href="/erp/assets/vendor-CLUPjUnv.css">
|
|
40
40
|
<link rel="stylesheet" crossorigin href="/erp/assets/index-CSiMTJfw.css">
|
|
41
41
|
</head>
|
package/dist/erpServer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "dotenv/config";
|
|
2
2
|
import "./schema-registry.js";
|
|
3
|
-
import { cwdWithTilde, ensureDotEnv, expandNaisysFolder, runSetupWizard, } from "@naisys/common-node";
|
|
3
|
+
import { cwdWithTilde, ensureDotEnv, expandNaisysFolder, promptSuperAdminPassword, runSetupWizard, } from "@naisys/common-node";
|
|
4
4
|
expandNaisysFolder();
|
|
5
5
|
// Important to load dotenv before any other imports, to ensure environment variables are available
|
|
6
6
|
import cookie from "@fastify/cookie";
|
|
@@ -9,10 +9,10 @@ import multipart from "@fastify/multipart";
|
|
|
9
9
|
import { fastifyRateLimit as rateLimit } from "@fastify/rate-limit";
|
|
10
10
|
import staticFiles from "@fastify/static";
|
|
11
11
|
import swagger from "@fastify/swagger";
|
|
12
|
-
import { commonErrorHandler, registerLenientJsonParser, registerSecurityHeaders, } from "@naisys/common";
|
|
12
|
+
import { commonErrorHandler, registerLenientJsonParser, registerSecurityHeaders, SUPER_ADMIN_USERNAME, } from "@naisys/common";
|
|
13
13
|
import { createFileLogger } from "@naisys/common-node";
|
|
14
14
|
import { createHubDatabaseClient, deployPrismaMigrations, } from "@naisys/hub-database";
|
|
15
|
-
import { createSupervisorDatabaseClient
|
|
15
|
+
import { createSupervisorDatabaseClient } from "@naisys/supervisor-database";
|
|
16
16
|
import Fastify from "fastify";
|
|
17
17
|
import { jsonSchemaTransform, jsonSchemaTransformObject, serializerCompiler, validatorCompiler, } from "fastify-type-provider-zod";
|
|
18
18
|
import path from "path";
|
|
@@ -23,7 +23,7 @@ import { ERP_DB_VERSION, erpDbPath } from "./dbConfig.js";
|
|
|
23
23
|
import { initErpDb } from "./erpDb.js";
|
|
24
24
|
import { erpRoutes } from "./erpRoutes.js";
|
|
25
25
|
import { isSupervisorAuth } from "./supervisorAuth.js";
|
|
26
|
-
import { ensureLocalSuperAdmin, ensureSupervisorSuperAdmin,
|
|
26
|
+
import { ensureLocalSuperAdmin, ensureSupervisorSuperAdmin, } from "./userService.js";
|
|
27
27
|
export { enableSupervisorAuth } from "./supervisorAuth.js";
|
|
28
28
|
const __filename = fileURLToPath(import.meta.url);
|
|
29
29
|
const __dirname = path.dirname(__filename);
|
|
@@ -31,7 +31,7 @@ const __dirname = path.dirname(__filename);
|
|
|
31
31
|
* Fastify plugin that registers ERP routes and static files.
|
|
32
32
|
* Can be used standalone or registered inside another Fastify app (e.g. supervisor).
|
|
33
33
|
*/
|
|
34
|
-
export const erpPlugin = async (fastify) => {
|
|
34
|
+
export const erpPlugin = async (fastify, opts) => {
|
|
35
35
|
const isProd = process.env.NODE_ENV === "production";
|
|
36
36
|
// Cookie plugin (guard for supervisor embedding)
|
|
37
37
|
if (!fastify.hasDecorator("parseCookie")) {
|
|
@@ -66,7 +66,7 @@ export const erpPlugin = async (fastify) => {
|
|
|
66
66
|
await ensureSupervisorSuperAdmin();
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
|
-
await ensureLocalSuperAdmin();
|
|
69
|
+
await ensureLocalSuperAdmin(opts.superAdminPassword);
|
|
70
70
|
}
|
|
71
71
|
fastify.setErrorHandler(commonErrorHandler);
|
|
72
72
|
registerAuthMiddleware(fastify);
|
|
@@ -125,7 +125,7 @@ export const erpPlugin = async (fastify) => {
|
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
127
|
};
|
|
128
|
-
async function startServer() {
|
|
128
|
+
async function startServer(wizardRan) {
|
|
129
129
|
const isProd = process.env.NODE_ENV === "production";
|
|
130
130
|
const fastify = Fastify({
|
|
131
131
|
pluginTimeout: 60_000,
|
|
@@ -159,7 +159,10 @@ async function startServer() {
|
|
|
159
159
|
fastify.get("/", { schema: { hide: true } }, async (_request, reply) => {
|
|
160
160
|
return reply.redirect("/erp/");
|
|
161
161
|
});
|
|
162
|
-
|
|
162
|
+
const superAdminPassword = wizardRan && !isSupervisorAuth()
|
|
163
|
+
? await promptSuperAdminPassword("ERP Setup")
|
|
164
|
+
: undefined;
|
|
165
|
+
await fastify.register(erpPlugin, { superAdminPassword });
|
|
163
166
|
const port = Number(process.env.SERVER_PORT) || 3302;
|
|
164
167
|
const host = isProd ? "0.0.0.0" : "localhost";
|
|
165
168
|
try {
|
|
@@ -167,6 +170,9 @@ async function startServer() {
|
|
|
167
170
|
console.log(`[ERP] Running on http://${host}:${port}/erp`);
|
|
168
171
|
console.log(`[ERP] API Reference: http://${host}:${port}/erp/api-reference`);
|
|
169
172
|
console.log(`[ERP] Auth mode: ${isSupervisorAuth() ? "supervisor" : "standalone"}`);
|
|
173
|
+
if (!isSupervisorAuth()) {
|
|
174
|
+
console.log(`[ERP] Sign in as '${SUPER_ADMIN_USERNAME}' with the password set during setup. Use --setup to change it.`);
|
|
175
|
+
}
|
|
170
176
|
}
|
|
171
177
|
catch (err) {
|
|
172
178
|
console.error("[ERP] Failed to start:", err);
|
|
@@ -191,43 +197,13 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
|
191
197
|
],
|
|
192
198
|
};
|
|
193
199
|
const erpExampleUrl = new URL("../.env.example", import.meta.url);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const password = passwordIdx !== -1 ? process.argv[passwordIdx + 1] : undefined;
|
|
199
|
-
await initErpDb();
|
|
200
|
-
if (isSupervisorAuth()) {
|
|
201
|
-
void handleResetPassword({
|
|
202
|
-
findLocalUser: async (username) => {
|
|
203
|
-
const prisma = (await import("./erpDb.js")).default;
|
|
204
|
-
const user = await prisma.user.findUnique({ where: { username } });
|
|
205
|
-
return user
|
|
206
|
-
? { id: user.id, username: user.username, uuid: user.uuid }
|
|
207
|
-
: null;
|
|
208
|
-
},
|
|
209
|
-
updateLocalPassword: async (userId, passwordHash) => {
|
|
210
|
-
const prisma = (await import("./erpDb.js")).default;
|
|
211
|
-
await prisma.user.update({
|
|
212
|
-
where: { id: userId },
|
|
213
|
-
data: { passwordHash },
|
|
214
|
-
});
|
|
215
|
-
},
|
|
216
|
-
username,
|
|
217
|
-
password,
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
void resetLocalPassword();
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
if (process.argv.includes("--setup")) {
|
|
226
|
-
await runSetupWizard(path.resolve(".env"), erpExampleUrl, erpWizardConfig);
|
|
227
|
-
expandNaisysFolder();
|
|
228
|
-
}
|
|
229
|
-
await ensureDotEnv(erpExampleUrl, erpWizardConfig);
|
|
230
|
-
void startServer();
|
|
200
|
+
let wizardRan = false;
|
|
201
|
+
if (process.argv.includes("--setup")) {
|
|
202
|
+
wizardRan = await runSetupWizard(path.resolve(".env"), erpExampleUrl, erpWizardConfig);
|
|
203
|
+
expandNaisysFolder();
|
|
231
204
|
}
|
|
205
|
+
wizardRan =
|
|
206
|
+
(await ensureDotEnv(erpExampleUrl, erpWizardConfig)) || wizardRan;
|
|
207
|
+
void startServer(wizardRan);
|
|
232
208
|
}
|
|
233
209
|
//# sourceMappingURL=erpServer.js.map
|
package/dist/userService.js
CHANGED
|
@@ -2,24 +2,30 @@ import { SUPER_ADMIN_USERNAME } from "@naisys/common";
|
|
|
2
2
|
import { ensureSuperAdmin } from "@naisys/supervisor-database";
|
|
3
3
|
import bcrypt from "bcryptjs";
|
|
4
4
|
import { randomBytes, randomUUID } from "crypto";
|
|
5
|
-
import readline from "readline/promises";
|
|
6
5
|
import erpDb from "./erpDb.js";
|
|
7
6
|
const SALT_ROUNDS = 10;
|
|
8
7
|
/**
|
|
9
8
|
* Ensure a superadmin user exists in the local ERP database.
|
|
9
|
+
* If a password is supplied, it is used on create and updates the existing one if present.
|
|
10
10
|
* For standalone mode (no supervisor auth).
|
|
11
11
|
*/
|
|
12
|
-
export async function ensureLocalSuperAdmin() {
|
|
12
|
+
export async function ensureLocalSuperAdmin(password) {
|
|
13
13
|
const existing = await erpDb.user.findUnique({
|
|
14
14
|
where: { username: SUPER_ADMIN_USERNAME },
|
|
15
15
|
});
|
|
16
16
|
if (existing) {
|
|
17
|
-
// Ensure superadmin has erp_admin permission
|
|
18
17
|
await ensureErpAdminPermission(existing.id);
|
|
18
|
+
if (password) {
|
|
19
|
+
const hash = await bcrypt.hash(password, SALT_ROUNDS);
|
|
20
|
+
await erpDb.user.update({
|
|
21
|
+
where: { id: existing.id },
|
|
22
|
+
data: { passwordHash: hash },
|
|
23
|
+
});
|
|
24
|
+
}
|
|
19
25
|
}
|
|
20
26
|
else {
|
|
21
|
-
const
|
|
22
|
-
const hash = await bcrypt.hash(
|
|
27
|
+
const finalPassword = password || randomUUID().slice(0, 8);
|
|
28
|
+
const hash = await bcrypt.hash(finalPassword, SALT_ROUNDS);
|
|
23
29
|
const user = await erpDb.user.create({
|
|
24
30
|
data: {
|
|
25
31
|
uuid: randomUUID(),
|
|
@@ -29,8 +35,10 @@ export async function ensureLocalSuperAdmin() {
|
|
|
29
35
|
},
|
|
30
36
|
});
|
|
31
37
|
await ensureErpAdminPermission(user.id);
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
if (!password) {
|
|
39
|
+
console.log(`\n ${SUPER_ADMIN_USERNAME} user created. Password: ${finalPassword}`);
|
|
40
|
+
console.log(` Change it via the admin UI or with --setup\n`);
|
|
41
|
+
}
|
|
34
42
|
}
|
|
35
43
|
// Warn if agent users exist without supervisor auth
|
|
36
44
|
const agentCount = await erpDb.user.count({ where: { isAgent: true } });
|
|
@@ -66,9 +74,6 @@ export async function ensureSupervisorSuperAdmin() {
|
|
|
66
74
|
if (localSuperAdmin) {
|
|
67
75
|
await ensureErpAdminPermission(localSuperAdmin.id);
|
|
68
76
|
}
|
|
69
|
-
if (result.created) {
|
|
70
|
-
console.log(`[ERP] ${SUPER_ADMIN_USERNAME} user created. Password: ${result.generatedPassword}`);
|
|
71
|
-
}
|
|
72
77
|
}
|
|
73
78
|
/**
|
|
74
79
|
* Ensure a user has the erp_admin permission.
|
|
@@ -83,36 +88,4 @@ export async function ensureErpAdminPermission(userId) {
|
|
|
83
88
|
});
|
|
84
89
|
}
|
|
85
90
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Interactive CLI to reset a local user's password.
|
|
88
|
-
* For standalone mode (no supervisor auth).
|
|
89
|
-
*/
|
|
90
|
-
export async function resetLocalPassword() {
|
|
91
|
-
const rl = readline.createInterface({
|
|
92
|
-
input: process.stdin,
|
|
93
|
-
output: process.stdout,
|
|
94
|
-
});
|
|
95
|
-
try {
|
|
96
|
-
const username = await rl.question("Username: ");
|
|
97
|
-
const user = await erpDb.user.findUnique({ where: { username } });
|
|
98
|
-
if (!user) {
|
|
99
|
-
console.error(`User '${username}' not found.`);
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
102
|
-
const password = await rl.question("New password: ");
|
|
103
|
-
if (password.length < 6) {
|
|
104
|
-
console.error("Password must be at least 6 characters.");
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
const hash = await bcrypt.hash(password, SALT_ROUNDS);
|
|
108
|
-
await erpDb.user.update({
|
|
109
|
-
where: { id: user.id },
|
|
110
|
-
data: { passwordHash: hash },
|
|
111
|
-
});
|
|
112
|
-
console.log(`Password reset for '${username}'.`);
|
|
113
|
-
}
|
|
114
|
-
finally {
|
|
115
|
-
rl.close();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
91
|
//# sourceMappingURL=userService.js.map
|