Files
madbase/_milestones/M10_admin_ui.md
Vlad Durnea cffdf8af86
Some checks failed
CI/CD Pipeline / unit-tests (push) Failing after 1m16s
CI/CD Pipeline / integration-tests (push) Failing after 2m32s
CI/CD Pipeline / lint (push) Successful in 5m22s
CI/CD Pipeline / e2e-tests (push) Has been skipped
CI/CD Pipeline / build (push) Has been skipped
wip:milestone 0 fixes
2026-03-15 12:35:42 +02:00

7.2 KiB

Milestone 10: Admin UI

Goal: MadBase Studio is a functional admin dashboard for core operations.

Depends on: M0 (Security), M1 (Foundation), M3 (Auth), M9 (Control Plane)


10.1 — Authentication

10.1.1 Real login form

File: web/js/admin.js

Replace the current auth check (hitting /platform/v1/projects and checking for 401) with a proper login flow:

async login() {
    const resp = await fetch('/platform/v1/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ password: this.loginPassword }),
    });
    if (resp.ok) {
        this.isAuthenticated = true;
        this.loginError = '';
        await this.loadDashboard();
    } else {
        this.loginError = 'Invalid password';
    }
}

The server sets an HttpOnly session cookie on success (implemented in M0).

10.1.2 Add logout

async logout() {
    await fetch('/platform/v1/logout', { method: 'POST' });
    this.isAuthenticated = false;
    // Clear all reactive state
}

10.1.3 CSRF protection

Generate a CSRF token on page load, include in all mutation requests:

// On page load
const csrfResp = await fetch('/platform/v1/csrf-token');
this.csrfToken = (await csrfResp.json()).token;

// On mutations
headers: { 'X-CSRF-Token': this.csrfToken }

10.2 — Security Fixes

10.2.1 Stop sending service role key to browser

File: web/js/admin.js line ~121

Remove fetchAdminConfig() and the serviceRoleKey reactive variable. All admin API calls should use session auth (the HttpOnly cookie), not the service role key.

Replace storage/data API calls that use the service role key with admin-proxied endpoints:

// BEFORE
headers: { 'Authorization': `Bearer ${this.serviceRoleKey}` }

// AFTER — use session cookie (automatic with same-origin requests)
// No Authorization header needed for /platform/v1/* routes

10.2.2 Bundle CDN dependencies

Replace CDN script tags with locally bundled files. Options:

  1. Simple: Download Vue, Chart.js, Tailwind to web/vendor/ and serve statically
  2. Better: Add a minimal build step with Vite that bundles everything into web/dist/

For air-gapped deployments, option 1 is essential.

10.2.3 Fix Tailwind @apply

File: web/css/admin.css

@apply directives don't work with CDN Tailwind JIT. Either:

  1. Remove @apply and use inline Tailwind classes in the HTML
  2. Or add a build step that processes the CSS with Tailwind CLI

10.3 — Missing Views

10.3.1 Auth management tab

Add a view showing:

  • User list with search/filter
  • User detail: email, created_at, confirmed_at, last_sign_in_at, providers
  • Actions: ban/unban, confirm email, delete user, reset password

10.3.2 Realtime console

Add a view showing:

  • Active WebSocket connections count
  • Active channel subscriptions
  • Live event stream (filterable by table/event type)
  • Presence information per channel

10.3.3 Object deletion in Storage view

Add a delete button next to each object in the storage file browser:

async deleteObject(bucketId, objectName) {
    if (!confirm(`Delete ${objectName}?`)) return;
    await fetch(`/platform/v1/storage/${bucketId}/${objectName}`, { method: 'DELETE' });
    await this.fetchObjects(bucketId);
}

10.4 — Usability

10.4.1 Configurable Grafana URL

File: web/admin.html — Grafana iframe (line ~414)

<!-- BEFORE -->
<iframe src="http://localhost:3000" ...></iframe>

<!-- AFTER -->
<iframe :src="grafanaUrl" ...></iframe>
// In admin.js data
grafanaUrl: window.MADBASE_GRAFANA_URL || '/grafana',

Set via env var or server-rendered config.

10.4.2 Confirmation dialogs

Add confirm() before all destructive operations:

  • Delete project
  • Delete user
  • Delete storage object
  • Remove server

10.4.3 Error handling

Add global error display:

methods: {
    async apiCall(url, options) {
        try {
            const resp = await fetch(url, options);
            if (!resp.ok) {
                const err = await resp.json();
                this.showError(err.error || 'Request failed');
                return null;
            }
            return resp;
        } catch (e) {
            this.showError(e.message);
            return null;
        }
    },
    showError(msg) {
        this.errorMessage = msg;
        setTimeout(() => this.errorMessage = '', 5000);
    }
}

Completion Requirements

This milestone is not complete until every item below is satisfied.

1. Full Test Suite — All Green

  • cargo test --workspace passes with zero failures (backend unchanged, but verify no regressions)
  • All pre-existing tests still pass
  • New end-to-end / browser tests cover the admin UI:
Test Location What it validates
test_login_success tests/e2e/admin_ui.rs or Playwright Correct password → dashboard loads, session cookie set
test_login_failure tests/e2e/admin_ui.rs or Playwright Wrong password → error message shown, no cookie
test_logout tests/e2e/admin_ui.rs or Playwright Logout → redirected to login, session cookie cleared
test_no_service_key_in_client tests/e2e/admin_ui.rs or Playwright Service role key absent from page source and network requests
test_auth_user_list tests/e2e/admin_ui.rs or Playwright Auth tab renders user list from API
test_auth_user_search tests/e2e/admin_ui.rs or Playwright Typing in search filters the user list
test_storage_delete_object tests/e2e/admin_ui.rs or Playwright Delete button removes object; confirm dialog appears first
test_grafana_iframe_configurable tests/e2e/admin_ui.rs or Playwright Iframe src matches configured MADBASE_GRAFANA_URL
test_delete_project_confirmation tests/e2e/admin_ui.rs or Playwright Delete project requires confirmation dialog
test_no_cdn_dependencies web/admin.html (static analysis) No <script> or <link> tags referencing external CDN URLs
test_csrf_token_present tests/e2e/admin_ui.rs or Playwright Mutating requests include a CSRF token

2. Manual / Visual Verification

  • Login with correct password → dashboard loads
  • Login with wrong password → error message shown
  • Logout → redirected to login, session cookie cleared
  • Service role key never appears in browser DevTools (Network, Application tabs)
  • Auth tab shows user list with working search
  • Storage tab allows deleting objects
  • Grafana iframe loads from configured URL
  • Delete project shows confirmation dialog
  • Works in air-gapped environment (no CDN dependencies)
  • Responsive layout works on 1024px and 1440px viewports
  • Error toast appears on API failures (e.g., network down)

3. CI Gate

  • cargo test --workspace green (backend)
  • E2E tests (Playwright or equivalent) run in CI against a docker compose up stack
  • Static analysis confirms no external CDN references in web/ HTML/JS files
  • All destructive API calls in the UI are confirmed via a dialog (code review checklist)