Files
task-board/server.js
T

419 lines
13 KiB
JavaScript
Raw Normal View History

const express = require('express');
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const app = express();
const PORT = 3000;
// 中间件
app.use(express.json());
app.use(express.static('public'));
// 数据库连接
const db = new sqlite3.Database('./tasks.db', (err) => {
if (err) {
console.error('❌ 数据库连接失败:', err.message);
process.exit(1);
}
console.log('✅ 已连接到 SQLite 数据库');
});
// 初始化数据库表
db.serialize(() => {
// 项目表
db.run(`CREATE TABLE IF NOT EXISTS projects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
status TEXT DEFAULT 'active',
created_by TEXT DEFAULT 'human',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`);
// 任务表
db.run(`CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER NOT NULL,
parent_id INTEGER,
title TEXT NOT NULL,
description TEXT,
type TEXT DEFAULT 'development',
priority TEXT DEFAULT 'normal',
status TEXT DEFAULT 'initial',
assigned_to TEXT,
created_by TEXT DEFAULT 'human',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES projects(id),
FOREIGN KEY (parent_id) REFERENCES tasks(id)
)`);
// 智能体表
db.run(`CREATE TABLE IF NOT EXISTS agents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
type TEXT,
capabilities TEXT,
status TEXT DEFAULT 'idle',
current_task TEXT,
completed_tasks INTEGER DEFAULT 0,
last_heartbeat DATETIME DEFAULT CURRENT_TIMESTAMP
)`);
// 附件表
db.run(`CREATE TABLE IF NOT EXISTS attachments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id INTEGER NOT NULL,
filename TEXT NOT NULL,
filepath TEXT NOT NULL,
size INTEGER,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (task_id) REFERENCES tasks(id)
)`);
// 规约表 (支持全局和项目级别)
db.run(`CREATE TABLE IF NOT EXISTS rules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER,
rule_no TEXT NOT NULL,
rule_type TEXT NOT NULL,
description TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES projects(id)
)`);
console.log('✅ 数据库表已初始化');
});
// ==================== API 路由 ====================
// 健康检查
app.get('/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
version: require('./package.json').version
});
});
// 统计信息
app.get('/api/stats', (req, res) => {
const stats = {};
db.get('SELECT COUNT(*) as count FROM projects', (err, row) => {
stats.projects = row ? row.count : 0;
db.get('SELECT COUNT(*) as count FROM tasks', (err, row) => {
stats.tasks = row ? row.count : 0;
db.get('SELECT COUNT(*) as count FROM tasks WHERE status = "in_progress"', (err, row) => {
stats.in_progress_tasks = row ? row.count : 0;
db.get('SELECT COUNT(*) as count FROM tasks WHERE status = "completed"', (err, row) => {
stats.completed_tasks = row ? row.count : 0;
db.get('SELECT COUNT(*) as count FROM agents', (err, row) => {
stats.total_agents = row ? row.count : 0;
db.get('SELECT COUNT(*) as count FROM agents WHERE status = "busy"', (err, row) => {
stats.active_agents = row ? row.count : 0;
res.json(stats);
});
});
});
});
});
});
});
// 项目管理
app.get('/api/projects', (req, res) => {
db.all('SELECT * FROM projects ORDER BY created_at DESC', (err, rows) => {
if (err) return res.status(500).json({ error: err.message });
res.json(rows || []);
});
});
app.post('/api/projects', (req, res) => {
const { name, description, created_by = 'human' } = req.body;
db.run(
'INSERT INTO projects (name, description, created_by) VALUES (?, ?, ?)',
[name, description, created_by],
function(err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ id: this.lastID, name, description, created_by });
}
);
});
app.get('/api/projects/:id', (req, res) => {
db.get('SELECT * FROM projects WHERE id = ?', [req.params.id], (err, row) => {
if (err) return res.status(500).json({ error: err.message });
if (!row) return res.status(404).json({ error: 'Project not found' });
res.json(row);
});
});
app.delete('/api/projects/:id', (req, res) => {
// 先删除项目下的所有任务
db.run('DELETE FROM tasks WHERE project_id = ?', [req.params.id], (err) => {
if (err) return res.status(500).json({ error: err.message });
// 然后删除项目
db.run('DELETE FROM projects WHERE id = ?', [req.params.id], function(err) {
if (err) return res.status(500).json({ error: err.message });
if (this.changes === 0) return res.status(404).json({ error: 'Project not found' });
res.json({ success: true });
});
});
});
// 任务管理
app.get('/api/tasks', (req, res) => {
const { project_id } = req.query;
let query = 'SELECT t.*, (SELECT COUNT(*) FROM tasks WHERE parent_id = t.id) as subtask_count, (SELECT COUNT(*) FROM attachments WHERE task_id = t.id) as attachment_count FROM tasks t';
let params = [];
if (project_id) {
query += ' WHERE t.project_id = ?';
params.push(project_id);
}
query += ' ORDER BY t.created_at DESC';
db.all(query, params, (err, rows) => {
if (err) return res.status(500).json({ error: err.message });
res.json(rows || []);
});
});
app.post('/api/tasks', (req, res) => {
const { project_id, parent_id, title, description, type = 'development', priority = 'normal', status = 'initial', assigned_to, created_by = 'human' } = req.body;
db.run(
'INSERT INTO tasks (project_id, parent_id, title, description, type, priority, status, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
[project_id, parent_id || null, title, description, type, priority, status, assigned_to || null, created_by],
function(err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ id: this.lastID, project_id, title, type, priority, status });
}
);
});
app.get('/api/tasks/:id', (req, res) => {
db.get('SELECT * FROM tasks WHERE id = ?', [req.params.id], (err, task) => {
if (err) return res.status(500).json({ error: err.message });
if (!task) return res.status(404).json({ error: 'Task not found' });
// 获取附件
db.all('SELECT * FROM attachments WHERE task_id = ?', [req.params.id], (err, attachments) => {
task.attachments = attachments || [];
// 获取子任务
db.all('SELECT * FROM tasks WHERE parent_id = ?', [req.params.id], (err, subtasks) => {
task.subtasks = subtasks || [];
res.json(task);
});
});
});
});
app.put('/api/tasks/:id', (req, res) => {
const updates = [];
const values = [];
['status', 'type', 'priority', 'assigned_to', 'title', 'description'].forEach(field => {
if (req.body[field] !== undefined) {
updates.push(`${field} = ?`);
values.push(req.body[field]);
}
});
if (updates.length === 0) {
return res.status(400).json({ error: 'No fields to update' });
}
values.push(req.params.id);
db.run(
`UPDATE tasks SET ${updates.join(', ')} WHERE id = ?`,
values,
function(err) {
if (err) return res.status(500).json({ error: err.message });
if (this.changes === 0) return res.status(404).json({ error: 'Task not found' });
res.json({ success: true });
}
);
});
app.delete('/api/tasks/:id', (req, res) => {
db.run('DELETE FROM tasks WHERE id = ?', [req.params.id], function(err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ success: true });
});
});
// 智能体管理
app.get('/api/agents', (req, res) => {
db.all('SELECT * FROM agents ORDER BY name', (err, rows) => {
if (err) return res.status(500).json({ error: err.message });
res.json(rows || []);
});
});
app.post('/api/agents', (req, res) => {
const { name, type, capabilities } = req.body;
db.run(
'INSERT INTO agents (name, type, capabilities) VALUES (?, ?, ?)',
[name, type, JSON.stringify(capabilities)],
function(err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ id: this.lastID, name, type, capabilities });
}
);
});
// 规约管理 (支持多条目)
// 获取全局规约列表
app.get('/api/rules/global', (req, res) => {
db.all('SELECT * FROM rules WHERE project_id IS NULL ORDER BY rule_no', (err, rows) => {
if (err) return res.status(500).json({ error: err.message });
res.json(rows || []);
});
});
// 获取项目规约列表
app.get('/api/rules/project/:projectId', (req, res) => {
db.all('SELECT * FROM rules WHERE project_id = ? ORDER BY rule_no', [req.params.projectId], (err, rows) => {
if (err) return res.status(500).json({ error: err.message });
res.json(rows || []);
});
});
// 获取单个规约
app.get('/api/rules/:id', (req, res) => {
db.get('SELECT * FROM rules WHERE id = ?', [req.params.id], (err, row) => {
if (err) return res.status(500).json({ error: err.message });
if (!row) return res.status(404).json({ error: 'Rule not found' });
res.json(row);
});
});
// 创建规约
app.post('/api/rules', (req, res) => {
const { project_id, rule_no, rule_type, description } = req.body;
db.run(
'INSERT INTO rules (project_id, rule_no, rule_type, description) VALUES (?, ?, ?, ?)',
[project_id || null, rule_no, rule_type, description],
function(err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ id: this.lastID, project_id, rule_no, rule_type, description });
}
);
});
// 更新规约
app.put('/api/rules/:id', (req, res) => {
const { rule_no, rule_type, description } = req.body;
const updates = [];
const values = [];
if (rule_no !== undefined) { updates.push('rule_no = ?'); values.push(rule_no); }
if (rule_type !== undefined) { updates.push('rule_type = ?'); values.push(rule_type); }
if (description !== undefined) { updates.push('description = ?'); values.push(description); }
updates.push('updated_at = CURRENT_TIMESTAMP');
if (values.length === 0) {
return res.status(400).json({ error: 'No fields to update' });
}
values.push(req.params.id);
db.run(
`UPDATE rules SET ${updates.join(', ')} WHERE id = ?`,
values,
function(err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ success: true });
}
);
});
// 删除规约
app.delete('/api/rules/:id', (req, res) => {
db.run('DELETE FROM rules WHERE id = ?', [req.params.id], function(err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ success: true });
});
});
// 错误处理中间件(必须在所有路由之后)
app.use((err, req, res, next) => {
console.error('❌ API错误:', err.stack);
res.status(500).json({
error: err.message || '服务器内部错误',
timestamp: new Date().toISOString()
});
});
// 启动服务器
const server = app.listen(PORT, () => {
console.log(`🚀 多智能体任务管理系统已启动`);
console.log(` 本地访问: http://localhost:${PORT}`);
console.log(` 远程访问: http://10.0.6.5:${PORT}`);
});
// 监听端口错误
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`❌ 端口 ${PORT} 已被占用`);
console.error(` 请先停止占用端口的进程: lsof -ti:${PORT} | xargs kill -9`);
process.exit(1);
} else {
console.error('❌ 服务器启动失败:', err);
process.exit(1);
}
});
// 全局异常捕获
process.on('uncaughtException', (err) => {
console.error('❌ 未捕获异常:', err);
console.error(' 堆栈:', err.stack);
// 不退出进程,继续运行
});
process.on('unhandledRejection', (reason, promise) => {
console.error('❌ 未处理的Promise拒绝:', reason);
console.error(' Promise:', promise);
// 不退出进程,继续运行
});
// 优雅关闭
process.on('SIGINT', () => {
console.log('\n⏸️ 收到关闭信号,准备关闭服务器...');
server.close(() => {
console.log('📡 HTTP服务器已关闭');
db.close((err) => {
if (err) {
console.error('❌ 数据库关闭失败:', err);
process.exit(1);
}
console.log('💾 数据库连接已关闭');
console.log('👋 服务器已完全关闭');
process.exit(0);
});
});
// 强制超时退出(10秒)
setTimeout(() => {
console.error('⚠️ 强制退出(超时)');
process.exit(1);
}, 10000);
});