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" },