@zhin.js/http 1.0.5 → 1.0.6
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/app/index.ts +732 -0
- package/lib/index.d.ts +5 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +138 -117
- package/lib/index.js.map +1 -1
- package/lib/router.d.ts.map +1 -1
- package/lib/router.js.map +1 -1
- package/package.json +6 -2
- package/CHANGELOG.md +0 -40
- package/src/index.ts +0 -682
- package/tsconfig.json +0 -23
- /package/{src → app}/router.ts +0 -0
package/lib/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Server } from
|
|
2
|
-
import Koa from
|
|
3
|
-
import { Router } from
|
|
4
|
-
export * from
|
|
5
|
-
declare module
|
|
1
|
+
import { Server } from "http";
|
|
2
|
+
import Koa from "koa";
|
|
3
|
+
import { Router } from "./router.js";
|
|
4
|
+
export * from "./router.js";
|
|
5
|
+
declare module "@zhin.js/types" {
|
|
6
6
|
interface GlobalContext {
|
|
7
7
|
koa: Koa;
|
|
8
8
|
router: Router;
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../app/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAgB,MAAM,EAAE,MAAM,MAAM,CAAC;AAE5C,OAAO,GAAG,MAAM,KAAK,CAAC;AAGtB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,cAAc,aAAa,CAAC;AAE5B,OAAO,QAAQ,gBAAgB,CAAC;IAC9B,UAAU,aAAa;QACrB,GAAG,EAAE,GAAG,CAAC;QACT,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAChB;CACF"}
|
package/lib/index.js
CHANGED
|
@@ -1,35 +1,43 @@
|
|
|
1
|
-
import { register, useApp, useDatabase } from
|
|
2
|
-
import { createServer } from
|
|
3
|
-
import os from
|
|
4
|
-
import Koa from
|
|
5
|
-
import auth from
|
|
6
|
-
import KoaBodyParser from
|
|
7
|
-
import { Router } from
|
|
8
|
-
import * as process from
|
|
9
|
-
export * from
|
|
1
|
+
import { register, defineSchema, Schema, usePlugin, useApp, useDatabase, } from "@zhin.js/core";
|
|
2
|
+
import { createServer } from "http";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import Koa from "koa";
|
|
5
|
+
import auth from "koa-basic-auth";
|
|
6
|
+
import KoaBodyParser from "koa-bodyparser";
|
|
7
|
+
import { Router } from "./router.js";
|
|
8
|
+
import * as process from "process";
|
|
9
|
+
export * from "./router.js";
|
|
10
|
+
const plugin = usePlugin();
|
|
11
|
+
const schema = defineSchema(Schema.object({
|
|
12
|
+
port: Schema.number("port").default(8086).description("HTTP 服务端口"),
|
|
13
|
+
username: Schema.string("username").description("HTTP 基本认证用户名, 默认为当前系统用户名"),
|
|
14
|
+
password: Schema.string("password").description("HTTP 基本认证密码, 默认为随机生成的6位字符串"),
|
|
15
|
+
base: Schema.string("base")
|
|
16
|
+
.default("/api")
|
|
17
|
+
.description("HTTP 路由前缀, 默认为 /api"),
|
|
18
|
+
}));
|
|
10
19
|
const koa = new Koa();
|
|
11
20
|
const server = createServer(koa.callback());
|
|
12
|
-
const router = new Router(server, { prefix: process.env.routerPrefix ||
|
|
21
|
+
const router = new Router(server, { prefix: process.env.routerPrefix || "" });
|
|
13
22
|
// 获取当前计算机登录用户名
|
|
14
23
|
const getCurrentUsername = () => {
|
|
15
24
|
try {
|
|
16
25
|
return os.userInfo().username;
|
|
17
26
|
}
|
|
18
27
|
catch {
|
|
19
|
-
return
|
|
28
|
+
return "admin"; // 如果获取失败,使用默认用户名
|
|
20
29
|
}
|
|
21
30
|
};
|
|
22
31
|
// 生成6位随机密码
|
|
23
32
|
const generateRandomPassword = () => {
|
|
24
|
-
const chars =
|
|
25
|
-
let result =
|
|
33
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
34
|
+
let result = "";
|
|
26
35
|
for (let i = 0; i < 6; i++) {
|
|
27
36
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
28
37
|
}
|
|
29
38
|
return result;
|
|
30
39
|
};
|
|
31
|
-
const
|
|
32
|
-
const password = process.env.password || generateRandomPassword();
|
|
40
|
+
const { port = 8086, username = getCurrentUsername(), password = generateRandomPassword(), base = "/api", } = schema(plugin.config, "http");
|
|
33
41
|
const app = useApp();
|
|
34
42
|
koa.use(auth({
|
|
35
43
|
name: username,
|
|
@@ -39,7 +47,7 @@ koa.use(auth({
|
|
|
39
47
|
// API 路由
|
|
40
48
|
// ============================================================================
|
|
41
49
|
// 系统状态 API
|
|
42
|
-
router.get(
|
|
50
|
+
router.get(`${base}/system/status`, async (ctx) => {
|
|
43
51
|
try {
|
|
44
52
|
ctx.body = {
|
|
45
53
|
success: true,
|
|
@@ -50,8 +58,8 @@ router.get('/api/system/status', async (ctx) => {
|
|
|
50
58
|
platform: process.platform,
|
|
51
59
|
nodeVersion: process.version,
|
|
52
60
|
pid: process.pid,
|
|
53
|
-
timestamp: new Date().toISOString()
|
|
54
|
-
}
|
|
61
|
+
timestamp: new Date().toISOString(),
|
|
62
|
+
},
|
|
55
63
|
};
|
|
56
64
|
}
|
|
57
65
|
catch (error) {
|
|
@@ -60,25 +68,25 @@ router.get('/api/system/status', async (ctx) => {
|
|
|
60
68
|
}
|
|
61
69
|
});
|
|
62
70
|
// 健康检查 API
|
|
63
|
-
router.get(
|
|
71
|
+
router.get(`${base}/health`, async (ctx) => {
|
|
64
72
|
ctx.body = {
|
|
65
73
|
success: true,
|
|
66
|
-
status:
|
|
67
|
-
timestamp: new Date().toISOString()
|
|
74
|
+
status: "ok",
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
68
76
|
};
|
|
69
77
|
});
|
|
70
78
|
// 统计信息 API
|
|
71
|
-
router.get(
|
|
79
|
+
router.get(`${base}/stats`, async (ctx) => {
|
|
72
80
|
try {
|
|
73
81
|
// 统计插件数量
|
|
74
82
|
const pluginCount = app.dependencyList.length;
|
|
75
|
-
const activePluginCount = app.dependencyList.filter(dep => dep.mounted).length;
|
|
83
|
+
const activePluginCount = app.dependencyList.filter((dep) => dep.mounted).length;
|
|
76
84
|
// 统计机器人数量
|
|
77
85
|
let botCount = 0;
|
|
78
86
|
let onlineBotCount = 0;
|
|
79
87
|
for (const context of app.contextList) {
|
|
80
88
|
const adapter = app.getContext(context.name);
|
|
81
|
-
if (adapter && typeof adapter ===
|
|
89
|
+
if (adapter && typeof adapter === "object" && "bots" in adapter) {
|
|
82
90
|
const adapterBots = adapter.bots;
|
|
83
91
|
if (adapterBots && adapterBots instanceof Map) {
|
|
84
92
|
botCount += adapterBots.size;
|
|
@@ -105,7 +113,7 @@ router.get('/api/stats', async (ctx) => {
|
|
|
105
113
|
components: componentCount,
|
|
106
114
|
uptime: process.uptime(),
|
|
107
115
|
memory: process.memoryUsage().heapUsed / 1024 / 1024, // MB
|
|
108
|
-
}
|
|
116
|
+
},
|
|
109
117
|
};
|
|
110
118
|
}
|
|
111
119
|
catch (error) {
|
|
@@ -114,18 +122,18 @@ router.get('/api/stats', async (ctx) => {
|
|
|
114
122
|
}
|
|
115
123
|
});
|
|
116
124
|
// 插件管理 API
|
|
117
|
-
router.get(
|
|
125
|
+
router.get(`${base}/plugins`, async (ctx) => {
|
|
118
126
|
try {
|
|
119
127
|
// 获取详细的插件数据
|
|
120
|
-
const plugins = app.dependencyList.map(dep => {
|
|
128
|
+
const plugins = app.dependencyList.map((dep) => {
|
|
121
129
|
return {
|
|
122
130
|
name: dep.name,
|
|
123
|
-
status: dep.mounted ?
|
|
131
|
+
status: dep.mounted ? "active" : "inactive",
|
|
124
132
|
commandCount: dep.commands.length,
|
|
125
133
|
componentCount: dep.components.size,
|
|
126
134
|
middlewareCount: dep.middlewares.length,
|
|
127
135
|
contextCount: dep.contexts.size,
|
|
128
|
-
description: dep.description ||
|
|
136
|
+
description: dep.description || "无描述",
|
|
129
137
|
};
|
|
130
138
|
});
|
|
131
139
|
ctx.body = { success: true, data: plugins, total: plugins.length };
|
|
@@ -136,53 +144,53 @@ router.get('/api/plugins', async (ctx) => {
|
|
|
136
144
|
}
|
|
137
145
|
});
|
|
138
146
|
// 插件详情 API
|
|
139
|
-
router.get(
|
|
147
|
+
router.get("/api/plugins/:name", async (ctx) => {
|
|
140
148
|
try {
|
|
141
149
|
const pluginName = ctx.params.name;
|
|
142
|
-
const plugin = app.dependencyList.find(dep => dep.name === pluginName);
|
|
150
|
+
const plugin = app.dependencyList.find((dep) => dep.name === pluginName);
|
|
143
151
|
if (!plugin) {
|
|
144
152
|
ctx.status = 404;
|
|
145
|
-
ctx.body = { success: false, error:
|
|
153
|
+
ctx.body = { success: false, error: "插件不存在" };
|
|
146
154
|
return;
|
|
147
155
|
}
|
|
148
156
|
// 获取命令详情
|
|
149
|
-
const commands = plugin.commands.map(cmd => ({
|
|
150
|
-
name: cmd.pattern
|
|
157
|
+
const commands = plugin.commands.map((cmd) => ({
|
|
158
|
+
name: cmd.pattern,
|
|
151
159
|
}));
|
|
152
160
|
// 获取组件详情
|
|
153
161
|
const components = Array.from(plugin.components.entries()).map(([name, comp]) => ({
|
|
154
162
|
name,
|
|
155
163
|
props: comp.props || {},
|
|
156
|
-
type: typeof comp
|
|
164
|
+
type: typeof comp,
|
|
157
165
|
}));
|
|
158
166
|
// 获取中间件详情
|
|
159
167
|
const middlewares = plugin.middlewares.map((_, index) => ({
|
|
160
168
|
id: `middleware-${index}`,
|
|
161
|
-
type:
|
|
169
|
+
type: "function",
|
|
162
170
|
}));
|
|
163
171
|
// 获取上下文详情
|
|
164
172
|
const contexts = Array.from(plugin.contexts.entries()).map(([name, ctx]) => ({
|
|
165
173
|
name,
|
|
166
|
-
description: ctx.description ||
|
|
174
|
+
description: ctx.description || "无描述",
|
|
167
175
|
}));
|
|
168
176
|
// 获取定时任务详情
|
|
169
177
|
const crons = plugin.crons.map((cron, index) => ({
|
|
170
178
|
id: `cron-${index}`,
|
|
171
|
-
pattern: cron.pattern ||
|
|
172
|
-
running: cron.running || false
|
|
179
|
+
pattern: cron.pattern || "unknown",
|
|
180
|
+
running: cron.running || false,
|
|
173
181
|
}));
|
|
174
182
|
// 获取数据模型详情
|
|
175
183
|
const definitions = Array.from(plugin.definitions.entries()).map(([name, definition]) => ({
|
|
176
184
|
name,
|
|
177
|
-
fields: Object.keys(definition)
|
|
185
|
+
fields: Object.keys(definition),
|
|
178
186
|
}));
|
|
179
187
|
ctx.body = {
|
|
180
188
|
success: true,
|
|
181
189
|
data: {
|
|
182
190
|
name: plugin.name,
|
|
183
191
|
filename: plugin.filename,
|
|
184
|
-
status: plugin.mounted ?
|
|
185
|
-
description: plugin.description ||
|
|
192
|
+
status: plugin.mounted ? "active" : "inactive",
|
|
193
|
+
description: plugin.description || "无描述",
|
|
186
194
|
commands,
|
|
187
195
|
components,
|
|
188
196
|
middlewares,
|
|
@@ -195,9 +203,9 @@ router.get('/api/plugins/:name', async (ctx) => {
|
|
|
195
203
|
middlewareCount: middlewares.length,
|
|
196
204
|
contextCount: contexts.length,
|
|
197
205
|
cronCount: crons.length,
|
|
198
|
-
definitionCount: definitions.length
|
|
199
|
-
}
|
|
200
|
-
}
|
|
206
|
+
definitionCount: definitions.length,
|
|
207
|
+
},
|
|
208
|
+
},
|
|
201
209
|
};
|
|
202
210
|
}
|
|
203
211
|
catch (error) {
|
|
@@ -206,13 +214,13 @@ router.get('/api/plugins/:name', async (ctx) => {
|
|
|
206
214
|
}
|
|
207
215
|
});
|
|
208
216
|
// 适配器和机器人状态 API
|
|
209
|
-
router.get(
|
|
217
|
+
router.get(`${base}/bots`, async (ctx) => {
|
|
210
218
|
try {
|
|
211
219
|
const bots = [];
|
|
212
220
|
// 遍历所有上下文,查找适配器
|
|
213
221
|
for (const context of app.contextList) {
|
|
214
222
|
const adapter = app.getContext(context.name);
|
|
215
|
-
if (adapter && typeof adapter ===
|
|
223
|
+
if (adapter && typeof adapter === "object" && "bots" in adapter) {
|
|
216
224
|
const adapterBots = adapter.bots;
|
|
217
225
|
if (adapterBots && adapterBots instanceof Map) {
|
|
218
226
|
for (const [botName, bot] of adapterBots.entries()) {
|
|
@@ -220,7 +228,7 @@ router.get('/api/bots', async (ctx) => {
|
|
|
220
228
|
name: botName,
|
|
221
229
|
adapter: context.name,
|
|
222
230
|
connected: bot.$connected || false,
|
|
223
|
-
status: bot.$connected ?
|
|
231
|
+
status: bot.$connected ? "online" : "offline",
|
|
224
232
|
// 移除 config 字段以保护隐私
|
|
225
233
|
});
|
|
226
234
|
}
|
|
@@ -235,7 +243,7 @@ router.get('/api/bots', async (ctx) => {
|
|
|
235
243
|
}
|
|
236
244
|
});
|
|
237
245
|
// 框架配置信息 API
|
|
238
|
-
router.get(
|
|
246
|
+
router.get(`${base}/config`, async (ctx) => {
|
|
239
247
|
try {
|
|
240
248
|
const config = app.getConfig();
|
|
241
249
|
ctx.body = { success: true, data: config };
|
|
@@ -246,13 +254,13 @@ router.get('/api/config', async (ctx) => {
|
|
|
246
254
|
}
|
|
247
255
|
});
|
|
248
256
|
// 获取所有插件的 Schema API
|
|
249
|
-
router.get(
|
|
257
|
+
router.get(`${base}/schemas`, async (ctx) => {
|
|
250
258
|
try {
|
|
251
259
|
const schemas = {};
|
|
252
260
|
// 获取 App 的 Schema
|
|
253
261
|
const appSchema = app.schema.toJSON();
|
|
254
262
|
if (appSchema) {
|
|
255
|
-
schemas[
|
|
263
|
+
schemas["app"] = appSchema;
|
|
256
264
|
}
|
|
257
265
|
// 获取所有插件的 Schema
|
|
258
266
|
for (const plugin of app.dependencyList) {
|
|
@@ -261,7 +269,11 @@ router.get('/api/schemas', async (ctx) => {
|
|
|
261
269
|
schemas[plugin.name] = schema;
|
|
262
270
|
}
|
|
263
271
|
}
|
|
264
|
-
ctx.body = {
|
|
272
|
+
ctx.body = {
|
|
273
|
+
success: true,
|
|
274
|
+
data: schemas,
|
|
275
|
+
total: Object.keys(schemas).length,
|
|
276
|
+
};
|
|
265
277
|
}
|
|
266
278
|
catch (error) {
|
|
267
279
|
ctx.status = 500;
|
|
@@ -269,14 +281,14 @@ router.get('/api/schemas', async (ctx) => {
|
|
|
269
281
|
}
|
|
270
282
|
});
|
|
271
283
|
// 获取单个插件的 Schema API
|
|
272
|
-
router.get(
|
|
284
|
+
router.get(`${base}/schemas/:name`, async (ctx) => {
|
|
273
285
|
try {
|
|
274
286
|
const { name } = ctx.params;
|
|
275
|
-
if (name ===
|
|
287
|
+
if (name === "app") {
|
|
276
288
|
const schema = app.schema?.toJSON();
|
|
277
289
|
if (!schema) {
|
|
278
290
|
ctx.status = 404;
|
|
279
|
-
ctx.body = { success: false, error:
|
|
291
|
+
ctx.body = { success: false, error: "App schema not found" };
|
|
280
292
|
return;
|
|
281
293
|
}
|
|
282
294
|
ctx.body = { success: true, data: schema };
|
|
@@ -291,7 +303,10 @@ router.get('/api/schemas/:name', async (ctx) => {
|
|
|
291
303
|
const schema = plugin.schema.toJSON();
|
|
292
304
|
if (!schema) {
|
|
293
305
|
ctx.status = 404;
|
|
294
|
-
ctx.body = {
|
|
306
|
+
ctx.body = {
|
|
307
|
+
success: false,
|
|
308
|
+
error: `Schema for plugin ${name} not found`,
|
|
309
|
+
};
|
|
295
310
|
return;
|
|
296
311
|
}
|
|
297
312
|
ctx.body = { success: true, data: schema };
|
|
@@ -302,10 +317,10 @@ router.get('/api/schemas/:name', async (ctx) => {
|
|
|
302
317
|
}
|
|
303
318
|
});
|
|
304
319
|
// 获取插件配置 API
|
|
305
|
-
router.get(
|
|
320
|
+
router.get(`${base}/config/:name`, async (ctx) => {
|
|
306
321
|
try {
|
|
307
322
|
const { name } = ctx.params;
|
|
308
|
-
if (name ===
|
|
323
|
+
if (name === "app") {
|
|
309
324
|
const config = app.getConfig();
|
|
310
325
|
ctx.body = { success: true, data: config };
|
|
311
326
|
return;
|
|
@@ -325,16 +340,16 @@ router.get('/api/config/:name', async (ctx) => {
|
|
|
325
340
|
}
|
|
326
341
|
});
|
|
327
342
|
// 更新插件配置 API
|
|
328
|
-
router.post(
|
|
343
|
+
router.post(`${base}/config/:name`, async (ctx) => {
|
|
329
344
|
try {
|
|
330
345
|
const { name } = ctx.params;
|
|
331
346
|
const newConfig = ctx.request.body;
|
|
332
|
-
if (name ===
|
|
347
|
+
if (name === "app") {
|
|
333
348
|
app.config = newConfig;
|
|
334
349
|
ctx.body = {
|
|
335
350
|
success: true,
|
|
336
|
-
message:
|
|
337
|
-
data: app.getConfig()
|
|
351
|
+
message: "App configuration updated successfully",
|
|
352
|
+
data: app.getConfig(),
|
|
338
353
|
};
|
|
339
354
|
return;
|
|
340
355
|
}
|
|
@@ -348,7 +363,7 @@ router.post('/api/config/:name', async (ctx) => {
|
|
|
348
363
|
ctx.body = {
|
|
349
364
|
success: true,
|
|
350
365
|
message: `Plugin ${name} configuration updated successfully`,
|
|
351
|
-
data: plugin.config
|
|
366
|
+
data: plugin.config,
|
|
352
367
|
};
|
|
353
368
|
}
|
|
354
369
|
catch (error) {
|
|
@@ -357,7 +372,7 @@ router.post('/api/config/:name', async (ctx) => {
|
|
|
357
372
|
}
|
|
358
373
|
});
|
|
359
374
|
// 消息发送 API
|
|
360
|
-
router.post(
|
|
375
|
+
router.post(`${base}/message/send`, async (ctx) => {
|
|
361
376
|
try {
|
|
362
377
|
const body = ctx.request.body;
|
|
363
378
|
const { context, bot, id, type, content } = body;
|
|
@@ -365,7 +380,7 @@ router.post('/api/message/send', async (ctx) => {
|
|
|
365
380
|
ctx.status = 400;
|
|
366
381
|
ctx.body = {
|
|
367
382
|
success: false,
|
|
368
|
-
error:
|
|
383
|
+
error: "Missing required fields: context, bot, id, type, content",
|
|
369
384
|
};
|
|
370
385
|
return;
|
|
371
386
|
}
|
|
@@ -373,8 +388,15 @@ router.post('/api/message/send', async (ctx) => {
|
|
|
373
388
|
// console.log 已替换为注释
|
|
374
389
|
ctx.body = {
|
|
375
390
|
success: true,
|
|
376
|
-
message:
|
|
377
|
-
data: {
|
|
391
|
+
message: "Message sent successfully",
|
|
392
|
+
data: {
|
|
393
|
+
context,
|
|
394
|
+
bot,
|
|
395
|
+
id,
|
|
396
|
+
type,
|
|
397
|
+
content,
|
|
398
|
+
timestamp: new Date().toISOString(),
|
|
399
|
+
},
|
|
378
400
|
};
|
|
379
401
|
}
|
|
380
402
|
catch (error) {
|
|
@@ -383,43 +405,45 @@ router.post('/api/message/send', async (ctx) => {
|
|
|
383
405
|
}
|
|
384
406
|
});
|
|
385
407
|
// 日志 API - 获取日志
|
|
386
|
-
router.get(
|
|
408
|
+
router.get(`${base}/logs`, async (ctx) => {
|
|
387
409
|
try {
|
|
388
410
|
const database = useDatabase();
|
|
389
411
|
if (!database) {
|
|
390
412
|
ctx.status = 503;
|
|
391
|
-
ctx.body = { success: false, error:
|
|
413
|
+
ctx.body = { success: false, error: "Database not available" };
|
|
392
414
|
return;
|
|
393
415
|
}
|
|
394
416
|
// 获取查询参数
|
|
395
417
|
const limit = parseInt(ctx.query.limit) || 100;
|
|
396
418
|
const level = ctx.query.level;
|
|
397
|
-
const LogModel = database.model(
|
|
419
|
+
const LogModel = database.model("SystemLog");
|
|
398
420
|
if (!LogModel) {
|
|
399
421
|
ctx.status = 500;
|
|
400
|
-
ctx.body = { success: false, error:
|
|
422
|
+
ctx.body = { success: false, error: "SystemLog model not found" };
|
|
401
423
|
return;
|
|
402
424
|
}
|
|
403
425
|
// 查询日志
|
|
404
426
|
let selection = LogModel.select();
|
|
405
427
|
// 按级别过滤
|
|
406
|
-
if (level && level !==
|
|
428
|
+
if (level && level !== "all") {
|
|
407
429
|
selection = selection.where({ level });
|
|
408
430
|
}
|
|
409
431
|
// 按时间倒序,限制数量
|
|
410
|
-
const logs = await selection.orderBy(
|
|
432
|
+
const logs = await selection.orderBy("timestamp", "DESC").limit(limit);
|
|
411
433
|
// 格式化返回数据
|
|
412
434
|
const formattedLogs = logs.map((log) => ({
|
|
413
435
|
level: log.level,
|
|
414
436
|
name: log.name,
|
|
415
437
|
message: log.message,
|
|
416
438
|
source: log.source,
|
|
417
|
-
timestamp: log.timestamp instanceof Date
|
|
439
|
+
timestamp: log.timestamp instanceof Date
|
|
440
|
+
? log.timestamp.toISOString()
|
|
441
|
+
: log.timestamp,
|
|
418
442
|
}));
|
|
419
443
|
ctx.body = {
|
|
420
444
|
success: true,
|
|
421
445
|
data: formattedLogs,
|
|
422
|
-
total: formattedLogs.length
|
|
446
|
+
total: formattedLogs.length,
|
|
423
447
|
};
|
|
424
448
|
}
|
|
425
449
|
catch (error) {
|
|
@@ -428,25 +452,25 @@ router.get('/api/logs', async (ctx) => {
|
|
|
428
452
|
}
|
|
429
453
|
});
|
|
430
454
|
// 日志 API - 清空日志
|
|
431
|
-
router.delete(
|
|
455
|
+
router.delete(`${base}/logs`, async (ctx) => {
|
|
432
456
|
try {
|
|
433
457
|
const database = useDatabase();
|
|
434
458
|
if (!database) {
|
|
435
459
|
ctx.status = 503;
|
|
436
|
-
ctx.body = { success: false, error:
|
|
460
|
+
ctx.body = { success: false, error: "Database not available" };
|
|
437
461
|
return;
|
|
438
462
|
}
|
|
439
|
-
const LogModel = database.model(
|
|
463
|
+
const LogModel = database.model("SystemLog");
|
|
440
464
|
if (!LogModel) {
|
|
441
465
|
ctx.status = 500;
|
|
442
|
-
ctx.body = { success: false, error:
|
|
466
|
+
ctx.body = { success: false, error: "SystemLog model not found" };
|
|
443
467
|
return;
|
|
444
468
|
}
|
|
445
469
|
// 删除所有日志
|
|
446
470
|
await LogModel.delete({});
|
|
447
471
|
ctx.body = {
|
|
448
472
|
success: true,
|
|
449
|
-
message:
|
|
473
|
+
message: "日志已清空",
|
|
450
474
|
};
|
|
451
475
|
}
|
|
452
476
|
catch (error) {
|
|
@@ -455,49 +479,46 @@ router.delete('/api/logs', async (ctx) => {
|
|
|
455
479
|
}
|
|
456
480
|
});
|
|
457
481
|
// 日志 API - 获取日志统计
|
|
458
|
-
router.get(
|
|
482
|
+
router.get(`${base}/logs/stats`, async (ctx) => {
|
|
459
483
|
try {
|
|
460
484
|
const database = useDatabase();
|
|
461
485
|
if (!database) {
|
|
462
486
|
ctx.status = 503;
|
|
463
|
-
ctx.body = { success: false, error:
|
|
487
|
+
ctx.body = { success: false, error: "Database not available" };
|
|
464
488
|
return;
|
|
465
489
|
}
|
|
466
|
-
const LogModel = database.model(
|
|
490
|
+
const LogModel = database.model("SystemLog");
|
|
467
491
|
if (!LogModel) {
|
|
468
492
|
ctx.status = 500;
|
|
469
|
-
ctx.body = { success: false, error:
|
|
493
|
+
ctx.body = { success: false, error: "SystemLog model not found" };
|
|
470
494
|
return;
|
|
471
495
|
}
|
|
472
496
|
// 获取总日志数
|
|
473
497
|
const total = await LogModel.select();
|
|
474
498
|
const totalCount = total.length;
|
|
475
499
|
// 获取各级别日志数
|
|
476
|
-
const levels = [
|
|
500
|
+
const levels = ["info", "warn", "error"];
|
|
477
501
|
const levelCounts = {};
|
|
478
502
|
for (const level of levels) {
|
|
479
|
-
const count = await LogModel
|
|
480
|
-
.select()
|
|
481
|
-
.where({ level });
|
|
503
|
+
const count = await LogModel.select().where({ level });
|
|
482
504
|
levelCounts[level] = count.length;
|
|
483
505
|
}
|
|
484
506
|
// 获取最旧日志时间
|
|
485
|
-
const oldestLog = await LogModel
|
|
486
|
-
.
|
|
487
|
-
.orderBy('timestamp', 'ASC')
|
|
507
|
+
const oldestLog = await LogModel.select("timestamp")
|
|
508
|
+
.orderBy("timestamp", "ASC")
|
|
488
509
|
.limit(1);
|
|
489
510
|
const oldestTimestamp = oldestLog.length > 0
|
|
490
|
-
?
|
|
511
|
+
? oldestLog[0].timestamp instanceof Date
|
|
491
512
|
? oldestLog[0].timestamp.toISOString()
|
|
492
|
-
: oldestLog[0].timestamp
|
|
513
|
+
: oldestLog[0].timestamp
|
|
493
514
|
: null;
|
|
494
515
|
ctx.body = {
|
|
495
516
|
success: true,
|
|
496
517
|
data: {
|
|
497
518
|
total: totalCount,
|
|
498
519
|
byLevel: levelCounts,
|
|
499
|
-
oldestTimestamp
|
|
500
|
-
}
|
|
520
|
+
oldestTimestamp,
|
|
521
|
+
},
|
|
501
522
|
};
|
|
502
523
|
}
|
|
503
524
|
catch (error) {
|
|
@@ -506,38 +527,36 @@ router.get('/api/logs/stats', async (ctx) => {
|
|
|
506
527
|
}
|
|
507
528
|
});
|
|
508
529
|
// 日志 API - 清理旧日志(手动触发)
|
|
509
|
-
router.post(
|
|
530
|
+
router.post(`${base}/logs/cleanup`, async (ctx) => {
|
|
510
531
|
try {
|
|
511
532
|
const database = useDatabase();
|
|
512
533
|
if (!database) {
|
|
513
534
|
ctx.status = 503;
|
|
514
|
-
ctx.body = { success: false, error:
|
|
535
|
+
ctx.body = { success: false, error: "Database not available" };
|
|
515
536
|
return;
|
|
516
537
|
}
|
|
517
|
-
const LogModel = database.model(
|
|
538
|
+
const LogModel = database.model("SystemLog");
|
|
518
539
|
if (!LogModel) {
|
|
519
540
|
ctx.status = 500;
|
|
520
|
-
ctx.body = { success: false, error:
|
|
541
|
+
ctx.body = { success: false, error: "SystemLog model not found" };
|
|
521
542
|
return;
|
|
522
543
|
}
|
|
523
544
|
const { days, maxRecords } = ctx.request.body || {};
|
|
524
545
|
let deletedCount = 0;
|
|
525
546
|
// 按天数清理
|
|
526
|
-
if (days && typeof days ===
|
|
547
|
+
if (days && typeof days === "number" && days > 0) {
|
|
527
548
|
const cutoffDate = new Date();
|
|
528
549
|
cutoffDate.setDate(cutoffDate.getDate() - days);
|
|
529
|
-
const deleted = await LogModel
|
|
530
|
-
.delete({ timestamp: { $lt: cutoffDate } });
|
|
550
|
+
const deleted = await LogModel.delete({ timestamp: { $lt: cutoffDate } });
|
|
531
551
|
deletedCount += deleted || 0;
|
|
532
552
|
}
|
|
533
553
|
// 按数量清理
|
|
534
|
-
if (maxRecords && typeof maxRecords ===
|
|
554
|
+
if (maxRecords && typeof maxRecords === "number" && maxRecords > 0) {
|
|
535
555
|
const totalCount = await LogModel.select();
|
|
536
556
|
if (totalCount.length > maxRecords) {
|
|
537
557
|
const excessCount = totalCount - maxRecords;
|
|
538
|
-
const oldestLogs = await LogModel
|
|
539
|
-
.
|
|
540
|
-
.orderBy('timestamp', 'ASC')
|
|
558
|
+
const oldestLogs = await LogModel.select("id", "timestamp")
|
|
559
|
+
.orderBy("timestamp", "ASC")
|
|
541
560
|
.limit(excessCount);
|
|
542
561
|
const idsToDelete = oldestLogs.map((log) => log.id);
|
|
543
562
|
if (idsToDelete.length > 0) {
|
|
@@ -551,7 +570,7 @@ router.post('/api/logs/cleanup', async (ctx) => {
|
|
|
551
570
|
ctx.body = {
|
|
552
571
|
success: true,
|
|
553
572
|
message: `已清理 ${deletedCount} 条日志`,
|
|
554
|
-
deletedCount
|
|
573
|
+
deletedCount,
|
|
555
574
|
};
|
|
556
575
|
}
|
|
557
576
|
catch (error) {
|
|
@@ -563,38 +582,40 @@ router.post('/api/logs/cleanup', async (ctx) => {
|
|
|
563
582
|
// 上下文注册
|
|
564
583
|
// ============================================================================
|
|
565
584
|
register({
|
|
566
|
-
name:
|
|
585
|
+
name: "server",
|
|
567
586
|
description: "http server",
|
|
568
587
|
mounted(p) {
|
|
569
588
|
return new Promise((resolve) => {
|
|
570
589
|
server.listen({
|
|
571
|
-
host:
|
|
572
|
-
port:
|
|
590
|
+
host: "0.0.0.0",
|
|
591
|
+
port: port,
|
|
573
592
|
}, () => {
|
|
574
593
|
const address = server.address();
|
|
575
594
|
if (!address)
|
|
576
595
|
return;
|
|
577
|
-
const visitAddress = typeof address ===
|
|
596
|
+
const visitAddress = typeof address === "string"
|
|
597
|
+
? address
|
|
598
|
+
: `${address.address}:${address.port}`;
|
|
578
599
|
p.logger.info(`server is running at http://${visitAddress}`);
|
|
579
|
-
p.logger.info(
|
|
580
|
-
p.logger.info(
|
|
600
|
+
p.logger.info("your username is:", username);
|
|
601
|
+
p.logger.info("your password is:", password);
|
|
581
602
|
resolve(server);
|
|
582
603
|
});
|
|
583
604
|
});
|
|
584
605
|
},
|
|
585
606
|
dispose(s) {
|
|
586
607
|
s.close();
|
|
587
|
-
}
|
|
608
|
+
},
|
|
588
609
|
});
|
|
589
610
|
register({
|
|
590
611
|
name: "koa",
|
|
591
612
|
description: "koa instance",
|
|
592
|
-
value: koa
|
|
613
|
+
value: koa,
|
|
593
614
|
});
|
|
594
615
|
register({
|
|
595
|
-
name:
|
|
616
|
+
name: "router",
|
|
596
617
|
description: "koa router",
|
|
597
|
-
value: router
|
|
618
|
+
value: router,
|
|
598
619
|
});
|
|
599
620
|
koa.use(KoaBodyParser()).use(router.routes()).use(router.allowedMethods());
|
|
600
621
|
//# sourceMappingURL=index.js.map
|