feat: 保存当前Supabase版本,准备进行本地化改造

This commit is contained in:
patdelphi
2025-08-18 20:53:16 +08:00
parent d19fccd12c
commit 29bc9d8c4a
35 changed files with 5116 additions and 3159 deletions

View File

@@ -1,349 +0,0 @@
import express from 'express';
import { numerologyService } from '../services/numerologyService.js';
import { authenticateToken, rateLimit } from '../middleware/auth.js';
const router = express.Router();
// 应用认证中间件
router.use(authenticateToken);
// 应用速率限制
router.use(rateLimit({ max: 50, windowMs: 15 * 60 * 1000 })); // 15分钟内最多50次请求
/**
* 八字命理分析
* POST /api/analysis/bazi
*/
router.post('/bazi', async (req, res, next) => {
try {
const { name, birthDate, birthTime, gender, birthPlace } = req.body;
// 参数验证
if (!birthDate) {
return res.status(400).json({
error: {
code: 'MISSING_PARAMETERS',
message: '出生日期不能为空'
}
});
}
// 日期格式验证
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(birthDate)) {
return res.status(400).json({
error: {
code: 'INVALID_DATE_FORMAT',
message: '日期格式应为YYYY-MM-DD'
}
});
}
// 时间格式验证(可选)
if (birthTime) {
const timeRegex = /^\d{2}:\d{2}$/;
if (!timeRegex.test(birthTime)) {
return res.status(400).json({
error: {
code: 'INVALID_TIME_FORMAT',
message: '时间格式应为HH:MM'
}
});
}
}
const result = await numerologyService.analyzeBazi(req.user.userId, {
name,
birthDate,
birthTime,
gender,
birthPlace
});
res.json({
data: result,
message: '八字分析完成'
});
} catch (error) {
next(error);
}
});
/**
* 紫微斗数分析
* POST /api/analysis/ziwei
*/
router.post('/ziwei', async (req, res, next) => {
try {
const { name, birthDate, birthTime, gender, birthPlace } = req.body;
// 参数验证
if (!birthDate) {
return res.status(400).json({
error: {
code: 'MISSING_PARAMETERS',
message: '出生日期不能为空'
}
});
}
// 日期格式验证
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(birthDate)) {
return res.status(400).json({
error: {
code: 'INVALID_DATE_FORMAT',
message: '日期格式应为YYYY-MM-DD'
}
});
}
const result = await numerologyService.analyzeZiwei(req.user.userId, {
name,
birthDate,
birthTime,
gender,
birthPlace
});
res.json({
data: result,
message: '紫微斗数分析完成'
});
} catch (error) {
next(error);
}
});
/**
* 易经占卜分析
* POST /api/analysis/yijing
*/
router.post('/yijing', async (req, res, next) => {
try {
const { question, method } = req.body;
// 参数验证
if (!question) {
return res.status(400).json({
error: {
code: 'MISSING_PARAMETERS',
message: '占卜问题不能为空'
}
});
}
if (question.length > 200) {
return res.status(400).json({
error: {
code: 'QUESTION_TOO_LONG',
message: '问题长度不能超过200字符'
}
});
}
const result = await numerologyService.analyzeYijing(req.user.userId, {
question,
method: method || '梅花易数时间起卦法'
});
res.json({
data: result,
message: '易经占卜分析完成'
});
} catch (error) {
next(error);
}
});
/**
* 五行分析
* POST /api/analysis/wuxing
*/
router.post('/wuxing', async (req, res, next) => {
try {
const { name, birthDate, birthTime, gender } = req.body;
// 参数验证
if (!birthDate) {
return res.status(400).json({
error: {
code: 'MISSING_PARAMETERS',
message: '出生日期不能为空'
}
});
}
// 日期格式验证
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(birthDate)) {
return res.status(400).json({
error: {
code: 'INVALID_DATE_FORMAT',
message: '日期格式应为YYYY-MM-DD'
}
});
}
const result = await numerologyService.analyzeWuxing(req.user.userId, {
name,
birthDate,
birthTime,
gender
});
res.json({
data: result,
message: '五行分析完成'
});
} catch (error) {
next(error);
}
});
/**
* 获取分析历史记录
* GET /api/analysis/history
*/
router.get('/history', async (req, res, next) => {
try {
const { type, limit = 20, offset = 0 } = req.query;
// 验证分析类型
if (type && !['bazi', 'ziwei', 'yijing', 'wuxing'].includes(type)) {
return res.status(400).json({
error: {
code: 'INVALID_TYPE',
message: '无效的分析类型'
}
});
}
const history = await numerologyService.getReadingHistory(req.user.userId, type);
// 简单的分页处理
const startIndex = parseInt(offset);
const endIndex = startIndex + parseInt(limit);
const paginatedHistory = history.slice(startIndex, endIndex);
res.json({
data: {
readings: paginatedHistory,
total: history.length,
hasMore: endIndex < history.length
},
message: '获取分析历史成功'
});
} catch (error) {
next(error);
}
});
/**
* 获取单个分析记录详情
* GET /api/analysis/history/:id
*/
router.get('/history/:id', async (req, res, next) => {
try {
const { id } = req.params;
if (!id || isNaN(parseInt(id))) {
return res.status(400).json({
error: {
code: 'INVALID_ID',
message: '无效的记录ID'
}
});
}
const history = await numerologyService.getReadingHistory(req.user.userId);
const reading = history.find(r => r.id === parseInt(id));
if (!reading) {
return res.status(404).json({
error: {
code: 'READING_NOT_FOUND',
message: '分析记录不存在'
}
});
}
res.json({
data: { reading },
message: '获取分析记录成功'
});
} catch (error) {
next(error);
}
});
/**
* 删除分析记录
* DELETE /api/analysis/history/:id
*/
router.delete('/history/:id', async (req, res, next) => {
try {
const { id } = req.params;
if (!id || isNaN(parseInt(id))) {
return res.status(400).json({
error: {
code: 'INVALID_ID',
message: '无效的记录ID'
}
});
}
const success = await numerologyService.deleteReading(req.user.userId, parseInt(id));
if (!success) {
return res.status(404).json({
error: {
code: 'READING_NOT_FOUND',
message: '分析记录不存在或无权删除'
}
});
}
res.json({
message: '分析记录删除成功'
});
} catch (error) {
next(error);
}
});
/**
* 获取用户分析统计信息
* GET /api/analysis/stats
*/
router.get('/stats', async (req, res, next) => {
try {
const history = await numerologyService.getReadingHistory(req.user.userId);
const stats = {
total: history.length,
byType: {
bazi: history.filter(r => r.type === 'bazi').length,
ziwei: history.filter(r => r.type === 'ziwei').length,
yijing: history.filter(r => r.type === 'yijing').length,
wuxing: history.filter(r => r.type === 'wuxing').length
},
recent: history.slice(0, 5).map(r => ({
id: r.id,
type: r.type,
name: r.name,
createdAt: r.createdAt
}))
};
res.json({
data: stats,
message: '获取统计信息成功'
});
} catch (error) {
next(error);
}
});
export default router;

