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
68 lines
2.4 KiB
TypeScript
68 lines
2.4 KiB
TypeScript
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>,
|
|
);
|