aios-management-web 0.1.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.
Files changed (91) hide show
  1. package/.env.json +21 -0
  2. package/README.md +257 -0
  3. package/data/management-console.db +0 -0
  4. package/data/management-console.db-shm +0 -0
  5. package/data/management-console.db-wal +0 -0
  6. package/dist/assets/index-CV_wjCAG.js +464 -0
  7. package/dist/assets/index-DfMPB0eV.css +1 -0
  8. package/dist/index.html +13 -0
  9. package/docs/spec.md +199 -0
  10. package/index.html +12 -0
  11. package/package.json +37 -0
  12. package/scripts/reset-kernel.js +59 -0
  13. package/scripts/reset-password.js +22 -0
  14. package/server/fakes.js +57 -0
  15. package/server/index.js +21 -0
  16. package/server/src/api/middleware/auth.js +29 -0
  17. package/server/src/api/middleware/internal.js +44 -0
  18. package/server/src/api/routes/index.js +677 -0
  19. package/server/src/app.js +90 -0
  20. package/server/src/background/index.js +106 -0
  21. package/server/src/background/protocol.js +15 -0
  22. package/server/src/config/env.js +90 -0
  23. package/server/src/db/index.js +501 -0
  24. package/server/src/infra/mqtt/management-rpc-client.js +213 -0
  25. package/server/src/infra/providers/hzg-provider-client.js +39 -0
  26. package/server/src/infra/s3/object-storage.js +97 -0
  27. package/server/src/services/agent-quota.js +54 -0
  28. package/server/src/services/agent-service.js +696 -0
  29. package/server/src/services/agent-status-sync-service.js +132 -0
  30. package/server/src/services/audit-log-service.js +39 -0
  31. package/server/src/services/auth-service.js +153 -0
  32. package/server/src/services/catalog-sync-service.js +712 -0
  33. package/server/src/services/external-service.js +308 -0
  34. package/server/src/services/kernel-reset-service.js +86 -0
  35. package/server/src/services/portal-service.js +555 -0
  36. package/server/src/services/system-service.js +580 -0
  37. package/server/src/services/topic-ping-service.js +282 -0
  38. package/server/src/utils/errors.js +36 -0
  39. package/server/src/utils/security.js +22 -0
  40. package/server/test/agent-service-alignment.test.js +316 -0
  41. package/server/test/agent-service-create.test.js +662 -0
  42. package/server/test/agent-status-sync-service.test.js +167 -0
  43. package/server/test/agent-update-audit.test.js +63 -0
  44. package/server/test/auth-middleware.test.js +71 -0
  45. package/server/test/background-services.test.js +160 -0
  46. package/server/test/catalog-sync-service.test.js +920 -0
  47. package/server/test/db-reset-migration.test.js +123 -0
  48. package/server/test/env-config.test.js +68 -0
  49. package/server/test/external-service.test.js +380 -0
  50. package/server/test/hzg-provider-client.test.js +50 -0
  51. package/server/test/internal-auth-middleware.test.js +66 -0
  52. package/server/test/kernel-reset-service.test.js +112 -0
  53. package/server/test/management-rpc-client.test.js +105 -0
  54. package/server/test/portal-service-access-tokens.test.js +121 -0
  55. package/server/test/portal-service-alignment.test.js +318 -0
  56. package/server/test/portal-service-management-logs.test.js +114 -0
  57. package/server/test/reset-kernel-cli.test.js +23 -0
  58. package/server/test/service-api-auth-middleware.test.js +59 -0
  59. package/server/test/system-service-alignment.test.js +265 -0
  60. package/server/test/topic-ping-service.test.js +182 -0
  61. package/server/test/usage-refresh-audit-route.test.js +82 -0
  62. package/src/App.jsx +1 -0
  63. package/src/api.js +1 -0
  64. package/src/app/App.jsx +346 -0
  65. package/src/app/api-client.js +112 -0
  66. package/src/components/AppShell.jsx +117 -0
  67. package/src/components/CardTitleWithReload.jsx +20 -0
  68. package/src/components/DeleteActionButton.jsx +31 -0
  69. package/src/main.jsx +14 -0
  70. package/src/pages/AgentsPage.jsx +647 -0
  71. package/src/pages/AiosUsersPage.jsx +151 -0
  72. package/src/pages/DashboardPage.jsx +72 -0
  73. package/src/pages/LoginPage.jsx +41 -0
  74. package/src/pages/SettingsPage.jsx +431 -0
  75. package/src/pages/SkillsPage.jsx +175 -0
  76. package/src/pages/SystemLogsPage.jsx +349 -0
  77. package/src/pages/SystemsPage.jsx +498 -0
  78. package/src/pages/TemplatesPage.jsx +207 -0
  79. package/src/pages/UserManagementPage.jsx +25 -0
  80. package/src/pages/UsersPage.jsx +192 -0
  81. package/src/pages/system-logs/SystemLogsTabs.jsx +362 -0
  82. package/src/styles.css +222 -0
  83. package/src/utils/format.js +63 -0
  84. package/test/.reports/fast-2026-05-25T08-32-39-420Z.json +299 -0
  85. package/test/integration/common.js +208 -0
  86. package/test/integration/fast.js +135 -0
  87. package/test/integration/full.js +306 -0
  88. package/test/run-browser-e2e.js +212 -0
  89. package/test/run-jasmine.js +21 -0
  90. package/test/setup.js +1 -0
  91. package/vite.config.js +12 -0
