This repository has been archived on 2026-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
todo/server/index.js
2025-06-13 14:04:40 +08:00

355 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express');
const mysql = require('mysql2/promise');
const cors = require('cors');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 5000;
const JWT_SECRET = 'weiMonkey2024_secret_key';
// 中间件
app.use(cors());
app.use(express.json());
// 数据库连接配置
const dbConfig = {
host: 'localhost',
port: 3306,
user: 'root',
password: '123456',
database: 'myuser'
};
// 创建数据库连接池
const pool = mysql.createPool(dbConfig);
// 初始化数据库表
async function initDatabase() {
try {
const connection = await pool.getConnection();
// 创建待办事项表
await connection.execute(`
CREATE TABLE IF NOT EXISTS todos (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
priority ENUM('low', 'medium', 'high', 'urgent') DEFAULT 'medium',
completed BOOLEAN DEFAULT FALSE,
suspended BOOLEAN DEFAULT FALSE,
date DATE NOT NULL,
suspended_date DATE NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
`);
// 添加suspended字段如果表已存在但没有该字段
try {
await connection.execute(`
ALTER TABLE todos
ADD COLUMN suspended BOOLEAN DEFAULT FALSE,
ADD COLUMN suspended_date DATE NULL
`);
} catch (error) {
// 字段可能已存在,忽略错误
console.log('Suspended columns may already exist');
}
connection.release();
console.log('数据库表初始化成功');
} catch (error) {
console.error('数据库初始化失败:', error);
}
}
// 验证密码的中间件
const authenticatePassword = (req, res, next) => {
const { password } = req.body;
const correctPassword = 'weiMonkey2024';
if (password !== correctPassword) {
return res.status(401).json({ error: '密码错误' });
}
next();
};
// 路由
// 验证密码
app.post('/api/auth', authenticatePassword, (req, res) => {
const token = jwt.sign({ authenticated: true }, JWT_SECRET, { expiresIn: '24h' });
res.json({ success: true, token });
});
// 验证token的中间件
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: '需要认证' });
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: '无效的token' });
}
req.user = user;
next();
});
};
// 获取历史待办事项(一周之前的)
app.get('/api/todos/history', authenticateToken, async (req, res) => {
try {
const connection = await pool.getConnection();
// 获取一周之前的历史数据
const today = new Date();
const oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
const [rows] = await connection.execute(
'SELECT * FROM todos WHERE date < ? ORDER BY date DESC, priority DESC, created_at DESC',
[oneWeekAgo.toISOString().split('T')[0]]
);
connection.release();
res.json(rows);
} catch (error) {
console.error('获取历史待办事项失败:', error);
res.status(500).json({ error: '服务器错误' });
}
});
// 获取最近一周的待办事项(排除挂起的)
app.get('/api/todos', authenticateToken, async (req, res) => {
try {
const connection = await pool.getConnection();
// 获取最近一周的日期范围
const today = new Date();
const oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
const [rows] = await connection.execute(
'SELECT * FROM todos WHERE date >= ? AND date <= ? AND suspended = FALSE ORDER BY date DESC, priority DESC, created_at DESC',
[oneWeekAgo.toISOString().split('T')[0], today.toISOString().split('T')[0]]
);
connection.release();
res.json(rows);
} catch (error) {
console.error('获取待办事项失败:', error);
res.status(500).json({ error: '服务器错误' });
}
});
// 创建新的待办事项
app.post('/api/todos', authenticateToken, async (req, res) => {
try {
const { title, priority, date } = req.body;
if (!title || !date) {
return res.status(400).json({ error: '标题和日期是必需的' });
}
const connection = await pool.getConnection();
const [result] = await connection.execute(
'INSERT INTO todos (title, priority, date) VALUES (?, ?, ?)',
[title, priority || 'medium', date]
);
const [newTodo] = await connection.execute(
'SELECT * FROM todos WHERE id = ?',
[result.insertId]
);
connection.release();
res.status(201).json(newTodo[0]);
} catch (error) {
console.error('创建待办事项失败:', error);
res.status(500).json({ error: '服务器错误' });
}
});
// 更新待办事项状态
app.put('/api/todos/:id', authenticateToken, async (req, res) => {
try {
const { id } = req.params;
const { completed } = req.body;
const connection = await pool.getConnection();
await connection.execute(
'UPDATE todos SET completed = ? WHERE id = ?',
[completed, id]
);
const [updatedTodo] = await connection.execute(
'SELECT * FROM todos WHERE id = ?',
[id]
);
connection.release();
res.json(updatedTodo[0]);
} catch (error) {
console.error('更新待办事项失败:', error);
res.status(500).json({ error: '服务器错误' });
}
});
// 删除待办事项
app.delete('/api/todos/:id', authenticateToken, async (req, res) => {
try {
const { id } = req.params;
const connection = await pool.getConnection();
await connection.execute('DELETE FROM todos WHERE id = ?', [id]);
connection.release();
res.json({ success: true });
} catch (error) {
console.error('删除待办事项失败:', error);
res.status(500).json({ error: '服务器错误' });
}
});
// 挂起今天的待办事项
app.put('/api/todos/:id/suspend', authenticateToken, async (req, res) => {
try {
const { id } = req.params;
const today = new Date().toISOString().split('T')[0];
const connection = await pool.getConnection();
// 检查是否是今天的待办事项
const [todoCheck] = await connection.execute(
'SELECT * FROM todos WHERE id = ? AND date = ?',
[id, today]
);
if (todoCheck.length === 0) {
connection.release();
return res.status(400).json({ error: '只能挂起今天的待办事项' });
}
// 更新挂起状态
await connection.execute(
'UPDATE todos SET suspended = TRUE, suspended_date = ? WHERE id = ?',
[today, id]
);
const [updatedTodo] = await connection.execute(
'SELECT * FROM todos WHERE id = ?',
[id]
);
connection.release();
res.json(updatedTodo[0]);
} catch (error) {
console.error('挂起待办事项失败:', error);
res.status(500).json({ error: '服务器错误' });
}
});
// 获取所有挂起的待办事项
app.get('/api/todos/suspended', authenticateToken, async (req, res) => {
try {
const connection = await pool.getConnection();
const [rows] = await connection.execute(
'SELECT * FROM todos WHERE suspended = TRUE ORDER BY suspended_date DESC, priority DESC, created_at DESC'
);
connection.release();
res.json(rows);
} catch (error) {
console.error('获取挂起待办事项失败:', error);
res.status(500).json({ error: '服务器错误' });
}
});
// 恢复挂起的待办事项到今天
app.put('/api/todos/:id/resume', authenticateToken, async (req, res) => {
try {
const { id } = req.params;
const today = new Date().toISOString().split('T')[0];
const connection = await pool.getConnection();
// 检查是否是挂起的待办事项
const [todoCheck] = await connection.execute(
'SELECT * FROM todos WHERE id = ? AND suspended = TRUE',
[id]
);
if (todoCheck.length === 0) {
connection.release();
return res.status(400).json({ error: '待办事项未处于挂起状态' });
}
// 恢复待办事项到今天
await connection.execute(
'UPDATE todos SET suspended = FALSE, suspended_date = NULL, date = ?, completed = FALSE WHERE id = ?',
[today, id]
);
const [updatedTodo] = await connection.execute(
'SELECT * FROM todos WHERE id = ?',
[id]
);
connection.release();
res.json(updatedTodo[0]);
} catch (error) {
console.error('恢复待办事项失败:', error);
res.status(500).json({ error: '服务器错误' });
}
});
// 将历史未完成待办迁移到今天
app.post('/api/todos/migrate-pending', authenticateToken, async (req, res) => {
try {
const connection = await pool.getConnection();
// 获取今天的日期
const today = new Date().toISOString().split('T')[0];
// 查找今天之前所有未完成且未挂起的待办事项
const [pendingTodos] = await connection.execute(
'SELECT * FROM todos WHERE date < ? AND completed = FALSE AND suspended = FALSE ORDER BY date DESC, priority DESC',
[today]
);
let migratedCount = 0;
// 将每个未完成的待办事项迁移到今天
for (const todo of pendingTodos) {
await connection.execute(
'UPDATE todos SET date = ? WHERE id = ?',
[today, todo.id]
);
migratedCount++;
}
connection.release();
res.json({
success: true,
migratedCount,
message: `已迁移 ${migratedCount} 个未完成的待办事项到今天`
});
} catch (error) {
console.error('迁移待办事项失败:', error);
res.status(500).json({ error: '服务器错误' });
}
});
// 启动服务器
app.listen(PORT, async () => {
console.log(`服务器运行在端口 ${PORT}`);
await initDatabase();
});