mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
Security + accessibility hardening release. Closes the highest-priority findings from a focused security audit of the v1.4.23 release: - Bearer auth fail-closed when a route declares no scope (api-handler) - Public /api/send Umami proxy now rate-limits 120 req/min/IP - Proxy nonce generation switched to btoa(...) for Edge portability - Session-expiry delete race uses deleteMany + catch (no more 500s) - POST /api/import returns the standard apiError envelope on 422 - Runtime Docker image pins Prisma 7.8 + worker deps to minors - Notifications page renders a real load-error alert - Accessibility: switch aria-labels, htmlFor pairing, mobile admin Overview link, deep-link anchors on notification settings - Chart overlays: per-chart comparisonBaseline pref, BMI/bodyFat/ sleep/steps overlay keys, TrendCard layout tolerates long labels Verification: pnpm typecheck && pnpm lint && pnpm test (2244/2244) && pnpm format:check all green.
75 lines
2.1 KiB
TypeScript
75 lines
2.1 KiB
TypeScript
"use client";
|
|
|
|
import { useQuery } from "@tanstack/react-query";
|
|
|
|
import { useAuth } from "@/hooks/use-auth";
|
|
import { useTranslations } from "@/lib/i18n/context";
|
|
import { NotificationStatusCard } from "@/components/settings/notification-status-card";
|
|
import { NtfyCard } from "@/components/settings/ntfy-card";
|
|
import { TelegramCard } from "@/components/settings/telegram-card";
|
|
import { WebPushCard } from "@/components/settings/web-push-card";
|
|
|
|
interface GlobalServiceAvailability {
|
|
telegramGlobal: boolean;
|
|
ntfyGlobal: boolean;
|
|
webPushGlobal: boolean;
|
|
apiGlobal: boolean;
|
|
moodLogGlobal: boolean;
|
|
}
|
|
|
|
export function NotificationsSection() {
|
|
const { t } = useTranslations();
|
|
const { isAuthenticated } = useAuth();
|
|
|
|
const { data: globalServices } = useQuery({
|
|
queryKey: ["settings", "global-services"],
|
|
queryFn: async () => {
|
|
const res = await fetch("/api/settings/global-services");
|
|
if (!res.ok) throw new Error("Failed");
|
|
return (await res.json()).data as GlobalServiceAvailability;
|
|
},
|
|
enabled: isAuthenticated,
|
|
});
|
|
|
|
const showTelegram = globalServices?.telegramGlobal ?? true;
|
|
const showNtfy = globalServices?.ntfyGlobal ?? true;
|
|
const showWebPush = globalServices?.webPushGlobal ?? true;
|
|
|
|
return (
|
|
<section
|
|
aria-labelledby="settings-section-notifications-title"
|
|
className="space-y-6"
|
|
>
|
|
<header className="space-y-1">
|
|
<h1
|
|
id="settings-section-notifications-title"
|
|
className="text-2xl font-semibold tracking-tight"
|
|
>
|
|
{t("settings.sections.notifications.title")}
|
|
</h1>
|
|
<p className="text-muted-foreground text-sm">
|
|
{t("settings.sections.notifications.description")}
|
|
</p>
|
|
</header>
|
|
|
|
<NotificationStatusCard />
|
|
|
|
{showTelegram && (
|
|
<div id="telegram">
|
|
<TelegramCard isAuthenticated={isAuthenticated} />
|
|
</div>
|
|
)}
|
|
{showNtfy && (
|
|
<div id="ntfy">
|
|
<NtfyCard isAuthenticated={isAuthenticated} />
|
|
</div>
|
|
)}
|
|
{showWebPush && (
|
|
<div id="web-push">
|
|
<WebPushCard />
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|