feat: 添加工作空间存活管理功能,重置和清理超时任务,使用 dayjs 格式化时间

This commit is contained in:
2026-01-31 00:40:29 +08:00
parent 6e5a642ab2
commit 7a01339ef2
3 changed files with 75 additions and 36 deletions

View File

@@ -1,7 +1,7 @@
import { createSkill, tool } from '@kevisual/router'; import { createSkill, tool } from '@kevisual/router';
import { app, cnb } from '../../app.ts'; import { app, cnb } from '../../app.ts';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import dayjs from 'dayjs';
import { createKeepAlive } from '../../../src/keep.ts'; import { createKeepAlive } from '../../../src/keep.ts';
type AliveInfo = { type AliveInfo = {
@@ -92,12 +92,19 @@ app.route({
tags: [], tags: [],
} }
}).define(async (ctx) => { }).define(async (ctx) => {
const list = Array.from(keepAliveMap.entries()).map(([id, info]) => ({ const list = Array.from(keepAliveMap.entries()).map(([id, info]) => {
id, const now = Date.now();
wsUrl: (info as any).KeepAlive?.wsUrl, const duration = Math.floor((now - info.startTime) / 60000); // 分钟
startTime: info.startTime, return {
updatedTime: info.updatedTime id,
})); wsUrl: (info as any).KeepAlive?.wsUrl,
startTime: info.startTime,
startTimeStr: dayjs(info.startTime).format('YYYY-MM-DD HH:mm'),
updatedTime: info.updatedTime,
updatedTimeStr: dayjs(info.updatedTime).format('YYYY-MM-DD HH:mm'),
duration,
}
});
ctx.body = { list }; ctx.body = { list };
}).addTo(app); }).addTo(app);
@@ -153,3 +160,55 @@ app.route({
ctx.body = { content: `没有找到对应的工作空间保持存活任务` }; ctx.body = { content: `没有找到对应的工作空间保持存活任务` };
} }
}).addTo(app); }).addTo(app);
app.route({
path: 'cnb',
key: 'reset-keep-workspace-alive',
description: '对存活的工作空间startTime进行重置',
middleware: ['admin-auth'],
metadata: {
tags: [],
}
}).define(async (ctx) => {
const now = Date.now();
for (const info of keepAliveMap.values()) {
info.startTime = now;
}
ctx.body = { content: `已重置所有存活工作空间的开始时间` };
}).addTo(app);
app.route({
path: 'cnb',
key: 'clear-keep-workspace-alive',
description: '对存活的工作空间超过5小时的进行清理',
middleware: ['admin-auth'],
metadata: {
tags: [],
}
}).define(async (ctx) => {
const res = clearKeepAlive();
ctx.body = {
content: `已清理所有存活工作空间中超过5小时的任务` + (res.length ? `,清理项:${res.map(i => i.wsUrl).join(', ')}` : ''),
list: res
};
}).addTo(app);
const clearKeepAlive = () => {
const now = Date.now();
let clearedArr: { id: string; wsUrl: string }[] = [];
for (const [id, info] of keepAliveMap.entries()) {
if (now - info.startTime > FIVE_HOURS) {
console.log(`工作空间 ${(info as any).KeepAlive?.wsUrl} 超过5小时自动停止`);
info.KeepAlive?.disconnect?.();
keepAliveMap.delete(id);
clearedArr.push({ id, wsUrl: (info as any).KeepAlive?.wsUrl });
}
}
return clearedArr;
}
// 每5小时自动清理超时的keepAlive任务
const FIVE_HOURS = 5 * 60 * 60 * 1000;
setInterval(() => {
clearKeepAlive();
}, FIVE_HOURS);

View File

