@heyhru/app-dms-server 0.1.9 → 0.3.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/index.js +43 -138
- package/package.json +9 -8
package/dist/index.js
CHANGED
|
@@ -18,6 +18,10 @@ var config = {
|
|
|
18
18
|
jwtExpiresIn: "24h",
|
|
19
19
|
encryptionKey: process.env.ENCRYPTION_KEY || "dev-encryption-key-32-bytes-long!",
|
|
20
20
|
dbUrl: process.env.DATABASE_URL,
|
|
21
|
+
dbSsl: process.env.DATABASE_SSL === "true",
|
|
22
|
+
dbPoolMin: Number(process.env.DATABASE_POOL_MIN) || 2,
|
|
23
|
+
dbPoolMax: Number(process.env.DATABASE_POOL_MAX) || 10,
|
|
24
|
+
dbIdleTimeoutMs: Number(process.env.DATABASE_IDLE_TIMEOUT) || 1e4,
|
|
21
25
|
nodeEnv: process.env.NODE_ENV || "development"
|
|
22
26
|
};
|
|
23
27
|
|
|
@@ -83,119 +87,14 @@ async function authHook(req, reply) {
|
|
|
83
87
|
|
|
84
88
|
// src/auth/auth.service.ts
|
|
85
89
|
import { signToken } from "@heyhru/server-plugin-jwt";
|
|
86
|
-
import { hashPassword
|
|
87
|
-
|
|
88
|
-
// src/users/users.service.ts
|
|
89
|
-
import { hashPassword } from "@heyhru/server-util-crypto";
|
|
90
|
-
|
|
91
|
-
// src/users/users.model.ts
|
|
92
|
-
import { getPgDb } from "@heyhru/server-plugin-pg";
|
|
93
|
-
|
|
94
|
-
// src/users/users.sql.ts
|
|
95
|
-
var LIST = `
|
|
96
|
-
SELECT id, username, email, role, created_at
|
|
97
|
-
FROM users
|
|
98
|
-
ORDER BY created_at DESC`;
|
|
99
|
-
var FIND_BY_ID = `
|
|
100
|
-
SELECT id, username, email, role, created_at
|
|
101
|
-
FROM users
|
|
102
|
-
WHERE id = ?`;
|
|
103
|
-
var FIND_BY_USERNAME = `
|
|
104
|
-
SELECT *
|
|
105
|
-
FROM users
|
|
106
|
-
WHERE username = ?`;
|
|
107
|
-
var CREATE = `
|
|
108
|
-
INSERT INTO users (username, email, password_hash, role)
|
|
109
|
-
VALUES (?, ?, ?, ?)
|
|
110
|
-
RETURNING id, username, email, role, created_at`;
|
|
111
|
-
var UPDATE_PASSWORD = () => `
|
|
112
|
-
UPDATE users
|
|
113
|
-
SET password_hash = ?, updated_at = NOW()
|
|
114
|
-
WHERE id = ?`;
|
|
115
|
-
var DELETE = `
|
|
116
|
-
DELETE FROM users
|
|
117
|
-
WHERE id = ?`;
|
|
118
|
-
|
|
119
|
-
// src/users/users.model.ts
|
|
120
|
-
function listUsers() {
|
|
121
|
-
return getPgDb().query(LIST);
|
|
122
|
-
}
|
|
123
|
-
function getUserById(id) {
|
|
124
|
-
return getPgDb().queryOne(FIND_BY_ID, [id]);
|
|
125
|
-
}
|
|
126
|
-
function getUserByUsername(username) {
|
|
127
|
-
return getPgDb().queryOne(FIND_BY_USERNAME, [username]);
|
|
128
|
-
}
|
|
129
|
-
function createUserRow(username, email, hash, role) {
|
|
130
|
-
return getPgDb().queryOne(CREATE, [username, email, hash, role]);
|
|
131
|
-
}
|
|
132
|
-
async function updateUserRow(id, data) {
|
|
133
|
-
const fields = [];
|
|
134
|
-
const values = [];
|
|
135
|
-
if (data.email) {
|
|
136
|
-
fields.push("email = ?");
|
|
137
|
-
values.push(data.email);
|
|
138
|
-
}
|
|
139
|
-
if (data.role) {
|
|
140
|
-
fields.push("role = ?");
|
|
141
|
-
values.push(data.role);
|
|
142
|
-
}
|
|
143
|
-
if (!fields.length) return getUserById(id);
|
|
144
|
-
fields.push("updated_at = NOW()");
|
|
145
|
-
values.push(id);
|
|
146
|
-
return getPgDb().queryOne(
|
|
147
|
-
`UPDATE users SET ${fields.join(", ")} WHERE id = ? RETURNING id, username, email, role, created_at`,
|
|
148
|
-
values
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
function deleteUser(id) {
|
|
152
|
-
return getPgDb().run(DELETE, [id]);
|
|
153
|
-
}
|
|
154
|
-
function updatePasswordHash(id, hash) {
|
|
155
|
-
return getPgDb().run(UPDATE_PASSWORD(), [hash, id]);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// src/users/users.service.ts
|
|
159
|
-
function getUserByUsername2(username) {
|
|
160
|
-
return getUserByUsername(username);
|
|
161
|
-
}
|
|
162
|
-
function updateUserPassword(id, hash) {
|
|
163
|
-
return updatePasswordHash(id, hash);
|
|
164
|
-
}
|
|
165
|
-
async function userList(_req, reply) {
|
|
166
|
-
return reply.send(await listUsers());
|
|
167
|
-
}
|
|
168
|
-
async function userGet(req, reply) {
|
|
169
|
-
const { id } = req.body ?? {};
|
|
170
|
-
const user = await getUserById(id);
|
|
171
|
-
if (!user) return reply.code(404).send({ error: "\u672A\u627E\u5230" });
|
|
172
|
-
return reply.send(user);
|
|
173
|
-
}
|
|
174
|
-
async function userCreate(req, reply) {
|
|
175
|
-
const body = req.body ?? {};
|
|
176
|
-
const hash = await hashPassword(body.password);
|
|
177
|
-
const user = await createUserRow(body.username, body.email, hash, body.role);
|
|
178
|
-
return reply.code(201).send(user);
|
|
179
|
-
}
|
|
180
|
-
async function userUpdate(req, reply) {
|
|
181
|
-
const { id, ...rest } = req.body ?? {};
|
|
182
|
-
const user = await updateUserRow(id, rest);
|
|
183
|
-
if (!user) return reply.code(404).send({ error: "\u672A\u627E\u5230" });
|
|
184
|
-
return reply.send(user);
|
|
185
|
-
}
|
|
186
|
-
async function userDelete(req, reply) {
|
|
187
|
-
const { id } = req.body ?? {};
|
|
188
|
-
await deleteUser(id);
|
|
189
|
-
return reply.code(204).send();
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// src/auth/auth.service.ts
|
|
90
|
+
import { hashPassword, verifyPassword } from "@heyhru/server-util-crypto";
|
|
91
|
+
import { getUserByUsername, updateUserPassword } from "@heyhru/business-dms-user";
|
|
193
92
|
async function authLogin(req, reply) {
|
|
194
93
|
const { username, password } = req.body ?? {};
|
|
195
94
|
if (!username || !password) {
|
|
196
95
|
return reply.code(400).send({ error: "\u7528\u6237\u540D\u548C\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A" });
|
|
197
96
|
}
|
|
198
|
-
const user = await
|
|
97
|
+
const user = await getUserByUsername(username);
|
|
199
98
|
if (!user || !await verifyPassword(password, user.password_hash)) {
|
|
200
99
|
req.log.warn("Login failed for user: %s", username);
|
|
201
100
|
return reply.code(401).send({ error: "\u7528\u6237\u540D\u6216\u5BC6\u7801\u9519\u8BEF" });
|
|
@@ -217,11 +116,11 @@ async function authChangePassword(req, reply) {
|
|
|
217
116
|
if (!currentPassword || !newPassword) {
|
|
218
117
|
return reply.code(400).send({ error: "\u5F53\u524D\u5BC6\u7801\u548C\u65B0\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A" });
|
|
219
118
|
}
|
|
220
|
-
const user = await
|
|
119
|
+
const user = await getUserByUsername(req.user.username);
|
|
221
120
|
if (!user || !await verifyPassword(currentPassword, user.password_hash)) {
|
|
222
121
|
return reply.code(400).send({ error: "\u5F53\u524D\u5BC6\u7801\u4E0D\u6B63\u786E" });
|
|
223
122
|
}
|
|
224
|
-
const hash = await
|
|
123
|
+
const hash = await hashPassword(newPassword);
|
|
225
124
|
await updateUserPassword(user.id, hash);
|
|
226
125
|
req.log.info("Password changed for user: %s", req.user.username);
|
|
227
126
|
return reply.send({ ok: true });
|
|
@@ -236,6 +135,7 @@ function authController(app) {
|
|
|
236
135
|
}
|
|
237
136
|
|
|
238
137
|
// src/users/users.controller.ts
|
|
138
|
+
import { userList, userGet, userCreate, userUpdate, userDelete } from "@heyhru/business-dms-user";
|
|
239
139
|
function userController(app) {
|
|
240
140
|
app.post("/users/list", userList);
|
|
241
141
|
app.post("/users/get", userGet);
|
|
@@ -250,14 +150,14 @@ import { createPool as createMysqlPool } from "@heyhru/server-plugin-mysql";
|
|
|
250
150
|
import { createPool as createPgPool } from "@heyhru/server-plugin-pg";
|
|
251
151
|
|
|
252
152
|
// src/datasources/datasources.model.ts
|
|
253
|
-
import { getPgDb
|
|
153
|
+
import { getPgDb } from "@heyhru/server-plugin-pg";
|
|
254
154
|
|
|
255
155
|
// src/datasources/datasources.sql.ts
|
|
256
|
-
var
|
|
156
|
+
var LIST = `
|
|
257
157
|
SELECT id, name, type, host, port, database, username, ssl, pool_min, pool_max, created_by, created_at
|
|
258
158
|
FROM data_sources
|
|
259
159
|
ORDER BY created_at DESC`;
|
|
260
|
-
var
|
|
160
|
+
var FIND_BY_ID = `
|
|
261
161
|
SELECT id, name, type, host, port, database, username, ssl, pool_min, pool_max, created_by, created_at
|
|
262
162
|
FROM data_sources
|
|
263
163
|
WHERE id = ?`;
|
|
@@ -265,27 +165,27 @@ var FIND_WITH_PASSWORD = `
|
|
|
265
165
|
SELECT *
|
|
266
166
|
FROM data_sources
|
|
267
167
|
WHERE id = ?`;
|
|
268
|
-
var
|
|
168
|
+
var CREATE = `
|
|
269
169
|
INSERT INTO data_sources (name, type, host, port, database, username, password_encrypted, ssl, pool_min, pool_max, created_by)
|
|
270
170
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
271
171
|
RETURNING id, name, type, host, port, database, username, ssl, pool_min, pool_max, created_by, created_at`;
|
|
272
172
|
var UPDATE_FIELDS = ["name", "type", "host", "port", "database", "username", "ssl", "pool_min", "pool_max"];
|
|
273
|
-
var
|
|
173
|
+
var DELETE = `
|
|
274
174
|
DELETE FROM data_sources
|
|
275
175
|
WHERE id = ?`;
|
|
276
176
|
|
|
277
177
|
// src/datasources/datasources.model.ts
|
|
278
178
|
function listDataSources() {
|
|
279
|
-
return
|
|
179
|
+
return getPgDb().query(LIST);
|
|
280
180
|
}
|
|
281
181
|
function getDataSourceById(id) {
|
|
282
|
-
return
|
|
182
|
+
return getPgDb().queryOne(FIND_BY_ID, [id]);
|
|
283
183
|
}
|
|
284
184
|
function getDataSourceRow(id) {
|
|
285
|
-
return
|
|
185
|
+
return getPgDb().queryOne(FIND_WITH_PASSWORD, [id]);
|
|
286
186
|
}
|
|
287
187
|
function insertDataSource(data, encryptedPassword, createdBy) {
|
|
288
|
-
return
|
|
188
|
+
return getPgDb().queryOne(CREATE, [
|
|
289
189
|
data.name,
|
|
290
190
|
data.type,
|
|
291
191
|
data.host,
|
|
@@ -314,13 +214,13 @@ async function updateDataSource(id, data, encryptedPassword) {
|
|
|
314
214
|
}
|
|
315
215
|
if (!fields.length) return getDataSourceById(id);
|
|
316
216
|
values.push(id);
|
|
317
|
-
return
|
|
217
|
+
return getPgDb().queryOne(
|
|
318
218
|
`UPDATE data_sources SET ${fields.join(", ")} WHERE id = ? RETURNING id, name, type, host, port, database, username, ssl, pool_min, pool_max, created_by, created_at`,
|
|
319
219
|
values
|
|
320
220
|
);
|
|
321
221
|
}
|
|
322
222
|
function removeDataSource(id) {
|
|
323
|
-
return
|
|
223
|
+
return getPgDb().run(DELETE, [id]);
|
|
324
224
|
}
|
|
325
225
|
|
|
326
226
|
// src/datasources/datasources.service.ts
|
|
@@ -430,13 +330,13 @@ function datasourceController(app) {
|
|
|
430
330
|
import NodeSqlParser from "node-sql-parser";
|
|
431
331
|
|
|
432
332
|
// src/audit/audit.model.ts
|
|
433
|
-
import { getPgDb as
|
|
333
|
+
import { getPgDb as getPgDb2 } from "@heyhru/server-plugin-pg";
|
|
434
334
|
|
|
435
335
|
// src/audit/audit.sql.ts
|
|
436
336
|
var INSERT = `
|
|
437
337
|
INSERT INTO audit_logs (user_id, data_source_id, action, sql_text, result_summary, ip_address)
|
|
438
338
|
VALUES (?, ?, ?, ?, ?, ?)`;
|
|
439
|
-
var
|
|
339
|
+
var LIST2 = `
|
|
440
340
|
SELECT al.*, u.username
|
|
441
341
|
FROM audit_logs al
|
|
442
342
|
LEFT JOIN users u ON al.user_id = u.id
|
|
@@ -444,7 +344,7 @@ WHERE 1=1`;
|
|
|
444
344
|
|
|
445
345
|
// src/audit/audit.model.ts
|
|
446
346
|
function insertAuditLog(entry) {
|
|
447
|
-
return
|
|
347
|
+
return getPgDb2().run(INSERT, [
|
|
448
348
|
entry.userId,
|
|
449
349
|
entry.dataSourceId ?? null,
|
|
450
350
|
entry.action,
|
|
@@ -454,7 +354,7 @@ function insertAuditLog(entry) {
|
|
|
454
354
|
]);
|
|
455
355
|
}
|
|
456
356
|
async function queryAuditLogs(filters) {
|
|
457
|
-
let query =
|
|
357
|
+
let query = LIST2;
|
|
458
358
|
const params = [];
|
|
459
359
|
if (filters?.userId) {
|
|
460
360
|
query += " AND al.user_id = ?";
|
|
@@ -466,7 +366,7 @@ async function queryAuditLogs(filters) {
|
|
|
466
366
|
}
|
|
467
367
|
query += " ORDER BY al.created_at DESC LIMIT ? OFFSET ?";
|
|
468
368
|
params.push(filters?.limit ?? 50, filters?.offset ?? 0);
|
|
469
|
-
return
|
|
369
|
+
return getPgDb2().query(query, params);
|
|
470
370
|
}
|
|
471
371
|
|
|
472
372
|
// src/audit/audit.service.ts
|
|
@@ -590,14 +490,14 @@ function sqlController(app) {
|
|
|
590
490
|
}
|
|
591
491
|
|
|
592
492
|
// src/approvals/approvals.model.ts
|
|
593
|
-
import { getPgDb as
|
|
493
|
+
import { getPgDb as getPgDb3 } from "@heyhru/server-plugin-pg";
|
|
594
494
|
|
|
595
495
|
// src/approvals/approvals.sql.ts
|
|
596
|
-
var
|
|
496
|
+
var FIND_BY_ID2 = `
|
|
597
497
|
SELECT *
|
|
598
498
|
FROM approvals
|
|
599
499
|
WHERE id = ?`;
|
|
600
|
-
var
|
|
500
|
+
var CREATE2 = `
|
|
601
501
|
INSERT INTO approvals (data_source_id, sql_text, submitted_by)
|
|
602
502
|
VALUES (?, ?, ?)
|
|
603
503
|
RETURNING *`;
|
|
@@ -628,22 +528,22 @@ async function listApprovals(filters) {
|
|
|
628
528
|
params.push(filters.submittedBy);
|
|
629
529
|
}
|
|
630
530
|
query += " ORDER BY created_at DESC";
|
|
631
|
-
return
|
|
531
|
+
return getPgDb3().query(query, params);
|
|
632
532
|
}
|
|
633
533
|
function getApprovalById(id) {
|
|
634
|
-
return
|
|
534
|
+
return getPgDb3().queryOne(FIND_BY_ID2, [id]);
|
|
635
535
|
}
|
|
636
536
|
function insertApproval(dataSourceId, sqlText, submittedBy) {
|
|
637
|
-
return
|
|
537
|
+
return getPgDb3().queryOne(CREATE2, [dataSourceId, sqlText, submittedBy]);
|
|
638
538
|
}
|
|
639
539
|
function updateReview(id, status, reviewedBy, rejectReason) {
|
|
640
|
-
return
|
|
540
|
+
return getPgDb3().queryOne(UPDATE_REVIEW(), [status, reviewedBy, rejectReason, id]);
|
|
641
541
|
}
|
|
642
542
|
function setExecuting(id) {
|
|
643
|
-
return
|
|
543
|
+
return getPgDb3().run(UPDATE_EXECUTING(), [id]);
|
|
644
544
|
}
|
|
645
545
|
function setExecuteResult(id, status, result) {
|
|
646
|
-
return
|
|
546
|
+
return getPgDb3().run(UPDATE_RESULT(), [status, result, id]);
|
|
647
547
|
}
|
|
648
548
|
|
|
649
549
|
// src/approvals/approvals.service.ts
|
|
@@ -781,7 +681,7 @@ async function buildApp() {
|
|
|
781
681
|
}
|
|
782
682
|
|
|
783
683
|
// src/migrate/runner.ts
|
|
784
|
-
import { getPgDb as
|
|
684
|
+
import { getPgDb as getPgDb4 } from "@heyhru/server-plugin-pg";
|
|
785
685
|
|
|
786
686
|
// src/migrate/migrations.ts
|
|
787
687
|
var migrations = [
|
|
@@ -873,7 +773,7 @@ CREATE INDEX IF NOT EXISTS idx_approvals_submitted_by ON approvals(submitted_by)
|
|
|
873
773
|
|
|
874
774
|
// src/migrate/runner.ts
|
|
875
775
|
async function runMigrations() {
|
|
876
|
-
const db =
|
|
776
|
+
const db = getPgDb4();
|
|
877
777
|
await db.exec(`CREATE TABLE IF NOT EXISTS _migrations (
|
|
878
778
|
name TEXT PRIMARY KEY,
|
|
879
779
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
@@ -890,7 +790,12 @@ async function runMigrations() {
|
|
|
890
790
|
|
|
891
791
|
// src/index.ts
|
|
892
792
|
async function main() {
|
|
893
|
-
await createPgDb(config.dbUrl
|
|
793
|
+
await createPgDb(config.dbUrl, {
|
|
794
|
+
ssl: config.dbSsl,
|
|
795
|
+
poolMin: config.dbPoolMin,
|
|
796
|
+
poolMax: config.dbPoolMax,
|
|
797
|
+
idleTimeoutMs: config.dbIdleTimeoutMs
|
|
798
|
+
});
|
|
894
799
|
await runMigrations();
|
|
895
800
|
const app = await buildApp();
|
|
896
801
|
await app.listen({ port: config.port, host: "0.0.0.0" });
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.3.0",
|
|
7
7
|
"description": "DMS backend API server built on Fastify",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "./dist/index.mjs",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"dev": "tsx watch src/index.ts",
|
|
14
|
+
"dev": "tsx watch --env-file=.env src/index.ts",
|
|
15
15
|
"build": "tsup",
|
|
16
16
|
"lint": "eslint src",
|
|
17
17
|
"test": "vitest run --passWithNoTests",
|
|
@@ -20,11 +20,12 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@fastify/cookie": "^11.0.2",
|
|
22
22
|
"@fastify/cors": "^11.0.0",
|
|
23
|
-
"@heyhru/
|
|
24
|
-
"@heyhru/
|
|
25
|
-
"@heyhru/server-plugin-
|
|
26
|
-
"@heyhru/server-plugin-
|
|
27
|
-
"@heyhru/server-
|
|
23
|
+
"@heyhru/business-dms-user": "0.3.0",
|
|
24
|
+
"@heyhru/common-util-logger": "0.3.0",
|
|
25
|
+
"@heyhru/server-plugin-jwt": "0.3.0",
|
|
26
|
+
"@heyhru/server-plugin-mysql": "0.3.0",
|
|
27
|
+
"@heyhru/server-plugin-pg": "0.3.0",
|
|
28
|
+
"@heyhru/server-util-crypto": "0.3.0",
|
|
28
29
|
"fastify": "^5.8.4",
|
|
29
30
|
"node-sql-parser": "^5.4.0"
|
|
30
31
|
},
|
|
@@ -36,5 +37,5 @@
|
|
|
36
37
|
"typescript": "^6.0.2",
|
|
37
38
|
"vitest": "^4.1.2"
|
|
38
39
|
},
|
|
39
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "577a6d89e51ac836bf14d9e6d12eeb61d1b17939"
|
|
40
41
|
}
|