@@ -0,0 +1,677 @@
1
+ import express from "express";
2
+ import multer from "multer";
3
+
4
+ import { createAuthMiddleware } from "../middleware/auth.js";
5
+ import { createInternalAuthMiddleware, createServiceApiAuthMiddleware } from "../middleware/internal.js";
6
+ import { badRequest } from "../../utils/errors.js";
7
+
8
+ const upload = multer({
9
+ storage: multer.memoryStorage(),
10
+ limits: {
11
+ fileSize: 50 * 1024 * 1024
12
+ }
13
+ });
14
+
15
+ function asyncRoute(handler) {
16
+ return (req, res, next) => Promise.resolve(handler(req, res, next)).catch(next);
17
+ }
18
+
19
+ function writeAuditLog(services, req, action, detail) {
20
+ services.auditLogService.write({
21
+ userId: req.currentUser?.id,
22
+ username: req.currentUser?.username || "",
23
+ action,
24
+ detail
25
+ });
26
+ }
27
+
28
+ function formatAuditTarget({ id, name, fallback = "-" }) {
29
+ const normalizedId = id === undefined || id === null || String(id).trim() === ""
30
+ ? fallback
31
+ : String(id).trim();
32
+ const normalizedName = name === undefined || name === null || String(name).trim() === ""
33
+ ? fallback
34
+ : String(name).trim();
35
+
36
+ return `ID=${normalizedId},名称=${normalizedName}`;
37
+ }
38
+
39
+ function hasOwnValue(payload, key) {
40
+ return Boolean(payload) && Object.prototype.hasOwnProperty.call(payload, key);
41
+ }
42
+
43
+ function normalizeAuditUsernames(values) {
44
+ if (!Array.isArray(values)) {
45
+ return [];
46
+ }
47
+
48
+ return [...new Set(values.map((item) => String(item || "").trim()).filter(Boolean))].sort();
49
+ }
50
+
51
+ function resolveAuditPermissionUsernames(payload) {
52
+ return normalizeAuditUsernames(
53
+ hasOwnValue(payload, "permission_usernames")
54
+ ? payload.permission_usernames
55
+ : payload.users
56
+ );
57
+ }
58
+
59
+ function formatUsernameList(usernames) {
60
+ return usernames.length > 0 ? usernames.join("、") : "无";
61
+ }
62
+
63
+ export function buildAgentUpdateAudit(current, updated, payload) {
64
+ const target = formatAuditTarget({
65
+ id: updated.id,
66
+ name: updated.slug
67
+ });
68
+
69
+ if (!hasOwnValue(payload, "permission_usernames") && !hasOwnValue(payload, "users")) {
70
+ return {
71
+ action: "编辑数字员工",
72
+ detail: `编辑数字员工:${target}`
73
+ };
74
+ }
75
+
76
+ const previousUsernames = normalizeAuditUsernames(
77
+ (current?.permissions || []).map((item) => item.username)
78
+ );
79
+ const nextUsernames = resolveAuditPermissionUsernames(payload);
80
+ const previousSet = new Set(previousUsernames);
81
+ const nextSet = new Set(nextUsernames);
82
+ const addedUsernames = nextUsernames.filter((username) => !previousSet.has(username));
83
+ const removedUsernames = previousUsernames.filter((username) => !nextSet.has(username));
84
+ const currentComparable = {
85
+ agent_name: current?.agent_name || "",
86
+ status: current?.status || "",
87
+ daily_limit: Number(current?.daily_limit ?? -1)
88
+ };
89
+ const updatedComparable = {
90
+ agent_name: updated?.agent_name || "",
91
+ status: updated?.status || "",
92
+ daily_limit: Number(updated?.daily_limit ?? -1)
93
+ };
94
+ const agentChanged = JSON.stringify(currentComparable) !== JSON.stringify(updatedComparable);
95
+ const assignmentsChanged = addedUsernames.length > 0 || removedUsernames.length > 0;
96
+
97
+ if (!agentChanged && assignmentsChanged) {
98
+ return {
99
+ action: "编辑数字员工人员分配",
100
+ detail: `编辑数字员工人员分配:${target};添加:${formatUsernameList(addedUsernames)};移除:${formatUsernameList(removedUsernames)}`
101
+ };
102
+ }
103
+
104
+ if (!agentChanged && !assignmentsChanged) {
105
+ return {
106
+ action: "编辑数字员工人员分配",
107
+ detail: `编辑数字员工人员分配:${target};添加:无;移除:无`
108
+ };
109
+ }
110
+
111
+ return {
112
+ action: "编辑数字员工",
113
+ detail: `编辑数字员工:${target};人员分配添加:${formatUsernameList(addedUsernames)};人员分配移除:${formatUsernameList(removedUsernames)}`
114
+ };
115
+ }
116
+
117
+ export function createRoutes(services) {
118
+ const router = express.Router();
119
+ const requireManagementAuth = createAuthMiddleware(services.authService);
120
+ const requireServiceApiAuth = createServiceApiAuthMiddleware(services.portalService);
121
+ const requireInternal = createInternalAuthMiddleware(services.portalService);
122
+
123
+ router.post("/auth/login", asyncRoute(async (req, res) => {
124
+ const result = services.authService.login(req.body);
125
+ services.auditLogService.write({
126
+ userId: result.user?.id,
127
+ username: result.user?.username || req.body?.username || "",
128
+ action: "登录",
129
+ detail: `用户 ${result.user?.username || req.body?.username || ""} 登录系统`
130
+ });
131
+ res.json(result);
132
+ }));
133
+
134
+ router.get("/internal/app-invoke/system", requireInternal, asyncRoute(async (req, res) => {
135
+ const provider = String(req.query.provider || "");
136
+ const applicationName = String(req.query.applicationName || "");
137
+ if (!provider || !applicationName) {
138
+ throw badRequest("缺少 provider 或 applicationName 参数");
139
+ }
140
+
141
+ const system = services.systemService.findSystemForInvocation(provider, applicationName);
142
+ if (!system) {
143
+ res.status(404).json({
144
+ code: "not_found",
145
+ message: `未找到业务系统:${provider}/${applicationName}`
146
+ });
147
+ return;
148
+ }
149
+
150
+ res.json(system);
151
+ }));
152
+
153
+ router.get("/internal/app-invoke/base-url", requireInternal, asyncRoute(async (req, res) => {
154
+ const applicationName = String(req.query.applicationName || "");
155
+ if (!applicationName) {
156
+ throw badRequest("缺少 applicationName 参数");
157
+ }
158
+
159
+ res.json(services.systemService.getInvocationBaseUrlByApplicationName(applicationName));
160
+ }));
161
+
162
+ router.post("/internal/app-invoke/logs", requireInternal, asyncRoute(async (req, res) => {
163
+ await services.systemService.recordInvocation(req.body);
164
+ res.status(201).json({ ok: true });
165
+ }));
166
+
167
+ router.get("/external/cookie", requireServiceApiAuth, asyncRoute(async (req, res) => {
168
+ const result = await services.externalService.getCookie(
169
+ req.query.sessionId,
170
+ req.query.provider,
171
+ req.query.applicationName
172
+ );
173
+ res.json(result);
174
+ }));
175
+
176
+ router.post("/external/invoke/logging", requireServiceApiAuth, asyncRoute(async (req, res) => {
177
+ await services.externalService.recordInvocation(req.body);
178
+ res.status(204).end();
179
+ }));
180
+
181
+ router.get("/external/agents", requireServiceApiAuth, asyncRoute(async (req, res) => {
182
+ res.json({
183
+ items: await services.externalService.listAgentsForUser(req.query.userName)
184
+ });
185
+ }));
186
+
187
+ router.get("/external/session", requireServiceApiAuth, asyncRoute(async (req, res) => {
188
+ const result = await services.externalService.getOrCreateSession({
189
+ userName: req.query.userName,
190
+ agentId: req.query.agentId,
191
+ cookie: req.query.cookie
192
+ });
193
+ res.json(result);
194
+ }));
195
+
196
+ router.get("/external/context", requireServiceApiAuth, asyncRoute(async (req, res) => {
197
+ const result = await services.externalService.getContext(req.query.sessionId);
198
+ res.json(result);
199
+ }));
200
+
201
+ router.get("/internal/cui/agents", requireInternal, asyncRoute(async (_req, res) => {
202
+ res.json({
203
+ items: await services.agentService.listActiveAgentDirectory()
204
+ });
205
+ }));
206
+
207
+ router.use(requireManagementAuth);
208
+
209
+ router.post("/auth/logout", asyncRoute(async (req, res) => {
210
+ writeAuditLog(services, req, "退出登录", `用户 ${req.currentUser.username} 退出登录`);
211
+ services.authService.logout(req.authToken);
212
+ res.status(204).end();
213
+ }));
214
+
215
+ router.get("/auth/me", asyncRoute(async (req, res) => {
216
+ res.json(req.currentUser);
217
+ }));
218
+
219
+ router.post("/auth/change-password", asyncRoute(async (req, res) => {
220
+ const result = services.authService.changePassword(req.currentUser.id, req.body);
221
+ writeAuditLog(services, req, "修改密码", `用户 ${req.currentUser.username} 修改了登录密码`);
222
+ res.json(result);
223
+ }));
224
+
225
+ router.get("/bootstrap", asyncRoute(async (req, res) => {
226
+ res.json({
227
+ current_user: req.currentUser,
228
+ settings: services.portalService.getSettings(),
229
+ access_tokens: services.portalService.listAccessTokens(),
230
+ agent_sync: services.catalogSyncService.getAgentSyncStatus(),
231
+ skill_sync: services.catalogSyncService.getSkillSyncStatus(),
232
+ template_sync: services.catalogSyncService.getTemplateSyncStatus(),
233
+ system_sync: services.catalogSyncService.getSystemSyncStatus(),
234
+ usage_refresh: services.catalogSyncService.getUsageRefreshStatus(),
235
+ env_config: {
236
+ admin_inbound_topic: services.env?.mqtt?.adminInboundTopic || ""
237
+ }
238
+ });
239
+ }));
240
+
241
+ router.get("/dashboard", asyncRoute(async (_req, res) => {
242
+ res.json(services.portalService.getDashboard());
243
+ }));
244
+
245
+ router.get("/audit-logs", asyncRoute(async (req, res) => {
246
+ services.authService.assertAdmin(req.currentUser);
247
+ res.json(services.auditLogService.list({
248
+ page: req.query.page || 1,
249
+ pageSize: req.query.pageSize || 50
250
+ }));
251
+ }));
252
+
253
+ router.get("/users", asyncRoute(async (req, res) => {
254
+ res.json(services.portalService.listUsers(req.query.role || ""));
255
+ }));
256
+
257
+ router.post("/users", asyncRoute(async (req, res) => {
258
+ services.authService.assertAdmin(req.currentUser);
259
+ const result = services.portalService.createUser(req.body);
260
+ writeAuditLog(services, req, "新增用户", `新增用户:${formatAuditTarget({
261
+ id: result.id,
262
+ name: result.username
263
+ })}`);
264
+ res.status(201).json(result);
265
+ }));
266
+
267
+ router.put("/users/:id", asyncRoute(async (req, res) => {
268
+ services.authService.assertAdmin(req.currentUser);
269
+ const result = services.portalService.updateUser(Number(req.params.id), req.body);
270
+ writeAuditLog(services, req, "编辑用户", `编辑用户:${formatAuditTarget({
271
+ id: result.id,
272
+ name: result.username
273
+ })}`);
274
+ res.json(result);
275
+ }));
276
+
277
+ router.delete("/users/:id", asyncRoute(async (req, res) => {
278
+ services.authService.assertAdmin(req.currentUser);
279
+ const users = services.portalService.listUsers("");
280
+ const target = users.find((item) => Number(item.id) === Number(req.params.id));
281
+ services.portalService.deleteUser(Number(req.params.id), req.currentUser.id);
282
+ writeAuditLog(services, req, "删除用户", `删除用户:${formatAuditTarget({
283
+ id: req.params.id,
284
+ name: target?.username
285
+ })}`);
286
+ res.status(204).end();
287
+ }));
288
+
289
+ router.post("/users/:id/reset-password", asyncRoute(async (req, res) => {
290
+ services.authService.assertAdmin(req.currentUser);
291
+ const password = req.body.password || "123456";
292
+ const result = services.authService.resetPassword(Number(req.params.id), password);
293
+ const users = services.portalService.listUsers("");
294
+ const target = users.find((item) => Number(item.id) === Number(req.params.id));
295
+ writeAuditLog(services, req, "重置用户密码", `重置用户密码:${formatAuditTarget({
296
+ id: req.params.id,
297
+ name: target?.username
298
+ })}`);
299
+ res.json(result);
300
+ }));
301
+
302
+ router.get("/templates", asyncRoute(async (_req, res) => {
303
+ res.json(services.portalService.listTemplates());
304
+ }));
305
+
306
+ router.post("/templates", upload.single("artifact"), asyncRoute(async (req, res) => {
307
+ services.authService.assertAdmin(req.currentUser);
308
+ if (!req.file) {
309
+ throw badRequest("请上传模板文件");
310
+ }
311
+ const result = await services.portalService.createTemplate({
312
+ templateName: req.body.template_name,
313
+ description: req.body.description || "",
314
+ file: req.file,
315
+ createdBy: req.currentUser.id
316
+ });
317
+ writeAuditLog(services, req, "上传模板", `上传模板:${formatAuditTarget({
318
+ id: result.id,
319
+ name: result.template_name
320
+ })}`);
321
+ res.status(201).json(result);
322
+ }));
323
+
324
+ router.delete("/templates/:templateName", asyncRoute(async (req, res) => {
325
+ services.authService.assertAdmin(req.currentUser);
326
+ const templates = services.portalService.listTemplates();
327
+ const target = templates.find((item) => item.template_name === req.params.templateName);
328
+ const result = await services.portalService.deleteTemplate(req.params.templateName);
329
+ writeAuditLog(services, req, "删除模板", `删除模板:${formatAuditTarget({
330
+ id: target?.id,
331
+ name: target?.template_name || req.params.templateName
332
+ })}`);
333
+ res.json(result);
334
+ }));
335
+
336
+ router.get("/skills", asyncRoute(async (_req, res) => {
337
+ res.json(services.portalService.listSkills());
338
+ }));
339
+
340
+ router.post("/skills", upload.single("artifact"), asyncRoute(async (req, res) => {
341
+ services.authService.assertAdmin(req.currentUser);
342
+ if (!req.file) {
343
+ throw badRequest("请上传技能文件");
344
+ }
345
+ const payload = {
346
+ slug: req.body.slug,
347
+ description: req.body.description || ""
348
+ };
349
+ const result = await services.portalService.createSkill({
350
+ payload,
351
+ file: req.file || null,
352
+ createdBy: req.currentUser.id
353
+ });
354
+ writeAuditLog(services, req, "新增技能", `新增技能:${formatAuditTarget({
355
+ id: result.id,
356
+ name: result.slug
357
+ })}`);
358
+ res.status(201).json(result);
359
+ }));
360
+
361
+ router.put("/skills/:id", upload.single("artifact"), asyncRoute(async (req, res) => {
362
+ services.authService.assertAdmin(req.currentUser);
363
+ if (!req.file) {
364
+ throw badRequest("请上传技能文件");
365
+ }
366
+ const payload = {
367
+ description: req.body.description || ""
368
+ };
369
+ const result = await services.portalService.updateSkill(Number(req.params.id), {
370
+ payload,
371
+ file: req.file,
372
+ createdBy: req.currentUser.id
373
+ });
374
+ writeAuditLog(services, req, "编辑技能", `编辑技能:${formatAuditTarget({
375
+ id: result.id,
376
+ name: result.slug
377
+ })}`);
378
+ res.json(result);
379
+ }));
380
+
381
+ router.delete("/skills/:id", asyncRoute(async (req, res) => {
382
+ services.authService.assertAdmin(req.currentUser);
383
+ const skills = services.portalService.listSkills();
384
+ const target = skills.find((item) => Number(item.id) === Number(req.params.id));
385
+ const result = await services.portalService.deleteSkill(Number(req.params.id));
386
+ writeAuditLog(services, req, "删除技能", `删除技能:${formatAuditTarget({
387
+ id: req.params.id,
388
+ name: target?.slug
389
+ })}`);
390
+ res.json(result);
391
+ }));
392
+
393
+ router.get("/agents", asyncRoute(async (_req, res) => {
394
+ res.json(await services.agentService.listAgents());
395
+ }));
396
+
397
+ router.get("/agents/by-slug/:slug", asyncRoute(async (req, res) => {
398
+ res.json(await services.agentService.getAgentBySlug(req.params.slug));
399
+ }));
400
+
401
+ router.post("/topics/admin/ping", asyncRoute(async (req, res) => {
402
+ services.authService.assertAdmin(req.currentUser);
403
+ const result = await services.topicPingService.pingAdmin();
404
+ writeAuditLog(
405
+ services,
406
+ req,
407
+ "探测管理主题",
408
+ `管理入站主题探测${result.ok ? "成功" : "失败"},耗时 ${result.responseTimeMs} ms`
409
+ );
410
+ res.json(result);
411
+ }));
412
+
413
+ router.post("/agents/:slug/ping", asyncRoute(async (req, res) => {
414
+ services.authService.assertAdmin(req.currentUser);
415
+ const result = await services.topicPingService.pingAgent(req.params.slug);
416
+ writeAuditLog(
417
+ services,
418
+ req,
419
+ "探测数字员工主题",
420
+ `数字员工 ${req.params.slug} 入站主题探测${result.ok ? "成功" : "失败"},耗时 ${result.responseTimeMs} ms`
421
+ );
422
+ res.json(result);
423
+ }));
424
+
425
+ router.get("/aios-users", asyncRoute(async (req, res) => {
426
+ services.authService.assertAdmin(req.currentUser);
427
+ const limit = Math.max(1, Math.min(200, Number(req.query.limit) || 50));
428
+ const page = Math.max(1, Number(req.query.page) || 1);
429
+ const offset = (page - 1) * limit;
430
+ const query = req.query.q || "";
431
+ res.json({
432
+ items: services.agentService.listAiosUsersWithUsage(query, limit, offset),
433
+ total: services.agentService.countAiosUsers(query),
434
+ page,
435
+ pageSize: limit
436
+ });
437
+ }));
438
+
439
+ router.post("/aios-users", asyncRoute(async (req, res) => {
440
+ services.authService.assertAdmin(req.currentUser);
441
+ const result = services.agentService.createAiosUser(req.body.username);
442
+ writeAuditLog(services, req, "新增AIOS用户", `新增AIOS用户:${result.username}`);
443
+ res.status(201).json(result);
444
+ }));
445
+
446
+ router.post("/aios-users/import", asyncRoute(async (req, res) => {
447
+ services.authService.assertAdmin(req.currentUser);
448
+ const result = services.agentService.importAiosUsers(req.body.content);
449
+ writeAuditLog(services, req, "批量导入AIOS用户", `批量导入AIOS用户:${result.total} 条`);
450
+ res.json(result);
451
+ }));
452
+
453
+ router.delete("/aios-users/:id", asyncRoute(async (req, res) => {
454
+ services.authService.assertAdmin(req.currentUser);
455
+ const result = services.agentService.deleteAiosUser(Number(req.params.id));
456
+ writeAuditLog(services, req, "删除AIOS用户", `删除AIOS用户:${result.username}`);
457
+ res.json(result);
458
+ }));
459
+
460
+ router.post("/agents", asyncRoute(async (req, res) => {
461
+ services.authService.assertAdmin(req.currentUser);
462
+ const result = await services.agentService.createAgent(req.body);
463
+ writeAuditLog(services, req, "新增数字员工", `新增数字员工:${formatAuditTarget({
464
+ id: result.id,
465
+ name: result.slug
466
+ })}`);
467
+ res.status(201).json(result);
468
+ }));
469
+
470
+ router.put("/agents/:id", asyncRoute(async (req, res) => {
471
+ services.authService.assertAdmin(req.currentUser);
472
+ const agents = await services.agentService.listAgents();
473
+ const current = agents.find((item) => Number(item.id) === Number(req.params.id));
474
+ const result = await services.agentService.updateAgent(Number(req.params.id), req.body);
475
+ const audit = buildAgentUpdateAudit(current, result, req.body);
476
+ writeAuditLog(services, req, audit.action, audit.detail);
477
+ res.json(result);
478
+ }));
479
+
480
+ router.delete("/agents/:id", asyncRoute(async (req, res) => {
481
+ services.authService.assertAdmin(req.currentUser);
482
+ const agents = await services.agentService.listAgents();
483
+ const target = agents.find((item) => Number(item.id) === Number(req.params.id));
484
+ const result = await services.agentService.deleteAgent(Number(req.params.id));
485
+ writeAuditLog(services, req, "删除数字员工", `删除数字员工:${formatAuditTarget({
486
+ id: req.params.id,
487
+ name: target?.slug
488
+ })}`);
489
+ res.json(result);
490
+ }));
491
+
492
+ router.get("/systems", asyncRoute(async (_req, res) => {
493
+ res.json(services.systemService.listSystems());
494
+ }));
495
+
496
+ router.post("/systems", upload.single("ontology"), asyncRoute(async (req, res) => {
497
+ services.authService.assertAdmin(req.currentUser);
498
+ const payload = {
499
+ provider: req.body.provider,
500
+ application_name: req.body.application_name,
501
+ description: req.body.description,
502
+ scheme: req.body.scheme,
503
+ host: req.body.host,
504
+ port: req.body.port,
505
+ status: req.body.status
506
+ };
507
+ const result = await services.systemService.createSystem({
508
+ payload,
509
+ file: req.file || null,
510
+ createdBy: req.currentUser.id
511
+ });
512
+ writeAuditLog(services, req, "新增业务系统", `新增业务系统:${formatAuditTarget({
513
+ id: result.system?.id,
514
+ name: result.system?.application_name || payload.application_name
515
+ })}`);
516
+ res.status(201).json(result);
517
+ }));
518
+
519
+ router.put("/systems/:id", upload.single("ontology"), asyncRoute(async (req, res) => {
520
+ services.authService.assertAdmin(req.currentUser);
521
+ const result = await services.systemService.updateSystem(Number(req.params.id), {
522
+ payload: req.body,
523
+ file: req.file || null,
524
+ createdBy: req.currentUser.id
525
+ });
526
+ writeAuditLog(services, req, "编辑业务系统", `编辑业务系统:${formatAuditTarget({
527
+ id: result.system?.id || req.params.id,
528
+ name: result.system?.application_name
529
+ })}`);
530
+ res.json(result);
531
+ }));
532
+
533
+ router.post("/systems/:id/test", asyncRoute(async (req, res) => {
534
+ services.authService.assertAdmin(req.currentUser);
535
+ const target = services.systemService.getSystemById(Number(req.params.id));
536
+ const result = await services.systemService.testConnectivity(Number(req.params.id));
537
+ writeAuditLog(services, req, "测试业务系统连通性", `测试业务系统:${formatAuditTarget({
538
+ id: req.params.id,
539
+ name: target?.application_name
540
+ })}`);
541
+ res.json(result);
542
+ }));
543
+
544
+ router.post("/systems/:id/status", asyncRoute(async (req, res) => {
545
+ services.authService.assertAdmin(req.currentUser);
546
+ const result = await services.systemService.setSystemStatus(Number(req.params.id), req.body.status);
547
+ writeAuditLog(services, req, "切换业务系统状态", `业务系统 ID ${req.params.id} 状态切换为:${req.body.status}`);
548
+ res.json(result);
549
+ }));
550
+
551
+ router.delete("/systems/:id", asyncRoute(async (req, res) => {
552
+ services.authService.assertAdmin(req.currentUser);
553
+ const target = services.systemService.getSystemById(Number(req.params.id));
554
+ const result = await services.systemService.deleteSystem(Number(req.params.id));
555
+ writeAuditLog(services, req, "删除业务系统", `删除业务系统:${formatAuditTarget({
556
+ id: req.params.id,
557
+ name: target?.application_name
558
+ })}`);
559
+ res.json(result);
560
+ }));
561
+
562
+ router.get("/systems/logs", asyncRoute(async (req, res) => {
563
+ res.json(services.systemService.listLogs({
564
+ applicationName: req.query.application_name || "",
565
+ agentSlug: req.query.agent_slug || ""
566
+ }));
567
+ }));
568
+
569
+ router.get("/systems/stats", asyncRoute(async (req, res) => {
570
+ res.json(services.systemService.listStats({
571
+ applicationName: req.query.application_name || "",
572
+ agentSlug: req.query.agent_slug || ""
573
+ }));
574
+ }));
575
+
576
+ router.get("/kernel/logs", asyncRoute(async (req, res) => {
577
+ services.authService.assertAdmin(req.currentUser);
578
+ res.json(services.portalService.listManagementRequests({
579
+ page: req.query.page || 1,
580
+ pageSize: req.query.pageSize || 50
581
+ }));
582
+ }));
583
+
584
+ router.get("/settings", asyncRoute(async (_req, res) => {
585
+ res.json(services.portalService.getSettings());
586
+ }));
587
+
588
+ router.put("/settings", asyncRoute(async (req, res) => {
589
+ services.authService.assertAdmin(req.currentUser);
590
+ const result = services.portalService.updateSettings(req.body);
591
+ writeAuditLog(services, req, "保存系统设置", "更新了系统设置");
592
+ res.json(result);
593
+ }));
594
+
595
+ router.get("/settings/access-tokens", asyncRoute(async (_req, res) => {
596
+ res.json(services.portalService.listAccessTokens());
597
+ }));
598
+
599
+ router.post("/settings/access-tokens", asyncRoute(async (req, res) => {
600
+ services.authService.assertAdmin(req.currentUser);
601
+ const result = services.portalService.createAccessToken();
602
+ writeAuditLog(services, req, "新增管理员令牌", `新增管理员令牌:${formatAuditTarget({
603
+ id: result.token,
604
+ name: req.currentUser.username
605
+ })}`);
606
+ res.status(201).json(result);
607
+ }));
608
+
609
+ router.delete("/settings/access-tokens/:token", asyncRoute(async (req, res) => {
610
+ services.authService.assertAdmin(req.currentUser);
611
+ const token = decodeURIComponent(req.params.token || "");
612
+ const result = services.portalService.deleteAccessToken(token);
613
+ writeAuditLog(services, req, "删除管理员令牌", `删除管理员令牌:${formatAuditTarget({
614
+ id: token,
615
+ name: req.currentUser.username
616
+ })}`);
617
+ res.json(result);
618
+ }));
619
+
620
+ router.post("/settings/agent-sync", asyncRoute(async (req, res) => {
621
+ services.authService.assertAdmin(req.currentUser);
622
+ const result = await services.catalogSyncService.syncAgentsTask({ trigger: "manual" });
623
+ writeAuditLog(services, req, "同步数字员工", "手动触发数字员工同步");
624
+ res.json(result);
625
+ }));
626
+
627
+ router.post("/settings/skill-sync", asyncRoute(async (req, res) => {
628
+ services.authService.assertAdmin(req.currentUser);
629
+ const result = await services.catalogSyncService.syncSkillsTask({ trigger: "manual" });
630
+ writeAuditLog(services, req, "同步技能", "手动触发技能同步");
631
+ res.json(result);
632
+ }));
633
+
634
+ router.post("/settings/template-sync", asyncRoute(async (req, res) => {
635
+ services.authService.assertAdmin(req.currentUser);
636
+ const result = await services.catalogSyncService.syncTemplatesTask({ trigger: "manual" });
637
+ writeAuditLog(services, req, "同步模板", "手动触发模板同步");
638
+ res.json(result);
639
+ }));
640
+
641
+ router.post("/settings/system-sync", asyncRoute(async (req, res) => {
642
+ services.authService.assertAdmin(req.currentUser);
643
+ const result = await services.catalogSyncService.syncSystemsTask({ trigger: "manual" });
644
+ writeAuditLog(services, req, "同步业务系统", "手动触发业务系统同步");
645
+ res.json(result);
646
+ }));
647
+
648
+ router.post("/settings/usage-refresh", asyncRoute(async (req, res) => {
649
+ services.authService.assertAdmin(req.currentUser);
650
+ const result = await services.catalogSyncService.refreshAgentUsage({ trigger: "manual" });
651
+ writeAuditLog(services, req, "同步员工用量", "手动触发数字员工用量同步");
652
+ res.json(result);
653
+ }));
654
+
655
+ router.post("/settings/server-status", asyncRoute(async (req, res) => {
656
+ services.authService.assertAdmin(req.currentUser);
657
+ const result = await services.rpcClient.call("gateway.status", {});
658
+ writeAuditLog(services, req, "查询服务器状态", "手动触发 gateway.status");
659
+ res.json({ result });
660
+ }));
661
+
662
+ router.post("/settings/server-restart", asyncRoute(async (req, res) => {
663
+ services.authService.assertAdmin(req.currentUser);
664
+ const result = await services.rpcClient.call("gateway.restart", {});
665
+ writeAuditLog(services, req, "重启服务", "手动触发 gateway.restart");
666
+ res.json({ result });
667
+ }));
668
+
669
+ router.post("/settings/server-diagnostics", asyncRoute(async (req, res) => {
670
+ services.authService.assertAdmin(req.currentUser);
671
+ const result = await services.rpcClient.call("diagnostics.run", {}, 180000);
672
+ writeAuditLog(services, req, "服务器诊断", "手动触发 diagnostics.run");
673
+ res.json({ result });
674
+ }));
675
+
676
+ return router;
677
+ }