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.
- package/.env.json +21 -0
- package/README.md +257 -0
- package/data/management-console.db +0 -0
- package/data/management-console.db-shm +0 -0
- package/data/management-console.db-wal +0 -0
- package/dist/assets/index-CV_wjCAG.js +464 -0
- package/dist/assets/index-DfMPB0eV.css +1 -0
- package/dist/index.html +13 -0
- package/docs/spec.md +199 -0
- package/index.html +12 -0
- package/package.json +37 -0
- package/scripts/reset-kernel.js +59 -0
- package/scripts/reset-password.js +22 -0
- package/server/fakes.js +57 -0
- package/server/index.js +21 -0
- package/server/src/api/middleware/auth.js +29 -0
- package/server/src/api/middleware/internal.js +44 -0
- package/server/src/api/routes/index.js +677 -0
- package/server/src/app.js +90 -0
- package/server/src/background/index.js +106 -0
- package/server/src/background/protocol.js +15 -0
- package/server/src/config/env.js +90 -0
- package/server/src/db/index.js +501 -0
- package/server/src/infra/mqtt/management-rpc-client.js +213 -0
- package/server/src/infra/providers/hzg-provider-client.js +39 -0
- package/server/src/infra/s3/object-storage.js +97 -0
- package/server/src/services/agent-quota.js +54 -0
- package/server/src/services/agent-service.js +696 -0
- package/server/src/services/agent-status-sync-service.js +132 -0
- package/server/src/services/audit-log-service.js +39 -0
- package/server/src/services/auth-service.js +153 -0
- package/server/src/services/catalog-sync-service.js +712 -0
- package/server/src/services/external-service.js +308 -0
- package/server/src/services/kernel-reset-service.js +86 -0
- package/server/src/services/portal-service.js +555 -0
- package/server/src/services/system-service.js +580 -0
- package/server/src/services/topic-ping-service.js +282 -0
- package/server/src/utils/errors.js +36 -0
- package/server/src/utils/security.js +22 -0
- package/server/test/agent-service-alignment.test.js +316 -0
- package/server/test/agent-service-create.test.js +662 -0
- package/server/test/agent-status-sync-service.test.js +167 -0
- package/server/test/agent-update-audit.test.js +63 -0
- package/server/test/auth-middleware.test.js +71 -0
- package/server/test/background-services.test.js +160 -0
- package/server/test/catalog-sync-service.test.js +920 -0
- package/server/test/db-reset-migration.test.js +123 -0
- package/server/test/env-config.test.js +68 -0
- package/server/test/external-service.test.js +380 -0
- package/server/test/hzg-provider-client.test.js +50 -0
- package/server/test/internal-auth-middleware.test.js +66 -0
- package/server/test/kernel-reset-service.test.js +112 -0
- package/server/test/management-rpc-client.test.js +105 -0
- package/server/test/portal-service-access-tokens.test.js +121 -0
- package/server/test/portal-service-alignment.test.js +318 -0
- package/server/test/portal-service-management-logs.test.js +114 -0
- package/server/test/reset-kernel-cli.test.js +23 -0
- package/server/test/service-api-auth-middleware.test.js +59 -0
- package/server/test/system-service-alignment.test.js +265 -0
- package/server/test/topic-ping-service.test.js +182 -0
- package/server/test/usage-refresh-audit-route.test.js +82 -0
- package/src/App.jsx +1 -0
- package/src/api.js +1 -0
- package/src/app/App.jsx +346 -0
- package/src/app/api-client.js +112 -0
- package/src/components/AppShell.jsx +117 -0
- package/src/components/CardTitleWithReload.jsx +20 -0
- package/src/components/DeleteActionButton.jsx +31 -0
- package/src/main.jsx +14 -0
- package/src/pages/AgentsPage.jsx +647 -0
- package/src/pages/AiosUsersPage.jsx +151 -0
- package/src/pages/DashboardPage.jsx +72 -0
- package/src/pages/LoginPage.jsx +41 -0
- package/src/pages/SettingsPage.jsx +431 -0
- package/src/pages/SkillsPage.jsx +175 -0
- package/src/pages/SystemLogsPage.jsx +349 -0
- package/src/pages/SystemsPage.jsx +498 -0
- package/src/pages/TemplatesPage.jsx +207 -0
- package/src/pages/UserManagementPage.jsx +25 -0
- package/src/pages/UsersPage.jsx +192 -0
- package/src/pages/system-logs/SystemLogsTabs.jsx +362 -0
- package/src/styles.css +222 -0
- package/src/utils/format.js +63 -0
- package/test/.reports/fast-2026-05-25T08-32-39-420Z.json +299 -0
- package/test/integration/common.js +208 -0
- package/test/integration/fast.js +135 -0
- package/test/integration/full.js +306 -0
- package/test/run-browser-e2e.js +212 -0
- package/test/run-jasmine.js +21 -0
- package/test/setup.js +1 -0
- package/vite.config.js +12 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import express from "express";
|
|
5
|
+
|
|
6
|
+
import { createRoutes } from "./api/routes/index.js";
|
|
7
|
+
import { loadEnv } from "./config/env.js";
|
|
8
|
+
import { db } from "./db/index.js";
|
|
9
|
+
import { ManagementRpcClient } from "./infra/mqtt/management-rpc-client.js";
|
|
10
|
+
import { HzgProviderClient } from "./infra/providers/hzg-provider-client.js";
|
|
11
|
+
import { ObjectStorage } from "./infra/s3/object-storage.js";
|
|
12
|
+
import { AgentService } from "./services/agent-service.js";
|
|
13
|
+
import { AgentStatusSyncService } from "./services/agent-status-sync-service.js";
|
|
14
|
+
import { AuditLogService } from "./services/audit-log-service.js";
|
|
15
|
+
import { AuthService } from "./services/auth-service.js";
|
|
16
|
+
import { CatalogSyncService } from "./services/catalog-sync-service.js";
|
|
17
|
+
import { ExternalService } from "./services/external-service.js";
|
|
18
|
+
import { PortalService } from "./services/portal-service.js";
|
|
19
|
+
import { SystemService } from "./services/system-service.js";
|
|
20
|
+
import { TopicPingService } from "./services/topic-ping-service.js";
|
|
21
|
+
import { AppError } from "./utils/errors.js";
|
|
22
|
+
|
|
23
|
+
export function createServerApp() {
|
|
24
|
+
const env = loadEnv();
|
|
25
|
+
const objectStorage = new ObjectStorage(env);
|
|
26
|
+
const authService = new AuthService({ db, env });
|
|
27
|
+
const rpcClient = new ManagementRpcClient({ env, db });
|
|
28
|
+
const hzgProviderClient = new HzgProviderClient();
|
|
29
|
+
const auditLogService = new AuditLogService({ db });
|
|
30
|
+
const catalogSyncService = new CatalogSyncService({ db, rpcClient, auditLogService, env });
|
|
31
|
+
const agentStatusSyncService = new AgentStatusSyncService({ db, rpcClient, auditLogService });
|
|
32
|
+
const portalService = new PortalService({ db, objectStorage, rpcClient, authService });
|
|
33
|
+
const topicPingService = new TopicPingService({ db, env, rpcClient });
|
|
34
|
+
const agentService = new AgentService({ db, rpcClient, objectStorage, env });
|
|
35
|
+
const systemService = new SystemService({ db, objectStorage, rpcClient });
|
|
36
|
+
const externalService = new ExternalService({
|
|
37
|
+
db,
|
|
38
|
+
agentService,
|
|
39
|
+
systemService,
|
|
40
|
+
hzgProviderClient,
|
|
41
|
+
auditLogService
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const services = {
|
|
45
|
+
env,
|
|
46
|
+
authService,
|
|
47
|
+
portalService,
|
|
48
|
+
agentService,
|
|
49
|
+
topicPingService,
|
|
50
|
+
auditLogService,
|
|
51
|
+
systemService,
|
|
52
|
+
externalService,
|
|
53
|
+
catalogSyncService,
|
|
54
|
+
agentStatusSyncService,
|
|
55
|
+
rpcClient,
|
|
56
|
+
hzgProviderClient
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const app = express();
|
|
60
|
+
app.use(express.json({ limit: "5mb" }));
|
|
61
|
+
app.use(express.urlencoded({ extended: true }));
|
|
62
|
+
app.use("/api", createRoutes(services));
|
|
63
|
+
|
|
64
|
+
const distDir = path.resolve("dist");
|
|
65
|
+
if (fs.existsSync(distDir)) {
|
|
66
|
+
app.use(express.static(distDir));
|
|
67
|
+
app.get(/^(?!\/api).*/, (_req, res) => {
|
|
68
|
+
res.sendFile(path.join(distDir, "index.html"));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
app.use((error, _req, res, _next) => {
|
|
73
|
+
if (error instanceof AppError) {
|
|
74
|
+
res.status(error.status).json({
|
|
75
|
+
code: error.code,
|
|
76
|
+
message: error.message,
|
|
77
|
+
details: error.details
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.error(error);
|
|
83
|
+
res.status(500).json({
|
|
84
|
+
code: "internal_error",
|
|
85
|
+
message: error?.message || "服务器内部错误"
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return { app, env, services };
|
|
90
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export function startBackgroundServices({ env, services }) {
|
|
2
|
+
const rpcStarter = services.rpcClient.start();
|
|
3
|
+
const starters = [rpcStarter];
|
|
4
|
+
let schedulerStarterTimer = null;
|
|
5
|
+
let schedulerTimer = null;
|
|
6
|
+
let scheduledTaskIndex = 0;
|
|
7
|
+
|
|
8
|
+
function createStartupTasks() {
|
|
9
|
+
return [
|
|
10
|
+
() => services.catalogSyncService.syncAgentsTask({ trigger: "startup" }),
|
|
11
|
+
() => services.catalogSyncService.refreshAgentUsage({ trigger: "startup" }),
|
|
12
|
+
() => services.catalogSyncService.syncSkillsTask({ trigger: "startup" }),
|
|
13
|
+
() => services.catalogSyncService.syncTemplatesTask({ trigger: "startup" }),
|
|
14
|
+
() => services.catalogSyncService.syncSystemsTask({ trigger: "startup" })
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function createScheduledTasks() {
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
label: "agent catalog sync",
|
|
22
|
+
run: () => services.catalogSyncService.syncAgentsTask({ trigger: "scheduled" })
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: "agent usage refresh",
|
|
26
|
+
run: () => services.catalogSyncService.refreshAgentUsage({ trigger: "scheduled" })
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: "skill catalog sync",
|
|
30
|
+
run: () => services.catalogSyncService.syncSkillsTask({ trigger: "scheduled" })
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: "template catalog sync",
|
|
34
|
+
run: () => services.catalogSyncService.syncTemplatesTask({ trigger: "scheduled" })
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: "ontology catalog sync",
|
|
38
|
+
run: () => services.catalogSyncService.syncSystemsTask({ trigger: "scheduled" })
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function scheduleGlobalClock() {
|
|
44
|
+
const startupDelayMs = 5 * 60 * 1000;
|
|
45
|
+
const intervalMs = 60 * 1000;
|
|
46
|
+
const scheduledTasks = createScheduledTasks();
|
|
47
|
+
const runNextScheduledTask = () => {
|
|
48
|
+
const task = scheduledTasks[scheduledTaskIndex];
|
|
49
|
+
scheduledTaskIndex = (scheduledTaskIndex + 1) % scheduledTasks.length;
|
|
50
|
+
task.run().catch((error) => {
|
|
51
|
+
console.error(`Scheduled ${task.label} failed`, error);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (schedulerStarterTimer) {
|
|
56
|
+
clearTimeout(schedulerStarterTimer);
|
|
57
|
+
}
|
|
58
|
+
if (schedulerTimer) {
|
|
59
|
+
clearInterval(schedulerTimer);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
scheduledTaskIndex = 0;
|
|
63
|
+
schedulerStarterTimer = setTimeout(() => {
|
|
64
|
+
schedulerStarterTimer = null;
|
|
65
|
+
runNextScheduledTask();
|
|
66
|
+
schedulerTimer = setInterval(runNextScheduledTask, intervalMs);
|
|
67
|
+
}, startupDelayMs);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Promise.allSettled(starters).then((results) => {
|
|
71
|
+
for (const result of results) {
|
|
72
|
+
if (result.status === "rejected") {
|
|
73
|
+
console.error("Background service failed to start", result.reason);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
rpcStarter
|
|
79
|
+
.then(async () => {
|
|
80
|
+
for (const run of createStartupTasks()) {
|
|
81
|
+
await run();
|
|
82
|
+
}
|
|
83
|
+
scheduleGlobalClock();
|
|
84
|
+
})
|
|
85
|
+
.catch((error) => {
|
|
86
|
+
console.error("Background startup sync failed", error);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
async stop() {
|
|
91
|
+
if (schedulerStarterTimer) {
|
|
92
|
+
clearTimeout(schedulerStarterTimer);
|
|
93
|
+
schedulerStarterTimer = null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (schedulerTimer) {
|
|
97
|
+
clearInterval(schedulerTimer);
|
|
98
|
+
schedulerTimer = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await Promise.allSettled([
|
|
102
|
+
services.rpcClient.stop()
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
let fileEnvCache = null;
|
|
5
|
+
|
|
6
|
+
function loadFileEnv() {
|
|
7
|
+
if (fileEnvCache !== null) {
|
|
8
|
+
return fileEnvCache;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const envFilePath = path.resolve(".env.json");
|
|
12
|
+
if (!fs.existsSync(envFilePath)) {
|
|
13
|
+
fileEnvCache = {};
|
|
14
|
+
return fileEnvCache;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(fs.readFileSync(envFilePath, "utf8"));
|
|
19
|
+
fileEnvCache = parsed && typeof parsed === "object" ? parsed : {};
|
|
20
|
+
} catch {
|
|
21
|
+
fileEnvCache = {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return fileEnvCache;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readEnvValue(name, fallback = "") {
|
|
28
|
+
const envValue = process.env[name];
|
|
29
|
+
if (envValue !== undefined && envValue !== null && String(envValue).trim() !== "") {
|
|
30
|
+
return String(envValue).trim();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const fileEnvValue = loadFileEnv()[name];
|
|
34
|
+
if (fileEnvValue !== undefined && fileEnvValue !== null && String(fileEnvValue).trim() !== "") {
|
|
35
|
+
return String(fileEnvValue).trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return fallback;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function readEnvBoolean(name, fallback = false) {
|
|
42
|
+
const value = readEnvValue(name, fallback ? "true" : "false");
|
|
43
|
+
return ["1", "true", "yes", "on"].includes(value.toLowerCase());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readEnvNumber(name, fallback) {
|
|
47
|
+
const value = Number(readEnvValue(name, String(fallback)));
|
|
48
|
+
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function loadEnv() {
|
|
52
|
+
const aiosRoot = readEnvValue("AIOS_ROOT", "");
|
|
53
|
+
const defaultDataDir = aiosRoot
|
|
54
|
+
? path.join(aiosRoot, "data", "web")
|
|
55
|
+
: path.join("data");
|
|
56
|
+
const dataDir = path.resolve(readEnvValue("AIOS_WEB_DATA_DIR", defaultDataDir));
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
port: 3030,
|
|
60
|
+
dataDir,
|
|
61
|
+
sessionTtlHours: Number(readEnvValue("AIOS_WEB_SESSION_TTL_HOURS", "12")),
|
|
62
|
+
managementTimeoutMs: readEnvNumber("AIOS_MANAGEMENT_WEBSITE_TIMEOUT", 120) * 1000,
|
|
63
|
+
mqtt: {
|
|
64
|
+
brokerUrl: readEnvValue("AIOS_MQTT_CHANNEL_BROKER"),
|
|
65
|
+
username: readEnvValue("AIOS_MQTT_CHANNEL_USERNAME"),
|
|
66
|
+
password: readEnvValue("AIOS_MQTT_CHANNEL_PASSWORD"),
|
|
67
|
+
adminInboundTopic: readEnvValue("AIOS_ADMIN_INBOUND_TOPIC", "aios/admin/inbound"),
|
|
68
|
+
adminOutboundTopic: readEnvValue("AIOS_ADMIN_OUTBOUND_TOPIC", "aios/admin/outbound"),
|
|
69
|
+
agentInboundTopicTemplate: readEnvValue(
|
|
70
|
+
"AIOS_AGENT_CHANNEL_INBOUND_TOPIC_TEMPLATE",
|
|
71
|
+
"aios/agent/{agentId}/inbound"
|
|
72
|
+
),
|
|
73
|
+
agentOutboundTopicTemplate: readEnvValue(
|
|
74
|
+
"AIOS_AGENT_CHANNEL_OUTBOUND_TOPIC_TEMPLATE",
|
|
75
|
+
"aios/agent/{agentId}/outbound"
|
|
76
|
+
)
|
|
77
|
+
},
|
|
78
|
+
s3: {
|
|
79
|
+
endpoint: readEnvValue("AIOS_S3_ENDPOINT"),
|
|
80
|
+
region: readEnvValue("AIOS_S3_REGION", "local"),
|
|
81
|
+
accessKeyId: readEnvValue("AIOS_S3_ACCESS_KEY_ID"),
|
|
82
|
+
secretAccessKey: readEnvValue("AIOS_S3_SECRET_ACCESS_KEY"),
|
|
83
|
+
forcePathStyle: readEnvBoolean("AIOS_S3_FORCE_PATH_STYLE", true),
|
|
84
|
+
adminInboxBucket: readEnvValue("AIOS_S3_ADMIN_INBOX_BUCKET"),
|
|
85
|
+
adminOutboxBucket: readEnvValue("AIOS_S3_ADMIN_OUTBOX_BUCKET"),
|
|
86
|
+
agentInboxBucket: readEnvValue("AIOS_S3_AGENT_INBOX_BUCKET"),
|
|
87
|
+
agentOutboxBucket: readEnvValue("AIOS_S3_AGENT_OUTBOX_BUCKET")
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|