View File

@@ -1,231 +0,0 @@
import express from 'express';
import { authService } from '../services/authService.js';
import { authenticateToken, rateLimit } from '../middleware/auth.js';
const router = express.Router();
// 应用速率限制
router.use(rateLimit({ max: 20, windowMs: 15 * 60 * 1000 })); // 15分钟内最多20次请求
/**
* 用户注册
* POST /api/auth/signup
*/
router.post('/signup', async (req, res, next) => {
try {
const { email, password, fullName, birthDate, birthTime, birthPlace, gender } = req.body;
// 基本验证
if (!email || !password) {
return res.status(400).json({
error: {
code: 'MISSING_PARAMETERS',
message: '邮箱和密码不能为空'
}
});
}
// 邮箱格式验证
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return res.status(400).json({
error: {
code: 'INVALID_EMAIL',
message: '邮箱格式不正确'
}
});
}
// 密码强度验证
if (password.length < 6) {
return res.status(400).json({
error: {
code: 'WEAK_PASSWORD',
message: '密码长度至少6位'
}
});
}
const result = await authService.signUp({
email,
password,
fullName,
birthDate,
birthTime,
birthPlace,
gender
});
res.status(201).json({
data: result,
message: '注册成功'
});
} catch (error) {
next(error);
}
});
/**
* 用户登录
* POST /api/auth/signin
*/
router.post('/signin', async (req, res, next) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({
error: {
code: 'MISSING_PARAMETERS',
message: '邮箱和密码不能为空'
}
});
}
const result = await authService.signIn(email, password);
res.json({
data: result,
message: '登录成功'
});
} catch (error) {
if (error.message.includes('邮箱或密码错误')) {
return res.status(401).json({
error: {
code: 'INVALID_CREDENTIALS',
message: error.message
}
});
}
next(error);
}
});
/**
* 获取当前用户信息
* GET /api/auth/user
*/
router.get('/user', authenticateToken, async (req, res, next) => {
try {
const user = await authService.getUserById(req.user.userId);
res.json({
data: { user },
message: '获取用户信息成功'
});
} catch (error) {
next(error);
}
});
/**
* 更新用户信息
* PUT /api/auth/user
*/
router.put('/user', authenticateToken, async (req, res, next) => {
try {
const { fullName, birthDate, birthTime, birthPlace, gender } = req.body;
const updatedUser = await authService.updateUser(req.user.userId, {
fullName,
birthDate,
birthTime,
birthPlace,
gender
});
res.json({
data: { user: updatedUser },
message: '用户信息更新成功'
});
} catch (error) {
next(error);
}
});
/**
* 验证token
* POST /api/auth/verify
*/
router.post('/verify', async (req, res, next) => {
try {
const { token } = req.body;
if (!token) {
return res.status(400).json({
error: {
code: 'MISSING_TOKEN',
message: 'Token不能为空'
}
});
}
const decoded = authService.verifyToken(token);
const user = await authService.getUserById(decoded.userId);
res.json({
data: { user, valid: true },
message: 'Token验证成功'
});
} catch (error) {
res.status(401).json({
error: {
code: 'INVALID_TOKEN',
message: 'Token无效或已过期'
}
});
}
});
/**
* 用户登出(客户端处理,服务端记录日志)
* POST /api/auth/signout
*/
router.post('/signout', authenticateToken, async (req, res) => {
try {
// 这里可以添加登出日志记录
console.log(`用户 ${req.user.userId}${new Date().toISOString()} 登出`);
res.json({
message: '登出成功'
});
} catch (error) {
res.status(500).json({
error: {
code: 'SIGNOUT_ERROR',
message: '登出失败'
}
});
}
});
/**
* 刷新token可选功能
* POST /api/auth/refresh
*/
router.post('/refresh', authenticateToken, async (req, res, next) => {
try {
const user = await authService.getUserById(req.user.userId);
// 生成新的token
const jwt = await import('jsonwebtoken');
const JWT_SECRET = process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-in-production';
const newToken = jwt.default.sign(
{ userId: user.id, email: user.email },
JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({
data: {
user,
token: newToken
},
message: 'Token刷新成功'
});
} catch (error) {
next(error);
}
});
export default router;