diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e19fc17..9a21eb2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,24 @@ on: branches: [main] jobs: + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + - run: deno fmt --check + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + - run: deno lint + build: runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index a380895..31cba3b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,19 @@ # Network Dashboard -Public dashboard showing the state of the Moonlight network. Serves as the council discovery layer — councils register their contracts and jurisdictions, anyone can browse. +Public dashboard showing the state of the Moonlight network. Serves as the +council discovery layer — councils register their contracts and jurisdictions, +anyone can browse. ## What it does -- **Map view**: World map with councils plotted by declared jurisdiction, dot size reflects number of channels -- **Council list**: All registered councils with on-chain state (supply, provider count, channel assets) -- **Transaction feed**: Recent on-chain events across all channels (bundles, provider changes) -- **Council detail**: Drill into a council to see its channels, providers, and activity +- **Map view**: World map with councils plotted by declared jurisdiction, dot + size reflects number of channels +- **Council list**: All registered councils with on-chain state (supply, + provider count, channel assets) +- **Transaction feed**: Recent on-chain events across all channels (bundles, + provider changes) +- **Council detail**: Drill into a council to see its channels, providers, and + activity ## Development @@ -27,7 +33,8 @@ deno task test ## Testing -Unit tests cover DOM helpers, SVG sanitization, provider counting logic, URL validation, config, and routing. +Unit tests cover DOM helpers, SVG sanitization, provider counting logic, URL +validation, config, and routing. ```bash deno task test @@ -35,17 +42,21 @@ deno task test ## Deployment -Static files are deployed to a public [Tigris](https://www.tigrisdata.com/) bucket on Fly.io. +Static files are deployed to a public [Tigris](https://www.tigrisdata.com/) +bucket on Fly.io. - **Bucket**: `network-dashboard` - **URL**: https://network-dashboard.fly.storage.tigris.dev/index.html - **Auto-deploy**: bump `version` in `deno.json`, push to `main` -- **Secrets** (set in GitHub repo settings): `TIGRIS_ACCESS_KEY_ID`, `TIGRIS_SECRET_ACCESS_KEY`, `COUNCILS_JSON` +- **Secrets** (set in GitHub repo settings): `TIGRIS_ACCESS_KEY_ID`, + `TIGRIS_SECRET_ACCESS_KEY`, `COUNCILS_JSON` Pipeline: -1. Push to `main` triggers `auto-version.yml` (reads version from `deno.json`, creates git tag) -2. Tag push (`v*`) triggers `deploy.yml` (generates config from secrets, builds production bundle, deploys to Tigris) +1. Push to `main` triggers `auto-version.yml` (reads version from `deno.json`, + creates git tag) +2. Tag push (`v*`) triggers `deploy.yml` (generates config from secrets, builds + production bundle, deploys to Tigris) ### Manual deploy @@ -58,7 +69,9 @@ aws s3 sync public/ s3://network-dashboard/ \ ## Architecture -Static SPA (no backend, no auth). Reads on-chain state via Stellar RPC. World map data fetched from jsDelivr CDN (Natural Earth TopoJSON). Council registry is a hardcoded config for MVP (eventually an API). +Static SPA (no backend, no auth). Reads on-chain state via Stellar RPC. World +map data fetched from jsDelivr CDN (Natural Earth TopoJSON). Council registry is +a hardcoded config for MVP (eventually an API). ``` Browser @@ -71,9 +84,9 @@ Browser Required for CI deploys: -| Secret | Purpose | -|--------|---------| -| `TIGRIS_ACCESS_KEY_ID` | Tigris CDN upload | -| `TIGRIS_SECRET_ACCESS_KEY` | Tigris CDN upload | -| `COUNCILS_JSON` | Council registry (JSON array) | -| `AUTO_VERSION_TOKEN` | PAT for auto-tag workflow | +| Secret | Purpose | +| -------------------------- | ----------------------------- | +| `TIGRIS_ACCESS_KEY_ID` | Tigris CDN upload | +| `TIGRIS_SECRET_ACCESS_KEY` | Tigris CDN upload | +| `COUNCILS_JSON` | Council registry (JSON array) | +| `AUTO_VERSION_TOKEN` | PAT for auto-tag workflow | diff --git a/deno.json b/deno.json index dd66b3c..831862d 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,5 @@ { - "version": "0.2.3", + "version": "0.2.4", "license": "MIT", "tasks": { "dev": "deno run --allow-all --watch src/server.ts", diff --git a/public/config.js b/public/config.js index 285bfbc..da784ba 100644 --- a/public/config.js +++ b/public/config.js @@ -1,6 +1,6 @@ -window.__DASHBOARD_CONFIG__ = { +globalThis.__DASHBOARD_CONFIG__ = { environment: "development", stellarNetwork: "testnet", rpcUrl: "https://soroban-testnet.stellar.org", - councilPlatformUrl: "http://localhost:3115" + councilPlatformUrl: "http://localhost:3115", }; diff --git a/public/index.html b/public/index.html index 1e1143e..7cbc843 100644 --- a/public/index.html +++ b/public/index.html @@ -1,14 +1,14 @@ - - - - Moonlight Network Dashboard - - - -
- - - + + + + Moonlight Network Dashboard + + + +
+ + + diff --git a/public/styles.css b/public/styles.css index 222a405..6f3518e 100644 --- a/public/styles.css +++ b/public/styles.css @@ -13,7 +13,11 @@ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } -* { margin: 0; padding: 0; box-sizing: border-box; } +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} body { font-family: var(--font-sans); @@ -43,7 +47,9 @@ nav { text-decoration: none; } -.nav-brand:hover { opacity: 0.9; } +.nav-brand:hover { + opacity: 0.9; +} .nav-links { display: flex; @@ -58,7 +64,9 @@ nav { transition: color 0.15s; } -.nav-links a:hover { color: var(--text); } +.nav-links a:hover { + color: var(--text); +} .container { max-width: 1200px; @@ -84,9 +92,15 @@ nav { gap: 0.25rem; } -.stat-card.active { border-color: var(--active); } -.stat-card.pending { border-color: var(--pending); } -.stat-card.inactive { border-color: var(--inactive); } +.stat-card.active { + border-color: var(--active); +} +.stat-card.pending { + border-color: var(--pending); +} +.stat-card.inactive { + border-color: var(--inactive); +} .stat-value { font-size: 1.5rem; @@ -125,7 +139,9 @@ th { word-break: break-all; } -.text-muted { color: var(--text-muted); } +.text-muted { + color: var(--text-muted); +} .badge { display: inline-block; @@ -136,9 +152,18 @@ th { text-transform: uppercase; } -.badge-active { background: rgba(34, 197, 94, 0.15); color: var(--active); } -.badge-pending { background: rgba(245, 158, 11, 0.15); color: var(--pending); } -.badge-inactive { background: rgba(239, 68, 68, 0.15); color: var(--inactive); } +.badge-active { + background: rgba(34, 197, 94, 0.15); + color: var(--active); +} +.badge-pending { + background: rgba(245, 158, 11, 0.15); + color: var(--pending); +} +.badge-inactive { + background: rgba(239, 68, 68, 0.15); + color: var(--inactive); +} .btn-link { background: none; @@ -150,7 +175,9 @@ th { text-decoration: none; } -.btn-link:hover { color: var(--text); } +.btn-link:hover { + color: var(--text); +} .login-container { display: flex; @@ -184,8 +211,16 @@ th { font-size: 0.875rem; } -h2 { margin-bottom: 0.5rem; } -h3 { margin: 1.5rem 0 0.5rem; color: var(--text-muted); font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.05em; } +h2 { + margin-bottom: 0.5rem; +} +h3 { + margin: 1.5rem 0 0.5rem; + color: var(--text-muted); + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.05em; +} .empty-state { background: var(--surface); @@ -195,7 +230,9 @@ h3 { margin: 1.5rem 0 0.5rem; color: var(--text-muted); font-size: 0.875rem; tex margin-top: 1rem; } -.empty-state p { margin-bottom: 0.75rem; } +.empty-state p { + margin-bottom: 0.75rem; +} .version-badge { font-size: 0.7rem; diff --git a/public/world-map.svg b/public/world-map.svg index 8f4ca25..10435de 100644 --- a/public/world-map.svg +++ b/public/world-map.svg @@ -1 +1,836 @@ -Simple World MapAuthor: Al MacDonald Editor: Fritz Lekschas License: CC BY-SA 3.0 ID: ISO 3166-1 or "_[a-zA-Z]" if an ISO code is not available \ No newline at end of file + + Simple World Map + Author: Al MacDonald Editor: Fritz Lekschas License: CC BY-SA 3.0 ID: ISO 3166-1 or "_[a-zA-Z]" if an ISO code is not available + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app.ts b/src/app.ts index e5d4059..e00f669 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,4 @@ -import { route, startRouter, navigate } from "./lib/router.ts"; +import { navigate, route, startRouter } from "./lib/router.ts"; import { mapView } from "./views/map.ts"; import { councilsView } from "./views/councils.ts"; import { councilDetailView } from "./views/council-detail.ts"; @@ -17,7 +17,8 @@ route("/", () => { route("/404", () => { const el = document.createElement("div"); el.className = "login-container"; - el.innerHTML = `

404

Page not found.

Back to dashboard
`; + el.innerHTML = + `

404

Page not found.

Back to dashboard
`; return el; }); diff --git a/src/build.ts b/src/build.ts index 6fe2991..e8392e9 100644 --- a/src/build.ts +++ b/src/build.ts @@ -2,7 +2,9 @@ * Bundles src/app.ts into public/app.js for the browser. * Uses esbuild via Deno with denoPlugins for import map resolution. */ +// deno-lint-ignore no-import-prefix -- build script intentionally pins the URL import * as esbuild from "https://deno.land/x/esbuild@v0.20.1/mod.js"; +// deno-lint-ignore no-import-prefix -- build script intentionally pins the version import { denoPlugins } from "jsr:@luca/esbuild-deno-loader@0.10"; const isProduction = Deno.args.includes("--production"); @@ -11,7 +13,9 @@ const version = denoJson.version ?? "0.0.0"; async function resolveSorobanCoreVersion(): Promise { try { - const res = await fetch("https://api.github.com/repos/Moonlight-Protocol/soroban-core/releases/latest"); + const res = await fetch( + "https://api.github.com/repos/Moonlight-Protocol/soroban-core/releases/latest", + ); if (!res.ok) return "unknown"; const release = await res.json(); return ((release.tag_name as string) ?? "unknown").replace(/^v/, ""); diff --git a/src/components/nav.ts b/src/components/nav.ts index e070ca5..0c0e244 100644 --- a/src/components/nav.ts +++ b/src/components/nav.ts @@ -7,7 +7,9 @@ export function renderNav(): HTMLElement { const nav = document.createElement("nav"); nav.innerHTML = `