Files
BYW-VoiceHub/server/utils/create-auth-session.ts
T
Henry_Du bd8df1f026
构建并部署 BYW VoiceHub / Preflight 语法与构建检查 (push) Failing after 6m31s
构建并部署 BYW VoiceHub / 构建并推送容器镜像 (push) Skipped
构建并部署 BYW VoiceHub / 部署到生产服务器 (push) Failing after 4s
feat(songs): 歌曲管理支持批量驳回、投稿留言自动审查与排期范围可见性过滤
- 新增 /api/admin/songs/batch-reject.post 批量驳回接口(事务清排期/投票/扣接纳数/黑名单去重/异步通知)
- requireSongAdmin 三角色校验 + createApiError 统一错误包装
- scheduleDaysBefore/After 四字段排期可见范围限制(开放 API 与公开排期接口三处缓存路径 + DB 条件过滤)
- submissionNoteAutoModeration 基于黑名单 KEYWORD/LANGUAGE/GENRE 自动判定留言 publicStatus,命中自动关闭 submissionNotePublic
- 更新 songs/request.post 与 songs/[id]/update.put 接入自动审查逻辑
2026-09-06 17:03:20 +08:00

84 lines
2.3 KiB
TypeScript

import { getHeader } from 'h3'
import { db, eq, and } from '~/drizzle/db'
import { authSessions } from '~/drizzle/schema'
import { parseUserAgent, type LoginMethod } from './user-agent-info'
import { getClientIP } from './ip-utils'
import { randomUUID } from 'node:crypto'
export interface CreateAuthSessionOptions {
userId: number
jti: string
loginMethod: LoginMethod
expiresAt?: Date
oauthProvider?: string
}
const DEFAULT_SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000
export async function createAuthSession(
event: any,
options: CreateAuthSessionOptions
): Promise<string> {
const { userId, jti, loginMethod, oauthProvider } = options
const ua = getHeader(event, 'user-agent')
const { browser, operatingSystem, device } = parseUserAgent(ua)
const ip = getClientIP(event)
const now = new Date()
const expiresAt = options.expiresAt ?? new Date(now.getTime() + DEFAULT_SESSION_TTL_MS)
try {
let method = loginMethod
if (oauthProvider && method === 'oauth') {
method = `oauth:${oauthProvider}` as any
}
await db.insert(authSessions).values({
id: randomUUID(),
userId,
sessionToken: jti,
browser,
operatingSystem,
device,
loginMethod: method,
ipAddress: ip,
userAgent: ua ?? null,
createdAt: now,
lastActiveAt: now,
expiresAt,
isRevoked: false
})
} catch (error) {
console.warn('[AuthSession] 创建会话失败,继续无会话登录:', error)
}
return jti
}
export async function isSessionRevokedByJti(jti: string | undefined): Promise<boolean> {
if (!jti) return false
try {
const row = await db.query.authSessions.findFirst({
where: and(eq(authSessions.sessionToken, jti)),
columns: { isRevoked: true, expiresAt: true }
})
if (!row) return false
if (row.expiresAt && row.expiresAt.getTime() < Date.now()) return true
return Boolean(row.isRevoked)
} catch (error) {
console.warn('[AuthSession] 会话状态校验失败,默认放行:', error)
return false
}
}
export async function touchAuthSession(jti: string | undefined): Promise<void> {
if (!jti) return
try {
await db
.update(authSessions)
.set({ lastActiveAt: new Date() })
.where(eq(authSessions.sessionToken, jti))
} catch (_error) {
// ignore
}
}