diff --git a/messages/de.json b/messages/de.json index 1bd4095f7..52c22e357 100644 --- a/messages/de.json +++ b/messages/de.json @@ -1273,6 +1273,8 @@ "monitoringSave": "Monitoring speichern", "bugReportGithub": "Bugreport (GitHub Issues)", "bugReportGithubDescription": "Hinterlege GitHub-Issue-Token und Repository für eingehende Bugreports.", + "bugReportEnabledLabel": "Bug-Reports aktiviert", + "bugReportEnabledDescription": "Wenn deaktiviert, ist der Report-Button im Frontend nicht mehr sichtbar — Token und Repository bleiben gespeichert.", "bugReportRepo": "Repository", "bugReportRepoPlaceholder": "owner/repo", "bugReportToken": "Issue-Token", diff --git a/messages/en.json b/messages/en.json index 3dff1947f..7b33c4cf8 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1273,6 +1273,8 @@ "monitoringSave": "Save monitoring", "bugReportGithub": "Bug report (GitHub Issues)", "bugReportGithubDescription": "Configure GitHub issue token and repository for incoming bug reports.", + "bugReportEnabledLabel": "Bug reports enabled", + "bugReportEnabledDescription": "When off, the in-app report button hides — token and repository stay configured.", "bugReportRepo": "Repository", "bugReportRepoPlaceholder": "owner/repo", "bugReportToken": "Issue token", diff --git a/prisma/migrations/0027_bug_report_enabled_toggle/migration.sql b/prisma/migrations/0027_bug_report_enabled_toggle/migration.sql new file mode 100644 index 000000000..8431b2f8a --- /dev/null +++ b/prisma/migrations/0027_bug_report_enabled_toggle/migration.sql @@ -0,0 +1,16 @@ +-- v1.4.3: explicit on/off switch for the bug-report surface. +-- +-- Until this release the bug-report feature was implicitly gated on +-- "is the GitHub token + repo configured?". That coupling meant an +-- admin who wanted to *temporarily* hide the bug-report button (e.g. +-- during a deploy freeze) had to clear the encrypted token, which +-- forced a re-entry on resume — destructive for a reversible +-- intent. +-- +-- Default `true` so existing instances with credentials configured +-- keep the feature visible after the upgrade. Admins who want it off +-- flip the switch in `/admin/integrations` (or whichever section +-- mounts the bug-report card). + +ALTER TABLE "app_settings" + ADD COLUMN "bug_report_enabled" BOOLEAN NOT NULL DEFAULT TRUE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 983dc4afc..4df027029 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -466,6 +466,11 @@ model AppSettings { glitchtipEnvironment String? @map("glitchtip_environment") githubIssueTokenEncrypted String? @map("github_issue_token_encrypted") githubIssueRepo String? @map("github_issue_repo") + // v1.4.3: explicit toggle so an admin can hide the bug-report + // surface without nuking the token+repo config. Default true so + // existing instances with credentials don't accidentally lose + // the feature on upgrade. + bugReportEnabled Boolean @default(true) @map("bug_report_enabled") // Medication reminder thresholds (minutes after windowEnd) reminderLateMinutes Int @default(120) @map("reminder_late_minutes") diff --git a/src/app/api/admin/settings/route.ts b/src/app/api/admin/settings/route.ts index 9ab0671f0..b4155f145 100644 --- a/src/app/api/admin/settings/route.ts +++ b/src/app/api/admin/settings/route.ts @@ -41,6 +41,7 @@ export const GET = apiHandler(async () => { bugReportConfigured: Boolean( settings?.githubIssueRepo && settings?.githubIssueTokenEncrypted, ), + bugReportEnabled: settings?.bugReportEnabled ?? true, reminderLateMinutes: settings?.reminderLateMinutes ?? 120, reminderMissedMinutes: settings?.reminderMissedMinutes ?? 240, moodLogGlobal: settings?.moodLogGlobal ?? true, @@ -73,6 +74,7 @@ export const PUT = apiHandler(async (request: NextRequest) => { "umamiEnabled", "glitchtipEnabled", "moodLogGlobal", + "bugReportEnabled", ] as const; for (const field of booleanFields) { if (data[field] !== undefined) { @@ -220,6 +222,7 @@ export const PUT = apiHandler(async (request: NextRequest) => { bugReportConfigured: Boolean( settings.githubIssueRepo && settings.githubIssueTokenEncrypted, ), + bugReportEnabled: settings.bugReportEnabled, reminderLateMinutes: settings.reminderLateMinutes, reminderMissedMinutes: settings.reminderMissedMinutes, moodLogGlobal: settings.moodLogGlobal, diff --git a/src/app/api/bugreport/route.ts b/src/app/api/bugreport/route.ts index aa1839e27..5d1e4a3d9 100644 --- a/src/app/api/bugreport/route.ts +++ b/src/app/api/bugreport/route.ts @@ -36,9 +36,18 @@ export const POST = apiHandler(async (request: NextRequest) => { select: { githubIssueTokenEncrypted: true, githubIssueRepo: true, + bugReportEnabled: true, }, }); + // v1.4.3: explicit toggle. When the admin disables bug reports we + // return 503 immediately — the client already renders a "not + // configured" UI for that status, so the report button gracefully + // disappears without needing additional client-side wiring. + if (appSettings && appSettings.bugReportEnabled === false) { + return apiError("Bug reports are disabled by the administrator", 503); + } + let configuredToken: string | null = null; if (appSettings?.githubIssueTokenEncrypted) { try { diff --git a/src/components/admin/_shared.tsx b/src/components/admin/_shared.tsx index c45b35ffc..f1b5dc4e7 100644 --- a/src/components/admin/_shared.tsx +++ b/src/components/admin/_shared.tsx @@ -70,6 +70,7 @@ export interface AdminSettings { glitchtipEnvironment: string | null; bugReportRepo: string | null; bugReportConfigured: boolean; + bugReportEnabled: boolean; reminderLateMinutes: number; reminderMissedMinutes: number; } diff --git a/src/components/admin/bug-report-section.tsx b/src/components/admin/bug-report-section.tsx index d16e1c023..b0cd9a392 100644 --- a/src/components/admin/bug-report-section.tsx +++ b/src/components/admin/bug-report-section.tsx @@ -6,6 +6,7 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { Switch } from "@/components/ui/switch"; import { useTranslations } from "@/lib/i18n/context"; import { PasswordInput, useAdminSettings, useUpdateSettings } from "./_shared"; @@ -21,6 +22,7 @@ export function BugReportSection({ id }: { id: string }) { const bugReportRepoValue = bugReportRepoDraft ?? settings?.bugReportRepo ?? ""; const configured = settings?.bugReportConfigured ?? false; + const enabled = settings?.bugReportEnabled ?? true; function saveBugReportSettings() { const payload: Record = { @@ -38,6 +40,10 @@ export function BugReportSection({ id }: { id: string }) { }); } + function toggleEnabled(next: boolean) { + updateSettings.mutate({ bugReportEnabled: next }); + } + return (
+
+
+

+ {t("admin.bugReportEnabledLabel")} +

+

+ {t("admin.bugReportEnabledDescription")} +

+
+ +
+
+ {/* v1.4.3: aligned with the Withings/moodLog disconnect + buttons — outline + text-destructive so the same action + reads the same everywhere in Settings. */} diff --git a/src/lib/validations/admin.ts b/src/lib/validations/admin.ts index 243f85b19..feb3ca36e 100644 --- a/src/lib/validations/admin.ts +++ b/src/lib/validations/admin.ts @@ -71,6 +71,7 @@ export const adminSettingsSchema = z .optional(), bugReportToken: z.string().optional(), clearBugReportToken: z.boolean().optional(), + bugReportEnabled: z.boolean().optional(), reminderLateMinutes: z.number().int().min(15).max(480).optional(), reminderMissedMinutes: z.number().int().min(30).max(720).optional(), moodLogGlobal: z.boolean().optional(),