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
2 changes: 2 additions & 0 deletions .github/workflows/ci-dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
run: npm install
- name: Lint
run: npm run lint
- name: Test
run: npm run test
- name: Build
run: npm run build
env:
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,30 @@ Versions map to GitHub pull requests; the project does not use semver tags yet.

---

## [0.12.0] - 2026-03-24 - PR #13: Documentation refresh

### Changed

- `README.md`: Go badge 1.22 → 1.23; stack table updated; `npm install` step
added to Quick Start; project structure updated with all new dirs and files;
CI/CD diagram reflects Vitest step and build-on-PR behaviour.
- `ROADMAP.md`: status line updated; Milestone 4 reflects Husky and build-on-PR
fixes; dashboard moved from v2.0 backlog to a completed Milestone 8.
- `docs/development/GETTING_STARTED.md`: Go 1.22 → 1.23; Node.js prerequisite
added; `npm install` step added; ports table expanded with all 16 containers.
- `docs/deployment/CI_CD_GUIDE.md`: pipeline diagram and workflow table include
`ci-subscription.yml`, `ci-dashboard.yml`, and `ci-e2e.yml`; Docker build
behaviour clarified (build on PRs, push on main only).
- `docs/architecture/DECISIONS.md`: ADR-016 `next.config.ts` → `next.config.mjs`.

### Added

- `docs/architecture/DECISIONS.md`: ADR-017 documenting the Vitest +
`@testing-library/react` + `happy-dom` decision for dashboard testing.
- `ci-dashboard.yml`: `npm run test` step added before `next build`.

---

## [0.11.0] - 2026-03-24 - PR #12: Vitest unit and integration tests for Next.js dashboard

### Added
Expand Down
26 changes: 17 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<strong>Functionally simple. Architecturally serious.</strong>
</p>

