diff --git a/apps/web/src/components/admin/SystemSettingsClient.tsx b/apps/web/src/components/admin/SystemSettingsClient.tsx index f1e5f83..aeaf39a 100644 --- a/apps/web/src/components/admin/SystemSettingsClient.tsx +++ b/apps/web/src/components/admin/SystemSettingsClient.tsx @@ -257,6 +257,12 @@ export function SystemSettingsClient() { }, }); + const [geminiTestResult, setGeminiTestResult] = useState<{ ok: boolean; model?: string; error?: string } | null>(null); + const testGeminiMut = trpc.settings.testGeminiConnection.useMutation({ + onSuccess: (data) => setGeminiTestResult(data as any), + onError: (err) => setGeminiTestResult({ ok: false, error: err.message }), + }); + function handleSaveSmtp() { saveSmtpMutation.mutate({ smtpHost: smtpHost || undefined, @@ -1187,7 +1193,7 @@ export function SystemSettingsClient() { )} -
+
+ {imageProvider === "gemini" && ( + + )} {imageSaved && ( Saved )}
+ {geminiTestResult && ( +
+ {geminiTestResult.ok + ? `Gemini image generation works! Model: ${(geminiTestResult as any).model}` + : `Test failed: ${(geminiTestResult as any).error}`} +
+ )}
{/* ── SMTP / Email ──────────────────────────────────────────── */} diff --git a/packages/api/src/router/settings.ts b/packages/api/src/router/settings.ts index 9aa2f0d..d1ddba5 100644 --- a/packages/api/src/router/settings.ts +++ b/packages/api/src/router/settings.ts @@ -319,6 +319,48 @@ export const settingsRouter = createTRPCRouter({ return result; }), + testGeminiConnection: adminProcedure.mutation(async ({ ctx }) => { + const settings = await ctx.db.systemSettings.findUnique({ + where: { id: "singleton" }, + select: { geminiApiKey: true, geminiModel: true }, + }); + + if (!settings?.geminiApiKey) { + return { ok: false, error: "Gemini API key is not configured." }; + } + + try { + const { generateGeminiImage } = await import("../gemini-client.js"); + const model = settings.geminiModel ?? "gemini-2.0-flash-preview-image-generation"; + + // Generate a tiny test image with a simple prompt + const dataUrl = await generateGeminiImage( + settings.geminiApiKey, + "A simple blue circle on white background, minimal, 256x256", + model, + ); + + const hasImage = dataUrl.startsWith("data:image/"); + + void createAuditEntry({ + db: ctx.db, + entityType: "SystemSettings", + entityId: "singleton", + entityName: "Gemini Connection Test", + action: "UPDATE", + userId: ctx.dbUser?.id, + after: { testResult: hasImage ? "success" : "failed" }, + source: "ui", + summary: hasImage ? "Gemini image generation test succeeded" : "Gemini test returned no image", + }); + + return { ok: hasImage, model, preview: hasImage ? dataUrl.slice(0, 100) + "..." : undefined }; + } catch (err) { + const { parseGeminiError } = await import("../gemini-client.js"); + return { ok: false, error: parseGeminiError(err) }; + } + }), + getAiConfigured: protectedProcedure.query(async ({ ctx }) => { const settings = await ctx.db.systemSettings.findUnique({ where: { id: "singleton" },