44 lines
1.3 KiB
TypeScript
44 lines
1.3 KiB
TypeScript
import type { Kysely } from 'kysely'
|
|
import type { DB } from '../types/db.types.js'
|
|
import { SESSION_COOKIE } from '../middleware/session-cookie.js'
|
|
|
|
export const SESSION_DAYS = 30
|
|
|
|
export const uid = () =>
|
|
globalThis.crypto?.randomUUID?.() ??
|
|
`id_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`
|
|
|
|
const isoOffsetDays = (days: number) => {
|
|
const d = new Date()
|
|
d.setUTCDate(d.getUTCDate() + days)
|
|
return d.toISOString()
|
|
}
|
|
|
|
/**
|
|
* Creates a server-side session row for `userId` and writes the session cookie.
|
|
* Shared by password login and the passwordless booking-form magic link so both
|
|
* issue identical sessions. `http` is optional (absent off the HTTP transport).
|
|
*/
|
|
export async function createUserSession(
|
|
kysely: Kysely<DB>,
|
|
http: { response?: { cookie: (name: string, value: string, opts: Record<string, unknown>) => void } } | undefined,
|
|
userId: string,
|
|
): Promise<string> {
|
|
const sessionId = uid()
|
|
await kysely
|
|
.insertInto('bookingSession')
|
|
.values({ sessionId, userId, expiresAt: isoOffsetDays(SESSION_DAYS) })
|
|
.execute()
|
|
|
|
if (http?.response) {
|
|
http.response.cookie(SESSION_COOKIE, sessionId, {
|
|
httpOnly: true,
|
|
secure: false,
|
|
sameSite: 'lax',
|
|
path: '/',
|
|
maxAge: 60 * 60 * 24 * SESSION_DAYS,
|
|
})
|
|
}
|
|
return sessionId
|
|
}
|