From 396212874783c64ad327592d021737ef245f8880 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:36:53 +0200 Subject: [PATCH 1/5] product pages: one KPI domain pill bar (perp, pm, feeds, hl, rpc), data-gated (#1061) Co-authored-by: Florent Tapponnier --- src/app/products/[slug]/page.tsx | 153 ++++++++++++++++++---------- src/components/venue-kpi-toggle.tsx | 15 +-- src/lib/rpc-hub-stats.ts | 18 ++++ 3 files changed, 124 insertions(+), 62 deletions(-) diff --git a/src/app/products/[slug]/page.tsx b/src/app/products/[slug]/page.tsx index 06f156af..ce9957c7 100644 --- a/src/app/products/[slug]/page.tsx +++ b/src/app/products/[slug]/page.tsx @@ -22,6 +22,9 @@ import { import { HlBuilderDashboard } from "@/components/hl-builder-dashboard"; import { RelatedProvidersSection } from "@/components/related-providers-section"; import { getPmVenueContext } from "@/lib/pm-venue-context"; +import { fetchPmDataFeedKpis } from "@/lib/pm-venue-data"; +import { fetchPerpVenueKpis } from "@/lib/perp-venue-data"; +import { hasRpcProviderData } from "@/lib/rpc-hub-stats"; import { PmVenueSection } from "@/components/pm-venue-section"; import { getPerpVenueContext, @@ -186,6 +189,26 @@ export default async function ProviderPage({ // Perp DEX cohort dashboard. Same pattern as the PM context above. const perpContext = await getPerpVenueContext(p.slug); + // KPI-domain availability, resolved upfront so the pill bar knows + // every domain before rendering. A pill must never open onto an empty + // section, so each check mirrors the section's own hide-if-empty + // rule. The KPI fetchers are the same unstable_cache entries the + // sections read, so none of this costs an extra roundtrip: + // - perp: PerpVenueSection nulls when no KPI feed AND no measured row + // - PM feed: PmDataFeedSection, same rule + // - RPC: hasRpcProviderData reads the cached rpc-hub snapshot and + // matches RpcProviderChainsSection's own row filter + const perpHasData = perpContext + ? perpContext.benchRows.some((r) => r.value !== null && r.rank !== null) || + (await fetchPerpVenueKpis(perpContext.cohortSlug)) !== null + : false; + const pmFeedHasData = + pmContext?.kind === "feed" + ? pmContext.benchRows.some((r) => r.value !== null && r.rank !== null) || + (await fetchPmDataFeedKpis(pmContext.slug)) !== null + : false; + const hasRpcData = await hasRpcProviderData(p.slug); + const sorted = [...p.appearances].sort((a, b) => { if (a.rank !== b.rank) return a.rank - b.rank; return a.benchmark.title.localeCompare(b.benchmark.title); @@ -539,59 +562,84 @@ export default async function ProviderPage({ ); })()} - {hlStats && } - {(() => { - // Cohort sections. When a product belongs to BOTH the PM venue - // cohort and the perp cohort, wrap the two sections in a pill - // toggle so both stay reachable on the same page. With a single - // cohort the section renders directly, no toggle bar. - const pmVenueSection = - pmContext?.kind === "venue" ? ( - - ) : null; - const perpVenueSection = perpContext ? ( - - ) : null; - if (pmVenueSection && perpVenueSection) { - return ( - - ); + // KPI domain sections. A product can belong to up to five KPI + // domains (perp venue, PM venue, PM data feed, HL builder, RPC + // provider). Every domain with data joins ONE pill bar so all + // stay reachable on the same page; with a single domain the + // section renders directly, no toggle bar (VenueKpiToggle + // handles both cases). Availability is resolved before render: + // perpContext / pmContext / hlStats above, hasRpcData for the + // rpc-hub snapshot. + const sections: { id: string; label: string; content: React.ReactNode }[] = []; + if (perpContext && perpHasData) { + sections.push({ + id: "perp", + label: "Perpetuals", + content: ( + + ), + }); + } + if (pmContext?.kind === "venue") { + sections.push({ + id: "pm", + label: "Prediction markets", + content: ( + + ), + }); + } + if (pmContext?.kind === "feed" && pmFeedHasData) { + sections.push({ + id: "pm-feed", + label: "Data feeds", + content: ( + + ), + }); } - return pmVenueSection ?? perpVenueSection; + if (hlStats) { + sections.push({ + id: "hl", + label: "Hyperliquid", + content: , + }); + } + if (hasRpcData) { + sections.push({ + id: "rpc", + label: "RPC", + content: ( + + ), + }); + } + return ; })()} - {pmContext?.kind === "feed" && ( - - )} {reg && (
@@ -749,11 +797,6 @@ export default async function ProviderPage({
- {/* Per-chain RPC deep-dive from the rpc-hub cohort snapshot. - Renders nothing for providers outside the free-RPC cluster - (the section fetches the cached snapshot and self-filters). */} - - {badgeCards.length > 0 && ( diff --git a/src/components/venue-kpi-toggle.tsx b/src/components/venue-kpi-toggle.tsx index 33435515..5e8c5d4d 100644 --- a/src/components/venue-kpi-toggle.tsx +++ b/src/components/venue-kpi-toggle.tsx @@ -3,15 +3,16 @@ import { useState } from "react"; /** - * Pill toggle that swaps between pre-rendered venue sections on - * /products/ without navigation. The sections are server + * Pill toggle that swaps between pre-rendered KPI domain sections on + * /products/ without navigation (perp venue, PM venue, PM data + * feed, Hyperliquid builder, RPC provider). The sections are server * components rendered by the page and passed in as ReactNode content, * so switching tabs costs zero network round trips. * - * Callers only mount this when at least two sections exist; with a - * single cohort the page renders the section directly (a one-button - * toggle bar would be noise). Inactive sections stay in the DOM under - * `hidden` so tab switches are instant and anchors keep working. + * With a single section the content renders directly (a one-button + * toggle bar would be noise); with none, nothing. Inactive sections + * stay in the DOM under `hidden` so tab switches are instant and + * anchors keep working. * * Pill styling mirrors PmHubTabs / PerpHubTabs. */ @@ -37,7 +38,7 @@ export function VenueKpiToggle({
{sections.map((s) => ( - )} - {canRemove && ( - - )} -
- {result && ( -
-
- {result.ok ? "✓" : "✗"} -
-
{result.message}
- {(result.prUrl || result.actionsUrl) && ( -
- {result.prUrl && ( - - view PR ↗ - - )} - {result.actionsUrl && ( - - follow CI deploy ↗ - - )} -
- )} -
-
-
- )} - - {confirmKind && ( -
-
e.stopPropagation()} className="bg-neutral-950 border border-neutral-700 rounded-lg p-6 max-w-md w-full"> -

- {confirmKind === "promote" ? "Promote to prod" : "Remove from prod"} -

-

- {slug} -

-

- {confirmKind === "promote" - ? "Copies the YAML from dev to main, opens an auto-merged PR, and triggers a prod deploy." - : "Removes the YAML from main (kept on dev), opens an auto-merged PR, and triggers a prod deploy."} -

- - setTyped(e.target.value)} - className="w-full bg-neutral-900 border border-neutral-700 rounded px-2 py-1 text-sm font-mono" - placeholder="PROD" - /> -
- - -
- {result && !result.ok && ( -
{result.message}
- )} -
-
- )} - - ); -} diff --git a/infrastructure/monitoring-ui/src/components/logs-modal.tsx b/infrastructure/monitoring-ui/src/components/logs-modal.tsx deleted file mode 100644 index b983ba84..00000000 --- a/infrastructure/monitoring-ui/src/components/logs-modal.tsx +++ /dev/null @@ -1,159 +0,0 @@ -"use client"; - -import { useEffect, useState, useTransition } from "react"; - -type Region = { region: string; service: string; logsUrl: string }; - -type Props = { - slug: string; - service: string; - rawUrl: string; - extraRegions?: Region[]; -}; - -export function LogsButton({ slug, service, rawUrl, extraRegions }: Props) { - const [open, setOpen] = useState(false); - return ( - <> - - {open && ( - setOpen(false)} - /> - )} - - ); -} - -function LogsModal({ - slug, - service, - rawUrl, - extraRegions, - onClose, -}: Props & { onClose: () => void }) { - const regions = [ - { region: "primary", service, rawUrl }, - ...(extraRegions ?? []).map((r) => ({ region: r.region, service: r.service, rawUrl: r.logsUrl })), - ]; - const [region, setRegion] = useState(regions[0].region); - const [tail, setTail] = useState(300); - const [text, setText] = useState("loading…"); - const [error, setError] = useState(null); - const [pending, startTransition] = useTransition(); - - const active = regions.find((r) => r.region === region) ?? regions[0]; - - useEffect(() => { - const handler = (e: KeyboardEvent) => { - if (e.key === "Escape") onClose(); - }; - window.addEventListener("keydown", handler); - return () => window.removeEventListener("keydown", handler); - }, [onClose]); - - useEffect(() => { - startTransition(async () => { - setError(null); - try { - const qs = new URLSearchParams({ tail: String(tail) }); - if (region !== "primary") qs.set("region", region); - const res = await fetch(`/api/logs/${slug}?${qs}`, { cache: "no-store" }); - const body = await res.text(); - if (!res.ok) { - setError(`HTTP ${res.status}`); - setText(body); - } else { - setText(body || "(empty)"); - } - } catch (e) { - setError((e as Error).message); - setText(""); - } - }); - }, [slug, region, tail]); - - return ( -
-
e.stopPropagation()} - className="bg-neutral-950 border border-neutral-700 rounded-lg w-full max-w-5xl max-h-[85vh] flex flex-col" - > -
-
-

- {slug}{" "} - ·{" "} - {active.service} -

- {regions.length > 1 && ( -
- {regions.map((r) => ( - - ))} -
- )} - - {pending && loading…} -
-
- - raw page ↗ - - -
-
- {error && ( -
- {error} -
- )} -
-          {text}
-        
-
-
- ); -} diff --git a/infrastructure/monitoring-ui/src/lib/bench-state.ts b/infrastructure/monitoring-ui/src/lib/bench-state.ts deleted file mode 100644 index a180885f..00000000 --- a/infrastructure/monitoring-ui/src/lib/bench-state.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { REGISTRY, OCB_REPO, MOBULA_REPO, type BenchEntry } from "./registry"; -import { fileState, lastCommitForPath, gh, type FileState } from "./github"; - -export type BranchSlot = { - state: FileState; - lastCommit: { sha: string; message: string; date: string; author: string } | null; -}; - -export type BenchStatus = - | "dev_only" - | "prod_only" - | "synced" - | "drift" - | "missing_everywhere"; - -export type BenchRow = { - entry: BenchEntry; - main: BranchSlot; - dev: BranchSlot; - status: BenchStatus; -}; - -function classify(main: FileState, dev: FileState): BenchStatus { - if (!main.exists && !dev.exists) return "missing_everywhere"; - if (main.exists && !dev.exists) return "prod_only"; - if (!main.exists && dev.exists) return "dev_only"; - if (main.exists && dev.exists) { - return main.sha === dev.sha ? "synced" : "drift"; - } - return "missing_everywhere"; -} - -async function rowForBench(entry: BenchEntry): Promise { - const [mainState, devState, mainCommit, devCommit] = await Promise.all([ - fileState(OCB_REPO.owner, OCB_REPO.repo, entry.ocbYaml, "main"), - fileState(OCB_REPO.owner, OCB_REPO.repo, entry.ocbYaml, "dev"), - lastCommitForPath(OCB_REPO.owner, OCB_REPO.repo, entry.ocbYaml, "main"), - lastCommitForPath(OCB_REPO.owner, OCB_REPO.repo, entry.ocbYaml, "dev"), - ]); - return { - entry, - main: { state: mainState, lastCommit: mainCommit }, - dev: { state: devState, lastCommit: devCommit }, - status: classify(mainState, devState), - }; -} - -async function listBenchSlugsFromBranch(branch: "main" | "dev"): Promise { - try { - const res = await gh.repos.getContent({ - ...OCB_REPO, - path: "benchmarks", - ref: branch, - }); - if (!Array.isArray(res.data)) return []; - return res.data - .filter((f) => f.type === "file" && f.name.endsWith(".yml")) - .map((f) => f.name.replace(/\.yml$/, "")); - } catch { - return []; - } -} - -function entryForSlug(slug: string): BenchEntry { - const known = REGISTRY.find((e) => e.slug === slug); - if (known) return known; - // Auto-discovered bench: minimal placeholder entry. - return { - slug, - name: slug, - ocbYaml: `benchmarks/${slug}.yml`, - harness: { type: "none" }, - sourceUrl: `https://github.com/${OCB_REPO.owner}/${OCB_REPO.repo}/blob/dev/benchmarks/${slug}.yml`, - }; -} - -export async function loadAllBenchRows(): Promise { - // Union of slugs found on main + dev, so any bench that lands on either - // branch shows up in the dashboard without a registry edit. Known benches - // still pick up their harness/sourceUrl config from REGISTRY. - const [mainSlugs, devSlugs] = await Promise.all([ - listBenchSlugsFromBranch("main"), - listBenchSlugsFromBranch("dev"), - ]); - const slugSet = new Set([ - ...mainSlugs, - ...devSlugs, - ...REGISTRY.map((e) => e.slug), - ]); - const entries = Array.from(slugSet) - .sort() - .map(entryForSlug); - const rows = await Promise.all(entries.map(rowForBench)); - return rows; -} - -// Re-export to keep the import surface stable. -export { MOBULA_REPO }; diff --git a/infrastructure/monitoring-ui/src/lib/github.ts b/infrastructure/monitoring-ui/src/lib/github.ts deleted file mode 100644 index af895f89..00000000 --- a/infrastructure/monitoring-ui/src/lib/github.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Octokit } from "@octokit/rest"; - -const GH_TOKEN = process.env.GITHUB_TOKEN; -if (!GH_TOKEN && process.env.NODE_ENV === "production") { - console.warn("GITHUB_TOKEN missing — read paths will rate-limit fast"); -} - -export const gh = new Octokit({ auth: GH_TOKEN, userAgent: "openbench-monitoring/0.1" }); - -export type FileState = - | { exists: true; sha: string; size: number; updatedAt?: string } - | { exists: false }; - -export async function fileState( - owner: string, - repo: string, - path: string, - ref: string, -): Promise { - try { - const res = await gh.repos.getContent({ owner, repo, path, ref }); - const data = res.data; - if (Array.isArray(data) || data.type !== "file") return { exists: false }; - return { exists: true, sha: data.sha, size: data.size }; - } catch (err) { - const e = err as { status?: number }; - if (e.status === 404) return { exists: false }; - throw err; - } -} - -export async function fileContent( - owner: string, - repo: string, - path: string, - ref: string, -): Promise { - try { - const res = await gh.repos.getContent({ owner, repo, path, ref }); - const data = res.data; - if (Array.isArray(data) || data.type !== "file" || !("content" in data)) return null; - return Buffer.from(data.content, "base64").toString("utf-8"); - } catch (err) { - const e = err as { status?: number }; - if (e.status === 404) return null; - throw err; - } -} - -export async function lastCommitForPath( - owner: string, - repo: string, - path: string, - ref: string, -): Promise<{ sha: string; message: string; date: string; author: string } | null> { - try { - const res = await gh.repos.listCommits({ owner, repo, path, sha: ref, per_page: 1 }); - const c = res.data[0]; - if (!c) return null; - return { - sha: c.sha.slice(0, 7), - message: c.commit.message.split("\n")[0], - date: c.commit.author?.date ?? "", - author: c.commit.author?.name ?? "unknown", - }; - } catch { - return null; - } -} diff --git a/infrastructure/monitoring-ui/src/lib/promote.ts b/infrastructure/monitoring-ui/src/lib/promote.ts deleted file mode 100644 index b974ca76..00000000 --- a/infrastructure/monitoring-ui/src/lib/promote.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { gh, fileContent } from "./github"; -import { OCB_REPO } from "./registry"; - -export type PromoteResult = { - ok: boolean; - prUrl?: string; - mergedSha?: string; - actionsUrl?: string; - message: string; -}; - -const PROD_DEPLOY_ACTIONS_URL = - "https://github.com/ChainBench/OpenChainBench/actions/workflows/prod-deploy.yml"; - -async function getDefaultBranchSha(branch: "main" | "dev"): Promise { - const res = await gh.repos.getBranch({ ...OCB_REPO, branch }); - return res.data.commit.sha; -} - -async function createBranch(name: string, fromSha: string) { - await gh.git.createRef({ ...OCB_REPO, ref: `refs/heads/${name}`, sha: fromSha }); -} - -async function getFileShaOnBranch(path: string, branch: string): Promise { - try { - const res = await gh.repos.getContent({ ...OCB_REPO, path, ref: branch }); - if (Array.isArray(res.data) || res.data.type !== "file") return null; - return res.data.sha; - } catch (err) { - if ((err as { status?: number }).status === 404) return null; - throw err; - } -} - -async function commitFile(opts: { - branch: string; - path: string; - content: string; - shaToReplace?: string | null; - message: string; -}) { - await gh.repos.createOrUpdateFileContents({ - ...OCB_REPO, - branch: opts.branch, - path: opts.path, - message: opts.message, - content: Buffer.from(opts.content, "utf-8").toString("base64"), - sha: opts.shaToReplace ?? undefined, - }); -} - -async function deleteFile(opts: { branch: string; path: string; sha: string; message: string }) { - await gh.repos.deleteFile({ - ...OCB_REPO, - branch: opts.branch, - path: opts.path, - message: opts.message, - sha: opts.sha, - }); -} - -async function openPR(opts: { head: string; base: string; title: string; body: string }) { - const res = await gh.pulls.create({ - ...OCB_REPO, - head: opts.head, - base: opts.base, - title: opts.title, - body: opts.body, - }); - return { number: res.data.number, url: res.data.html_url }; -} - -async function mergePR(number: number): Promise { - const res = await gh.pulls.merge({ ...OCB_REPO, pull_number: number, merge_method: "squash" }); - return res.data.sha; -} - -export async function promoteBenchToMain(slug: string, yamlPath: string): Promise { - const devContent = await fileContent(OCB_REPO.owner, OCB_REPO.repo, yamlPath, "dev"); - if (devContent == null) return { ok: false, message: `not found on dev: ${yamlPath}` }; - - const ts = Date.now(); - const branch = `auto/promote-${slug}-${ts}`; - const mainSha = await getDefaultBranchSha("main"); - await createBranch(branch, mainSha); - - const existingOnMain = await getFileShaOnBranch(yamlPath, "main"); - await commitFile({ - branch, - path: yamlPath, - content: devContent, - shaToReplace: existingOnMain, - message: `chore(${slug}): promote from dev to main`, - }); - - const pr = await openPR({ - head: branch, - base: "main", - title: `promote: ${slug} → main`, - body: `Auto-promotion from dev to main via openbench-monitoring.\n\nContent copied from dev's \`${yamlPath}\`.`, - }); - - const mergedSha = await mergePR(pr.number); - - return { - ok: true, - prUrl: pr.url, - mergedSha, - actionsUrl: PROD_DEPLOY_ACTIONS_URL, - message: `PR #${pr.number} merged into main. CI deploy started — prod will update in ~3-5 min.`, - }; -} - -export async function removeBenchFromMain(slug: string, yamlPath: string): Promise { - const existingSha = await getFileShaOnBranch(yamlPath, "main"); - if (existingSha == null) return { ok: false, message: `not on main: ${yamlPath}` }; - - const ts = Date.now(); - const branch = `auto/remove-${slug}-${ts}`; - const mainSha = await getDefaultBranchSha("main"); - await createBranch(branch, mainSha); - - await deleteFile({ - branch, - path: yamlPath, - sha: existingSha, - message: `chore(${slug}): remove from main (keep on dev)`, - }); - - const pr = await openPR({ - head: branch, - base: "main", - title: `rollback: ${slug} → staging only`, - body: `Auto-rollback from main via openbench-monitoring. YAML remains on dev.`, - }); - - const mergedSha = await mergePR(pr.number); - - return { - ok: true, - prUrl: pr.url, - mergedSha, - actionsUrl: PROD_DEPLOY_ACTIONS_URL, - message: `PR #${pr.number} merged into main. CI deploy started — prod will update in ~3-5 min.`, - }; -} diff --git a/infrastructure/monitoring-ui/src/lib/registry.ts b/infrastructure/monitoring-ui/src/lib/registry.ts deleted file mode 100644 index 498d9f4d..00000000 --- a/infrastructure/monitoring-ui/src/lib/registry.ts +++ /dev/null @@ -1,272 +0,0 @@ -export type HarnessRuntime = - | { - type: "railway"; - service: string; - logsUrl: string; - auth: "logs-token"; - extraRegions?: { region: string; service: string; logsUrl: string }[]; - } - | { type: "ovh-systemd"; host: string; service: string; logsUrl: string; auth: "basic" } - | { type: "none" }; - -export type BenchEntry = { - slug: string; - name: string; - ocbYaml: string; - harness: HarnessRuntime; - /** Public GitHub URL of the harness source code (or YAML spec if no harness). */ - sourceUrl: string; - promPrefix?: string; -}; - -export const OCB_REPO = { owner: "OpenChainBench", repo: "OpenChainBench" } as const; -export const MOBULA_REPO = { owner: "MobulaFi", repo: "mobula-monorepo" } as const; - -export const PROD_URL = "https://openchainbench.com"; -export const STAGING_URL = "https://staging-openchainbench.vercel.app"; - -// Public OCB harness tree. Prefer this for sourceUrl whenever an open-source -// harness exists so the dashboard never leaks paths inside the private monorepo. -const OCB_HARNESS_BASE = `https://github.com/${OCB_REPO.owner}/${OCB_REPO.repo}/tree/main/harnesses`; - -// Private MobulaFi miniapps tree. Only use for benches that don't yet have a -// public OCB harness counterpart (currently network-fees + pm-data-freshness). -const MOBULA_BASE = `https://github.com/${MOBULA_REPO.owner}/${MOBULA_REPO.repo}/tree/dev/miniapps`; - -export function benchPageUrl(branch: "main" | "dev", slug: string) { - const base = branch === "main" ? PROD_URL : STAGING_URL; - return `${base}/benchmarks/${slug}`; -} - -export function diffUrl(slug: string) { - return `https://github.com/${OCB_REPO.owner}/${OCB_REPO.repo}/compare/main...dev?expand=1&file=benchmarks/${slug}.yml`; -} - -const RW = (service: string, host: string): HarnessRuntime => ({ - type: "railway", - service, - logsUrl: `https://${host}/logs`, - auth: "logs-token", -}); - -const RW_MULTI = ( - primary: { service: string; host: string }, - extras: { region: string; service: string; host: string }[], -): HarnessRuntime => ({ - type: "railway", - service: primary.service, - logsUrl: `https://${primary.host}/logs`, - auth: "logs-token", - extraRegions: extras.map((e) => ({ - region: e.region, - service: e.service, - logsUrl: `https://${e.host}/logs`, - })), -}); - -export const REGISTRY: BenchEntry[] = [ - { - slug: "hyperliquid-frontends", - name: "Hyperliquid frontends builder revenue", - ocbYaml: "benchmarks/hyperliquid-frontends.yml", - harness: { - type: "ovh-systemd", - host: "15.235.224.14", - service: "hl-frontends-local", - logsUrl: "http://15.235.224.14:8088/logs", - auth: "basic", - }, - sourceUrl: `${OCB_HARNESS_BASE}/hyperliquid-frontends`, - promPrefix: "hl_frontend_", - }, - { - slug: "network-fees", - name: "Current native transfer fee L1/L2", - ocbYaml: "benchmarks/network-fees.yml", - harness: RW("transaction-fee", "transaction-fee-production.up.railway.app"), - sourceUrl: `${MOBULA_BASE}/transaction-fee`, - promPrefix: "tx_fee_", - }, - { - slug: "solana-tx-landing", - name: "Solana tx landing market share", - ocbYaml: "benchmarks/solana-tx-landing.yml", - harness: RW("tx-landing-solana-bench", "tx-landing-solana-bench-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/solana-tx-landing`, - promPrefix: "solana_tx_landing_", - }, - { - slug: "solana-tx-landing-latency", - name: "Solana tx landing latency (active probing)", - ocbYaml: "benchmarks/solana-tx-landing-latency.yml", - harness: RW("tx-landing-solana-bench", "tx-landing-solana-bench-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/solana-tx-landing`, - promPrefix: "solana_landing_probe_", - }, - { - slug: "solana-dex-quote-latency", - name: "Fastest Solana DEX quote API", - ocbYaml: "benchmarks/solana-dex-quote-latency.yml", - harness: RW_MULTI( - { service: "solana-quote-us-east", host: "solana-quote-us-east-jd9g-cwjh-production.up.railway.app" }, - [ - { region: "eu-west", service: "solana-quote-eu-west", host: "solana-quote-eu-west-h888-za1j-production.up.railway.app" }, - { region: "sgp", service: "solana-quote-sgp", host: "solana-quote-sgp-i105-ytds-production.up.railway.app" }, - ], - ), - sourceUrl: `${OCB_HARNESS_BASE}/solana-quote-latency`, - promPrefix: "solana_dex_quote_", - }, - { - slug: "evm-quote-latency", - name: "Fastest EVM swap quote API", - ocbYaml: "benchmarks/evm-quote-latency.yml", - harness: { type: "none" }, - sourceUrl: `${OCB_HARNESS_BASE}/evm-swap-quoting`, - promPrefix: "evm_swap_quote_", - }, - { - slug: "aggregator-head-lag", - name: "Aggregator head lag", - ocbYaml: "benchmarks/aggregator-head-lag.yml", - harness: RW_MULTI( - { service: "aggregator-east-usa", host: "aggregator-east-usa-production.up.railway.app" }, - [ - { region: "eu-west", service: "agg-eu-west", host: "agg-eu-west-production.up.railway.app" }, - { region: "sgp", service: "agg-sgp", host: "agg-sgp-production.up.railway.app" }, - ], - ), - sourceUrl: `${OCB_HARNESS_BASE}/aggregator-head-lag`, - promPrefix: "agg_", - }, - { - slug: "bridge-fee", - name: "Bridge fee", - ocbYaml: "benchmarks/bridge-fee.yml", - harness: { type: "none" }, - sourceUrl: `${OCB_HARNESS_BASE}/bridge-monitor`, - promPrefix: "bridge_", - }, - { - slug: "bridge-quote-latency", - name: "Bridge quote latency", - ocbYaml: "benchmarks/bridge-quote-latency.yml", - harness: { type: "none" }, - sourceUrl: `${OCB_HARNESS_BASE}/bridge-monitor`, - promPrefix: "bridge_", - }, - { - slug: "bridge-revenue", - name: "Cross-chain bridge implied protocol revenue", - ocbYaml: "benchmarks/bridge-revenue.yml", - harness: { type: "none" }, - sourceUrl: `${OCB_HARNESS_BASE}/bridge-monitor`, - promPrefix: "bridge_", - }, - { - slug: "buyback-audit", - name: "Buyback audit", - ocbYaml: "benchmarks/buyback-audit.yml", - harness: RW("buyback-audit", "buyback-audit-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/buyback-audit`, - }, - { - slug: "gas-estimation", - name: "Gas estimation", - ocbYaml: "benchmarks/gas-estimation.yml", - harness: RW("gas-fee-estimation", "gas-fee-estimation-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/gas-estimation`, - }, - { - slug: "l1-finality", - name: "L1 finality", - ocbYaml: "benchmarks/l1-finality.yml", - harness: RW("l1-finality", "l1-finality-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/l1-finality`, - promPrefix: "l1_finality_", - }, - { - slug: "l2-block-time", - name: "L2 block time", - ocbYaml: "benchmarks/l2-block-time.yml", - harness: RW("l2-block-time", "l2-block-time-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/l2-block-time`, - }, - { - slug: "metadata-coverage", - name: "Metadata coverage", - ocbYaml: "benchmarks/metadata-coverage.yml", - harness: RW("metadata-coverage", "metadata-coverage-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/metadata-coverage`, - }, - { - slug: "network-coverage", - name: "Network coverage", - ocbYaml: "benchmarks/network-coverage.yml", - harness: RW("network-coverage", "network-coverage-production-9eff.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/network-coverage`, - }, - { - slug: "oracle-deviation", - name: "Oracle deviation", - ocbYaml: "benchmarks/oracle-deviation.yml", - harness: RW("oracle-deviation", "oracle-deviation-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/oracle-deviation`, - }, - { - slug: "perp-fees", - name: "Perp fees", - ocbYaml: "benchmarks/perp-fees.yml", - harness: RW("perp-fee", "perp-fee-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/perp-fees`, - }, - { - slug: "rpc-capabilities", - name: "RPC capabilities", - ocbYaml: "benchmarks/rpc-capabilities.yml", - harness: RW_MULTI( - { service: "rpc-capabilities-us", host: "rpc-capabilities-us-production.up.railway.app" }, - [ - { region: "eu", service: "rpc-capabilities-eu", host: "rpc-capabilities-eu-production.up.railway.app" }, - { region: "sgp", service: "rpc-capabilities-sgp", host: "rpc-capabilities-sgp-production.up.railway.app" }, - ], - ), - sourceUrl: `${OCB_HARNESS_BASE}/rpc-capabilities`, - }, - { - slug: "stablecoin-peg", - name: "Stablecoin peg", - ocbYaml: "benchmarks/stablecoin-peg.yml", - harness: RW("stablecoin-peg-bench", "stablecoin-peg-bench-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/stablecoin-peg`, - }, - { - slug: "stablecoin-peg-usdt-anchored", - name: "Stablecoin peg (USDT anchored)", - ocbYaml: "benchmarks/stablecoin-peg-usdt-anchored.yml", - harness: RW("stablecoin-peg-bench", "stablecoin-peg-bench-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/stablecoin-peg`, - }, - { - slug: "validator-yield", - name: "Validator yield", - ocbYaml: "benchmarks/validator-yield.yml", - harness: RW("validator-yield", "validator-yield-production.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/validator-yield`, - }, - { - slug: "wallet-labels-coverage", - name: "Wallet labels coverage", - ocbYaml: "benchmarks/wallet-labels-coverage.yml", - harness: RW("wallet-labels", "mobula-monorepo-production-1f1b.up.railway.app"), - sourceUrl: `${OCB_HARNESS_BASE}/wallet-labels`, - }, - { - slug: "pm-data-freshness", - name: "Best prediction market data API by freshness", - ocbYaml: "benchmarks/pm-data-freshness.yml", - harness: RW("pm-freshness-bench", "pm-freshness-bench-production.up.railway.app"), - sourceUrl: `${MOBULA_BASE}/pm-freshness-bench`, - promPrefix: "pm_freshness_", - }, -]; diff --git a/infrastructure/monitoring-ui/src/middleware.ts b/infrastructure/monitoring-ui/src/middleware.ts deleted file mode 100644 index 05842d3d..00000000 --- a/infrastructure/monitoring-ui/src/middleware.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NextResponse, type NextRequest } from "next/server"; - -const REALM = 'Basic realm="OpenBench monitoring", charset="UTF-8"'; - -export function middleware(req: NextRequest) { - const expected = process.env.ADMIN_BASIC_AUTH; - if (!expected) return NextResponse.next(); - - const header = req.headers.get("authorization"); - if (header) { - const [scheme, value] = header.split(" "); - if (scheme === "Basic" && value) { - const decoded = Buffer.from(value, "base64").toString("utf-8"); - if (decoded === expected) return NextResponse.next(); - } - } - return new NextResponse("Unauthorized", { - status: 401, - headers: { "WWW-Authenticate": REALM }, - }); -} - -export const config = { - matcher: ["/((?!_next/static|_next/image|favicon.ico|healthz).*)"], -}; diff --git a/infrastructure/monitoring-ui/tailwind.config.ts b/infrastructure/monitoring-ui/tailwind.config.ts deleted file mode 100644 index b269d72c..00000000 --- a/infrastructure/monitoring-ui/tailwind.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Config } from "tailwindcss"; - -const config: Config = { - content: ["./src/**/*.{ts,tsx}"], - theme: { extend: {} }, - plugins: [], -}; - -export default config; diff --git a/infrastructure/monitoring-ui/tsconfig.json b/infrastructure/monitoring-ui/tsconfig.json deleted file mode 100644 index 334fafd8..00000000 --- a/infrastructure/monitoring-ui/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [{ "name": "next" }], - "paths": { "@/*": ["./src/*"] } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -}