Files
ggl/api/routes/auth.ts
2025-10-05 13:34:28 +08:00

280 lines
6.3 KiB
TypeScript
Executable File
Raw Permalink 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.

/**
* This is a user authentication API route.
* Handle user registration, login, token management, etc.
*/
import { Router, type Request, type Response } from 'express';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { query, queryOne, execute } from '../config/database.js';
const router = Router();
// JWT密钥
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
/**
* User Registration
* POST /api/auth/register
*/
router.post('/register', async (req: Request, res: Response): Promise<void> => {
try {
const { username, email, password } = req.body;
// 验证输入
if (!username || !email || !password) {
res.status(400).json({
success: false,
message: '用户名、邮箱和密码都是必填项'
});
return;
}
// 检查用户是否已存在
const existingUser = await queryOne(
'SELECT id FROM users WHERE username = ? OR email = ?',
[username, email]
);
if (existingUser) {
res.status(400).json({
success: false,
message: '用户名或邮箱已存在'
});
return;
}
// 加密密码
const hashedPassword = await bcrypt.hash(password, 10);
// 插入新用户
const result = await execute(
'INSERT INTO users (username, email, password, created_at) VALUES (?, ?, ?, datetime("now"))',
[username, email, hashedPassword]
);
res.status(201).json({
success: true,
message: '用户注册成功',
data: {
id: result.lastID,
username,
email
}
});
} catch (error) {
console.error('注册失败:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
});
/**
* User Login
* POST /api/auth/login
*/
router.post('/login', async (req: Request, res: Response): Promise<void> => {
try {
const { username, password } = req.body;
// 验证输入
if (!username || !password) {
res.status(400).json({
success: false,
message: '用户名和密码都是必填项'
});
return;
}
// 查找用户
const user = await queryOne(
'SELECT id, username, email, password, role FROM users WHERE username = ? OR email = ?',
[username, username]
);
if (!user) {
res.status(401).json({
success: false,
message: '用户名或密码错误'
});
return;
}
// 验证密码
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
res.status(401).json({
success: false,
message: '用户名或密码错误'
});
return;
}
// 生成JWT token
const token = jwt.sign(
{
id: user.id,
username: user.username,
email: user.email,
role: user.role
},
JWT_SECRET,
{ expiresIn: '24h' }
);
res.json({
success: true,
message: '登录成功',
data: {
token,
user: {
id: user.id,
username: user.username,
email: user.email,
role: user.role
}
}
});
} catch (error) {
console.error('登录失败:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
});
/**
* Token Refresh
* POST /api/auth/refresh
*/
router.post('/refresh', async (req: Request, res: Response): Promise<void> => {
try {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
res.status(401).json({
success: false,
message: '访问令牌缺失'
});
return;
}
// 验证当前token即使过期也要能解析出用户信息
let decoded: any;
try {
decoded = jwt.verify(token, JWT_SECRET);
} catch (error: any) {
// 如果是过期错误尝试解析过期的token
if (error.name === 'TokenExpiredError') {
decoded = jwt.decode(token);
if (!decoded) {
res.status(401).json({
success: false,
message: '无效的访问令牌'
});
return;
}
} else {
res.status(401).json({
success: false,
message: '无效的访问令牌'
});
return;
}
}
// 验证用户是否仍然存在
const user = await queryOne(
'SELECT id, username, email, role FROM users WHERE id = ?',
[decoded.id]
);
if (!user) {
res.status(401).json({
success: false,
message: '用户不存在'
});
return;
}
// 生成新的JWT token
const newToken = jwt.sign(
{
id: user.id,
username: user.username,
email: user.email,
role: user.role
},
JWT_SECRET,
{ expiresIn: '24h' }
);
res.json({
success: true,
message: 'Token刷新成功',
data: {
token: newToken,
user: {
id: user.id,
username: user.username,
email: user.email,
role: user.role
}
}
});
} catch (error) {
console.error('Token刷新失败:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
});
/**
* Get Captcha
* GET /api/auth/captcha
*/
router.get('/captcha', async (req: Request, res: Response): Promise<void> => {
try {
// 生成简单的数学验证码
const num1 = Math.floor(Math.random() * 10) + 1;
const num2 = Math.floor(Math.random() * 10) + 1;
const answer = num1 + num2;
// 生成验证码ID
const captchaId = Math.random().toString(36).substring(2, 15);
res.json({
success: true,
data: {
captchaId,
question: `${num1} + ${num2} = ?`,
answer // 在实际生产环境中,这应该存储在服务器端而不是返回给客户端
}
});
} catch (error) {
console.error('获取验证码失败:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
});
/**
* User Logout
* POST /api/auth/logout
*/
router.post('/logout', async (req: Request, res: Response): Promise<void> => {
// 由于使用JWTlogout主要在前端处理删除token
res.json({
success: true,
message: '退出登录成功'
});
});
export default router;