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:
Marc-André Bombeck committed 2026-05-15 22:21:03 +02:00
1 parent 4eb22387a2
commit 82a23069a5
8 files changed
+99

No files matched your search

@@ -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