admin dashboard, metrics and telemetry
Cross Compile Go / build (push) Canceled after 0s

- embedded React admin UI at /admin/ui (go:embed), login with ADMIN_SECRET
  via signed HttpOnly session cookie, X-ADMIN-KEY still works for scripts
- admin API: overview stats, indexing queue, addon management (priority,
  reindex, delete/restore/purge, bulk), whitelist CRUD, duplicate hashes,
  traffic, metrics explorer, services, events/audit log, system info
- built-in metrics: request latency per route, visitors, top addons and
  searches, indexer/scraper counters; minute buckets in the DB with hourly
  rollups and retention; optional Prometheus /metrics (METRICS_TOKEN)
- POST /back/telemetry for indexers/scrapers (metrics, events, heartbeat)
  and a stdlib-only Go client in telemetry/
- auth hardening: empty secrets no longer allow access, constant-time
  compare, CORS not applied to /admin and /back, TRUSTED_PROXIES setting
- Dockerfile builds the UI and no longer bakes config.yaml into the image;
  CI builds the UI, runs tests and builds arm64 with CGO
This commit is contained in:
ilbinek committed 2026-10-03 18:10:57 +02:00
1 parent 0ef27b0072
commit 56d9e10621
79 files changed
+13146 -118

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
import { StrictMode, lazy, Suspense } from "react";
import { createRoot } from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import "./index.css";
import { SettingsProvider } from "./lib/settings";
import { FeedbackProvider, Loading } from "./components/ui";
import { RequireAuth } from "./components/Shell";
import { ApiError } from "./lib/api";
import Login from "./pages/Login";
import Overview from "./pages/Overview";
const Traffic = lazy(() => import("./pages/Traffic"));
const Queue = lazy(() => import("./pages/Queue"));
const Addons = lazy(() => import("./pages/Addons"));
const Whitelist = lazy(() => import("./pages/Whitelist"));
const Services = lazy(() => import("./pages/Services"));
const Explorer = lazy(() => import("./pages/Explorer"));
const Events = lazy(() => import("./pages/Events"));
const System = lazy(() => import("./pages/System"));
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 10_000,
refetchOnWindowFocus: false,
retry: (count, err) => !(err instanceof ApiError && err.status < 500) && count < 2,
},
},
});
const page = (el: React.ReactNode) => <Suspense fallback={<Loading />}>{el}</Suspense>;
const router = createBrowserRouter(
[
{ path: "/login", element: <Login /> },
{
element: <RequireAuth />,
children: [
{ index: true, element: <Overview /> },
{ path: "traffic", element: page(<Traffic />) },
{ path: "queue", element: page(<Queue />) },
{ path: "addons", element: page(<Addons />) },
{ path: "addons/:id", element: page(<Addons />) },
{ path: "whitelist", element: page(<Whitelist />) },
{ path: "services", element: page(<Services />) },
{ path: "explorer", element: page(<Explorer />) },
{ path: "events", element: page(<Events />) },
{ path: "system", element: page(<System />) },
{ path: "*", element: <Overview /> },
],
},
],
{ basename: "/admin/ui" },
);
createRoot(document.getElementById("root")!).render(
<StrictMode>
<SettingsProvider>
<QueryClientProvider client={queryClient}>
<FeedbackProvider>
<RouterProvider router={router} />
</FeedbackProvider>
</QueryClientProvider>
</SettingsProvider>
</StrictMode>,
);