diff --git a/src/context.ts b/src/context.ts index a825e6a..f6b257a 100644 --- a/src/context.ts +++ b/src/context.ts @@ -140,8 +140,9 @@ export const serverContext = new Elysia({ name: 'server-context' }).use(httpErro rh: rabbitHole, log, db, -}).onBeforeHandle(({ headers, HttpError }) => { +}).onBeforeHandle({ as: 'scoped' }, ({ headers, path, HttpError }) => { const apiKey = headers.token, realKey = parsedEnv.apiKey + if (path.startsWith('/docs')) return if (realKey && realKey !== apiKey) throw HttpError.Unauthorized('Invalid API key') }).derive({ as: 'global' }, ({ headers }) => { diff --git a/src/errors.ts b/src/errors.ts index a1d0fbc..00b96f4 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -91,10 +91,10 @@ export const httpError = new Elysia({ name: 'http-error' }) description: 'The model for an HTTP error response', }), }) - .onError(({ code, error, set }) => { + .onError({ as: 'global' }, ({ code, error, set }) => { if (code === 'HTTP_ERROR') { set.status = error.status - set.headers['content-type'] = 'application/json+problem' + set.headers['content-type'] = 'application/json' return { code: error.message, status: error.status, diff --git a/src/logger.ts b/src/logger.ts index 32d10cc..8c94277 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -5,7 +5,7 @@ import { createConsola } from 'consola' import { getColor } from 'consola/utils' import { Table } from 'console-table-printer' import { format } from 'date-fns' -import { Logestic, type LogesticOptions } from 'logestic' +import { Logestic } from 'logestic' import { catPaths, LogLevel, parsedEnv } from './utils.ts' const logger = createConsola({ @@ -103,25 +103,22 @@ export const log = Object.freeze({ debug: logger.debug, }) -export function httpLogger(options?: LogesticOptions) { - return new Logestic({ - showLevel: true, - ...options, - }).use(['time', 'method', 'path', 'duration']).format({ - onSuccess({ time, method, path, duration }) { - const dateTime = chalk.gray(format(time, 'dd/MM/yyyy HH:mm:ss')) - const methodPath = chalk.cyan(`${method} ${decodeURIComponent(path)}`) - return `${dateTime} ${methodPath} ${Number(duration) / 1000}ms` - }, - onFailure({ request, datetime, error }) { - const { method, url } = request - const err = error as HttpError - const baseUrl = url.substring(catPaths.baseUrl.length - 1) - const dateTime = chalk.gray(format(datetime, 'dd/MM/yyyy HH:mm:ss')) - const methodPath = chalk.red(`${method} ${decodeURIComponent(baseUrl)}`) - const errorCode = chalk.bgRed(`[${err.status} - ${err.message}]`) - const errorText = `${errorCode} ${chalk.red(err.cause)}` - return `${dateTime} ${methodPath}\n${errorText}` - }, - }) -} +export const httpLogger = new Logestic({ + showLevel: true, +}).use(['time', 'method', 'path', 'duration']).format({ + onSuccess({ time, method, path, duration }) { + const dateTime = chalk.gray(format(time, 'dd/MM/yyyy HH:mm:ss')) + const methodPath = chalk.cyan(`${method} ${decodeURIComponent(path)}`) + return `${dateTime} ${methodPath} ${Number(duration) / 1000}ms` + }, + onFailure({ request, datetime, error }) { + const { method, url } = request + const err = error as HttpError + const baseUrl = url.substring(catPaths.baseUrl.length - 1) + const dateTime = chalk.gray(format(datetime, 'dd/MM/yyyy HH:mm:ss')) + const methodPath = chalk.red(`${method} ${decodeURIComponent(baseUrl)}`) + const errorCode = chalk.bgRed(`[${err.status} - ${err.message}]`) + const errorText = `${errorCode} ${chalk.red(err.cause)}` + return `${dateTime} ${methodPath}\n${errorText}` + }, +}) diff --git a/src/main.ts b/src/main.ts index 67890af..d715aef 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,12 +8,12 @@ import { Elysia } from 'elysia' import { checkPort } from 'get-port-please' import isDocker from 'is-docker' import pkg from '~/package.json' -import { swaggerTags } from './context.ts' +import { serverContext, swaggerTags } from './context.ts' import { httpLogger, log } from './logger.ts' import { logWelcome, parsedEnv } from './utils.ts' const app = new Elysia() - .use(httpLogger()) + .use(httpLogger) .use(serverTiming()) .use(cors({ origin: parsedEnv.corsAllowedOrigins, @@ -42,7 +42,7 @@ const app = new Elysia() version: pkg.version, }, tags: Object.values(swaggerTags), - security: [{ apiKey: ['token'] }], + security: [{ token: [] }], components: { securitySchemes: { token: { @@ -62,6 +62,7 @@ const app = new Elysia() }, }, })) + .use(serverContext) .use(generalRoutes) .use(settingsRoutes) .use(llmRoutes) diff --git a/src/routes/general.ts b/src/routes/general.ts index f123f7f..f123300 100644 --- a/src/routes/general.ts +++ b/src/routes/general.ts @@ -12,13 +12,21 @@ export const generalRoutes = new Elysia({ params: t.Object({ userId: t.String({ default: 'user' }), }), + query: t.Object({ + save: t.Optional(t.Boolean()), + token: t.Optional(t.String()), + }), body: t.Intersect([ t.Object({ text: t.String(), - save: t.Optional(t.Boolean()), }), t.Record(t.String(), t.Any()), ]), + beforeHandle: ({ query, HttpError }) => { + const apiKey = query.token, realKey = parsedEnv.apiKey + if (realKey && realKey !== apiKey) + throw HttpError.Unauthorized('Invalid API key') + }, open: async (ws) => { const { data: { params } } = ws const user = params.userId @@ -36,10 +44,10 @@ export const generalRoutes = new Elysia({ cat.removeStray(user) log.debug(`User ${user} disconnected from the WebSocket.`) }, - message: ({ data: { params, body } }) => { + message: ({ data: { params, body, query } }) => { const user = params.userId const stray = cat.getStray(user)! - stray.run(body, body.save).then(stray.send).catch(log.error) + stray.run(body, query.save).then(stray.send).catch(log.error) }, error: ({ error }) => { log.dir(error)