Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
47 changes: 30 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -27,25 +33,30 @@ 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
```

## 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

Expand All @@ -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
Expand All @@ -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 |
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.2.3",
"version": "0.2.4",
"license": "MIT",
"tasks": {
"dev": "deno run --allow-all --watch src/server.ts",
Expand Down
4 changes: 2 additions & 2 deletions public/config.js
Original file line number Diff line number Diff line change
@@ -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",
};
22 changes: 11 additions & 11 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Moonlight Network Dashboard</title>
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<div id="app"></div>
<script src="/config.js"></script>
<script type="module" src="/app.js"></script>
</body>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Moonlight Network Dashboard</title>
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<div id="app"></div>
<script src="/config.js"></script>
<script type="module" src="/app.js"></script>
</body>
</html>
65 changes: 51 additions & 14 deletions public/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -43,7 +47,9 @@ nav {
text-decoration: none;
}

.nav-brand:hover { opacity: 0.9; }
.nav-brand:hover {
opacity: 0.9;
}

.nav-links {
display: flex;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -150,7 +175,9 @@ th {
text-decoration: none;
}

.btn-link:hover { color: var(--text); }
.btn-link:hover {
color: var(--text);
}

.login-container {
display: flex;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
837 changes: 836 additions & 1 deletion public/world-map.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -17,7 +17,8 @@ route("/", () => {
route("/404", () => {
const el = document.createElement("div");
el.className = "login-container";
el.innerHTML = `<div class="login-card"><h1>404</h1><p>Page not found.</p><a href="#/map">Back to dashboard</a></div>`;
el.innerHTML =
`<div class="login-card"><h1>404</h1><p>Page not found.</p><a href="#/map">Back to dashboard</a></div>`;
return el;
});

Expand Down
6 changes: 5 additions & 1 deletion src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -11,7 +13,9 @@ const version = denoJson.version ?? "0.0.0";

async function resolveSorobanCoreVersion(): Promise<string> {
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/, "");
Expand Down
4 changes: 3 additions & 1 deletion src/components/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ export function renderNav(): HTMLElement {
const nav = document.createElement("nav");
nav.innerHTML = `
<div class="nav-inner">
<a href="#/" class="nav-brand">Moonlight Network <span class="version-badge">v${escapeHtml(appVersion)}</span></a>
<a href="#/" class="nav-brand">Moonlight Network <span class="version-badge">v${
escapeHtml(appVersion)
}</span></a>
<div class="nav-links">
<a href="#/map">Map</a>
<a href="#/councils">Councils</a>
Expand Down
Loading
Loading