feat: 小菜记账 v1.0 - 完整功能实现
核心功能: - 记账CRUD(支出/收入/分类/备注/日期) - 统计分析(概览/分类占比/每日趋势) - 预算管理(按月设置/进度条/超支提醒) - 数据导出CSV 安全与认证: - HMAC-SHA256签名token认证 - 用户数据隔离 - 输入验证与错误处理 - CORS配置 前端优化: - 骨架屏加载 - 账单按日期分组 - 预算页面重构(快捷预设+Numpad) - SvgIcon组件(H5+微信双端适配) - 下拉刷新 后端优化: - 共享日期工具函数 - 数据库连接池优化 - 健康检查端点 - 优雅关闭处理 技术栈: - 前端:Uni-app (Vue 3 + Pinia) - 后端:Node.js + Express + MySQL
This commit is contained in:
53
server/src/routes/auth.ts
Normal file
53
server/src/routes/auth.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Router, Request, Response } from 'express'
|
||||
import { createHmac } from 'crypto'
|
||||
import pool from '../db/connection'
|
||||
|
||||
const router = Router()
|
||||
|
||||
const APPID = process.env.WX_APPID || ''
|
||||
const SECRET = process.env.WX_SECRET || ''
|
||||
const TOKEN_SECRET = process.env.TOKEN_SECRET || 'xiaocai-token-secret-change-in-production'
|
||||
|
||||
export function signToken(userId: number): string {
|
||||
const payload = `${userId}:${Date.now()}`
|
||||
const signature = createHmac('sha256', TOKEN_SECRET).update(payload).digest('hex')
|
||||
return Buffer.from(`${payload}:${signature}`).toString('base64')
|
||||
}
|
||||
|
||||
router.post('/login', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { code } = req.body
|
||||
if (!code) {
|
||||
return res.status(400).json({ code: 40001, message: '缺少code参数' })
|
||||
}
|
||||
|
||||
// 调用微信 code2session 接口
|
||||
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=${code}&grant_type=authorization_code`
|
||||
const wxRes = await fetch(url)
|
||||
const wxData = await wxRes.json() as any
|
||||
|
||||
if (wxData.errcode) {
|
||||
console.error('[Auth] WeChat API error:', wxData.errcode, wxData.errmsg)
|
||||
return res.status(400).json({ code: 40002, message: '微信登录失败' })
|
||||
}
|
||||
|
||||
const { openid, session_key } = wxData
|
||||
|
||||
// 查找或创建用户 (atomic: handles race condition)
|
||||
const [result] = await pool.query(
|
||||
'INSERT INTO users (openid, session_key) VALUES (?, ?) ON DUPLICATE KEY UPDATE session_key = ?, id = LAST_INSERT_ID(id)',
|
||||
[openid, session_key, session_key]
|
||||
)
|
||||
const userId = (result as any).insertId
|
||||
|
||||
// HMAC signed token with expiry
|
||||
const token = signToken(userId)
|
||||
|
||||
res.json({ code: 0, data: { token, userId } })
|
||||
} catch (err: any) {
|
||||
console.error('[Auth] Login failed:', err.message)
|
||||
res.status(500).json({ code: 50000, message: '服务器错误' })
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user