Files
newapi-dashboard/server/routes/auth.ts
LAMCLOD f6036cab66 feat: newapi-dashboard 全栈项目初始化
为 new-api (LLM API 网关) 构建独立前端管理面板:
- React 18 + TypeScript + Vite + Ant Design 5 前端
- Node.js + Express + better-sqlite3 BFF 后端
- 登录页: 站点选择器 + UserID + AccessToken 认证
- 仪表盘: 用户信息、额度环形图、7天趋势图、模型饼图、令牌概览、日志时间线
- 账单页: 筛选日志表格、模型消耗柱状图、充值记录、CSV/PDF 导出(HMAC签名)
- 管理员站点管理: 站点 CRUD
- API 代理: 多站点切换,会话管理

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:00:28 +08:00

79 lines
2.3 KiB
TypeScript
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.
import { Router, Request, Response } from 'express'
import { v4 as uuidv4 } from 'uuid'
import db from '../db.js'
import { sessionAuth } from '../middleware/auth.js'
const router = Router()
// POST /api/auth/login
router.post('/login', async (req: Request, res: Response) => {
const { userId, accessToken, siteId } = req.body
if (!userId || !accessToken || !siteId) {
res.json({ success: false, message: '请填写完整信息' })
return
}
const site = db.prepare('SELECT * FROM sites WHERE id = ?').get(siteId) as any
if (!site) {
res.json({ success: false, message: '站点不存在' })
return
}
try {
const response = await fetch(`${site.url}/api/user/self`, {
headers: { 'Authorization': accessToken }
})
const result = await response.json() as any
if (!result.success) {
res.json({ success: false, message: result.message || '认证失败请检查用户ID和令牌' })
return
}
const userInfo = result.data
if (userInfo.id !== Number(userId)) {
res.json({ success: false, message: '用户ID与令牌不匹配' })
return
}
const sessionId = uuidv4()
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
db.prepare(
'INSERT INTO sessions (id, user_id, access_token, site_id, site_url, user_info, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)'
).run(sessionId, userId, accessToken, siteId, site.url, JSON.stringify(userInfo), expiresAt)
res.json({
success: true,
data: {
sessionToken: sessionId,
userInfo,
site: { id: site.id, name: site.name, url: site.url }
}
})
} catch (error: any) {
res.json({ success: false, message: `无法连接站点: ${error.message}` })
}
})
// POST /api/auth/logout
router.post('/logout', sessionAuth, (req: Request, res: Response) => {
db.prepare('DELETE FROM sessions WHERE id = ?').run(req.session!.id)
res.json({ success: true })
})
// GET /api/auth/me
router.get('/me', sessionAuth, (req: Request, res: Response) => {
const site = db.prepare('SELECT id, name, url FROM sites WHERE id = ?').get(req.session!.site_id)
res.json({
success: true,
data: {
userInfo: JSON.parse(req.session!.user_info),
site
}
})
})
export default router