- 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:
1 parent
0ef27b0072
commit
56d9e10621
79 files changed
+13146
-118
No files matched your search
@@ -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>,
|
||||
);
|
||||
Reference in new issue
Block a user