81 lines
2.1 KiB
TypeScript
81 lines
2.1 KiB
TypeScript
|
|
import { stopImpersonating } from './impersonation'
|
||
|
|
import { prisma } from './prisma'
|
||
|
|
import { redisSessions } from './redis/redisSessions'
|
||
|
|
|
||
|
|
import type { APIContext, AstroCookies, AstroCookieSetOptions } from 'astro'
|
||
|
|
|
||
|
|
const COOKIE_NAME = 'user_session_id'
|
||
|
|
const COOKIE_MAX_AGE = 60 * 60 * 24 * 7 // 1 week
|
||
|
|
|
||
|
|
const defaultCookieOptions = {
|
||
|
|
path: '/',
|
||
|
|
secure: true,
|
||
|
|
httpOnly: true,
|
||
|
|
sameSite: 'strict',
|
||
|
|
maxAge: COOKIE_MAX_AGE,
|
||
|
|
} as const satisfies AstroCookieSetOptions
|
||
|
|
|
||
|
|
export function getUserSessionIdCookie(cookies: AstroCookies) {
|
||
|
|
return cookies.get(COOKIE_NAME)?.value
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getUserFromCookies(cookies: AstroCookies) {
|
||
|
|
const userSessionId = getUserSessionIdCookie(cookies)
|
||
|
|
if (!userSessionId) return null
|
||
|
|
|
||
|
|
const userSecretTokenHash = await redisSessions.getUserBySessionId(userSessionId)
|
||
|
|
if (!userSecretTokenHash) return null
|
||
|
|
|
||
|
|
return prisma.user.findFirst({
|
||
|
|
where: {
|
||
|
|
secretTokenHash: userSecretTokenHash,
|
||
|
|
},
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function setUserSessionIdCookie(
|
||
|
|
cookies: AstroCookies,
|
||
|
|
userSecretTokenHash: string,
|
||
|
|
options: AstroCookieSetOptions = {}
|
||
|
|
) {
|
||
|
|
const sessId = await redisSessions.createSession(userSecretTokenHash)
|
||
|
|
cookies.set(COOKIE_NAME, sessId, {
|
||
|
|
...defaultCookieOptions,
|
||
|
|
...options,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function removeUserSessionIdCookie(cookies: AstroCookies) {
|
||
|
|
const sessionId = cookies.get(COOKIE_NAME)?.value
|
||
|
|
if (sessionId) {
|
||
|
|
await redisSessions.deleteSession(sessionId)
|
||
|
|
}
|
||
|
|
cookies.delete(COOKIE_NAME, { path: '/' })
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function logout(context: Pick<APIContext, 'cookies' | 'locals'>) {
|
||
|
|
await stopImpersonating(context)
|
||
|
|
|
||
|
|
await removeUserSessionIdCookie(context.cookies)
|
||
|
|
|
||
|
|
context.locals.user = null
|
||
|
|
context.locals.actualUser = null
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function login(
|
||
|
|
context: Pick<APIContext, 'cookies' | 'locals'>,
|
||
|
|
user: NonNullable<APIContext['locals']['user']>
|
||
|
|
) {
|
||
|
|
await stopImpersonating(context)
|
||
|
|
|
||
|
|
await setUserSessionIdCookie(context.cookies, user.secretTokenHash)
|
||
|
|
|
||
|
|
await prisma.user.update({
|
||
|
|
where: { id: user.id },
|
||
|
|
data: { lastLoginAt: new Date() },
|
||
|
|
})
|
||
|
|
|
||
|
|
context.locals.user = user
|
||
|
|
context.locals.actualUser = null
|
||
|
|
}
|