切换mongodb到NeDB
This commit is contained in:
parent
d2c78b75d3
commit
8dcdf3508f
@ -31,10 +31,8 @@ TOS_REGION=cn-beijing
|
|||||||
TOS_ENDPOINT=tos-cn-beijing.volces.com
|
TOS_ENDPOINT=tos-cn-beijing.volces.com
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# 心跳服务配置
|
# 心跳配置已移除(使用 NeDB 本地存储)
|
||||||
# ===========================================
|
# ===========================================
|
||||||
HEARTBEAT_ENABLED=true
|
|
||||||
HEARTBEAT_INTERVAL=30
|
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# 使用方法:
|
# 使用方法:
|
||||||
|
|||||||
@ -4,7 +4,3 @@ name: jimeng-free-api
|
|||||||
host: '0.0.0.0'
|
host: '0.0.0.0'
|
||||||
# 服务绑定端口
|
# 服务绑定端口
|
||||||
port: 3302
|
port: 3302
|
||||||
|
|
||||||
heartbeat:
|
|
||||||
enabled: true
|
|
||||||
interval: 30
|
|
||||||
11
package.json
11
package.json
@ -20,6 +20,8 @@
|
|||||||
"author": "Vinlic",
|
"author": "Vinlic",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@seald-io/nedb": "^4.1.2",
|
||||||
|
"@volcengine/tos-sdk": "^2.6.7",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
"crc-32": "^1.2.2",
|
"crc-32": "^1.2.2",
|
||||||
@ -38,14 +40,13 @@
|
|||||||
"koa2-cors": "^2.0.6",
|
"koa2-cors": "^2.0.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mime": "^4.0.1",
|
"mime": "^4.0.1",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
"minimist": "^1.2.8",
|
"minimist": "^1.2.8",
|
||||||
|
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
"randomstring": "^1.3.0",
|
"randomstring": "^1.3.0",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"yaml": "^2.3.4",
|
"yaml": "^2.3.4"
|
||||||
"@volcengine/tos-sdk": "^2.6.7",
|
|
||||||
"mime-types": "^2.1.35",
|
|
||||||
"mongoose": "^8.0.3",
|
|
||||||
"node-cron": "^3.0.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/lodash": "^4.14.202",
|
"@types/lodash": "^4.14.202",
|
||||||
|
|||||||
@ -49,8 +49,10 @@ export async function createCompletion(
|
|||||||
const { model, width, height } = parseModel(_model);
|
const { model, width, height } = parseModel(_model);
|
||||||
logger.info(messages);
|
logger.info(messages);
|
||||||
|
|
||||||
|
const taskId = util.uuid();
|
||||||
const imageUrls = await generateImages(
|
const imageUrls = await generateImages(
|
||||||
model,
|
model,
|
||||||
|
taskId,
|
||||||
messages[messages.length - 1].content,
|
messages[messages.length - 1].content,
|
||||||
{
|
{
|
||||||
width,
|
width,
|
||||||
@ -135,8 +137,10 @@ export async function createCompletionStream(
|
|||||||
"\n\n"
|
"\n\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const taskId = util.uuid();
|
||||||
generateImages(
|
generateImages(
|
||||||
model,
|
model,
|
||||||
|
taskId,
|
||||||
messages[messages.length - 1].content,
|
messages[messages.length - 1].content,
|
||||||
{ width, height },
|
{ width, height },
|
||||||
refreshToken
|
refreshToken
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import Response from '@/lib/response/Response.ts';
|
import Response from '@/lib/response/Response.js';
|
||||||
import { HeartbeatService } from '@/lib/services/HeartbeatService.ts';
|
import Environment from '@/lib/environment.js';
|
||||||
import JimengServer from '@/lib/database/models/ServiceHeartbeat.ts';
|
// 心跳服务已移除
|
||||||
import logger from '@/lib/logger.ts';
|
import logger from '@/lib/logger.js';
|
||||||
|
import nedbCleanupService from '@/lib/services/NeDBCleanupService.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 获取当前服务器信息
|
// 获取当前服务器信息
|
||||||
@ -10,7 +11,14 @@ export default {
|
|||||||
// 获取所有活跃服务器
|
// 获取所有活跃服务器
|
||||||
'/servers/active': async () => {
|
'/servers/active': async () => {
|
||||||
try {
|
try {
|
||||||
const activeServers = await HeartbeatService.getActiveServers();
|
// 心跳服务已移除,返回当前服务器信息
|
||||||
|
const activeServers = [{
|
||||||
|
server_id: Environment.name || process.env.SERVICE_ID || 'jimeng-free-api',
|
||||||
|
server_name: Environment.name || process.env.SERVICE_NAME || 'jimeng-free-api',
|
||||||
|
host: Environment.host || process.env.SERVER_HOST || '0.0.0.0',
|
||||||
|
port: Environment.port || parseInt(process.env.SERVER_PORT || '3302'),
|
||||||
|
is_active: true
|
||||||
|
}];
|
||||||
|
|
||||||
return new Response({
|
return new Response({
|
||||||
success: true,
|
success: true,
|
||||||
@ -29,7 +37,14 @@ export default {
|
|||||||
// 获取所有在线服务器(基于心跳超时检查)
|
// 获取所有在线服务器(基于心跳超时检查)
|
||||||
'/servers/online': async () => {
|
'/servers/online': async () => {
|
||||||
try {
|
try {
|
||||||
const onlineServers = await HeartbeatService.getOnlineServers();
|
// 心跳服务已移除,返回当前服务器信息
|
||||||
|
const onlineServers = [{
|
||||||
|
server_id: Environment.name || process.env.SERVICE_ID || 'jimeng-free-api',
|
||||||
|
server_name: Environment.name || process.env.SERVICE_NAME || 'jimeng-free-api',
|
||||||
|
host: Environment.host || process.env.SERVER_HOST || '0.0.0.0',
|
||||||
|
port: Environment.port || parseInt(process.env.SERVER_PORT || '3302'),
|
||||||
|
is_active: true
|
||||||
|
}];
|
||||||
|
|
||||||
return new Response({
|
return new Response({
|
||||||
success: true,
|
success: true,
|
||||||
@ -48,36 +63,13 @@ export default {
|
|||||||
// 获取服务器状态统计
|
// 获取服务器状态统计
|
||||||
'/servers/stats': async () => {
|
'/servers/stats': async () => {
|
||||||
try {
|
try {
|
||||||
const stats = await JimengServer.aggregate([
|
// 心跳服务已移除,返回简单统计
|
||||||
{
|
const result = {
|
||||||
$group: {
|
totalServers: 1,
|
||||||
_id: null,
|
activeServers: 1,
|
||||||
totalServers: { $sum: 1 },
|
onlineServers: 1,
|
||||||
activeServers: {
|
healthRate: '100%'
|
||||||
$sum: {
|
};
|
||||||
$cond: [{ $eq: ['$is_active', true] }, 1, 0]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
avgHeartbeatInterval: { $avg: '$heartbeat_interval' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
|
||||||
const onlineServers = await JimengServer.countDocuments({
|
|
||||||
is_active: true,
|
|
||||||
$expr: {
|
|
||||||
$lte: [
|
|
||||||
{ $subtract: [currentTime, '$last_heartbeat'] },
|
|
||||||
{ $multiply: ['$heartbeat_interval', 1.5] }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = stats[0] || { totalServers: 0, activeServers: 0, avgHeartbeatInterval: 0 };
|
|
||||||
result.onlineServers = onlineServers;
|
|
||||||
result.healthRate = result.totalServers > 0 ?
|
|
||||||
((onlineServers / result.totalServers) * 100).toFixed(2) + '%' : '0%';
|
|
||||||
|
|
||||||
return new Response({
|
return new Response({
|
||||||
success: true,
|
success: true,
|
||||||
@ -96,29 +88,44 @@ export default {
|
|||||||
'/servers/:serverId': async (request) => {
|
'/servers/:serverId': async (request) => {
|
||||||
try {
|
try {
|
||||||
const serverId = request.params.serverId;
|
const serverId = request.params.serverId;
|
||||||
const server = await JimengServer.findOne({ server_id: serverId });
|
// 心跳服务已移除,返回当前服务器信息
|
||||||
|
const server = {
|
||||||
|
server_id: Environment.name || process.env.SERVICE_ID || 'jimeng-free-api',
|
||||||
|
server_name: Environment.name || process.env.SERVICE_NAME || 'jimeng-free-api',
|
||||||
|
host: Environment.host || process.env.SERVER_HOST || '0.0.0.0',
|
||||||
|
port: Environment.port || parseInt(process.env.SERVER_PORT || '3302'),
|
||||||
|
is_active: true,
|
||||||
|
isOnline: true
|
||||||
|
};
|
||||||
|
|
||||||
if (!server) {
|
return new Response({
|
||||||
|
success: true,
|
||||||
|
data: server
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to get server details:', error);
|
||||||
return new Response({
|
return new Response({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Server not found'
|
error: error.message
|
||||||
}, { statusCode: 404 });
|
}, { statusCode: 500 });
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 检查服务器是否在线
|
// 获取 NeDB 清理服务状态
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
'/nedb/cleanup/status': async () => {
|
||||||
const heartbeatTimeout = server.heartbeat_interval * 1.5;
|
try {
|
||||||
const isOnline = server.is_active && (currentTime - server.last_heartbeat) <= heartbeatTimeout;
|
const status = nedbCleanupService.getStatus();
|
||||||
|
|
||||||
return new Response({
|
return new Response({
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
...server.toObject(),
|
isRunning: status.isRunning,
|
||||||
isOnline
|
nextCleanup: status.nextCleanup ? new Date(status.nextCleanup).toISOString() : null,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to get server details:', error);
|
logger.error('Failed to get NeDB cleanup status:', error);
|
||||||
return new Response({
|
return new Response({
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message
|
error: error.message
|
||||||
@ -131,7 +138,7 @@ export default {
|
|||||||
// 手动清理离线服务器
|
// 手动清理离线服务器
|
||||||
'/servers/cleanup': async () => {
|
'/servers/cleanup': async () => {
|
||||||
try {
|
try {
|
||||||
await HeartbeatService.cleanupOfflineServers();
|
// 心跳服务已移除,无需清理
|
||||||
|
|
||||||
return new Response({
|
return new Response({
|
||||||
success: true,
|
success: true,
|
||||||
@ -146,33 +153,44 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 手动触发 NeDB 数据清理
|
||||||
|
'/nedb/cleanup': async () => {
|
||||||
|
try {
|
||||||
|
const result = await nedbCleanupService.manualCleanup();
|
||||||
|
|
||||||
|
return new Response({
|
||||||
|
success: true,
|
||||||
|
message: 'NeDB cleanup completed successfully',
|
||||||
|
data: {
|
||||||
|
expiredResults: result.expiredResults,
|
||||||
|
expiredTasks: result.expiredTasks,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to perform NeDB cleanup:', error);
|
||||||
|
return new Response({
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
}, { statusCode: 500 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 更新服务器心跳(为Python项目提供)
|
// 更新服务器心跳(为Python项目提供)
|
||||||
'/servers/:serverId/heartbeat': async (request) => {
|
'/servers/:serverId/heartbeat': async (request) => {
|
||||||
try {
|
try {
|
||||||
const serverId = request.params.serverId;
|
const serverId = request.params.serverId;
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
|
||||||
const result = await JimengServer.findOneAndUpdate(
|
// 心跳服务已移除,返回成功响应以保持兼容性
|
||||||
{ server_id: serverId },
|
|
||||||
{
|
|
||||||
last_heartbeat: currentTime,
|
|
||||||
updated_at: currentTime,
|
|
||||||
is_active: true
|
|
||||||
},
|
|
||||||
{ new: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
return new Response({
|
|
||||||
success: false,
|
|
||||||
error: 'Server not found'
|
|
||||||
}, { statusCode: 404 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response({
|
return new Response({
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Heartbeat updated successfully',
|
message: 'Heartbeat service disabled (using NeDB local storage)',
|
||||||
data: result
|
data: {
|
||||||
|
server_id: serverId,
|
||||||
|
updated_at: currentTime,
|
||||||
|
is_active: true
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to update server heartbeat:', error);
|
logger.error('Failed to update server heartbeat:', error);
|
||||||
|
|||||||
29
src/index.ts
29
src/index.ts
@ -6,8 +6,9 @@ import "@/lib/initialize.ts";
|
|||||||
import server from "@/lib/server.ts";
|
import server from "@/lib/server.ts";
|
||||||
import routes from "@/api/routes/index.ts";
|
import routes from "@/api/routes/index.ts";
|
||||||
import logger from "@/lib/logger.ts";
|
import logger from "@/lib/logger.ts";
|
||||||
import mongoDBManager from "@/lib/database/mongodb.ts";
|
import nedbManager from "@/lib/database/nedb.ts";
|
||||||
import heartbeatService from "@/lib/services/HeartbeatService.ts";
|
import nedbCleanupService from "@/lib/services/NeDBCleanupService.ts";
|
||||||
|
|
||||||
import taskPollingService from "@/lib/services/TaskPollingService.js";
|
import taskPollingService from "@/lib/services/TaskPollingService.js";
|
||||||
|
|
||||||
const startupTime = performance.now();
|
const startupTime = performance.now();
|
||||||
@ -23,30 +24,26 @@ const startupTime = performance.now();
|
|||||||
logger.info("Service host:", config.service.host);
|
logger.info("Service host:", config.service.host);
|
||||||
logger.info("Service port:", config.service.port);
|
logger.info("Service port:", config.service.port);
|
||||||
|
|
||||||
// 初始化MongoDB连接
|
// 初始化 NeDB 数据库
|
||||||
try {
|
try {
|
||||||
await mongoDBManager.connect();
|
await nedbManager.initialize();
|
||||||
logger.success("MongoDB connected successfully");
|
logger.success("NeDB database initialized");
|
||||||
|
|
||||||
|
// 启动数据清理服务
|
||||||
|
await nedbCleanupService.start();
|
||||||
|
logger.success("NeDB cleanup service started");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn("MongoDB connection failed, continuing without database:", error.message);
|
logger.warn("NeDB initialization failed, continuing without database:", error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
server.attachRoutes(routes);
|
server.attachRoutes(routes);
|
||||||
await server.listen();
|
await server.listen();
|
||||||
|
|
||||||
// 启动心跳服务
|
// 心跳服务已移除,使用 NeDB 本地存储
|
||||||
if (config.heartbeat?.enabled !== false && mongoDBManager.isMongoConnected()) {
|
|
||||||
try {
|
|
||||||
await heartbeatService.start();
|
|
||||||
logger.success("Heartbeat service started");
|
|
||||||
} catch (error) {
|
|
||||||
logger.warn("Failed to start heartbeat service:", error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启动任务轮询服务(仅在数据库模式下)
|
// 启动任务轮询服务(仅在数据库模式下)
|
||||||
const useDatabaseMode = process.env.USE_DATABASE_MODE === 'true';
|
const useDatabaseMode = process.env.USE_DATABASE_MODE === 'true';
|
||||||
if (useDatabaseMode && mongoDBManager.isMongoConnected()) {
|
if (useDatabaseMode) {
|
||||||
try {
|
try {
|
||||||
await taskPollingService.start();
|
await taskPollingService.start();
|
||||||
logger.success("Task polling service started");
|
logger.success("Task polling service started");
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import serviceConfig, { ServiceConfig } from "./configs/service-config.ts";
|
import serviceConfig, { ServiceConfig } from "./configs/service-config.js";
|
||||||
import systemConfig from "./configs/system-config.ts";
|
import systemConfig from "./configs/system-config.js";
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
|
|
||||||
@ -9,11 +9,7 @@ class Config {
|
|||||||
/** 系统配置 */
|
/** 系统配置 */
|
||||||
system = systemConfig;
|
system = systemConfig;
|
||||||
|
|
||||||
// 代理属性以便直接访问心跳和数据库配置
|
// 代理属性以便直接访问数据库配置
|
||||||
get heartbeat() {
|
|
||||||
return this.service.heartbeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
get database() {
|
get database() {
|
||||||
return this.service.database;
|
return this.service.database;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,21 +30,17 @@ export class ServiceConfig {
|
|||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/** 心跳配置 */
|
// 心跳服务已移除
|
||||||
heartbeat?: {
|
|
||||||
enabled: boolean;
|
|
||||||
interval: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(options?: any) {
|
constructor(options?: any) {
|
||||||
const { name, host, port, urlPrefix, bindAddress, database, heartbeat } = options || {};
|
const { name, host, port, urlPrefix, bindAddress, database } = options || {};
|
||||||
this.name = _.defaultTo(name, 'jimeng-free-api');
|
this.name = _.defaultTo(name, 'jimeng-free-api');
|
||||||
this.host = _.defaultTo(host, '0.0.0.0');
|
this.host = _.defaultTo(host, '0.0.0.0');
|
||||||
this.port = _.defaultTo(port, 5566);
|
this.port = _.defaultTo(port, 5566);
|
||||||
this.urlPrefix = _.defaultTo(urlPrefix, '');
|
this.urlPrefix = _.defaultTo(urlPrefix, '');
|
||||||
this.bindAddress = bindAddress;
|
this.bindAddress = bindAddress;
|
||||||
this.database = database;
|
this.database = database;
|
||||||
this.heartbeat = _.defaultTo(heartbeat, { enabled: true, interval: 30 });
|
// 心跳服务已移除
|
||||||
}
|
}
|
||||||
|
|
||||||
get addressHost() {
|
get addressHost() {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import mongoose, { Schema, Document } from 'mongoose';
|
import NeDBManager from '@/lib/database/nedb.js';
|
||||||
|
|
||||||
// 生成结果表数据模型
|
// 生成结果表数据模型
|
||||||
export interface IGenerationResult extends Document {
|
export interface IGenerationResult {
|
||||||
task_id: string; // 关联的任务ID,主键
|
task_id: string; // 关联的任务ID,主键
|
||||||
task_type: 'image' | 'video'; // 任务类型
|
task_type: 'image' | 'video'; // 任务类型
|
||||||
server_id: string; // 处理服务器ID
|
server_id: string; // 处理服务器ID
|
||||||
@ -31,67 +31,213 @@ export interface IGenerationResult extends Document {
|
|||||||
read_count: number; // 读取次数
|
read_count: number; // 读取次数
|
||||||
}
|
}
|
||||||
|
|
||||||
const GenerationResultSchema: Schema = new Schema({
|
// NeDB 数据操作类
|
||||||
task_id: {
|
export class GenerationResult {
|
||||||
type: String,
|
private static dbName = 'generation_results';
|
||||||
required: true,
|
|
||||||
unique: true
|
// 默认过期时间:24小时(86400秒)
|
||||||
},
|
private static DEFAULT_EXPIRY_SECONDS = 24 * 60 * 60;
|
||||||
task_type: {
|
|
||||||
type: String,
|
// 创建结果
|
||||||
required: true,
|
static async create(resultData: Omit<IGenerationResult, '_id'>): Promise<IGenerationResult> {
|
||||||
enum: ['image', 'video']
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
},
|
const result = {
|
||||||
server_id: {
|
...resultData,
|
||||||
type: String,
|
created_at: resultData.created_at || currentTime,
|
||||||
required: true
|
expires_at: resultData.expires_at || (currentTime + this.DEFAULT_EXPIRY_SECONDS),
|
||||||
},
|
is_read: resultData.is_read || false,
|
||||||
status: {
|
read_count: resultData.read_count || 0
|
||||||
type: String,
|
};
|
||||||
required: true,
|
|
||||||
enum: ['success', 'failed']
|
return await NeDBManager.insert(this.dbName, result);
|
||||||
},
|
|
||||||
original_urls: {
|
|
||||||
type: [String],
|
|
||||||
default: []
|
|
||||||
},
|
|
||||||
tos_urls: {
|
|
||||||
type: [String],
|
|
||||||
default: []
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
generation_time: Number,
|
|
||||||
tos_upload_time: Number,
|
|
||||||
total_files: { type: Number, required: true },
|
|
||||||
successful_uploads: { type: Number, required: true },
|
|
||||||
tos_upload_errors: [String],
|
|
||||||
fail_reason: String
|
|
||||||
},
|
|
||||||
created_at: {
|
|
||||||
type: Number,
|
|
||||||
default: () => Math.floor(Date.now() / 1000)
|
|
||||||
},
|
|
||||||
expires_at: {
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
is_read: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
first_read_at: {
|
|
||||||
type: Number
|
|
||||||
},
|
|
||||||
read_count: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
collection: 'jimeng_free_generation_results',
|
|
||||||
timestamps: false // 使用自定义时间戳
|
|
||||||
});
|
|
||||||
|
|
||||||
// 添加TTL索引,自动清理过期数据
|
// 查找单个结果
|
||||||
GenerationResultSchema.index({ expires_at: 1 }, { expireAfterSeconds: 0 });
|
static async findOne(query: any): Promise<IGenerationResult | null> {
|
||||||
|
// 自动过滤过期数据
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const filterQuery = {
|
||||||
|
...query,
|
||||||
|
expires_at: { $gt: currentTime }
|
||||||
|
};
|
||||||
|
|
||||||
export default mongoose.model<IGenerationResult>('GenerationResult', GenerationResultSchema);
|
return await NeDBManager.findOne(this.dbName, filterQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找多个结果
|
||||||
|
static async find(query: any, sort?: any, limit?: number): Promise<IGenerationResult[]> {
|
||||||
|
// 自动过滤过期数据
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const filterQuery = {
|
||||||
|
...query,
|
||||||
|
expires_at: { $gt: currentTime }
|
||||||
|
};
|
||||||
|
|
||||||
|
return await NeDBManager.find(this.dbName, filterQuery, sort, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新结果
|
||||||
|
static async updateOne(query: any, update: any, options: any = {}): Promise<number> {
|
||||||
|
// 自动过滤过期数据
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const filterQuery = {
|
||||||
|
...query,
|
||||||
|
expires_at: { $gt: currentTime }
|
||||||
|
};
|
||||||
|
|
||||||
|
return await NeDBManager.update(this.dbName, filterQuery, update, { ...options, multi: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量更新结果
|
||||||
|
static async updateMany(query: any, update: any, options: any = {}): Promise<number> {
|
||||||
|
// 自动过滤过期数据
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const filterQuery = {
|
||||||
|
...query,
|
||||||
|
expires_at: { $gt: currentTime }
|
||||||
|
};
|
||||||
|
|
||||||
|
return await NeDBManager.update(this.dbName, filterQuery, update, { ...options, multi: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除结果
|
||||||
|
static async deleteOne(query: any): Promise<number> {
|
||||||
|
return await NeDBManager.remove(this.dbName, query, { multi: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除结果
|
||||||
|
static async deleteMany(query: any): Promise<number> {
|
||||||
|
return await NeDBManager.remove(this.dbName, query, { multi: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量插入结果
|
||||||
|
static async insertMany(results: Omit<IGenerationResult, '_id'>[]): Promise<IGenerationResult[]> {
|
||||||
|
const insertedResults: IGenerationResult[] = [];
|
||||||
|
|
||||||
|
for (const resultData of results) {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const result = {
|
||||||
|
...resultData,
|
||||||
|
created_at: resultData.created_at || currentTime,
|
||||||
|
expires_at: resultData.expires_at || (currentTime + this.DEFAULT_EXPIRY_SECONDS),
|
||||||
|
is_read: resultData.is_read || false,
|
||||||
|
read_count: resultData.read_count || 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const insertedResult = await NeDBManager.insert(this.dbName, result);
|
||||||
|
insertedResults.push(insertedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
return insertedResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计数(排除过期数据)
|
||||||
|
static async countDocuments(query: any): Promise<number> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const filterQuery = {
|
||||||
|
...query,
|
||||||
|
expires_at: { $gt: currentTime }
|
||||||
|
};
|
||||||
|
|
||||||
|
return await NeDBManager.count(this.dbName, filterQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据任务ID查找结果并更新读取状态
|
||||||
|
static async findByTaskIdAndMarkRead(taskId: string): Promise<IGenerationResult | null> {
|
||||||
|
const result = await this.findOne({ task_id: taskId });
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
// 更新读取状态和计数
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
await this.updateOne(
|
||||||
|
{ task_id: taskId },
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
is_read: true,
|
||||||
|
first_read_at: result.first_read_at || currentTime
|
||||||
|
},
|
||||||
|
$inc: { read_count: 1 }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 返回更新后的结果
|
||||||
|
result.is_read = true;
|
||||||
|
result.first_read_at = result.first_read_at || currentTime;
|
||||||
|
result.read_count = (result.read_count || 0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理过期数据
|
||||||
|
static async cleanupExpiredResults(): Promise<number> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
|
||||||
|
return await this.deleteMany({
|
||||||
|
expires_at: { $lte: currentTime }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取即将过期的结果(用于预警)
|
||||||
|
static async getExpiringResults(warningSeconds: number = 3600): Promise<IGenerationResult[]> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const warningTime = currentTime + warningSeconds;
|
||||||
|
|
||||||
|
return await this.find({
|
||||||
|
expires_at: {
|
||||||
|
$gt: currentTime,
|
||||||
|
$lte: warningTime
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 延长结果过期时间
|
||||||
|
static async extendExpiry(taskId: string, additionalSeconds: number): Promise<boolean> {
|
||||||
|
const numUpdated = await this.updateOne(
|
||||||
|
{ task_id: taskId },
|
||||||
|
{
|
||||||
|
$inc: { expires_at: additionalSeconds }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return numUpdated > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取统计信息
|
||||||
|
static async getStats(): Promise<{
|
||||||
|
total: number;
|
||||||
|
unread: number;
|
||||||
|
read: number;
|
||||||
|
byType: { [key: string]: number };
|
||||||
|
byStatus: { [key: string]: number };
|
||||||
|
}> {
|
||||||
|
const allResults = await this.find({});
|
||||||
|
|
||||||
|
const stats = {
|
||||||
|
total: allResults.length,
|
||||||
|
unread: 0,
|
||||||
|
read: 0,
|
||||||
|
byType: {} as { [key: string]: number },
|
||||||
|
byStatus: {} as { [key: string]: number }
|
||||||
|
};
|
||||||
|
|
||||||
|
allResults.forEach(result => {
|
||||||
|
// 读取状态统计
|
||||||
|
if (!result.is_read) {
|
||||||
|
stats.unread++;
|
||||||
|
} else {
|
||||||
|
stats.read++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务类型统计
|
||||||
|
stats.byType[result.task_type] = (stats.byType[result.task_type] || 0) + 1;
|
||||||
|
|
||||||
|
// 最终状态统计
|
||||||
|
stats.byStatus[result.status] = (stats.byStatus[result.status] || 0) + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GenerationResult;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import mongoose, { Schema, Document } from 'mongoose';
|
import NeDBManager from '@/lib/database/nedb.js';
|
||||||
|
|
||||||
// 生成任务表数据模型
|
// 生成任务表数据模型
|
||||||
export interface IGenerationTask extends Document {
|
export interface IGenerationTask {
|
||||||
task_id: string; // 任务唯一标识,主键
|
task_id: string; // 任务唯一标识,主键
|
||||||
task_type: 'image' | 'video'; // 任务类型
|
task_type: 'image' | 'video'; // 任务类型
|
||||||
server_id: string; // 分配的服务器ID(外部分配,对应SERVICE_ID)
|
server_id: string; // 分配的服务器ID(外部分配,对应SERVICE_ID)
|
||||||
@ -55,85 +55,151 @@ export interface IGenerationTask extends Document {
|
|||||||
fail_code?: string; // 即梦平台返回的失败代码
|
fail_code?: string; // 即梦平台返回的失败代码
|
||||||
}
|
}
|
||||||
|
|
||||||
const GenerationTaskSchema: Schema = new Schema({
|
// NeDB 数据操作类
|
||||||
task_id: {
|
export class GenerationTask {
|
||||||
type: String,
|
private static dbName = 'generation_tasks';
|
||||||
required: true,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
task_type: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
enum: ['image', 'video']
|
|
||||||
},
|
|
||||||
server_id: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
original_params: {
|
|
||||||
model: String,
|
|
||||||
prompt: { type: String, required: true },
|
|
||||||
negative_prompt: String,
|
|
||||||
width: Number,
|
|
||||||
height: Number,
|
|
||||||
sample_strength: Number,
|
|
||||||
images: [{
|
|
||||||
url: String,
|
|
||||||
width: Number,
|
|
||||||
height: Number
|
|
||||||
}],
|
|
||||||
is_pro: Boolean,
|
|
||||||
duration: Number,
|
|
||||||
ratio: String,
|
|
||||||
response_format: String
|
|
||||||
},
|
|
||||||
internal_params: {
|
|
||||||
refresh_token: { type: String, required: true },
|
|
||||||
component_id: String,
|
|
||||||
history_id: String,
|
|
||||||
mapped_model: String,
|
|
||||||
submit_id: String
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
enum: ['pending', 'processing', 'polling', 'completed', 'failed'],
|
|
||||||
default: 'pending'
|
|
||||||
},
|
|
||||||
retry_count: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
max_retries: {
|
|
||||||
type: Number,
|
|
||||||
default: 3
|
|
||||||
},
|
|
||||||
next_poll_at: {
|
|
||||||
type: Number
|
|
||||||
},
|
|
||||||
poll_interval: {
|
|
||||||
type: Number,
|
|
||||||
default: 10
|
|
||||||
},
|
|
||||||
task_timeout: {
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
created_at: {
|
|
||||||
type: Number,
|
|
||||||
default: () => Math.floor(Date.now() / 1000)
|
|
||||||
},
|
|
||||||
updated_at: {
|
|
||||||
type: Number,
|
|
||||||
default: () => Math.floor(Date.now() / 1000)
|
|
||||||
},
|
|
||||||
started_at: Number,
|
|
||||||
completed_at: Number,
|
|
||||||
error_message: String,
|
|
||||||
fail_code: String
|
|
||||||
}, {
|
|
||||||
collection: 'jimeng_free_generation_tasks',
|
|
||||||
timestamps: false // 使用自定义时间戳
|
|
||||||
});
|
|
||||||
|
|
||||||
export default mongoose.model<IGenerationTask>('GenerationTask', GenerationTaskSchema);
|
// 创建任务
|
||||||
|
static async create(taskData: Omit<IGenerationTask, '_id'>): Promise<IGenerationTask> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
const task = {
|
||||||
|
...taskData,
|
||||||
|
created_at: taskData.created_at || currentTime,
|
||||||
|
updated_at: taskData.updated_at || currentTime,
|
||||||
|
status: taskData.status || 'pending',
|
||||||
|
retry_count: taskData.retry_count || 0,
|
||||||
|
max_retries: taskData.max_retries || 3,
|
||||||
|
poll_interval: taskData.poll_interval || 10
|
||||||
|
};
|
||||||
|
|
||||||
|
return await NeDBManager.insert(this.dbName, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找单个任务
|
||||||
|
static async findOne(query: any): Promise<IGenerationTask | null> {
|
||||||
|
return await NeDBManager.findOne(this.dbName, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找多个任务
|
||||||
|
static async find(query: any, sort?: any, limit?: number): Promise<IGenerationTask[]> {
|
||||||
|
return await NeDBManager.find(this.dbName, query, sort, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新任务
|
||||||
|
static async updateOne(query: any, update: any, options: any = {}): Promise<number> {
|
||||||
|
// 自动更新 updated_at 时间戳
|
||||||
|
if (update.$set) {
|
||||||
|
update.$set.updated_at = Math.floor(Date.now() / 1000);
|
||||||
|
} else {
|
||||||
|
update.updated_at = Math.floor(Date.now() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await NeDBManager.update(this.dbName, query, update, { ...options, multi: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量更新任务
|
||||||
|
static async updateMany(query: any, update: any, options: any = {}): Promise<number> {
|
||||||
|
// 自动更新 updated_at 时间戳
|
||||||
|
if (update.$set) {
|
||||||
|
update.$set.updated_at = Math.floor(Date.now() / 1000);
|
||||||
|
} else {
|
||||||
|
update.updated_at = Math.floor(Date.now() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await NeDBManager.update(this.dbName, query, update, { ...options, multi: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除任务
|
||||||
|
static async deleteOne(query: any): Promise<number> {
|
||||||
|
return await NeDBManager.remove(this.dbName, query, { multi: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除任务
|
||||||
|
static async deleteMany(query: any): Promise<number> {
|
||||||
|
return await NeDBManager.remove(this.dbName, query, { multi: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计数
|
||||||
|
static async countDocuments(query: any): Promise<number> {
|
||||||
|
return await NeDBManager.count(this.dbName, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原子更新任务状态
|
||||||
|
static async atomicUpdateTaskStatus(
|
||||||
|
taskId: string,
|
||||||
|
fromStatus: string,
|
||||||
|
toStatus: string,
|
||||||
|
additionalUpdate: any = {}
|
||||||
|
): Promise<boolean> {
|
||||||
|
const updateData = {
|
||||||
|
$set: {
|
||||||
|
status: toStatus,
|
||||||
|
updated_at: Math.floor(Date.now() / 1000),
|
||||||
|
...additionalUpdate
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const numUpdated = await this.updateOne(
|
||||||
|
{ task_id: taskId, status: fromStatus },
|
||||||
|
updateData
|
||||||
|
);
|
||||||
|
|
||||||
|
return numUpdated > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取待处理的任务批次
|
||||||
|
static async getBatchPendingTasks(
|
||||||
|
serverId: string,
|
||||||
|
limit: number = 10
|
||||||
|
): Promise<IGenerationTask[]> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
|
||||||
|
return await this.find(
|
||||||
|
{
|
||||||
|
server_id: serverId,
|
||||||
|
status: 'pending',
|
||||||
|
$or: [
|
||||||
|
{ next_poll_at: { $exists: false } },
|
||||||
|
{ next_poll_at: { $lte: currentTime } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ created_at: 1 }, // 按创建时间升序
|
||||||
|
limit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取需要轮询的任务
|
||||||
|
static async getPollingTasks(
|
||||||
|
serverId: string,
|
||||||
|
limit: number = 10
|
||||||
|
): Promise<IGenerationTask[]> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
|
||||||
|
return await this.find(
|
||||||
|
{
|
||||||
|
server_id: serverId,
|
||||||
|
status: 'polling',
|
||||||
|
next_poll_at: { $lte: currentTime }
|
||||||
|
},
|
||||||
|
{ next_poll_at: 1 }, // 按轮询时间升序
|
||||||
|
limit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取超时的任务
|
||||||
|
static async getTimeoutTasks(): Promise<IGenerationTask[]> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
|
||||||
|
return await this.find({
|
||||||
|
status: { $in: ['processing', 'polling'] },
|
||||||
|
$expr: {
|
||||||
|
$lt: [
|
||||||
|
{ $add: ['$created_at', '$task_timeout'] },
|
||||||
|
currentTime
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GenerationTask;
|
||||||
@ -1,54 +0,0 @@
|
|||||||
import mongoose, { Schema, Document } from 'mongoose';
|
|
||||||
|
|
||||||
// 即梦服务器配置表数据模型 - 适配Python项目
|
|
||||||
export interface IJimengServer extends Document {
|
|
||||||
server_id: string; // 服务器唯一标识
|
|
||||||
server_name: string; // 服务器名称
|
|
||||||
base_url: string; // 服务器基础URL
|
|
||||||
is_active: boolean; // 管理员控制开关
|
|
||||||
last_heartbeat: number; // 最后心跳时间戳(秒)
|
|
||||||
heartbeat_interval: number; // 心跳间隔(秒)
|
|
||||||
created_at: number; // 创建时间戳(秒)
|
|
||||||
updated_at: number; // 更新时间戳(秒)
|
|
||||||
}
|
|
||||||
|
|
||||||
const JimengServerSchema: Schema = new Schema({
|
|
||||||
server_id: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
server_name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
base_url: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
is_active: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
last_heartbeat: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
heartbeat_interval: {
|
|
||||||
type: Number,
|
|
||||||
default: 60
|
|
||||||
},
|
|
||||||
created_at: {
|
|
||||||
type: Number,
|
|
||||||
default: () => Math.floor(Date.now() / 1000)
|
|
||||||
},
|
|
||||||
updated_at: {
|
|
||||||
type: Number,
|
|
||||||
default: () => Math.floor(Date.now() / 1000)
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
collection: 'jimeng_servers',
|
|
||||||
timestamps: false // 使用自定义时间戳
|
|
||||||
});
|
|
||||||
|
|
||||||
export default mongoose.model<IJimengServer>('JimengServer', JimengServerSchema);
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
import mongoose from 'mongoose';
|
|
||||||
import logger from '@/lib/logger.ts';
|
|
||||||
import config from '@/lib/config.ts';
|
|
||||||
|
|
||||||
class MongoDBManager {
|
|
||||||
private static instance: MongoDBManager;
|
|
||||||
private isConnected: boolean = false;
|
|
||||||
|
|
||||||
private constructor() {}
|
|
||||||
|
|
||||||
public static getInstance(): MongoDBManager {
|
|
||||||
if (!MongoDBManager.instance) {
|
|
||||||
MongoDBManager.instance = new MongoDBManager();
|
|
||||||
}
|
|
||||||
return MongoDBManager.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async connect(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const mongoUrl = process.env.MONGODB_URL || config.database?.mongodb?.url || 'mongodb://localhost:27017/jimeng-api';
|
|
||||||
|
|
||||||
await mongoose.connect(mongoUrl, {
|
|
||||||
maxPoolSize: 10,
|
|
||||||
serverSelectionTimeoutMS: 5000,
|
|
||||||
socketTimeoutMS: 45000,
|
|
||||||
bufferCommands: false
|
|
||||||
});
|
|
||||||
|
|
||||||
this.isConnected = true;
|
|
||||||
logger.success('MongoDB connected successfully');
|
|
||||||
|
|
||||||
// 监听连接事件
|
|
||||||
mongoose.connection.on('error', (err) => {
|
|
||||||
logger.error('MongoDB connection error:', err);
|
|
||||||
this.isConnected = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
mongoose.connection.on('disconnected', () => {
|
|
||||||
logger.warn('MongoDB disconnected');
|
|
||||||
this.isConnected = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
mongoose.connection.on('reconnected', () => {
|
|
||||||
logger.success('MongoDB reconnected');
|
|
||||||
this.isConnected = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('MongoDB connection failed:', error);
|
|
||||||
this.isConnected = false;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async disconnect(): Promise<void> {
|
|
||||||
try {
|
|
||||||
await mongoose.disconnect();
|
|
||||||
this.isConnected = false;
|
|
||||||
logger.info('MongoDB disconnected');
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('MongoDB disconnect error:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public isMongoConnected(): boolean {
|
|
||||||
return this.isConnected && mongoose.connection.readyState === 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getConnection() {
|
|
||||||
return mongoose.connection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MongoDBManager.getInstance();
|
|
||||||
250
src/lib/database/nedb.ts
Normal file
250
src/lib/database/nedb.ts
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
import Datastore from '@seald-io/nedb';
|
||||||
|
import logger from '@/lib/logger.js';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
type DatastoreInstance = any;
|
||||||
|
|
||||||
|
class NeDBManager {
|
||||||
|
private static instance: NeDBManager;
|
||||||
|
private databases: Map<string, DatastoreInstance> = new Map();
|
||||||
|
private isInitialized: boolean = false;
|
||||||
|
private dataDir: string;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
this.dataDir = path.resolve('./data');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): NeDBManager {
|
||||||
|
if (!NeDBManager.instance) {
|
||||||
|
NeDBManager.instance = new NeDBManager();
|
||||||
|
}
|
||||||
|
return NeDBManager.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async initialize(): Promise<void> {
|
||||||
|
try {
|
||||||
|
// 确保数据目录存在
|
||||||
|
// 确保目录存在
|
||||||
|
if (!fs.existsSync(this.dataDir)) {
|
||||||
|
fs.mkdirSync(this.dataDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化数据库
|
||||||
|
await this.initDatabase('generation_tasks', {
|
||||||
|
filename: path.join(this.dataDir, 'generation_tasks.db'),
|
||||||
|
autoload: true,
|
||||||
|
timestampData: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.initDatabase('generation_results', {
|
||||||
|
filename: path.join(this.dataDir, 'generation_results.db'),
|
||||||
|
autoload: true,
|
||||||
|
timestampData: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建索引
|
||||||
|
await this.createIndexes();
|
||||||
|
|
||||||
|
this.isInitialized = true;
|
||||||
|
logger.success('NeDB initialized successfully');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('NeDB initialization failed:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initDatabase(name: string, options: any): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const db = new (Datastore as any)(options);
|
||||||
|
|
||||||
|
db.loadDatabase((err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
this.databases.set(name, db);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createIndexes(): Promise<void> {
|
||||||
|
const tasksDb = this.getDatabase('generation_tasks');
|
||||||
|
const resultsDb = this.getDatabase('generation_results');
|
||||||
|
|
||||||
|
// 为任务表创建索引
|
||||||
|
await this.ensureIndex(tasksDb, { fieldName: 'task_id', unique: true });
|
||||||
|
await this.ensureIndex(tasksDb, { fieldName: 'status' });
|
||||||
|
await this.ensureIndex(tasksDb, { fieldName: 'server_id' });
|
||||||
|
await this.ensureIndex(tasksDb, { fieldName: 'created_at' });
|
||||||
|
await this.ensureIndex(tasksDb, { fieldName: 'next_poll_at' });
|
||||||
|
|
||||||
|
// 为结果表创建索引
|
||||||
|
await this.ensureIndex(resultsDb, { fieldName: 'task_id', unique: true });
|
||||||
|
await this.ensureIndex(resultsDb, { fieldName: 'expires_at' });
|
||||||
|
await this.ensureIndex(resultsDb, { fieldName: 'created_at' });
|
||||||
|
|
||||||
|
logger.info('NeDB indexes created successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ensureIndex(db: DatastoreInstance, options: any): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.ensureIndex(options, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDatabase(name: string): DatastoreInstance {
|
||||||
|
const db = this.databases.get(name);
|
||||||
|
if (!db) {
|
||||||
|
throw new Error(`Database ${name} not found`);
|
||||||
|
}
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isNeDBConnected(): boolean {
|
||||||
|
return this.isInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async disconnect(): Promise<void> {
|
||||||
|
// NeDB 不需要显式断开连接,但可以清理资源
|
||||||
|
this.databases.clear();
|
||||||
|
this.isInitialized = false;
|
||||||
|
logger.info('NeDB disconnected');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据库操作辅助方法
|
||||||
|
public async insert(dbName: string, doc: any): Promise<any> {
|
||||||
|
const db = this.getDatabase(dbName);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.insert(doc, (err, newDoc) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(newDoc);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findOne(dbName: string, query: any): Promise<any> {
|
||||||
|
const db = this.getDatabase(dbName);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.findOne(query, (err, doc) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(doc);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async find(dbName: string, query: any, sort?: any, limit?: number): Promise<any[]> {
|
||||||
|
const db = this.getDatabase(dbName);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let cursor = db.find(query);
|
||||||
|
|
||||||
|
if (sort) {
|
||||||
|
cursor = cursor.sort(sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit) {
|
||||||
|
cursor = cursor.limit(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.exec((err, docs) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(docs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async update(dbName: string, query: any, update: any, options: any = {}): Promise<number> {
|
||||||
|
const db = this.getDatabase(dbName);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.update(query, update, options, (err, numReplaced) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(numReplaced);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async remove(dbName: string, query: any, options: any = {}): Promise<number> {
|
||||||
|
const db = this.getDatabase(dbName);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.remove(query, options, (err, numRemoved) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(numRemoved);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async count(dbName: string, query: any): Promise<number> {
|
||||||
|
const db = this.getDatabase(dbName);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.count(query, (err, count) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(count);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据过期清理方法
|
||||||
|
public async cleanupExpiredData(): Promise<void> {
|
||||||
|
const currentTime = Math.floor(Date.now() / 1000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 清理过期的结果数据
|
||||||
|
const removedResults = await this.remove('generation_results', {
|
||||||
|
expires_at: { $lt: currentTime }
|
||||||
|
}, { multi: true });
|
||||||
|
|
||||||
|
// 清理超过24小时的已完成任务
|
||||||
|
const taskExpireTime = currentTime - (24 * 60 * 60); // 24小时前
|
||||||
|
const removedTasks = await this.remove('generation_tasks', {
|
||||||
|
$and: [
|
||||||
|
{ status: { $in: ['completed', 'failed'] } },
|
||||||
|
{ updated_at: { $lt: taskExpireTime } }
|
||||||
|
]
|
||||||
|
}, { multi: true });
|
||||||
|
|
||||||
|
if (removedResults > 0 || removedTasks > 0) {
|
||||||
|
logger.info(`Cleaned up expired data: ${removedResults} results, ${removedTasks} tasks`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to cleanup expired data:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动定期清理
|
||||||
|
public startPeriodicCleanup(): void {
|
||||||
|
// 每小时清理一次过期数据
|
||||||
|
setInterval(() => {
|
||||||
|
this.cleanupExpiredData();
|
||||||
|
}, 60 * 60 * 1000); // 1小时
|
||||||
|
|
||||||
|
logger.info('Periodic cleanup started for NeDB');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NeDBManager.getInstance();
|
||||||
@ -1,5 +1,7 @@
|
|||||||
import GenerationTask, { IGenerationTask } from '@/lib/database/models/GenerationTask.js';
|
import GenerationTask, { IGenerationTask } from '@/lib/database/models/GenerationTask.js';
|
||||||
import GenerationResult, { IGenerationResult } from '@/lib/database/models/GenerationResult.js';
|
import GenerationResult, { IGenerationResult } from '@/lib/database/models/GenerationResult.js';
|
||||||
|
import NeDBManager from '@/lib/database/nedb.js';
|
||||||
|
import Environment from '@/lib/environment.js';
|
||||||
import logger from '@/lib/logger.js';
|
import logger from '@/lib/logger.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,6 +41,9 @@ export class DatabaseGenerationService {
|
|||||||
refreshToken: string
|
refreshToken: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
// 确保 NeDB 数据库初始化
|
||||||
|
await NeDBManager.initialize();
|
||||||
|
|
||||||
const currentServerId = this.currentServerId;
|
const currentServerId = this.currentServerId;
|
||||||
const imageTimeout = parseInt(process.env.IMAGE_TASK_TIMEOUT || '3600');
|
const imageTimeout = parseInt(process.env.IMAGE_TASK_TIMEOUT || '3600');
|
||||||
|
|
||||||
@ -103,6 +108,9 @@ export class DatabaseGenerationService {
|
|||||||
refreshToken: string
|
refreshToken: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
// 确保 NeDB 数据库初始化
|
||||||
|
await NeDBManager.initialize();
|
||||||
|
|
||||||
const currentServerId = this.currentServerId;
|
const currentServerId = this.currentServerId;
|
||||||
const videoTimeout = parseInt(process.env.VIDEO_TASK_TIMEOUT || '86400');
|
const videoTimeout = parseInt(process.env.VIDEO_TASK_TIMEOUT || '86400');
|
||||||
|
|
||||||
@ -151,6 +159,9 @@ export class DatabaseGenerationService {
|
|||||||
*/
|
*/
|
||||||
async queryTaskResult(taskId: string): Promise<any> {
|
async queryTaskResult(taskId: string): Promise<any> {
|
||||||
try {
|
try {
|
||||||
|
// 确保 NeDB 数据库初始化
|
||||||
|
await NeDBManager.initialize();
|
||||||
|
|
||||||
// 1. 先查询结果表
|
// 1. 先查询结果表
|
||||||
const result = await GenerationResult.findOne({ task_id: taskId });
|
const result = await GenerationResult.findOne({ task_id: taskId });
|
||||||
|
|
||||||
@ -223,7 +234,8 @@ export class DatabaseGenerationService {
|
|||||||
// 发生错误时返回任务不存在状态
|
// 发生错误时返回任务不存在状态
|
||||||
return {
|
return {
|
||||||
created: Math.floor(Date.now() / 1000),
|
created: Math.floor(Date.now() / 1000),
|
||||||
data: { task_id: taskId, url: "", status: 0 }
|
data: { task_id: taskId, url: "", status: 0 },
|
||||||
|
message: `接口报错 taskId: ${taskId} error:${error.message}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,21 +247,15 @@ export class DatabaseGenerationService {
|
|||||||
try {
|
try {
|
||||||
const filter = serverId ? { server_id: serverId } : {};
|
const filter = serverId ? { server_id: serverId } : {};
|
||||||
|
|
||||||
const stats = await GenerationTask.aggregate([
|
// NeDB不支持aggregate,使用find替代
|
||||||
{ $match: filter },
|
const allTasks = await GenerationTask.find(filter);
|
||||||
{
|
const stats = allTasks.reduce((acc, task) => {
|
||||||
$group: {
|
acc[task.status] = (acc[task.status] || 0) + 1;
|
||||||
_id: "$status",
|
|
||||||
count: { $sum: 1 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const result = stats.reduce((acc, curr) => {
|
|
||||||
acc[curr._id] = curr.count;
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number>);
|
}, {} as Record<string, number>);
|
||||||
|
|
||||||
|
const result = stats;
|
||||||
|
|
||||||
// 添加服务器负载信息
|
// 添加服务器负载信息
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
result['server_load'] = await this.getServerLoad(serverId);
|
result['server_load'] = await this.getServerLoad(serverId);
|
||||||
@ -307,7 +313,7 @@ export class DatabaseGenerationService {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const cancelled = result.modifiedCount > 0;
|
const cancelled = result > 0;
|
||||||
|
|
||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
logger.info(`Task cancelled: ${taskId}`);
|
logger.info(`Task cancelled: ${taskId}`);
|
||||||
@ -331,7 +337,9 @@ export class DatabaseGenerationService {
|
|||||||
fail_reason: 'Task cancelled by user'
|
fail_reason: 'Task cancelled by user'
|
||||||
},
|
},
|
||||||
created_at: currentTime,
|
created_at: currentTime,
|
||||||
expires_at: expireTime
|
expires_at: expireTime,
|
||||||
|
is_read: false,
|
||||||
|
read_count: 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,8 +372,8 @@ export class DatabaseGenerationService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const cleanupStats = {
|
const cleanupStats = {
|
||||||
tasks: taskResult.deletedCount || 0,
|
tasks: taskResult || 0,
|
||||||
results: resultResult.deletedCount || 0
|
results: resultResult || 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cleanupStats.tasks > 0 || cleanupStats.results > 0) {
|
if (cleanupStats.tasks > 0 || cleanupStats.results > 0) {
|
||||||
|
|||||||
@ -1,223 +0,0 @@
|
|||||||
import * as cron from 'node-cron';
|
|
||||||
import * as os from 'os';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
import JimengServer, { IJimengServer } from '@/lib/database/models/ServiceHeartbeat.ts';
|
|
||||||
import mongoDBManager from '@/lib/database/mongodb.ts';
|
|
||||||
import logger from '@/lib/logger.ts';
|
|
||||||
import config from '@/lib/config.ts';
|
|
||||||
import environment from '@/lib/environment.ts';
|
|
||||||
|
|
||||||
export class HeartbeatService {
|
|
||||||
private static instance: HeartbeatService;
|
|
||||||
private serverId: string;
|
|
||||||
private serverName: string;
|
|
||||||
private heartbeatTask: cron.ScheduledTask | null = null;
|
|
||||||
private isRunning: boolean = false;
|
|
||||||
|
|
||||||
private constructor() {
|
|
||||||
// 优先从环境变量获取配置,否则使用默认值
|
|
||||||
this.serverId = process.env.SERVICE_ID || config.service?.name || 'jimeng-free-api';
|
|
||||||
this.serverName = process.env.SERVICE_NAME || this.serverId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getInstance(): HeartbeatService {
|
|
||||||
if (!HeartbeatService.instance) {
|
|
||||||
HeartbeatService.instance = new HeartbeatService();
|
|
||||||
}
|
|
||||||
return HeartbeatService.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
|
||||||
if (this.isRunning) {
|
|
||||||
logger.warn('Heartbeat service is already running');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 确保MongoDB连接可用
|
|
||||||
if (!mongoDBManager.isMongoConnected()) {
|
|
||||||
logger.warn('MongoDB not connected, skipping heartbeat service');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化或更新服务器信息
|
|
||||||
await this.registerServer();
|
|
||||||
|
|
||||||
// 立即发送第一次心跳
|
|
||||||
await this.sendHeartbeat();
|
|
||||||
|
|
||||||
// 设置定时任务 - 每60秒发送一次心跳(适配 Python 项目的默认间隔)
|
|
||||||
const heartbeatInterval = process.env.HEARTBEAT_INTERVAL || config.heartbeat?.interval || 60;
|
|
||||||
const cronExpression = `*/${heartbeatInterval} * * * * *`; // 每 N 秒
|
|
||||||
|
|
||||||
this.heartbeatTask = cron.schedule(cronExpression, async () => {
|
|
||||||
try {
|
|
||||||
await this.sendHeartbeat();
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Heartbeat failed:', error);
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
scheduled: false
|
|
||||||
});
|
|
||||||
|
|
||||||
this.heartbeatTask.start();
|
|
||||||
this.isRunning = true;
|
|
||||||
|
|
||||||
logger.success(`Heartbeat service started for server: ${this.serverId}`);
|
|
||||||
|
|
||||||
// 监听进程退出事件
|
|
||||||
process.on('SIGINT', () => this.gracefulShutdown());
|
|
||||||
process.on('SIGTERM', () => this.gracefulShutdown());
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to start heartbeat service:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async stop(): Promise<void> {
|
|
||||||
if (!this.isRunning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.heartbeatTask) {
|
|
||||||
this.heartbeatTask.stop();
|
|
||||||
this.heartbeatTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标记服务为非活跃状态
|
|
||||||
await this.markInactive();
|
|
||||||
|
|
||||||
this.isRunning = false;
|
|
||||||
logger.info('Heartbeat service stopped');
|
|
||||||
}
|
|
||||||
|
|
||||||
private async registerServer(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
|
||||||
|
|
||||||
// 检查服务器是否已经存在
|
|
||||||
const existingServer = await JimengServer.findOne({ server_id: this.serverId });
|
|
||||||
|
|
||||||
if (!existingServer) {
|
|
||||||
logger.error(`服务器信息不存在 serverId: ${this.serverId}`);
|
|
||||||
// } else {
|
|
||||||
// // 服务器已存在:只更新心跳相关字段,不修改 base_url
|
|
||||||
// await JimengServer.findOneAndUpdate(
|
|
||||||
// { server_id: this.serverId },
|
|
||||||
// {
|
|
||||||
// server_name: this.serverName, // 允许更新服务器名称
|
|
||||||
// is_active: true,
|
|
||||||
// last_heartbeat: currentTime,
|
|
||||||
// heartbeat_interval: parseInt(process.env.HEARTBEAT_INTERVAL || '60'),
|
|
||||||
// updated_at: currentTime
|
|
||||||
// // 注意:不更新 base_url,这由其他服务维护
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// logger.info(`Server re-registered: ${this.serverId} (base_url preserved)`);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to register server:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async sendHeartbeat(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
|
||||||
|
|
||||||
// 只更新心跳相关字段,不修改 base_url 等其他服务信息
|
|
||||||
await JimengServer.findOneAndUpdate(
|
|
||||||
{ server_id: this.serverId },
|
|
||||||
{
|
|
||||||
last_heartbeat: currentTime, // 更新最后心跳时间
|
|
||||||
updated_at: currentTime, // 更新记录修改时间
|
|
||||||
// is_active: true // 标记为活跃状态
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// logger.debug(`Heartbeat sent for server ${this.serverId}`);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to send heartbeat:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async markInactive(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
|
||||||
|
|
||||||
await JimengServer.findOneAndUpdate(
|
|
||||||
{ server_id: this.serverId },
|
|
||||||
{
|
|
||||||
is_active: false,
|
|
||||||
updated_at: currentTime
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.info(`Server ${this.serverId} marked as inactive`);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to mark server as inactive:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async gracefulShutdown(): Promise<void> {
|
|
||||||
logger.info('Received shutdown signal, stopping heartbeat service...');
|
|
||||||
await this.stop();
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取所有活跃服务器
|
|
||||||
public static async getActiveServers(): Promise<IJimengServer[]> {
|
|
||||||
try {
|
|
||||||
return await JimengServer.find({
|
|
||||||
is_active: true
|
|
||||||
}).sort({ last_heartbeat: -1 });
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to get active servers:', error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取在线服务器(基于心跳超时检查)
|
|
||||||
public static async getOnlineServers(): Promise<IJimengServer[]> {
|
|
||||||
try {
|
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
|
||||||
const timeoutFactor = 1.5; // 超时倍数
|
|
||||||
|
|
||||||
const servers = await JimengServer.find({ is_active: true });
|
|
||||||
|
|
||||||
return servers.filter(server => {
|
|
||||||
const heartbeatTimeout = server.heartbeat_interval * timeoutFactor;
|
|
||||||
return (currentTime - server.last_heartbeat) <= heartbeatTimeout;
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to get online servers:', error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理离线服务器记录
|
|
||||||
public static async cleanupOfflineServers(): Promise<void> {
|
|
||||||
try {
|
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
|
||||||
const cleanupTimeout = 24 * 60 * 60; // 24小时
|
|
||||||
|
|
||||||
await JimengServer.deleteMany({
|
|
||||||
$or: [
|
|
||||||
{ is_active: false },
|
|
||||||
{ last_heartbeat: { $lt: currentTime - cleanupTimeout } }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.debug('Cleaned up offline servers');
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to cleanup offline servers:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default HeartbeatService.getInstance();
|
|
||||||
159
src/lib/services/NeDBCleanupService.ts
Normal file
159
src/lib/services/NeDBCleanupService.ts
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import logger from '@/lib/logger.js';
|
||||||
|
import { GenerationTask } from '@/lib/database/models/GenerationTask.js';
|
||||||
|
import { GenerationResult } from '@/lib/database/models/GenerationResult.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NeDB 数据清理服务
|
||||||
|
* 负责定期清理过期的任务和结果数据
|
||||||
|
*/
|
||||||
|
export class NeDBCleanupService {
|
||||||
|
private static instance: NeDBCleanupService;
|
||||||
|
private cleanupInterval: NodeJS.Timeout | null = null;
|
||||||
|
private readonly CLEANUP_INTERVAL = 60 * 60 * 1000; // 1小时清理一次
|
||||||
|
private isRunning = false;
|
||||||
|
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
static getInstance(): NeDBCleanupService {
|
||||||
|
if (!NeDBCleanupService.instance) {
|
||||||
|
NeDBCleanupService.instance = new NeDBCleanupService();
|
||||||
|
}
|
||||||
|
return NeDBCleanupService.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动清理服务
|
||||||
|
*/
|
||||||
|
async start(): Promise<void> {
|
||||||
|
if (this.isRunning) {
|
||||||
|
logger.warn('NeDB cleanup service is already running');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isRunning = true;
|
||||||
|
logger.info('Starting NeDB cleanup service...');
|
||||||
|
|
||||||
|
// 立即执行一次清理
|
||||||
|
await this.performCleanup();
|
||||||
|
|
||||||
|
// 设置定期清理
|
||||||
|
this.cleanupInterval = setInterval(async () => {
|
||||||
|
await this.performCleanup();
|
||||||
|
}, this.CLEANUP_INTERVAL);
|
||||||
|
|
||||||
|
logger.success('NeDB cleanup service started successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止清理服务
|
||||||
|
*/
|
||||||
|
stop(): void {
|
||||||
|
if (!this.isRunning) {
|
||||||
|
logger.warn('NeDB cleanup service is not running');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cleanupInterval) {
|
||||||
|
clearInterval(this.cleanupInterval);
|
||||||
|
this.cleanupInterval = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isRunning = false;
|
||||||
|
logger.info('NeDB cleanup service stopped');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行清理操作
|
||||||
|
*/
|
||||||
|
private async performCleanup(): Promise<void> {
|
||||||
|
try {
|
||||||
|
logger.info('Starting NeDB data cleanup...');
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
// 清理过期的结果数据
|
||||||
|
const expiredResultsCount = await this.cleanupExpiredResults();
|
||||||
|
|
||||||
|
// 清理过期的任务数据
|
||||||
|
const expiredTasksCount = await this.cleanupExpiredTasks();
|
||||||
|
|
||||||
|
const duration = Date.now() - startTime;
|
||||||
|
logger.success(
|
||||||
|
`NeDB cleanup completed in ${duration}ms. ` +
|
||||||
|
`Cleaned ${expiredResultsCount} expired results and ${expiredTasksCount} expired tasks`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to perform NeDB cleanup:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期的结果数据
|
||||||
|
*/
|
||||||
|
private async cleanupExpiredResults(): Promise<number> {
|
||||||
|
try {
|
||||||
|
const deletedCount = await GenerationResult.cleanupExpiredResults();
|
||||||
|
if (deletedCount > 0) {
|
||||||
|
logger.info(`Cleaned up ${deletedCount} expired generation results`);
|
||||||
|
}
|
||||||
|
return deletedCount;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to cleanup expired results:', error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期的任务数据
|
||||||
|
* 清理超过 7 天的已完成任务
|
||||||
|
*/
|
||||||
|
private async cleanupExpiredTasks(): Promise<number> {
|
||||||
|
try {
|
||||||
|
const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
// 删除超过 7 天的已完成任务
|
||||||
|
const deletedCount = await GenerationTask.deleteMany({
|
||||||
|
status: { $in: ['completed', 'failed', 'cancelled'] },
|
||||||
|
created_at: { $lt: sevenDaysAgo }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (deletedCount > 0) {
|
||||||
|
logger.info(`Cleaned up ${deletedCount} expired generation tasks`);
|
||||||
|
}
|
||||||
|
return deletedCount;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to cleanup expired tasks:', error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动触发清理
|
||||||
|
*/
|
||||||
|
async manualCleanup(): Promise<{ expiredResults: number; expiredTasks: number }> {
|
||||||
|
logger.info('Manual NeDB cleanup triggered');
|
||||||
|
|
||||||
|
const expiredResults = await this.cleanupExpiredResults();
|
||||||
|
const expiredTasks = await this.cleanupExpiredTasks();
|
||||||
|
|
||||||
|
return { expiredResults, expiredTasks };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取清理服务状态
|
||||||
|
*/
|
||||||
|
getStatus(): { isRunning: boolean; nextCleanup?: number } {
|
||||||
|
const status: { isRunning: boolean; nextCleanup?: number } = {
|
||||||
|
isRunning: this.isRunning
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.isRunning && this.cleanupInterval) {
|
||||||
|
// 估算下次清理时间(不完全准确,但提供参考)
|
||||||
|
status.nextCleanup = Date.now() + this.CLEANUP_INTERVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出单例实例
|
||||||
|
export default NeDBCleanupService.getInstance();
|
||||||
@ -3,7 +3,7 @@ import path from 'path';
|
|||||||
import { formatInTimeZone } from 'date-fns-tz';
|
import { formatInTimeZone } from 'date-fns-tz';
|
||||||
import GenerationTask, { IGenerationTask } from '@/lib/database/models/GenerationTask.js';
|
import GenerationTask, { IGenerationTask } from '@/lib/database/models/GenerationTask.js';
|
||||||
import GenerationResult, { IGenerationResult } from '@/lib/database/models/GenerationResult.js';
|
import GenerationResult, { IGenerationResult } from '@/lib/database/models/GenerationResult.js';
|
||||||
import mongoDBManager from '@/lib/database/mongodb.js';
|
import NeDBManager from '@/lib/database/nedb.js';
|
||||||
import logger from '@/lib/logger.js';
|
import logger from '@/lib/logger.js';
|
||||||
import TOSService from '@/lib/tos/tos-service.js';
|
import TOSService from '@/lib/tos/tos-service.js';
|
||||||
import { generateImages as originalGenerateImages } from '@/api/controllers/images.js';
|
import { generateImages as originalGenerateImages } from '@/api/controllers/images.js';
|
||||||
@ -51,7 +51,6 @@ export class TaskPollingService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动轮询服务
|
* 启动轮询服务
|
||||||
* 注意:与HeartbeatService分离,使用不同的定时器
|
|
||||||
*/
|
*/
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
if (this.isRunning) {
|
if (this.isRunning) {
|
||||||
@ -60,11 +59,9 @@ export class TaskPollingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 确保MongoDB连接可用
|
// 确保NeDB数据库初始化
|
||||||
if (!mongoDBManager.isMongoConnected()) {
|
await NeDBManager.initialize();
|
||||||
taskLog('MongoDB not connected, skipping task polling service');
|
taskLog('NeDB database initialized for task polling service');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pollIntervalMs = parseInt(process.env.TASK_POLL_INTERVAL || '5') * 1000;
|
const pollIntervalMs = parseInt(process.env.TASK_POLL_INTERVAL || '5') * 1000;
|
||||||
|
|
||||||
@ -156,12 +153,15 @@ export class TaskPollingService {
|
|||||||
const availableSlots = this.maxConcurrentTasks - currentLoad;
|
const availableSlots = this.maxConcurrentTasks - currentLoad;
|
||||||
|
|
||||||
// 获取待处理任务
|
// 获取待处理任务
|
||||||
const pendingTasks = await GenerationTask.find({
|
const allTasks = await GenerationTask.find({
|
||||||
server_id: this.currentServerId,
|
server_id: this.currentServerId,
|
||||||
status: 'pending'
|
status: 'pending'
|
||||||
})
|
});
|
||||||
.sort({ created_at: 1 }) // 先入先出
|
|
||||||
.limit(availableSlots);
|
// 手动排序并限制数量
|
||||||
|
const pendingTasks = allTasks
|
||||||
|
.sort((a, b) => a.created_at - b.created_at) // 先入先出
|
||||||
|
.slice(0, availableSlots);
|
||||||
|
|
||||||
for (const task of pendingTasks) {
|
for (const task of pendingTasks) {
|
||||||
await this.startTask(task, currentTime);
|
await this.startTask(task, currentTime);
|
||||||
@ -217,7 +217,7 @@ export class TaskPollingService {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (updateResult.modifiedCount === 0) {
|
if (updateResult === 0) {
|
||||||
return; // 任务已被其他进程处理
|
return; // 任务已被其他进程处理
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +359,9 @@ export class TaskPollingService {
|
|||||||
tos_upload_errors: tosUrls.length < originalUrls.length ? ['Some uploads failed'] : undefined
|
tos_upload_errors: tosUrls.length < originalUrls.length ? ['Some uploads failed'] : undefined
|
||||||
},
|
},
|
||||||
created_at: currentTime,
|
created_at: currentTime,
|
||||||
expires_at: expireTime
|
expires_at: expireTime,
|
||||||
|
is_read: false,
|
||||||
|
read_count: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
// 标记任务完成
|
// 标记任务完成
|
||||||
@ -403,7 +405,9 @@ export class TaskPollingService {
|
|||||||
fail_reason: errorMessage
|
fail_reason: errorMessage
|
||||||
},
|
},
|
||||||
created_at: now,
|
created_at: now,
|
||||||
expires_at: expireTime
|
expires_at: expireTime,
|
||||||
|
is_read: false,
|
||||||
|
read_count: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
// 标记任务失败
|
// 标记任务失败
|
||||||
@ -870,7 +874,7 @@ export class DatabaseCleanupService {
|
|||||||
expires_at: { $lt: currentTime }
|
expires_at: { $lt: currentTime }
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.deletedCount || 0;
|
return result || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -909,7 +913,7 @@ export class DatabaseCleanupService {
|
|||||||
task_id: task.task_id,
|
task_id: task.task_id,
|
||||||
task_type: task.task_type,
|
task_type: task.task_type,
|
||||||
server_id: task.server_id,
|
server_id: task.server_id,
|
||||||
status: 'failed',
|
status: 'failed' as const,
|
||||||
original_urls: [],
|
original_urls: [],
|
||||||
tos_urls: [],
|
tos_urls: [],
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -918,7 +922,9 @@ export class DatabaseCleanupService {
|
|||||||
fail_reason: 'Task timeout after ' + task.task_timeout + ' seconds'
|
fail_reason: 'Task timeout after ' + task.task_timeout + ' seconds'
|
||||||
},
|
},
|
||||||
created_at: currentTime,
|
created_at: currentTime,
|
||||||
expires_at: currentTime + parseInt(process.env.RESULT_EXPIRE_TIME || '86400')
|
expires_at: currentTime + parseInt(process.env.RESULT_EXPIRE_TIME || '86400'),
|
||||||
|
is_read: false,
|
||||||
|
read_count: 0
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (failedResults.length > 0) {
|
if (failedResults.length > 0) {
|
||||||
|
|||||||
@ -121,8 +121,7 @@ setup_env() {
|
|||||||
export PORT=${PORT:-"3302"}
|
export PORT=${PORT:-"3302"}
|
||||||
export TOS_REGION=${TOS_REGION:-"cn-beijing"}
|
export TOS_REGION=${TOS_REGION:-"cn-beijing"}
|
||||||
export TOS_ENDPOINT=${TOS_ENDPOINT:-"tos-cn-beijing.volces.com"}
|
export TOS_ENDPOINT=${TOS_ENDPOINT:-"tos-cn-beijing.volces.com"}
|
||||||
export HEARTBEAT_ENABLED=${HEARTBEAT_ENABLED:-"true"}
|
# 心跳配置已移除(使用 NeDB 本地存储)
|
||||||
export HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-"30"}
|
|
||||||
|
|
||||||
# 验证必要的环境变量
|
# 验证必要的环境变量
|
||||||
if ! validate_env; then
|
if ! validate_env; then
|
||||||
@ -300,8 +299,7 @@ show_help() {
|
|||||||
echo " TOS_REGION TOS地区 (默认: cn-beijing)"
|
echo " TOS_REGION TOS地区 (默认: cn-beijing)"
|
||||||
echo " TOS_ENDPOINT TOS端点 (默认: tos-cn-beijing.volces.com)"
|
echo " TOS_ENDPOINT TOS端点 (默认: tos-cn-beijing.volces.com)"
|
||||||
echo " TOS_SELF_DOMAIN TOS自定义域名"
|
echo " TOS_SELF_DOMAIN TOS自定义域名"
|
||||||
echo " HEARTBEAT_ENABLED 启用心跳 (默认: true)"
|
echo " # 心跳配置已移除(使用 NeDB 本地存储)"
|
||||||
echo " HEARTBEAT_INTERVAL 心跳间隔秒数 (默认: 30)"
|
|
||||||
echo
|
echo
|
||||||
echo "示例:"
|
echo "示例:"
|
||||||
echo " # 快速开始 (使用 .env 文件)"
|
echo " # 快速开始 (使用 .env 文件)"
|
||||||
|
|||||||
@ -23,8 +23,7 @@ services:
|
|||||||
- TOS_SELF_DOMAIN=${TOS_SELF_DOMAIN}
|
- TOS_SELF_DOMAIN=${TOS_SELF_DOMAIN}
|
||||||
- TOS_REGION=${TOS_REGION:-cn-beijing}
|
- TOS_REGION=${TOS_REGION:-cn-beijing}
|
||||||
- TOS_ENDPOINT=${TOS_ENDPOINT:-tos-cn-beijing.volces.com}
|
- TOS_ENDPOINT=${TOS_ENDPOINT:-tos-cn-beijing.volces.com}
|
||||||
- HEARTBEAT_ENABLED=${HEARTBEAT_ENABLED:-true}
|
# 心跳配置已移除(使用 NeDB 本地存储)
|
||||||
- HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
|
|
||||||
- USE_DATABASE_MODE=${USE_DATABASE_MODE:-false}
|
- USE_DATABASE_MODE=${USE_DATABASE_MODE:-false}
|
||||||
- MAX_CONCURRENT_TASKS=${MAX_CONCURRENT_TASKS:-3}
|
- MAX_CONCURRENT_TASKS=${MAX_CONCURRENT_TASKS:-3}
|
||||||
- TASK_POLL_INTERVAL=${TASK_POLL_INTERVAL:-5}
|
- TASK_POLL_INTERVAL=${TASK_POLL_INTERVAL:-5}
|
||||||
|
|||||||
@ -20,8 +20,7 @@
|
|||||||
"TOS_SELF_DOMAIN": "",
|
"TOS_SELF_DOMAIN": "",
|
||||||
"TOS_REGION": "cn-beijing",
|
"TOS_REGION": "cn-beijing",
|
||||||
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
||||||
"HEARTBEAT_ENABLED": true,
|
|
||||||
"HEARTBEAT_INTERVAL": 30,
|
|
||||||
"USE_DATABASE_MODE": false,
|
"USE_DATABASE_MODE": false,
|
||||||
"MAX_CONCURRENT_TASKS": 3,
|
"MAX_CONCURRENT_TASKS": 3,
|
||||||
"TASK_POLL_INTERVAL": 5,
|
"TASK_POLL_INTERVAL": 5,
|
||||||
@ -42,8 +41,7 @@
|
|||||||
"TOS_SELF_DOMAIN": "https://your-domain.com",
|
"TOS_SELF_DOMAIN": "https://your-domain.com",
|
||||||
"TOS_REGION": "cn-beijing",
|
"TOS_REGION": "cn-beijing",
|
||||||
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
||||||
"HEARTBEAT_ENABLED": true,
|
|
||||||
"HEARTBEAT_INTERVAL": 30,
|
|
||||||
"USE_DATABASE_MODE": false,
|
"USE_DATABASE_MODE": false,
|
||||||
"MAX_CONCURRENT_TASKS": 3,
|
"MAX_CONCURRENT_TASKS": 3,
|
||||||
"TASK_POLL_INTERVAL": 5,
|
"TASK_POLL_INTERVAL": 5,
|
||||||
@ -80,8 +78,7 @@
|
|||||||
"TOS_SELF_DOMAIN": "",
|
"TOS_SELF_DOMAIN": "",
|
||||||
"TOS_REGION": "cn-beijing",
|
"TOS_REGION": "cn-beijing",
|
||||||
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
||||||
"HEARTBEAT_ENABLED": true,
|
|
||||||
"HEARTBEAT_INTERVAL": 30,
|
|
||||||
"USE_DATABASE_MODE": false,
|
"USE_DATABASE_MODE": false,
|
||||||
"MAX_CONCURRENT_TASKS": 3,
|
"MAX_CONCURRENT_TASKS": 3,
|
||||||
"TASK_POLL_INTERVAL": 5,
|
"TASK_POLL_INTERVAL": 5,
|
||||||
@ -102,8 +99,7 @@
|
|||||||
"TOS_SELF_DOMAIN": "https://your-domain.com",
|
"TOS_SELF_DOMAIN": "https://your-domain.com",
|
||||||
"TOS_REGION": "cn-beijing",
|
"TOS_REGION": "cn-beijing",
|
||||||
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
||||||
"HEARTBEAT_ENABLED": true,
|
|
||||||
"HEARTBEAT_INTERVAL": 30,
|
|
||||||
"USE_DATABASE_MODE": false,
|
"USE_DATABASE_MODE": false,
|
||||||
"MAX_CONCURRENT_TASKS": 3,
|
"MAX_CONCURRENT_TASKS": 3,
|
||||||
"TASK_POLL_INTERVAL": 5,
|
"TASK_POLL_INTERVAL": 5,
|
||||||
@ -140,8 +136,7 @@
|
|||||||
"TOS_SELF_DOMAIN": "",
|
"TOS_SELF_DOMAIN": "",
|
||||||
"TOS_REGION": "cn-beijing",
|
"TOS_REGION": "cn-beijing",
|
||||||
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
||||||
"HEARTBEAT_ENABLED": true,
|
|
||||||
"HEARTBEAT_INTERVAL": 30,
|
|
||||||
"USE_DATABASE_MODE": false,
|
"USE_DATABASE_MODE": false,
|
||||||
"MAX_CONCURRENT_TASKS": 3,
|
"MAX_CONCURRENT_TASKS": 3,
|
||||||
"TASK_POLL_INTERVAL": 5,
|
"TASK_POLL_INTERVAL": 5,
|
||||||
@ -162,8 +157,7 @@
|
|||||||
"TOS_SELF_DOMAIN": "https://your-domain.com",
|
"TOS_SELF_DOMAIN": "https://your-domain.com",
|
||||||
"TOS_REGION": "cn-beijing",
|
"TOS_REGION": "cn-beijing",
|
||||||
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
"TOS_ENDPOINT": "tos-cn-beijing.volces.com",
|
||||||
"HEARTBEAT_ENABLED": true,
|
|
||||||
"HEARTBEAT_INTERVAL": 30,
|
|
||||||
"USE_DATABASE_MODE": false,
|
"USE_DATABASE_MODE": false,
|
||||||
"MAX_CONCURRENT_TASKS": 3,
|
"MAX_CONCURRENT_TASKS": 3,
|
||||||
"TASK_POLL_INTERVAL": 5,
|
"TASK_POLL_INTERVAL": 5,
|
||||||
|
|||||||
212
yarn.lock
212
yarn.lock
@ -94,6 +94,20 @@
|
|||||||
resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz"
|
resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz"
|
||||||
integrity sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==
|
integrity sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==
|
||||||
|
|
||||||
|
"@seald-io/binary-search-tree@^1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.npmmirror.com/@seald-io/binary-search-tree/-/binary-search-tree-1.0.3.tgz"
|
||||||
|
integrity sha512-qv3jnwoakeax2razYaMsGI/luWdliBLHTdC6jU55hQt1hcFqzauH/HsBollQ7IR4ySTtYhT+xyHoijpA16C+tA==
|
||||||
|
|
||||||
|
"@seald-io/nedb@^4.1.2":
|
||||||
|
version "4.1.2"
|
||||||
|
resolved "https://registry.npmmirror.com/@seald-io/nedb/-/nedb-4.1.2.tgz"
|
||||||
|
integrity sha512-bDr6TqjBVS2rDyYM9CPxAnotj5FuNL9NF8o7h7YyFXM7yruqT4ddr+PkSb2mJvvw991bqdftazkEo38gykvaww==
|
||||||
|
dependencies:
|
||||||
|
"@seald-io/binary-search-tree" "^1.0.3"
|
||||||
|
localforage "^1.10.0"
|
||||||
|
util "^0.12.5"
|
||||||
|
|
||||||
"@types/estree@1.0.5":
|
"@types/estree@1.0.5":
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz"
|
resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz"
|
||||||
@ -225,6 +239,13 @@ asynckit@^0.4.0:
|
|||||||
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz"
|
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz"
|
||||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||||
|
|
||||||
|
available-typed-arrays@^1.0.7:
|
||||||
|
version "1.0.7"
|
||||||
|
resolved "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz"
|
||||||
|
integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==
|
||||||
|
dependencies:
|
||||||
|
possible-typed-array-names "^1.0.0"
|
||||||
|
|
||||||
axios-adapter-uniapp@^0.1.4:
|
axios-adapter-uniapp@^0.1.4:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.npmmirror.com/axios-adapter-uniapp/-/axios-adapter-uniapp-0.1.4.tgz"
|
resolved "https://registry.npmmirror.com/axios-adapter-uniapp/-/axios-adapter-uniapp-0.1.4.tgz"
|
||||||
@ -310,16 +331,31 @@ cache-content-type@^1.0.0:
|
|||||||
mime-types "^2.1.18"
|
mime-types "^2.1.18"
|
||||||
ylru "^1.2.0"
|
ylru "^1.2.0"
|
||||||
|
|
||||||
call-bind@^1.0.7:
|
call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
|
||||||
version "1.0.7"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz"
|
resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz"
|
||||||
integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
|
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
es-define-property "^1.0.0"
|
|
||||||
es-errors "^1.3.0"
|
es-errors "^1.3.0"
|
||||||
function-bind "^1.1.2"
|
function-bind "^1.1.2"
|
||||||
|
|
||||||
|
call-bind@^1.0.7, call-bind@^1.0.8:
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz"
|
||||||
|
integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers "^1.0.0"
|
||||||
|
es-define-property "^1.0.0"
|
||||||
get-intrinsic "^1.2.4"
|
get-intrinsic "^1.2.4"
|
||||||
set-function-length "^1.2.1"
|
set-function-length "^1.2.2"
|
||||||
|
|
||||||
|
call-bound@^1.0.2, call-bound@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz"
|
||||||
|
integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers "^1.0.2"
|
||||||
|
get-intrinsic "^1.3.0"
|
||||||
|
|
||||||
chokidar@^3.6.0:
|
chokidar@^3.6.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
@ -519,6 +555,15 @@ dir-glob@^3.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
path-type "^4.0.0"
|
path-type "^4.0.0"
|
||||||
|
|
||||||
|
dunder-proto@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz"
|
||||||
|
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers "^1.0.1"
|
||||||
|
es-errors "^1.3.0"
|
||||||
|
gopd "^1.2.0"
|
||||||
|
|
||||||
eastasianwidth@^0.2.0:
|
eastasianwidth@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
|
resolved "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
|
||||||
@ -544,18 +589,23 @@ encodeurl@^1.0.2:
|
|||||||
resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz"
|
resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz"
|
||||||
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
||||||
|
|
||||||
es-define-property@^1.0.0:
|
es-define-property@^1.0.0, es-define-property@^1.0.1:
|
||||||
version "1.0.0"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz"
|
resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz"
|
||||||
integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
|
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
|
||||||
dependencies:
|
|
||||||
get-intrinsic "^1.2.4"
|
|
||||||
|
|
||||||
es-errors@^1.3.0:
|
es-errors@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz"
|
resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz"
|
||||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||||
|
|
||||||
|
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz"
|
||||||
|
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
|
||||||
|
dependencies:
|
||||||
|
es-errors "^1.3.0"
|
||||||
|
|
||||||
esbuild@^0.23.0, esbuild@>=0.18:
|
esbuild@^0.23.0, esbuild@>=0.18:
|
||||||
version "0.23.0"
|
version "0.23.0"
|
||||||
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.23.0.tgz"
|
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.23.0.tgz"
|
||||||
@ -646,6 +696,13 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.14.9, fol
|
|||||||
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.6.tgz"
|
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.6.tgz"
|
||||||
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
|
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
|
||||||
|
|
||||||
|
for-each@^0.3.5:
|
||||||
|
version "0.3.5"
|
||||||
|
resolved "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz"
|
||||||
|
integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==
|
||||||
|
dependencies:
|
||||||
|
is-callable "^1.2.7"
|
||||||
|
|
||||||
foreground-child@^3.1.0:
|
foreground-child@^3.1.0:
|
||||||
version "3.2.1"
|
version "3.2.1"
|
||||||
resolved "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.2.1.tgz"
|
resolved "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.2.1.tgz"
|
||||||
@ -692,16 +749,29 @@ function-bind@^1.1.2:
|
|||||||
resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz"
|
resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz"
|
||||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||||
|
|
||||||
get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
|
get-intrinsic@^1.2.4, get-intrinsic@^1.3.0:
|
||||||
version "1.2.4"
|
version "1.3.0"
|
||||||
resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz"
|
resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
|
||||||
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
|
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
|
call-bind-apply-helpers "^1.0.2"
|
||||||
|
es-define-property "^1.0.1"
|
||||||
es-errors "^1.3.0"
|
es-errors "^1.3.0"
|
||||||
|
es-object-atoms "^1.1.1"
|
||||||
function-bind "^1.1.2"
|
function-bind "^1.1.2"
|
||||||
has-proto "^1.0.1"
|
get-proto "^1.0.1"
|
||||||
has-symbols "^1.0.3"
|
gopd "^1.2.0"
|
||||||
hasown "^2.0.0"
|
has-symbols "^1.1.0"
|
||||||
|
hasown "^2.0.2"
|
||||||
|
math-intrinsics "^1.1.0"
|
||||||
|
|
||||||
|
get-proto@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz"
|
||||||
|
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
|
||||||
|
dependencies:
|
||||||
|
dunder-proto "^1.0.1"
|
||||||
|
es-object-atoms "^1.0.0"
|
||||||
|
|
||||||
get-stream@^6.0.0:
|
get-stream@^6.0.0:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
@ -739,12 +809,10 @@ globby@^11.1.0:
|
|||||||
merge2 "^1.4.1"
|
merge2 "^1.4.1"
|
||||||
slash "^3.0.0"
|
slash "^3.0.0"
|
||||||
|
|
||||||
gopd@^1.0.1:
|
gopd@^1.0.1, gopd@^1.2.0:
|
||||||
version "1.0.1"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz"
|
resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz"
|
||||||
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
|
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
||||||
dependencies:
|
|
||||||
get-intrinsic "^1.1.3"
|
|
||||||
|
|
||||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||||
version "4.2.11"
|
version "4.2.11"
|
||||||
@ -758,24 +826,19 @@ has-property-descriptors@^1.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
es-define-property "^1.0.0"
|
es-define-property "^1.0.0"
|
||||||
|
|
||||||
has-proto@^1.0.1:
|
has-symbols@^1.0.3, has-symbols@^1.1.0:
|
||||||
version "1.0.3"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz"
|
resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz"
|
||||||
integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
|
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
|
||||||
|
|
||||||
has-symbols@^1.0.3:
|
has-tostringtag@^1.0.0, has-tostringtag@^1.0.2:
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz"
|
|
||||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
|
||||||
|
|
||||||
has-tostringtag@^1.0.0:
|
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz"
|
resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz"
|
||||||
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
|
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols "^1.0.3"
|
has-symbols "^1.0.3"
|
||||||
|
|
||||||
hasown@^2.0.0:
|
hasown@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz"
|
resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz"
|
||||||
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
|
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
|
||||||
@ -870,16 +933,29 @@ image-size@^2.0.2:
|
|||||||
resolved "https://registry.npmmirror.com/image-size/-/image-size-2.0.2.tgz"
|
resolved "https://registry.npmmirror.com/image-size/-/image-size-2.0.2.tgz"
|
||||||
integrity sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==
|
integrity sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==
|
||||||
|
|
||||||
|
immediate@~3.0.5:
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz"
|
||||||
|
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
|
||||||
|
|
||||||
inflation@^2.0.0:
|
inflation@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmmirror.com/inflation/-/inflation-2.1.0.tgz"
|
resolved "https://registry.npmmirror.com/inflation/-/inflation-2.1.0.tgz"
|
||||||
integrity sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==
|
integrity sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==
|
||||||
|
|
||||||
inherits@2.0.4:
|
inherits@^2.0.3, inherits@2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz"
|
resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
|
is-arguments@^1.0.4:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.npmmirror.com/is-arguments/-/is-arguments-1.2.0.tgz"
|
||||||
|
integrity sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==
|
||||||
|
dependencies:
|
||||||
|
call-bound "^1.0.2"
|
||||||
|
has-tostringtag "^1.0.2"
|
||||||
|
|
||||||
is-binary-path@~2.1.0:
|
is-binary-path@~2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz"
|
resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz"
|
||||||
@ -887,6 +963,11 @@ is-binary-path@~2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
binary-extensions "^2.0.0"
|
binary-extensions "^2.0.0"
|
||||||
|
|
||||||
|
is-callable@^1.2.7:
|
||||||
|
version "1.2.7"
|
||||||
|
resolved "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz"
|
||||||
|
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
|
||||||
|
|
||||||
is-extglob@^2.1.1:
|
is-extglob@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz"
|
resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz"
|
||||||
@ -926,6 +1007,13 @@ is-stream@^2.0.0:
|
|||||||
resolved "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz"
|
resolved "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz"
|
||||||
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
||||||
|
|
||||||
|
is-typed-array@^1.1.3:
|
||||||
|
version "1.1.15"
|
||||||
|
resolved "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz"
|
||||||
|
integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==
|
||||||
|
dependencies:
|
||||||
|
which-typed-array "^1.1.16"
|
||||||
|
|
||||||
isexe@^2.0.0:
|
isexe@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz"
|
resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz"
|
||||||
@ -1049,6 +1137,13 @@ koa2-cors@^2.0.6:
|
|||||||
resolved "https://registry.npmmirror.com/koa2-cors/-/koa2-cors-2.0.6.tgz"
|
resolved "https://registry.npmmirror.com/koa2-cors/-/koa2-cors-2.0.6.tgz"
|
||||||
integrity sha512-JRCcSM4lamM+8kvKGDKlesYk2ASrmSTczDtGUnIadqMgnHU4Ct5Gw7Bxt3w3m6d6dy3WN0PU4oMP43HbddDEWg==
|
integrity sha512-JRCcSM4lamM+8kvKGDKlesYk2ASrmSTczDtGUnIadqMgnHU4Ct5Gw7Bxt3w3m6d6dy3WN0PU4oMP43HbddDEWg==
|
||||||
|
|
||||||
|
lie@3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.npmmirror.com/lie/-/lie-3.1.1.tgz"
|
||||||
|
integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==
|
||||||
|
dependencies:
|
||||||
|
immediate "~3.0.5"
|
||||||
|
|
||||||
lilconfig@^3.1.1:
|
lilconfig@^3.1.1:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.2.tgz"
|
resolved "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.2.tgz"
|
||||||
@ -1064,6 +1159,13 @@ load-tsconfig@^0.2.3:
|
|||||||
resolved "https://registry.npmmirror.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz"
|
resolved "https://registry.npmmirror.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz"
|
||||||
integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==
|
integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==
|
||||||
|
|
||||||
|
localforage@^1.10.0:
|
||||||
|
version "1.10.0"
|
||||||
|
resolved "https://registry.npmmirror.com/localforage/-/localforage-1.10.0.tgz"
|
||||||
|
integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
|
||||||
|
dependencies:
|
||||||
|
lie "3.1.1"
|
||||||
|
|
||||||
lodash.sortby@^4.7.0:
|
lodash.sortby@^4.7.0:
|
||||||
version "4.7.0"
|
version "4.7.0"
|
||||||
resolved "https://registry.npmmirror.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz"
|
resolved "https://registry.npmmirror.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz"
|
||||||
@ -1084,6 +1186,11 @@ luxon@~3.4.0:
|
|||||||
resolved "https://registry.npmmirror.com/luxon/-/luxon-3.4.4.tgz"
|
resolved "https://registry.npmmirror.com/luxon/-/luxon-3.4.4.tgz"
|
||||||
integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==
|
integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==
|
||||||
|
|
||||||
|
math-intrinsics@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz"
|
||||||
|
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
|
||||||
|
|
||||||
media-typer@0.3.0:
|
media-typer@0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz"
|
resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz"
|
||||||
@ -1325,6 +1432,11 @@ pirates@^4.0.1:
|
|||||||
resolved "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz"
|
resolved "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz"
|
||||||
integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
|
integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
|
||||||
|
|
||||||
|
possible-typed-array-names@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz"
|
||||||
|
integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==
|
||||||
|
|
||||||
postcss-load-config@^6.0.1:
|
postcss-load-config@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz"
|
resolved "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz"
|
||||||
@ -1440,7 +1552,7 @@ safe-buffer@5.2.1:
|
|||||||
resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz"
|
resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
set-function-length@^1.2.1:
|
set-function-length@^1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz"
|
resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz"
|
||||||
integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
|
integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
|
||||||
@ -1709,6 +1821,17 @@ unpipe@1.0.0:
|
|||||||
resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz"
|
resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz"
|
||||||
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
||||||
|
|
||||||
|
util@^0.12.5:
|
||||||
|
version "0.12.5"
|
||||||
|
resolved "https://registry.npmmirror.com/util/-/util-0.12.5.tgz"
|
||||||
|
integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==
|
||||||
|
dependencies:
|
||||||
|
inherits "^2.0.3"
|
||||||
|
is-arguments "^1.0.4"
|
||||||
|
is-generator-function "^1.0.7"
|
||||||
|
is-typed-array "^1.1.3"
|
||||||
|
which-typed-array "^1.1.2"
|
||||||
|
|
||||||
uuid@^9.0.1:
|
uuid@^9.0.1:
|
||||||
version "9.0.1"
|
version "9.0.1"
|
||||||
resolved "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz"
|
resolved "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz"
|
||||||
@ -1751,6 +1874,19 @@ whatwg-url@^7.0.0:
|
|||||||
tr46 "^1.0.1"
|
tr46 "^1.0.1"
|
||||||
webidl-conversions "^4.0.2"
|
webidl-conversions "^4.0.2"
|
||||||
|
|
||||||
|
which-typed-array@^1.1.16, which-typed-array@^1.1.2:
|
||||||
|
version "1.1.19"
|
||||||
|
resolved "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz"
|
||||||
|
integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==
|
||||||
|
dependencies:
|
||||||
|
available-typed-arrays "^1.0.7"
|
||||||
|
call-bind "^1.0.8"
|
||||||
|
call-bound "^1.0.4"
|
||||||
|
for-each "^0.3.5"
|
||||||
|
get-proto "^1.0.1"
|
||||||
|
gopd "^1.2.0"
|
||||||
|
has-tostringtag "^1.0.2"
|
||||||
|
|
||||||
which@^2.0.1:
|
which@^2.0.1:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz"
|
resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user