@@ -25,6 +25,7 @@
"@types/bun": "^1.3.8", "@types/bun": "^1.3.8",
"@types/node": "^25.1.0", "@types/node": "^25.1.0",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"dayjs": "^1.11.19",
"dotenv": "^17.2.3" "dotenv": "^17.2.3"
}, },
"publishConfig": { "publishConfig": {

37
pnpm-lock.yaml generated
View File

@@ -39,9 +39,6 @@ importers:
'@kevisual/ai': '@kevisual/ai':
specifier: ^0.0.24 specifier: ^0.0.24
version: 0.0.24 version: 0.0.24
'@kevisual/cnb':
specifier: ^0.0.10
version: 0.0.10(dotenv@17.2.3)(typescript@5.9.3)
'@kevisual/context': '@kevisual/context':
specifier: ^0.0.4 specifier: ^0.0.4
version: 0.0.4 version: 0.0.4
@@ -60,6 +57,9 @@ importers:
'@types/ws': '@types/ws':
specifier: ^8.18.1 specifier: ^8.18.1
version: 8.18.1 version: 8.18.1
dayjs:
specifier: ^1.11.19
version: 1.11.19
dotenv: dotenv:
specifier: ^17.2.3 specifier: ^17.2.3
version: 17.2.3 version: 17.2.3
@@ -80,9 +80,6 @@ packages:
'@kevisual/ai@0.0.24': '@kevisual/ai@0.0.24':
resolution: {integrity: sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw==} resolution: {integrity: sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw==}
'@kevisual/cnb@0.0.10':
resolution: {integrity: sha512-zwdJOj64FAkbUIGy+3CC+uQMUptWGf1uuQZXy0ZLfBfdFO2BcoHuzvomWhYxF0eexOBsLrz1F9jNWS0heBrp2Q==}
'@kevisual/context@0.0.4': '@kevisual/context@0.0.4':
resolution: {integrity: sha512-HJeLeZQLU+7tCluSfOyvkgKLs0HjCZrdJlZgEgKRSa8XTwZfMAUt6J7qZTbrZAHBlPtX68EPu/PI8JMCeu3WAQ==} resolution: {integrity: sha512-HJeLeZQLU+7tCluSfOyvkgKLs0HjCZrdJlZgEgKRSa8XTwZfMAUt6J7qZTbrZAHBlPtX68EPu/PI8JMCeu3WAQ==}
@@ -102,9 +99,6 @@ packages:
'@kevisual/query@0.0.38': '@kevisual/query@0.0.38':
resolution: {integrity: sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q==} resolution: {integrity: sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q==}
'@kevisual/router@0.0.63':
resolution: {integrity: sha512-rM/FELiNtTJkjb00sdQ2f8NpWHzfOwrtgQJhsX9Np9KdzAQ5dP6pI5nAHlWvU2QyrC1VH5IibUmsBIeMMV3nWg==}
'@kevisual/router@0.0.64': '@kevisual/router@0.0.64':
resolution: {integrity: sha512-EYz1MZxrltgySUL0Y+/MtZf2FEmqC5U8GmFAqvHNjgtS5FJdHpxRjo6zab4+0wSUlVyCxCpZXFY5vHB/g+nQBw==} resolution: {integrity: sha512-EYz1MZxrltgySUL0Y+/MtZf2FEmqC5U8GmFAqvHNjgtS5FJdHpxRjo6zab4+0wSUlVyCxCpZXFY5vHB/g+nQBw==}
@@ -339,6 +333,9 @@ packages:
crossws@0.3.5: crossws@0.3.5:
resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
dayjs@1.11.19:
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
deepmerge@4.3.1: deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -575,19 +572,6 @@ snapshots:
'@kevisual/permission': 0.0.3 '@kevisual/permission': 0.0.3
'@kevisual/query': 0.0.38 '@kevisual/query': 0.0.38
'@kevisual/cnb@0.0.10(dotenv@17.2.3)(typescript@5.9.3)':
dependencies:
'@kevisual/query': 0.0.38
'@kevisual/router': 0.0.63(typescript@5.9.3)
'@kevisual/use-config': 1.0.28(dotenv@17.2.3)
es-toolkit: 1.44.0
nanoid: 5.1.6
ws: '@kevisual/ws@8.19.0'
zod: 4.3.6
transitivePeerDependencies:
- dotenv
- typescript
'@kevisual/context@0.0.4': {} '@kevisual/context@0.0.4': {}
'@kevisual/dts@0.0.3(typescript@5.9.3)': '@kevisual/dts@0.0.3(typescript@5.9.3)':
@@ -613,13 +597,6 @@ snapshots:
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
'@kevisual/router@0.0.63(typescript@5.9.3)':
dependencies:
'@kevisual/dts': 0.0.3(typescript@5.9.3)
hono: 4.11.7
transitivePeerDependencies:
- typescript
'@kevisual/router@0.0.64(typescript@5.9.3)': '@kevisual/router@0.0.64(typescript@5.9.3)':
dependencies: dependencies:
'@kevisual/dts': 0.0.3(typescript@5.9.3) '@kevisual/dts': 0.0.3(typescript@5.9.3)
@@ -794,6 +771,8 @@ snapshots:
dependencies: dependencies:
uncrypto: 0.1.3 uncrypto: 0.1.3
dayjs@1.11.19: {}
deepmerge@4.3.1: {} deepmerge@4.3.1: {}
defu@6.1.4: {} defu@6.1.4: {}