refactor: rename SolaceScanScout to Solace and update related configurations

- Updated branding from "SolaceScanScout" to "Solace" across various files including deployment scripts, API responses, and documentation.
- Changed default base URL for Playwright tests and updated security headers to reflect the new branding.
- Enhanced README and API documentation to include new authentication endpoints and product access details.

This refactor aligns the project branding and improves clarity in the API documentation.
This commit is contained in:
defiQUG
2026-04-10 12:52:17 -07:00
parent bdae5a9f6e
commit f46bd213ba
160 changed files with 13274 additions and 1061 deletions

View File

@@ -0,0 +1,315 @@
import { useEffect, useMemo, useState } from 'react'
import Link from 'next/link'
import { Card } from '@/libs/frontend-ui-primitives'
import { explorerFeaturePages } from '@/data/explorerOperations'
import { configApi, type CapabilitiesResponse, type NetworksConfigResponse, type TokenListResponse } from '@/services/api/config'
import { getMissionControlRelays, missionControlApi, type MissionControlBridgeStatusResponse } from '@/services/api/missionControl'
import { routesApi, type RouteMatrixResponse } from '@/services/api/routes'
function relativeAge(isoString?: string): string {
if (!isoString) return 'Unknown'
const parsed = Date.parse(isoString)
if (!Number.isFinite(parsed)) return 'Unknown'
const seconds = Math.max(0, Math.round((Date.now() - parsed) / 1000))
if (seconds < 60) return `${seconds}s ago`
const minutes = Math.round(seconds / 60)
if (minutes < 60) return `${minutes}m ago`
const hours = Math.round(minutes / 60)
return `${hours}h ago`
}
function ActionLink({
href,
label,
external,
}: {
href: string
label: string
external?: boolean
}) {
const className = 'inline-flex items-center text-sm font-semibold text-primary-600 hover:underline'
const text = `${label} ->`
if (external) {
return (
<a href={href} className={className} target="_blank" rel="noopener noreferrer">
{text}
</a>
)
}
return (
<Link href={href} className={className}>
{text}
</Link>
)
}
interface OperationsHubPageProps {
initialBridgeStatus?: MissionControlBridgeStatusResponse | null
initialRouteMatrix?: RouteMatrixResponse | null
initialNetworksConfig?: NetworksConfigResponse | null
initialTokenList?: TokenListResponse | null
initialCapabilities?: CapabilitiesResponse | null
}
export default function OperationsHubPage({
initialBridgeStatus = null,
initialRouteMatrix = null,
initialNetworksConfig = null,
initialTokenList = null,
initialCapabilities = null,
}: OperationsHubPageProps) {
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(initialRouteMatrix)
const [networksConfig, setNetworksConfig] = useState<NetworksConfigResponse | null>(initialNetworksConfig)
const [tokenList, setTokenList] = useState<TokenListResponse | null>(initialTokenList)
const [capabilities, setCapabilities] = useState<CapabilitiesResponse | null>(initialCapabilities)
const [loadingError, setLoadingError] = useState<string | null>(null)
const page = explorerFeaturePages.operations
useEffect(() => {
let cancelled = false
const load = async () => {
const [bridgeResult, routesResult, networksResult, tokenListResult, capabilitiesResult] =
await Promise.allSettled([
missionControlApi.getBridgeStatus(),
routesApi.getRouteMatrix(),
configApi.getNetworks(),
configApi.getTokenList(),
configApi.getCapabilities(),
])
if (cancelled) return
if (bridgeResult.status === 'fulfilled') setBridgeStatus(bridgeResult.value)
if (routesResult.status === 'fulfilled') setRouteMatrix(routesResult.value)
if (networksResult.status === 'fulfilled') setNetworksConfig(networksResult.value)
if (tokenListResult.status === 'fulfilled') setTokenList(tokenListResult.value)
if (capabilitiesResult.status === 'fulfilled') setCapabilities(capabilitiesResult.value)
const failedCount = [
bridgeResult,
routesResult,
networksResult,
tokenListResult,
capabilitiesResult,
].filter((result) => result.status === 'rejected').length
if (failedCount === 5) {
setLoadingError('Public explorer operations data is temporarily unavailable.')
}
}
load().catch((error) => {
if (!cancelled) {
setLoadingError(
error instanceof Error ? error.message : 'Public explorer operations data is temporarily unavailable.'
)
}
})
return () => {
cancelled = true
}
}, [])
const relayCount = useMemo(() => {
const relays = getMissionControlRelays(bridgeStatus)
return relays ? Object.keys(relays).length : 0
}, [bridgeStatus])
const totalQueue = useMemo(() => {
const relays = getMissionControlRelays(bridgeStatus)
if (!relays) return 0
return Object.values(relays).reduce((sum, relay) => {
const queueSize = relay.url_probe?.body?.queue?.size ?? relay.file_snapshot?.queue?.size ?? 0
return sum + queueSize
}, 0)
}, [bridgeStatus])
const tokenChainCoverage = useMemo(() => {
return new Set((tokenList?.tokens || []).map((token) => token.chainId).filter(Boolean)).size
}, [tokenList])
const topSymbols = useMemo(() => {
return Array.from(
new Set((tokenList?.tokens || []).map((token) => token.symbol).filter(Boolean) as string[])
).slice(0, 8)
}, [tokenList])
return (
<div className="container mx-auto px-4 py-6 sm:py-8">
<div className="mb-6 max-w-4xl sm:mb-8">
<div className="mb-3 inline-flex rounded-full border border-violet-200 bg-violet-50 px-3 py-1 text-xs font-semibold uppercase tracking-[0.2em] text-violet-700">
{page.eyebrow}
</div>
<h1 className="mb-3 text-3xl font-bold text-gray-900 dark:text-white sm:text-4xl">
{page.title}
</h1>
<p className="text-base leading-7 text-gray-600 dark:text-gray-400 sm:text-lg sm:leading-8">
{page.description}
</p>
</div>
{page.note ? (
<Card className="mb-6 border border-amber-200 bg-amber-50/70 dark:border-amber-900/50 dark:bg-amber-950/20">
<p className="text-sm leading-6 text-amber-950 dark:text-amber-100">
{page.note}
</p>
</Card>
) : null}
{loadingError ? (
<Card className="mb-6 border border-red-200 bg-red-50/70 dark:border-red-900/50 dark:bg-red-950/20">
<p className="text-sm leading-6 text-red-900 dark:text-red-100">{loadingError}</p>
</Card>
) : null}
<div className="mb-6 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
<Card className="border border-sky-200 bg-sky-50/70 dark:border-sky-900/50 dark:bg-sky-950/20">
<div className="text-sm font-semibold uppercase tracking-wide text-sky-800 dark:text-sky-100">
Bridge Fleet
</div>
<div className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">
{bridgeStatus?.data?.status || 'unknown'}
</div>
<div className="mt-2 text-sm text-gray-700 dark:text-gray-300">
{relayCount} managed lanes · queue {totalQueue}
</div>
</Card>
<Card className="border border-emerald-200 bg-emerald-50/70 dark:border-emerald-900/50 dark:bg-emerald-950/20">
<div className="text-sm font-semibold uppercase tracking-wide text-emerald-800 dark:text-emerald-100">
Route Coverage
</div>
<div className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">
{routeMatrix?.counts?.filteredLiveRoutes ?? 0}
</div>
<div className="mt-2 text-sm text-gray-700 dark:text-gray-300">
{routeMatrix?.counts?.liveSwapRoutes ?? 0} swaps · {routeMatrix?.counts?.liveBridgeRoutes ?? 0} bridges
</div>
</Card>
<Card className="border border-gray-200 dark:border-gray-700">
<div className="text-sm font-semibold uppercase tracking-wide text-gray-700 dark:text-gray-300">
Wallet Surface
</div>
<div className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">
{networksConfig?.chains?.length ?? 0}
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
Chains · {(tokenList?.tokens || []).length} tokens across {tokenChainCoverage} networks
</div>
</Card>
<Card className="border border-gray-200 dark:border-gray-700">
<div className="text-sm font-semibold uppercase tracking-wide text-gray-700 dark:text-gray-300">
RPC Capabilities
</div>
<div className="mt-2 text-3xl font-bold text-gray-900 dark:text-white">
{capabilities?.http?.supportedMethods?.length ?? 0}
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
HTTP methods · {capabilities?.tracing?.supportedMethods?.length ?? 0} tracing methods
</div>
</Card>
</div>
<div className="mb-8 grid gap-6 lg:grid-cols-[1fr_1fr]">
<Card title="Operations Snapshot">
<div className="grid gap-4 md:grid-cols-2">
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-sm text-gray-500 dark:text-gray-400">Bridge checked</div>
<div className="mt-1 text-lg font-semibold text-gray-900 dark:text-white">
{relativeAge(bridgeStatus?.data?.checked_at)}
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
Public mission-control snapshot freshness.
</div>
</div>
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-sm text-gray-500 dark:text-gray-400">Route matrix updated</div>
<div className="mt-1 text-lg font-semibold text-gray-900 dark:text-white">
{relativeAge(routeMatrix?.updated)}
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
Token-aggregation route inventory timestamp.
</div>
</div>
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-sm text-gray-500 dark:text-gray-400">Default chain</div>
<div className="mt-1 text-lg font-semibold text-gray-900 dark:text-white">
{networksConfig?.defaultChainId ?? 'Unknown'}
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
Wallet onboarding points at Chain 138 by default.
</div>
</div>
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-sm text-gray-500 dark:text-gray-400">Wallet support</div>
<div className="mt-1 text-lg font-semibold text-gray-900 dark:text-white">
{capabilities?.walletSupport?.walletAddEthereumChain && capabilities?.walletSupport?.walletWatchAsset
? 'Ready'
: 'Partial'}
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
`wallet_addEthereumChain` and `wallet_watchAsset` compatibility.
</div>
</div>
</div>
</Card>
<Card title="Public Config Highlights">
<div className="space-y-4">
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-sm text-gray-500 dark:text-gray-400">Featured symbols</div>
<div className="mt-2 flex flex-wrap gap-2">
{topSymbols.map((symbol) => (
<span
key={symbol}
className="rounded-full bg-primary-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-primary-700 dark:bg-primary-900/30 dark:text-primary-300"
>
{symbol}
</span>
))}
</div>
</div>
<div className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
<div className="text-sm text-gray-500 dark:text-gray-400">Tracing posture</div>
<div className="mt-2 text-sm text-gray-700 dark:text-gray-300">
Supported: {(capabilities?.tracing?.supportedMethods || []).join(', ') || 'None'}
</div>
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
Unsupported: {(capabilities?.tracing?.unsupportedMethods || []).join(', ') || 'None'}
</div>
</div>
</div>
</Card>
</div>
<div className="grid gap-4 lg:grid-cols-2">
{page.actions.map((action) => (
<Card key={`${action.title}-${action.href}`} className="border border-gray-200 dark:border-gray-700">
<div className="flex h-full flex-col">
<div className="text-base font-semibold text-gray-900 dark:text-white">
{action.title}
</div>
<p className="mt-2 flex-1 text-sm leading-6 text-gray-600 dark:text-gray-400">
{action.description}
</p>
<div className="mt-4">
<ActionLink
href={action.href}
label={action.label}
external={'external' in action ? action.external : undefined}
/>
</div>
</div>
</Card>
))}
</div>
</div>
)
}