Files
suanming/server/routes/auth.cjs
patdelphi d9c57dedb7 feat: 完成易经64卦数据补全和本地化改造
- 完全按照logic/yijing.txt补全所有64卦的完整数据结构
- 包含每卦的卦辞、象传、六爻详解和人生指导
- 重建八字、易经、紫微斗数三个核心分析器
- 实现完整的本地SQLite数据库替代Supabase
- 添加本地Express.js后端服务器
- 更新前端API调用为本地接口
- 实现JWT本地认证系统
- 完善历史记录和用户管理功能
2025-08-18 22:34:39 +08:00

220 lines
5.4 KiB
JavaScript

const express = require('express');
const bcrypt = require('bcryptjs');
const { getDB } = require('../database/index.cjs');
const {
generateToken,
authenticate,
createSession,
deleteSession,
deleteAllSessions
} = require('../middleware/auth.cjs');
const { AppError, asyncHandler } = require('../middleware/errorHandler.cjs');
const router = express.Router();
// 用户注册
router.post('/register', asyncHandler(async (req, res) => {
const { email, password, full_name } = req.body;
// 输入验证
if (!email || !password) {
throw new AppError('邮箱和密码不能为空', 400, 'MISSING_FIELDS');
}
if (password.length < 6) {
throw new AppError('密码长度至少6位', 400, 'PASSWORD_TOO_SHORT');
}
// 验证邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new AppError('邮箱格式不正确', 400, 'INVALID_EMAIL');
}
const db = getDB();
// 检查邮箱是否已存在
const existingUser = db.prepare('SELECT id FROM users WHERE email = ?').get(email);
if (existingUser) {
throw new AppError('该邮箱已被注册', 400, 'EMAIL_EXISTS');
}
// 加密密码
const saltRounds = 12;
const passwordHash = await bcrypt.hash(password, saltRounds);
// 创建用户
const insertUser = db.prepare(
'INSERT INTO users (email, password_hash) VALUES (?, ?)'
);
const result = insertUser.run(email, passwordHash);
const userId = result.lastInsertRowid;
// 创建用户档案
if (full_name) {
const insertProfile = db.prepare(
'INSERT INTO user_profiles (user_id, full_name) VALUES (?, ?)'
);
insertProfile.run(userId, full_name);
}
// 生成JWT token
const token = generateToken(userId);
// 创建会话
createSession(userId, token);
res.status(201).json({
data: {
user: {
id: userId,
email: email
},
token: token
}
});
}));
// 用户登录
router.post('/login', asyncHandler(async (req, res) => {
const { email, password } = req.body;
// 输入验证
if (!email || !password) {
throw new AppError('邮箱和密码不能为空', 400, 'MISSING_FIELDS');
}
const db = getDB();
// 查找用户
const user = db.prepare('SELECT id, email, password_hash FROM users WHERE email = ?').get(email);
if (!user) {
throw new AppError('邮箱或密码错误', 401, 'INVALID_CREDENTIALS');
}
// 验证密码
const isPasswordValid = await bcrypt.compare(password, user.password_hash);
if (!isPasswordValid) {
throw new AppError('邮箱或密码错误', 401, 'INVALID_CREDENTIALS');
}
// 生成JWT token
const token = generateToken(user.id);
// 创建会话
createSession(user.id, token);
res.json({
data: {
user: {
id: user.id,
email: user.email
},
token: token
}
});
}));
// 获取当前用户信息
router.get('/me', authenticate, asyncHandler(async (req, res) => {
const db = getDB();
// 获取用户基本信息
const user = db.prepare('SELECT id, email, created_at FROM users WHERE id = ?').get(req.user.id);
// 获取用户档案信息
const profile = db.prepare(
'SELECT username, full_name, birth_date, birth_time, birth_location, gender, avatar_url FROM user_profiles WHERE user_id = ?'
).get(req.user.id);
res.json({
data: {
user: {
...user,
profile: profile || null
}
}
});
}));
// 用户登出
router.post('/logout', authenticate, asyncHandler(async (req, res) => {
// 删除当前会话
deleteSession(req.user.id, req.token);
res.json({
data: {
message: '登出成功'
}
});
}));
// 登出所有设备
router.post('/logout-all', authenticate, asyncHandler(async (req, res) => {
// 删除用户所有会话
const result = deleteAllSessions(req.user.id);
res.json({
data: {
message: `已登出 ${result.changes} 个设备`
}
});
}));
// 修改密码
router.post('/change-password', authenticate, asyncHandler(async (req, res) => {
const { current_password, new_password } = req.body;
// 输入验证
if (!current_password || !new_password) {
throw new AppError('当前密码和新密码不能为空', 400, 'MISSING_FIELDS');
}
if (new_password.length < 6) {
throw new AppError('新密码长度至少6位', 400, 'PASSWORD_TOO_SHORT');
}
const db = getDB();
// 获取用户当前密码
const user = db.prepare('SELECT password_hash FROM users WHERE id = ?').get(req.user.id);
// 验证当前密码
const isCurrentPasswordValid = await bcrypt.compare(current_password, user.password_hash);
if (!isCurrentPasswordValid) {
throw new AppError('当前密码错误', 401, 'INVALID_CURRENT_PASSWORD');
}
// 加密新密码
const saltRounds = 12;
const newPasswordHash = await bcrypt.hash(new_password, saltRounds);
// 更新密码
const updatePassword = db.prepare('UPDATE users SET password_hash = ? WHERE id = ?');
updatePassword.run(newPasswordHash, req.user.id);
// 删除所有会话(强制重新登录)
deleteAllSessions(req.user.id);
res.json({
data: {
message: '密码修改成功,请重新登录'
}
});
}));
// 验证token有效性
router.get('/verify', authenticate, asyncHandler(async (req, res) => {
res.json({
data: {
valid: true,
user: {
id: req.user.id,
email: req.user.email
}
}
});
}));
module.exports = router;