[![Go](https://img.shields.io/badge/Go-1.22-00ADD8?logo=go)](https://golang.org)
[![Go](https://img.shields.io/badge/Go-1.23-00ADD8?logo=go)](https://golang.org)
[![License](https://img.shields.io/github/license/KaelSensei/OnChainHealthMonitor)](LICENSE)

[![CI - api](https://github.com/KaelSensei/OnChainHealthMonitor/actions/workflows/ci-api.yml/badge.svg)](https://github.com/KaelSensei/OnChainHealthMonitor/actions/workflows/ci-api.yml)
Expand Down Expand Up @@ -195,15 +195,15 @@ All services expose:

| Theme | Tool | Why |
|----------------------------|-----------------------------|----------------------------------------------------------|
| Language (backend) | Go 1.22 | Fast, minimal stdlib, perfect for microservices |
| Language (backend) | Go 1.23 | Fast, minimal stdlib, perfect for microservices |
| Language (frontend) | TypeScript + Next.js 14 | App Router enables SSR for initial data; client components for real-time updates; API routes as BFF proxy |
| Message Broker | Apache Kafka (KRaft) | High-throughput event streaming; decouples all services; replay support |
| User notification routing | RabbitMQ | Topic exchange pattern routes alerts to specific users; auto-delete queues per connected client |
| Subscription storage | Redis | Fast set-based lookup by protocol; per-user subscription index |
| Containers | Docker + Docker Compose | Reproducible local environment, mirrors prod topology |
| Observability: Metrics | Prometheus + Grafana | Industry standard; scrape model fits pull-based services |
| Observability: Tracing | OpenTelemetry + OTel Collector + Jaeger | OTLP gRPC pipeline: services -> collector -> Jaeger UI |
| CI/CD | GitHub Actions | Native to GitHub, path-based triggers for monorepos |
| CI/CD | GitHub Actions + Husky | GHA for remote; Husky for local commit-msg + pre-commit hooks |
| Reliability / Alerting | Grafana Alerting | Unified alerting with SLO-based rules, no extra infra |
| API Gateway | Kong (open-source) | Plugin ecosystem (rate limit, auth, logging) on OSS |
| Infra as Code | Terraform | Declarative, provider-agnostic, auditable history |
Expand All @@ -230,6 +230,9 @@ See [Infrastructure Guide](docs/deployment/INFRASTRUCTURE_GUIDE.md) for full det
git clone https://github.com/KaelSensei/OnChainHealthMonitor.git
cd OnChainHealthMonitor

# Install dev tooling (Husky hooks, commitlint, lint-staged)
npm install

# Start the full stack
docker-compose up --build

Expand Down Expand Up @@ -322,7 +325,8 @@ OnChainHealthMonitor/
│ ├── notifier/ # Alert engine
│ ├── api/ # Public REST API
│ └── subscription/ # Subscription CRUD + WebSocket alert delivery
├── dashboard/ # Next.js 14 frontend (protocol health + subscriptions)
├── dashboard/ # Next.js 14 frontend (protocol health + subscriptions + 55 Vitest tests)
├── e2e/ # End-to-end smoke test suite
├── infra/
│ ├── terraform/ # GCP/GKE infrastructure as code
│ ├── helm/ # Helm charts per service
Expand All @@ -333,11 +337,14 @@ OnChainHealthMonitor/
│ ├── otel/ # OpenTelemetry collector config
│ └── jaeger/ # Jaeger configuration
├── docs/
│ ├── architecture/ # ARCHITECTURE.md + ADRs
│ ├── architecture/ # ARCHITECTURE.md + 16 ADRs
│ ├── deployment/ # Infrastructure & CI/CD guides
│ └── development/ # Onboarding guide
│ └── development/ # Onboarding, contributing, tracing guides
├── scripts/ # Developer utility scripts
├── .github/
│ └── workflows/ # GitHub Actions pipelines
│ └── workflows/ # GitHub Actions pipelines (10 workflows)
├── .husky/ # Git hooks: commit-msg + pre-commit
├── package.json # Root dev tooling: Husky, commitlint, lint-staged
├── docker-compose.yml
└── README.md
```
Expand All @@ -361,7 +368,8 @@ graph LR
Build["go build"]
NpmLint["npm run lint"]
NpmBuild["npm run build"]
Docker["docker build\n+ push GHCR"]
NpmTest["npm run test"]
Docker["docker build (all branches)\n+ push GHCR (main only)"]
end

subgraph Infra["Infra validation"]
Expand All @@ -378,7 +386,7 @@ graph LR
Commit --> CommitLint
Commit --> MDLint
Commit --> Vet --> Static --> Test --> Build --> Docker
Commit --> NpmLint --> NpmBuild --> Docker
Commit --> NpmLint --> NpmBuild --> NpmTest --> Docker
Commit --> Compose
Commit --> Kong
Commit --> OApi
Expand Down
19 changes: 12 additions & 7 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OnChain Health Monitor - Roadmap

## Status: 🟢 v1.1 - Kafka event pipeline complete
## Status: 🟢 v1.1 - Event-driven pipeline + dashboard complete

---

Expand Down Expand Up @@ -38,10 +38,11 @@

- [x] GitHub Actions: `go vet` + `staticcheck` + `go test -race` + Docker build per service
- [x] Path-based triggers (only rebuilds the changed service)
- [x] Push to GHCR on merge to `main`
- [x] Semantic version releases on `v*.*.*` tags (matrix build across all 4 services)
- [x] Docker build runs on every PR; push to GHCR on merge to `main` only
- [x] Semantic version releases on `v*.*.*` tags (matrix build across all services)
- [x] PR quality checks: commitlint + markdownlint
- [ ] Deploy to Kubernetes via Helm on merge to main _(planned v1.1)_
- [x] Husky local hooks: `commit-msg` (commitlint) + `pre-commit` (lint-staged)
- [ ] Deploy to Kubernetes via Helm on merge to main _(planned v1.2)_

## ✅ Milestone 5 - Infrastructure as Code

Expand Down Expand Up @@ -120,10 +121,14 @@ Complete the three pillars of observability - metrics (Prometheus) and traces (J
- [ ] Export k6 results to Prometheus remote write → Grafana dashboard for trend analysis
- [ ] Define error budget burn rate alerts based on load test SLO targets

## 🔜 v2.0 - Next.js Dashboard
## ✅ Milestone 8 - Next.js Dashboard

- [ ] Next.js dashboard with real-time protocol health feed and subscription management UI
- [ ] Historical health score API (`GET /api/v1/protocols/{id}/history`)
- [x] Next.js 14 App Router dashboard (port 3001)
- [x] Protocol health feed with live score polling every 5s
- [x] Subscription management UI (create / list / delete)
- [x] Real-time alert panel via WebSocket
- [x] 55 Vitest unit + integration tests (components + API route handlers)
- [x] `CI - dashboard` workflow: ESLint lint + `next build` + Vitest + Docker build

## 🔜 v2.1 - Real On-Chain Indexing

Expand Down
44 changes: 43 additions & 1 deletion docs/architecture/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ Key design choices:
- **WebSocket connects browser-direct** to the subscription service on port 8084 (`ws://{hostname}:8084/ws`). Proxying WebSocket through Next.js would require a custom server and is not worth the complexity for this scope.
- **User identity via localStorage UUID** - no authentication layer required; a random UUID is generated on first visit and persisted, giving each browser session a stable user ID for subscription management.
- **Port 3001** - port 3000 is already occupied by Grafana in the Compose stack.
- **Standalone output** (`output: 'standalone'` in `next.config.ts`) - produces a self-contained Node.js server without `node_modules`, keeping the Docker image small.
- **Standalone output** (`output: 'standalone'` in `next.config.mjs`) - produces a self-contained Node.js server without `node_modules`, keeping the Docker image small.

**Consequences:**
- ✅ Initial page load delivers a fully rendered protocol grid with no loading flash
Expand All @@ -428,3 +428,45 @@ Key design choices:
- ✅ Standalone Docker output means the runtime image is `node:20-alpine` + a ~50MB `.next/standalone` directory
- ⚠️ WebSocket URL is derived from `window.location.hostname` at runtime; in a production deployment behind a reverse proxy, port 8084 must be exposed or the WebSocket traffic routed through the proxy
- ⚠️ Protocol polling is client-side every 5 s; at scale, server-sent events or a Next.js streaming response would be more efficient

---

## ADR-017: Vitest for dashboard testing

**Date:** 2026-03-24
**Status:** Accepted

**Context:**
The Next.js dashboard needed a test suite covering React components and API route handlers. The existing Go services use the standard `go test` runner; the frontend needed an equivalent that integrates with the TypeScript and JSX build pipeline.

Alternatives considered:

| Option | Rejected because |
|---|---|
| Jest | Requires additional Babel transform config for ESM + Next.js; slower cold start |
| Playwright (only) | E2E tests only; cannot test components in isolation or mock fetch calls |
| No tests | Unacceptable; components and API routes have non-trivial logic |

**Decision:**
Use Vitest with `@testing-library/react` and `happy-dom`.

- **Vitest** shares the Vite transform pipeline so TypeScript + JSX work without extra config.
- **`@testing-library/react`** encourages testing from the user's perspective (rendered output, interactions) rather than implementation details.
- **`happy-dom`** is a lightweight DOM implementation that is significantly faster than `jsdom` for unit tests that do not need a full browser environment.
- API route handlers are tested directly (imported and called), with `fetch` mocked via `vi.spyOn(global, 'fetch')`.

**Test coverage:**

| Suite | Tests | What is covered |
|---|---|---|
| `ProtocolCard` | 13 | Health state rendering, score display, protocol metadata |
| `AlertFeed` | 12 | WebSocket lifecycle, incoming messages, empty state |
| `SubscriptionPanel` | 15 | Create/list/delete flows, threshold validation, error states |
| API `/api/protocols` | 7 | Backend proxy, error handling, response shape |
| API `/api/subscriptions` | 8 | Full CRUD route handlers |

**Consequences:**
- ✅ `npm run test` runs all 55 tests in under 3 seconds
- ✅ No Babel config needed; Vitest uses the same esbuild transform as the Next.js build
- ✅ Tests run in CI as part of `ci-dashboard.yml` before the Docker build step
- ⚠️ `happy-dom` does not support all browser APIs; tests requiring `WebSocket` or `localStorage` need explicit mocking
25 changes: 17 additions & 8 deletions docs/deployment/CI_CD_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,22 @@ GitHub Actions detects the push
│ commitlint + markdownlint
├─ services/api/** changed? ──► ci-api.yml
│ go vet → staticcheck → go test → docker build push to GHCR
│ go vet → staticcheck → go test → docker build (PR) / push GHCR (main)
├─ services/collector/** changed? ──► ci-collector.yml
│ go vet → staticcheck → go test → docker build push to GHCR
│ go vet → staticcheck → go test → docker build (PR) / push GHCR (main)
├─ services/analyzer/** changed? ──► ci-analyzer.yml
│ go vet → staticcheck → go test → docker build push to GHCR
│ go vet → staticcheck → go test → docker build (PR) / push GHCR (main)
├─ services/notifier/** changed? ──► ci-notifier.yml
│ go vet → staticcheck → go test → docker build → push to GHCR
│ go vet → staticcheck → go test → docker build (PR) / push GHCR (main)
├─ services/subscription/** changed? ──► ci-subscription.yml
│ go vet → staticcheck → go test → docker build (PR) / push GHCR (main)
├─ dashboard/** changed? ──► ci-dashboard.yml
│ eslint → next build → vitest → docker build (PR) / push GHCR (main)
├─ docker-compose / Kong / OpenAPI changed? ──► ci-infra.yml
│ validate configs
Expand All @@ -46,11 +52,14 @@ All workflows live in `.github/workflows/`. Here is what each one does:

| File | Trigger | What it does |
|------|---------|--------------|
| `ci-api.yml` | Push/PR touching `services/api/**` | Lint, test, build, push `api` image |
| `ci-collector.yml` | Push/PR touching `services/collector/**` | Lint, test, build, push `collector` image |
| `ci-analyzer.yml` | Push/PR touching `services/analyzer/**` | Lint, test, build, push `analyzer` image |
| `ci-notifier.yml` | Push/PR touching `services/notifier/**` | Lint, test, build, push `notifier` image |
| `ci-api.yml` | Push/PR touching `services/api/**` | Lint, test, build image (push to GHCR on main only) |
| `ci-collector.yml` | Push/PR touching `services/collector/**` | Lint, test, build image (push to GHCR on main only) |
| `ci-analyzer.yml` | Push/PR touching `services/analyzer/**` | Lint, test, build image (push to GHCR on main only) |
| `ci-notifier.yml` | Push/PR touching `services/notifier/**` | Lint, test, build image (push to GHCR on main only) |
| `ci-subscription.yml` | Push/PR touching `services/subscription/**` | Lint, test, build image (push to GHCR on main only) |
| `ci-dashboard.yml` | Push/PR touching `dashboard/**` | ESLint, next build, vitest, build image (push to GHCR on main only) |
| `ci-infra.yml` | Push/PR touching infra files | Validate docker-compose, Kong config, OpenAPI spec |
| `ci-e2e.yml` | Push/PR touching `e2e/**` | End-to-end smoke tests |
| `release.yml` | Push of a `v*.*.*` tag | Matrix build of all services with semver tags |
| `pr-checks.yml` | Every pull request | commitlint + markdownlint |

Expand Down
34 changes: 23 additions & 11 deletions docs/development/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ This guide walks you through setting up the project locally and verifying everyt

| Tool | Minimum version | Install |
|------|----------------|---------|
| Go | 1.22 | https://go.dev/dl/ |
| Go | 1.23 | https://go.dev/dl/ |
| Node.js | 20.x | https://nodejs.org/ (for dashboard dev + Husky hooks) |
| Docker | 24.x | https://docs.docker.com/get-docker/ |
| Docker Compose | v2.x (`docker compose`) | Bundled with Docker Desktop; or `apt install docker-compose-plugin` |
| Make | any | Usually pre-installed; `brew install make` or `apt install make` |
Expand All @@ -24,19 +25,24 @@ This guide walks you through setting up the project locally and verifying everyt
git clone https://github.com/KaelSensei/OnChainHealthMonitor.git
cd OnChainHealthMonitor

# Build all 4 services and start the full stack (7 containers)
# Install dev tooling (activates Husky hooks automatically via prepare script)
npm install

# Build all services and start the full stack
docker compose up --build
```

The first build downloads base images and compiles the Go binaries - expect ~2 minutes.
The first build downloads base images and compiles the Go binaries - expect ~2 minutes.
On subsequent runs, `docker compose up` (without `--build`) starts in seconds.

You should see log lines like:

```
onchain_collector | [collector] Starting collector service on :8081 (mock mode)
onchain_analyzer | [analyzer] Starting analyzer service on :8082
onchain_notifier | [notifier] Starting notifier service on :8083 (alert threshold: score < 30)
onchain_api | [api] Starting API service on :8080
onchain_collector | [collector] Starting collector service on :8081 (mock mode)
onchain_analyzer | [analyzer] Starting analyzer service on :8082
onchain_notifier | [notifier] Starting notifier service on :8083 (alert threshold: score < 30)
onchain_api | [api] Starting API service on :8080
onchain_subscription | [subscription] Starting subscription service on :8084
```

---
Expand All @@ -46,14 +52,20 @@ onchain_api | [api] Starting API service on :8080
| Service | Container name | Port | Purpose |
|---------|---------------|------|---------|
| `api` | `onchain_api` | `8080` | Public REST API |
| `collector` | `onchain_collector` | `8081` | Mock event generator |
| `collector` | `onchain_collector` | `8081` | Mock DeFi event generator |
| `analyzer` | `onchain_analyzer` | `8082` | Health score computation |
| `notifier` | `onchain_notifier` | `8083` | Alert engine |
| `notifier` | `onchain_notifier` | `8083` | Alert engine + RabbitMQ routing |
| `subscription` | `onchain_subscription` | `8084` | Subscription CRUD + WebSocket alerts |
| `dashboard` | `onchain_dashboard` | `3001` | Next.js frontend |
| Kafka | `onchain_kafka` | `9092` | Event streaming (KRaft mode) |
| RabbitMQ | `onchain_rabbitmq` | `5672` / `15672` | Per-user alert routing / management UI |
| Redis | `onchain_redis` | `6379` | Subscription storage |
| Prometheus | `onchain_prometheus` | `9090` | Metrics scraping + query UI |
| Grafana | `onchain_grafana` | `3000` | Dashboards (admin / admin) |
| Jaeger | `onchain_jaeger` | `16686` | Distributed trace UI |
| OTel Collector | `onchain_otel_collector` | `4317` | OTLP gRPC receiver (used by services internally) |
| OTel Collector | `onchain_otel_collector` | `4318` | OTLP HTTP receiver |
| Kong | `onchain_kong` | `8000` / `8001` | API gateway proxy / admin |
| Swagger UI | `onchain_swagger` | `8090` | Interactive API docs |
| OTel Collector | `onchain_otel_collector` | `4317` | OTLP gRPC receiver |
| OTel Collector | `onchain_otel_collector` | `55679` | zpages debug interface |

---
Expand Down
Loading