| import { Hono } from 'hono'; |
| import { eq } from 'drizzle-orm'; |
| import { settings } from '../db/schema'; |
| import { backupDatabaseToRclone } from '../backup'; |
| import { hashPassword } from '../password'; |
| import type { Env, Variables } from '../index'; |
|
|
| export const settingsApi = new Hono<{ Bindings: Env; Variables: Variables }>(); |
|
|
| const DEFAULT_ID = 'default'; |
|
|
| async function ensureRow(db: any) { |
| const row = (await db.select().from(settings).where(eq(settings.id, DEFAULT_ID))).at(0); |
| if (row) return row; |
| await db.insert(settings).values({ id: DEFAULT_ID, updatedAt: Date.now() }); |
| return (await db.select().from(settings).where(eq(settings.id, DEFAULT_ID))).at(0); |
| } |
|
|
| |
| settingsApi.get('/', async (c) => { |
| const row = await ensureRow(c.var.db); |
| let customSizes: string[] = []; |
| try { customSizes = JSON.parse(row.customSizes || '[]'); } catch {} |
| return c.json({ |
| openaiBaseUrl: row.openaiBaseUrl || '', |
| openaiApiKeySet: !!row.openaiApiKey, |
| imageModel: row.imageModel || '', |
| translateModel: row.translateModel || '', |
| customSizes, |
| defaultSize: row.defaultSize || '1024x1024', |
| accessPasswordSet: !!row.accessPassword, |
| jobPollInterval: row.jobPollInterval || 3000, |
| }); |
| }); |
|
|
| |
| settingsApi.put('/', async (c) => { |
| const db = c.var.db; |
| await ensureRow(db); |
| const body = await c.req.json<Record<string, any>>(); |
| const update: Record<string, any> = { updatedAt: Date.now() }; |
|
|
| if (body.openaiBaseUrl !== undefined) |
| update.openaiBaseUrl = String(body.openaiBaseUrl).trim() || 'https://api.openai.com/v1'; |
| if (body.imageModel !== undefined) |
| update.imageModel = String(body.imageModel).trim() || 'gpt-image-2'; |
| if (body.translateModel !== undefined) |
| update.translateModel = String(body.translateModel).trim() || 'gpt-4o-mini'; |
|
|
| if (body.defaultSize !== undefined) { |
| const v = String(body.defaultSize).trim().toLowerCase().replace(/×/g, 'x'); |
| if (/^\d{2,5}x\d{2,5}$/.test(v)) update.defaultSize = v; |
| } |
|
|
| if (body.customSizes !== undefined) { |
| let arr: string[] = []; |
| if (Array.isArray(body.customSizes)) arr = body.customSizes; |
| else if (typeof body.customSizes === 'string') { |
| arr = body.customSizes.split(/[\s,;\n]+/); |
| } |
| arr = arr |
| .map((s) => String(s).trim().toLowerCase().replace(/×/g, 'x')) |
| .filter((s) => /^\d{2,5}x\d{2,5}$/.test(s)); |
| |
| const presets = new Set(['1024x1024', '1792x1024', '1024x1792']); |
| arr = Array.from(new Set(arr)).filter((s) => !presets.has(s)); |
| update.customSizes = JSON.stringify(arr); |
| } |
|
|
| if (body.jobPollInterval !== undefined) { |
| const n = Number(body.jobPollInterval); |
| if (Number.isFinite(n)) { |
| update.jobPollInterval = Math.max(1000, Math.min(30000, Math.round(n))); |
| } |
| } |
|
|
| |
| if (body.openaiApiKey && String(body.openaiApiKey).trim()) |
| update.openaiApiKey = String(body.openaiApiKey).trim(); |
|
|
| |
| if (body.accessPassword === null) { |
| update.accessPassword = ''; |
| } else if (typeof body.accessPassword === 'string' && body.accessPassword.trim()) { |
| update.accessPassword = hashPassword(body.accessPassword.trim()); |
| } |
|
|
| await db.update(settings).set(update).where(eq(settings.id, DEFAULT_ID)); |
| const backup = await backupDatabaseToRclone(); |
| return c.json({ ok: true, backup }); |
| }); |
|
|
| |
| export async function getFullSettings(db: any) { |
| return await ensureRow(db); |
| } |
|
|