mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
feat(admin): surface the GeoLite2 availability state on the system status page
Both the `/admin` overview snapshot and the full `/admin/system-status` page now read `offlineGeoEnabled` from `/api/version` and render a status row with a green chip when the offline databases are loaded or a yellow chip with the configuration hint when the runtime is on the ipwho.is fallback. The row only renders when the field is present so legacy `/api/version` responses do not produce a placeholder slot. Adds six locale keys per bundle (`admin.offlineGeoLabel`, `offlineGeoEnabled`, `offlineGeoFallback` plus the three `admin.overview.snapshotOfflineGeo*` siblings) across all six locales.
This commit is contained in:
1 parent
4eb22387a2
commit
82a23069a5
8 files changed
+99
No files matched your search
@@ -1996,6 +1996,9 @@
|
||||
"webPushGlobal": "Browser-Push-Benachrichtigungen erlauben.",
|
||||
"integrationWebPush": "Web Push",
|
||||
"integrationBugReport": "Bug-Reports",
|
||||
"offlineGeoLabel": "Geodaten",
|
||||
"offlineGeoEnabled": "Offline (GeoLite2)",
|
||||
"offlineGeoFallback": "Fallback (ipwho.is) — MAXMIND_LICENSE_KEY in CI setzen",
|
||||
"apiGlobal": "API-Endpunkte und API-Tokens erlauben.",
|
||||
"umamiTitle": "Umami",
|
||||
"umamiDescription": "Web-Analyse mit Umami",
|
||||
@@ -2176,6 +2179,9 @@
|
||||
"snapshotStarted": "Gestartet",
|
||||
"snapshotBuildSha": "Build-SHA",
|
||||
"snapshotBuiltAt": "Gebaut am",
|
||||
"snapshotOfflineGeo": "Geodaten",
|
||||
"snapshotOfflineGeoOn": "Offline (GeoLite2)",
|
||||
"snapshotOfflineGeoOff": "Fallback (ipwho.is)",
|
||||
"snapshotLoadError": "System-Snapshot konnte nicht geladen werden.",
|
||||
"snapshotLoading": "Snapshot wird geladen...",
|
||||
"auditTitle": "Letzte Aktivitäten",
|
||||
|
||||
@@ -1996,6 +1996,9 @@
|
||||
"webPushGlobal": "Allow browser push notifications.",
|
||||
"integrationWebPush": "Web Push",
|
||||
"integrationBugReport": "Bug Report",
|
||||
"offlineGeoLabel": "Geo data",
|
||||
"offlineGeoEnabled": "Offline (GeoLite2)",
|
||||
"offlineGeoFallback": "Fallback (ipwho.is) — set MAXMIND_LICENSE_KEY in CI",
|
||||
"apiGlobal": "Allow API endpoints and API tokens.",
|
||||
"umamiTitle": "Umami",
|
||||
"umamiDescription": "Web analytics with Umami",
|
||||
@@ -2176,6 +2179,9 @@
|
||||
"snapshotStarted": "Started",
|
||||
"snapshotBuildSha": "Build SHA",
|
||||
"snapshotBuiltAt": "Built at",
|
||||
"snapshotOfflineGeo": "Geo data",
|
||||
"snapshotOfflineGeoOn": "Offline (GeoLite2)",
|
||||
"snapshotOfflineGeoOff": "Fallback (ipwho.is)",
|
||||
"snapshotLoadError": "Failed to load system snapshot.",
|
||||
"snapshotLoading": "Loading snapshot...",
|
||||
"auditTitle": "Recent activity",
|
||||
|
||||
@@ -1996,6 +1996,9 @@
|
||||
"webPushGlobal": "Allow browser push notifications.",
|
||||
"integrationWebPush": "Web Push",
|
||||
"integrationBugReport": "Bug Report",
|
||||
"offlineGeoLabel": "Datos geográficos",
|
||||
"offlineGeoEnabled": "Sin conexión (GeoLite2)",
|
||||
"offlineGeoFallback": "Respaldo (ipwho.is) — configura MAXMIND_LICENSE_KEY en CI",
|
||||
"apiGlobal": "Allow API endpoints and API tokens.",
|
||||
"umamiTitle": "Umami",
|
||||
"umamiDescription": "Web analytics with Umami",
|
||||
@@ -2176,6 +2179,9 @@
|
||||
"snapshotStarted": "Started",
|
||||
"snapshotBuildSha": "Build SHA",
|
||||
"snapshotBuiltAt": "Built at",
|
||||
"snapshotOfflineGeo": "Datos geográficos",
|
||||
"snapshotOfflineGeoOn": "Sin conexión (GeoLite2)",
|
||||
"snapshotOfflineGeoOff": "Respaldo (ipwho.is)",
|
||||
"snapshotLoadError": "Failed to load system snapshot.",
|
||||
"snapshotLoading": "Loading snapshot...",
|
||||
"auditTitle": "Recent activity",
|
||||
|
||||
@@ -1996,6 +1996,9 @@
|
||||
"webPushGlobal": "Allow browser push notifications.",
|
||||
"integrationWebPush": "Web Push",
|
||||
"integrationBugReport": "Bug Report",
|
||||
"offlineGeoLabel": "Données géographiques",
|
||||
"offlineGeoEnabled": "Hors ligne (GeoLite2)",
|
||||
"offlineGeoFallback": "Fallback (ipwho.is) — définissez MAXMIND_LICENSE_KEY dans la CI",
|
||||
"apiGlobal": "Allow API endpoints and API tokens.",
|
||||
"umamiTitle": "Umami",
|
||||
"umamiDescription": "Web analytics with Umami",
|
||||
@@ -2176,6 +2179,9 @@
|
||||
"snapshotStarted": "Started",
|
||||
"snapshotBuildSha": "Build SHA",
|
||||
"snapshotBuiltAt": "Built at",
|
||||
"snapshotOfflineGeo": "Données géographiques",
|
||||
"snapshotOfflineGeoOn": "Hors ligne (GeoLite2)",
|
||||
"snapshotOfflineGeoOff": "Fallback (ipwho.is)",
|
||||
"snapshotLoadError": "Failed to load system snapshot.",
|
||||
"snapshotLoading": "Loading snapshot...",
|
||||
"auditTitle": "Recent activity",
|
||||
|
||||
@@ -1996,6 +1996,9 @@
|
||||
"webPushGlobal": "Allow browser push notifications.",
|
||||
"integrationWebPush": "Web Push",
|
||||
"integrationBugReport": "Bug Report",
|
||||
"offlineGeoLabel": "Dati geografici",
|
||||
"offlineGeoEnabled": "Offline (GeoLite2)",
|
||||
"offlineGeoFallback": "Fallback (ipwho.is) — imposta MAXMIND_LICENSE_KEY in CI",
|
||||
"apiGlobal": "Allow API endpoints and API tokens.",
|
||||
"umamiTitle": "Umami",
|
||||
"umamiDescription": "Web analytics with Umami",
|
||||
@@ -2176,6 +2179,9 @@
|
||||
"snapshotStarted": "Started",
|
||||
"snapshotBuildSha": "Build SHA",
|
||||
"snapshotBuiltAt": "Built at",
|
||||
"snapshotOfflineGeo": "Dati geografici",
|
||||
"snapshotOfflineGeoOn": "Offline (GeoLite2)",
|
||||
"snapshotOfflineGeoOff": "Fallback (ipwho.is)",
|
||||
"snapshotLoadError": "Failed to load system snapshot.",
|
||||
"snapshotLoading": "Loading snapshot...",
|
||||
"auditTitle": "Recent activity",
|
||||
|
||||
@@ -1996,6 +1996,9 @@
|
||||
"webPushGlobal": "Allow browser push notifications.",
|
||||
"integrationWebPush": "Web Push",
|
||||
"integrationBugReport": "Bug Report",
|
||||
"offlineGeoLabel": "Dane geograficzne",
|
||||
"offlineGeoEnabled": "Offline (GeoLite2)",
|
||||
"offlineGeoFallback": "Awaryjne (ipwho.is) — ustaw MAXMIND_LICENSE_KEY w CI",
|
||||
"apiGlobal": "Allow API endpoints and API tokens.",
|
||||
"umamiTitle": "Umami",
|
||||
"umamiDescription": "Web analytics with Umami",
|
||||
@@ -2176,6 +2179,9 @@
|
||||
"snapshotStarted": "Started",
|
||||
"snapshotBuildSha": "Build SHA",
|
||||
"snapshotBuiltAt": "Built at",
|
||||
"snapshotOfflineGeo": "Dane geograficzne",
|
||||
"snapshotOfflineGeoOn": "Offline (GeoLite2)",
|
||||
"snapshotOfflineGeoOff": "Awaryjne (ipwho.is)",
|
||||
"snapshotLoadError": "Failed to load system snapshot.",
|
||||
"snapshotLoading": "Loading snapshot...",
|
||||
"auditTitle": "Recent activity",
|
||||
|
||||
@@ -13,15 +13,36 @@ import {
|
||||
Globe,
|
||||
Key,
|
||||
Loader2,
|
||||
Map,
|
||||
RotateCw,
|
||||
Server,
|
||||
Users,
|
||||
} from "lucide-react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { formatDateTime } from "@/lib/format";
|
||||
import { useFormatters, useTranslations } from "@/lib/i18n/context";
|
||||
import { StatusItem, useSystemStatus } from "./_shared";
|
||||
|
||||
interface VersionResponse {
|
||||
version: string;
|
||||
buildSha: string | null;
|
||||
builtAt: string | null;
|
||||
offlineGeoEnabled?: boolean;
|
||||
}
|
||||
|
||||
function useOfflineGeoState() {
|
||||
return useQuery({
|
||||
queryKey: ["public", "version"],
|
||||
queryFn: async () => {
|
||||
const res = await fetch("/api/version");
|
||||
if (!res.ok) throw new Error("Failed to load version");
|
||||
return (await res.json()).data as VersionResponse;
|
||||
},
|
||||
staleTime: 5 * 60_000,
|
||||
});
|
||||
}
|
||||
|
||||
// v1.4.16 phase B3: Recharts is ~108 KiB Brotli — defer-load the host
|
||||
// metrics chart so the system-status page only ships it when an admin
|
||||
// actually opens this view. Splitting at the wrapper boundary (instead
|
||||
@@ -44,6 +65,7 @@ export function SystemStatusSection() {
|
||||
const { t } = useTranslations();
|
||||
const fmt = useFormatters();
|
||||
const { data: status, isError, refetch, isFetching } = useSystemStatus();
|
||||
const { data: version } = useOfflineGeoState();
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -163,6 +185,25 @@ export function SystemStatusSection() {
|
||||
className="text-dracula-green"
|
||||
/>
|
||||
)}
|
||||
{/* v1.4.27 R5 — offline GeoLite2 availability. Renders only
|
||||
when /api/version returns the new field so legacy
|
||||
responses do not produce a placeholder row. */}
|
||||
{version?.offlineGeoEnabled !== undefined && (
|
||||
<StatusItem
|
||||
icon={Map}
|
||||
label={t("admin.offlineGeoLabel")}
|
||||
value={
|
||||
version.offlineGeoEnabled
|
||||
? t("admin.offlineGeoEnabled")
|
||||
: t("admin.offlineGeoFallback")
|
||||
}
|
||||
className={
|
||||
version.offlineGeoEnabled
|
||||
? "text-dracula-green"
|
||||
: "text-dracula-yellow"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : isError ? (
|
||||
// P19: surface load failures inline instead of leaving the
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
Cog,
|
||||
Database,
|
||||
GitCommit,
|
||||
Globe,
|
||||
Loader2,
|
||||
Server,
|
||||
Tag,
|
||||
@@ -30,6 +31,7 @@ interface VersionResponse {
|
||||
version: string;
|
||||
buildSha: string | null;
|
||||
builtAt: string | null;
|
||||
offlineGeoEnabled?: boolean;
|
||||
}
|
||||
|
||||
function useVersion() {
|
||||
@@ -116,6 +118,26 @@ export function SystemStatusSummary() {
|
||||
value={formatDateTime(version.builtAt)}
|
||||
/>
|
||||
)}
|
||||
{/* v1.4.27 R5 — surface the offline-geo state so the maintainer
|
||||
spots the missing MAXMIND_LICENSE_KEY without crawling logs.
|
||||
The field is undefined on legacy responses; the row only
|
||||
renders when /api/version answers the new shape. */}
|
||||
{version?.offlineGeoEnabled !== undefined && (
|
||||
<StatusItem
|
||||
icon={Globe}
|
||||
label={t("admin.overview.snapshotOfflineGeo")}
|
||||
value={
|
||||
version.offlineGeoEnabled
|
||||
? t("admin.overview.snapshotOfflineGeoOn")
|
||||
: t("admin.overview.snapshotOfflineGeoOff")
|
||||
}
|
||||
className={
|
||||
version.offlineGeoEnabled
|
||||
? "text-dracula-green"
|
||||
: "text-dracula-yellow"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : isError ? (
|
||||
<div
|
||||
|
||||
Reference in new issue
Block a user