- 后台全面引入 Naive UI 组件库,统一 UI 规范 - 前台 usePublicApi 切换到 API 调用(GET /api/content/[key]) - 前后台数据源统一(site_content 表) - 产品线统一管理(tour/camp/course 三套编辑器) - 产品数据归一化(itinerary/faq/pricing 格式统一) - 表单提交 API 改写入 submissions 表 - 新增 submissions 标记已读 API - site-content PUT 支持 upsert - 修复前台 bug(stories 路由冲突、占位符警告、selector breadcrumb) - 消除 PageHero 类型警告(useSEO reactive 修复) - 25 个前台页面全部 HTTP 200,展示效果不变 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
78 行
2.2 KiB
JavaScript
78 行
2.2 KiB
JavaScript
import { createHash } from 'crypto'
|
||
import Database from 'better-sqlite3'
|
||
import { join } from 'path'
|
||
|
||
// ── 数据库连接(用于 session 持久化)────────────────
|
||
let _sessionDB = null
|
||
function getSessionDB() {
|
||
if (!_sessionDB) {
|
||
const dbPath = join(process.cwd(), 'server', 'database', 'hulai.db')
|
||
_sessionDB = new Database(dbPath)
|
||
_sessionDB.pragma('journal_mode = WAL')
|
||
}
|
||
return _sessionDB
|
||
}
|
||
|
||
export function hashPassword(password) {
|
||
return createHash('sha256').update(password).digest('hex')
|
||
}
|
||
|
||
export function createSession(userId) {
|
||
const token = createHash('sha256')
|
||
.update(`${userId}-${Date.now()}-${Math.random()}`)
|
||
.digest('hex')
|
||
|
||
const db = getSessionDB()
|
||
db.prepare('INSERT OR REPLACE INTO sessions (token, user_id, created_at) VALUES (?, ?, ?)')
|
||
.run(token, userId, new Date().toISOString())
|
||
|
||
return token
|
||
}
|
||
|
||
export function validateSession(token) {
|
||
const db = getSessionDB()
|
||
const session = db.prepare('SELECT * FROM sessions WHERE token = ?').get(token)
|
||
|
||
if (!session) return null
|
||
|
||
// 7 天过期
|
||
const createdAt = new Date(session.created_at).getTime()
|
||
if (Date.now() - createdAt > 7 * 24 * 60 * 60 * 1000) {
|
||
db.prepare('DELETE FROM sessions WHERE token = ?').run(token)
|
||
return null
|
||
}
|
||
|
||
return { userId: session.user_id, createdAt }
|
||
}
|
||
|
||
export function destroySession(token) {
|
||
const db = getSessionDB()
|
||
db.prepare('DELETE FROM sessions WHERE token = ?').run(token)
|
||
}
|
||
|
||
// 清理过期 session(启动时执行一次)
|
||
try {
|
||
const db = getSessionDB()
|
||
db.prepare("DELETE FROM sessions WHERE datetime(created_at) < datetime('now', '-7 days')").run()
|
||
} catch {}
|
||
|
||
/**
|
||
* 从请求中提取并验证管理员身份
|
||
* 返回 session 对象或抛出 401 错误
|
||
*/
|
||
export function requireAdmin(event) {
|
||
const authHeader = getRequestHeader(event, 'authorization')
|
||
const token = authHeader?.replace('Bearer ', '')
|
||
|
||
if (!token) {
|
||
throw createError({ statusCode: 401, message: '请先登录' })
|
||
}
|
||
|
||
const session = validateSession(token)
|
||
if (!session) {
|
||
throw createError({ statusCode: 401, message: '登录已过期,请重新登录' })
|
||
}
|
||
|
||
return session
|
||
}
|