| import { Hono } from 'hono'; |
| import type { Context, MiddlewareHandler } from 'hono'; |
| import { eq } from 'drizzle-orm'; |
| import { settings } from '../db/schema'; |
| import { getFullSettings } from './settings'; |
| import { hashPassword, isPasswordHash, verifyPassword } from '../password'; |
| import type { Env, Variables } from '../index'; |
|
|
| export const authApi = new Hono<{ Bindings: Env; Variables: Variables }>(); |
|
|
| type AppContext = Context<{ Bindings: Env; Variables: Variables }>; |
|
|
| async function verifyAndUpgradePassword(c: AppContext, provided: string): Promise<{ required: boolean; authorized: boolean }> { |
| const s = await getFullSettings(c.var.db); |
| if (!s.accessPassword) return { required: false, authorized: true }; |
|
|
| const authorized = verifyPassword(provided, s.accessPassword); |
| if (authorized && provided && !isPasswordHash(s.accessPassword)) { |
| await c.var.db |
| .update(settings) |
| .set({ accessPassword: hashPassword(provided), updatedAt: Date.now() }) |
| .where(eq(settings.id, 'default')); |
| } |
|
|
| return { required: true, authorized }; |
| } |
|
|
| |
| authApi.get('/check', async (c) => { |
| const provided = c.req.header('X-Access-Password') || ''; |
| return c.json(await verifyAndUpgradePassword(c, provided)); |
| }); |
|
|
| |
| authApi.post('/login', async (c) => { |
| const body = await c.req.json<{ password?: string }>().catch(() => ({} as { password?: string })); |
| const result = await verifyAndUpgradePassword(c, body.password || ''); |
| if (!result.required) return c.json({ ok: true }); |
| if (!result.authorized) { |
| return c.json({ error: 'invalid_password' }, 401); |
| } |
| return c.json({ ok: true }); |
| }); |
|
|
| export const authMiddleware: MiddlewareHandler<{ Bindings: Env; Variables: Variables }> = async (c, next) => { |
| |
| if (c.req.path.startsWith('/api/auth')) return next(); |
| const provided = c.req.header('X-Access-Password') || ''; |
| const result = await verifyAndUpgradePassword(c, provided); |
| if (!result.required) return next(); |
| if (!result.authorized) { |
| return c.json({ error: 'unauthorized' }, 401); |
| } |
| return next(); |
| }; |
|
|