sinProject-Inc/talk

View on GitHub
src/hooks.server.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { base } from '$app/paths'
import { logger } from '$lib/app/logger'
import { Repository } from '$lib/app/repository'
import { Session } from '$lib/auth/session'
import { Signing } from '$lib/auth/signing'
import { ClientAddress } from '$lib/network/client_address'
// import { ClientHostName } from '$lib/network/client_hostname'
import type { AuthToken, Role, User } from '@prisma/client'
import type { Handle, HandleServerError, RequestEvent } from '@sveltejs/kit'

// NOTE: https://kit.svelte.jp/docs/errors
// eslint-disable-next-line @typescript-eslint/naming-convention
export const handleError: HandleServerError = ({ error, event }) => {
    const client_address = new ClientAddress(event.request, event.getClientAddress).value

    const { code } = error as { code?: string }
    const { message } = error as { message?: string }

    if (message?.startsWith('Not found')) {
        logger.warn(`${client_address} [SERVER] 404: ${message}`)
    } else {
        logger.error(`${client_address} [SERVER] Unhandled Error:`, error, { event })
    }

    // eslint-disable-next-line no-console
    // console.error('[server] Unhandled Error:', error)

    return {
        code: code ?? 'UNKNOWN',
        message: 'Whoops!',
    }
}

function is_authorized_api(pathname: string): boolean {
    return pathname.startsWith(`${base}/api/`) && !pathname.includes(`${base}/api/log`)
}

async function find_auth_token(event: RequestEvent): Promise<
    | (AuthToken & {
            user: User & {
                role: Role
            }
      })
    | null
> {
    const session = new Session(event.cookies)

    if (!session.id) return null

    return await Repository.auth_token.find(session)
}

export const handle: Handle = async ({ event, resolve }) => {
    const client_address = new ClientAddress(event.request, event.getClientAddress).value
    // const client_hostname = await new ClientHostName(client_address).reverse()

    const auth_token = await find_auth_token(event)

    let theme = ''

    if (auth_token) {
        await Signing.access_valid(auth_token, event.cookies)

        event.locals.user = {
            email: auth_token.user.email,
            role: auth_token.user.role.name,
        }

        theme = auth_token.user.theme.replace('system', '')
    } else {
        if (is_authorized_api(event.url.pathname)) {
            logger.warn(
                `${client_address} [API] Unauthorized API request: [${event.request.method}] ${event.url}`
            )

            return new Response('Unauthorized', { status: 401 })
        }
    }

    if (event.url.pathname !== `${base}/api/log`) {
        logger.info(`${client_address} [${event.request.method}] ${event.url}`)
    }

    const response = await resolve(event, {
        // eslint-disable-next-line @typescript-eslint/naming-convention
        transformPageChunk: ({ html }) =>
            html.replace('data-theme=""', `data-theme="${theme}" class="${theme}"`),
    })

    return response
}