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) => { // 放行 /api/auth/* —— 内部检查/登陆都不需要密码 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(); };