Files
dticket.tootaio.com/server/utils/session.ts
xiaomai 377a9617be feat: implement auth system, passkeys, and user management
Add PostgreSQL and Redis integration for users and sessions
Implement password and WebAuthn passkey login flows
Add Docker stack, super-admin seeding, and protected routes
2026-04-12 20:16:43 +08:00

104 lines
2.4 KiB
TypeScript

import { randomUUID } from 'node:crypto'
import type { H3Event } from 'h3'
import { deleteCookie, getCookie, getRequestURL, setCookie } from 'h3'
import { randomToken } from './base64url'
import { getRedisClient } from './redis'
const DEFAULT_SESSION_TTL_SECONDS = 60 * 60 * 24 * 7
const SHORT_SESSION_TTL_SECONDS = 60 * 60 * 24
interface StoredSession {
id: string
userId: string
remember: boolean
createdAt: string
}
function getSessionCookieName() {
const config = useRuntimeConfig()
return config.sessionCookieName || 'dinner_ticket_session'
}
function getSessionStorageKey(token: string) {
return `auth:session:${token}`
}
function shouldUseSecureCookies(event: H3Event) {
const url = getRequestURL(event)
return url.protocol === 'https:'
}
function getSessionTtl(remember: boolean) {
return remember ? DEFAULT_SESSION_TTL_SECONDS : SHORT_SESSION_TTL_SECONDS
}
export async function createUserSession(event: H3Event, input: {
userId: string
remember: boolean
}) {
const token = randomToken(32)
const session: StoredSession = {
id: randomUUID(),
userId: input.userId,
remember: input.remember,
createdAt: new Date().toISOString()
}
const redis = await getRedisClient()
const ttl = getSessionTtl(input.remember)
await redis.set(getSessionStorageKey(token), JSON.stringify(session), {
expiration: {
type: 'EX',
value: ttl
}
})
setCookie(event, getSessionCookieName(), token, {
httpOnly: true,
sameSite: 'lax',
secure: shouldUseSecureCookies(event),
path: '/',
maxAge: ttl
})
return session
}
export async function getUserSession(event: H3Event): Promise<StoredSession | null> {
const token = getCookie(event, getSessionCookieName())
if (!token) {
return null
}
const redis = await getRedisClient()
const raw = await redis.get(getSessionStorageKey(token))
if (!raw) {
deleteCookie(event, getSessionCookieName(), { path: '/' })
return null
}
try {
return JSON.parse(raw) as StoredSession
} catch {
await redis.del(getSessionStorageKey(token))
deleteCookie(event, getSessionCookieName(), { path: '/' })
return null
}
}
export async function destroyUserSession(event: H3Event) {
const token = getCookie(event, getSessionCookieName())
if (token) {
const redis = await getRedisClient()
await redis.del(getSessionStorageKey(token))
}
deleteCookie(event, getSessionCookieName(), { path: '/' })
}