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 { 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 { 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 { if (!jti) return try { await db .update(authSessions) .set({ lastActiveAt: new Date() }) .where(eq(authSessions.sessionToken, jti)) } catch (_error) { // ignore } }