Skip to content

Admin Portal#87

Merged
arnaugiralt merged 42 commits intomasterfrom
feat/admin-portal
Apr 30, 2026
Merged

Admin Portal#87
arnaugiralt merged 42 commits intomasterfrom
feat/admin-portal

Conversation

@victor-cuevas
Copy link
Copy Markdown
Contributor

This PR adds an admin portal to monitor chaperone instances. The code has already been reviewed in many different steps.

victor-cuevas and others added 30 commits April 10, 2026 16:58
- Entry point with YAML config, --config flag, and env var overrides
- SQLite store with migration framework (instances, users, audit_log, sessions)
- HTTP server with security headers (CSP, X-Frame-Options, etc.)
- SPA handler with embed.FS for single-binary distribution
- Makefile targets: build-admin, run-admin, build-admin-dev
- Vue 3 + Vite + Pinia + Vue Router with Vite proxy to Go backend
- Design system: CSS custom properties, BaseButton, BaseCard, BaseInput, StatusIndicator, BaseEmptyState
- App shell with sidebar nav, Fleet Dashboard and Audit Log routes
- WCAG AA contrast, keyboard focus styles, aria attributes
- Vitest + jsdom configured for testing
- Lint (Go + ESLint), test (Go + Vitest), build (Go + Vite)
- Path-scoped triggers on admin/** changes only
RetentionDays as int made it impossible to set retention_days: 0
(keep forever) in YAML — the zero value was indistinguishable from
omission and silently became 90.

Change to *int so nil means unset (gets default 90) while &0 means
explicitly keep forever. Add regression test.
- Fix gofmt import ordering in main.go (errors before flag)
- Add context.Context to store.Open and migrate for noctx compliance
- Use ExecContext/QueryRowContext/BeginTx throughout store package
- Extract parseDuration helper to reduce applyEnvOverrides complexity
- Split migrate into ensureMigrationsTable/currentSchemaVersion/applyMigration
- Add justifying comment to blank sqlite driver import
- Extract default config constants to eliminate goconst violations
- CI: use --version instead of --help || true for binary verification
- BaseInput: add aria-invalid and aria-describedby for screen readers
Add #nosec G304 annotation to admin config loader (path from trusted
sources: CLI flag, env var, or hardcoded default).

Explicitly discard db.Close() errors on cleanup paths in store.Open()
to satisfy G104 (CWE-703).
Instance CRUD API (POST/GET/PUT/DELETE /api/instances) with SQLite store,
background health poller with three-state machine (unknown → healthy →
unreachable after 3 failures, instant recovery), test-connection endpoint,
and Vue frontend with Pinia store, AddInstanceModal, InstanceCard/Table
with density toggle, and staleness detection.
Replace minimal empty state with a guided welcome screen (portal
description + 3-step onboarding flow). Replace window.confirm() with
a ConfirmDialog component for instance removal. Add ghost button
variant for the Remove action.
Validate addresses as strict host:port (rejects :9090, host:abc,
host:0). Cap JSON request bodies at 1 MB. Fix poller failures map
leak for deleted instances. Make Probe() accept *http.Client to avoid
per-call allocation. Extract shared formatTime, isInstanceStale,
STALE_THRESHOLD_MS to utils/instance.js with tests. Fix stale-status
inconsistency between table and card views.
Extract component logic into pure functions (validation, formatting,
filtering), composables (useInstanceForm, usePolling, useConfirmDialog),
and a thin API client so all meaningful frontend logic is testable
without vue-test-utils. 65 tests across 7 files.
Fix 12 lint issues (funlen, rangeValCopy, httpNoBody, gosec, govet
shadow, revive var-naming, unparam) across Go sources. Add gosec G706
exclusion for admin module matching core. Switch Prettier to single
quotes and reformat UI sources.
Parse Prometheus text format from proxy /metrics endpoints using expfmt, store snapshots in per-instance ring buffers (~1h retention at 10s intervals), compute per-second rates from counter deltas (handling resets), compute p50/p95/p99 from histogram buckets via linear interpolation, and expose results via REST API (GET /api/metrics/{id}, GET /api/metrics/fleet).

New admin/metrics package: parser, ring buffer, rate/percentile math, and Collector orchestrator. Poller extended to scrape /metrics on successful health probes. Fleet endpoint aggregates KPIs across instances with trend indicators vs 1h ago.
…l, and vendor charts

- Add ECharts + vue-echarts with tree-shaken imports (line chart, grid, tooltip, legend, data zoom)
- Add metrics formatting utilities (formatRps, formatLatency, formatErrorRate, formatCount, trendDirection) with 25 tests
- Add metrics Pinia store for fleet and instance metrics with 6 tests
- Add FleetKpiPanel with 4 KPI cards (RPS, error rate, connections, panics) and trend indicators
- Add instance detail view at /instances/:id with breadcrumb, status header, and Overview/Traffic tabs
- Overview tab: 5 KPI cards + P50/P95/P99 latency time-series chart
- Traffic tab: interactive vendor table with checkboxes, color dots, totals row; 3 stacked charts (RPS, Latency, Error Rate) per selected vendor
- Latency chart shows P50/P95/P99 for single vendor, P99-only for multiple (per design spec)
- Auto-select top 3 vendors by RPS, prune stale selections when vendor list changes
- Make instance cards and table rows clickable to navigate to detail view
- Add P50/P95 fields to SeriesPoint in Go backend for overview latency chart
- Escape HTML in ECharts tooltip formatters to prevent XSS via vendor IDs
- Add tabpanel ARIA roles for accessibility
Add useAnimatedValue composable that transitions numeric values over
400ms using requestAnimationFrame with ease-out cubic easing. Applied
to fleet and instance KPI cards so values animate smoothly instead of
jumping on each 10s poll cycle.
Bcrypt password hashing, session cookies (24h TTL / 2h idle), CSRF
double-submit cookie, IP-based login rate limiting, and CLI subcommands
for user creation and password reset.
…gement

Wire the Go auth backend into the Vue frontend: login page, password
change settings, navigation guards, CSRF token injection, and 401
session-expiry interceptor.
- Avoid swallowing DeleteSession errors
- Discarded CreateUser errors in tests
- Rename Token to TokenHash struct field
- Added comment about clientIP assumptions
- Fix rate limiter unbounded memory by adding a Sweep method and adding a 5min goroutine in main.go
…anup

Add audit logging to the admin portal:

- Store layer: InsertAuditEntry, ListAuditEntries (paginated, filtered
  by user/action/instance/date range, FTS5 full-text search on detail),
  DeleteAuditEntriesBefore for retention cleanup
- Migration v2: FTS5 virtual table with insert/delete/update triggers
  for automatic index sync
- REST API: GET /api/audit with query params (user, action, instance_id,
  from, to, q, page, per_page) capped at 100 per page
- Write-on-action: instance create/update/delete, login, logout, and
  password change all produce audit entries with contextual detail
- Retention cleanup: background goroutine runs immediately on startup
  then daily, configurable via audit.retention_days (default 90, 0 to
  keep forever)
Filterable, paginated audit log table with debounced FTS5 search,
action type dropdown, and date range pickers. Extracts logic into
pure functions (utils/audit.js) and composable (useAuditLog.js).

- Date filters use local-timezone boundaries, not UTC
- Stale response guard discards out-of-order fetches
- 25 utility + 11 composable tests (165 total passing)
feat(admin): improve UI accessibility, responsiveness, and polish
Copy link
Copy Markdown
Member

@arnaugiralt arnaugiralt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, congratulations, awesome job 🚀

@arnaugiralt arnaugiralt merged commit fccf785 into master Apr 30, 2026
18 checks passed
@arnaugiralt arnaugiralt deleted the feat/admin-portal branch April 30, 2026 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants