为 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>
42 lines
1.5 KiB
TypeScript
42 lines
1.5 KiB
TypeScript
import { Router, Request, Response } from 'express'
|
|
import db from '../db.js'
|
|
|
|
const router = Router()
|
|
|
|
// GET /api/sites — list all sites (public, for login page)
|
|
router.get('/', (_req: Request, res: Response) => {
|
|
const sites = db.prepare('SELECT id, name, url, created_at, updated_at FROM sites ORDER BY id').all()
|
|
res.json({ success: true, data: sites })
|
|
})
|
|
|
|
// POST /api/sites — create site
|
|
router.post('/', (req: Request, res: Response) => {
|
|
const { name, url } = req.body
|
|
if (!name || !url) {
|
|
res.json({ success: false, message: '站点名称和 URL 不能为空' })
|
|
return
|
|
}
|
|
const result = db.prepare('INSERT INTO sites (name, url) VALUES (?, ?)').run(name, url.replace(/\/+$/, ''))
|
|
const site = db.prepare('SELECT * FROM sites WHERE id = ?').get(result.lastInsertRowid)
|
|
res.json({ success: true, data: site })
|
|
})
|
|
|
|
// PUT /api/sites/:id — update site
|
|
router.put('/:id', (req: Request, res: Response) => {
|
|
const { name, url } = req.body
|
|
const { id } = req.params
|
|
db.prepare("UPDATE sites SET name = ?, url = ?, updated_at = datetime('now') WHERE id = ?")
|
|
.run(name, url.replace(/\/+$/, ''), id)
|
|
const site = db.prepare('SELECT * FROM sites WHERE id = ?').get(id)
|
|
res.json({ success: true, data: site })
|
|
})
|
|
|
|
// DELETE /api/sites/:id — delete site
|
|
router.delete('/:id', (req: Request, res: Response) => {
|
|
const { id } = req.params
|
|
db.prepare('DELETE FROM sites WHERE id = ?').run(id)
|
|
res.json({ success: true })
|
|
})
|
|
|
|
export default router
|