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

227 lines
7.2 KiB
Markdown

# 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:
```javascript
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
```javascript
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:
```javascript
// 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:
```javascript
// 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:
```javascript
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)
```html
<!-- BEFORE -->
<iframe src="http://localhost:3000" ...></iframe>
<!-- AFTER -->
<iframe :src="grafanaUrl" ...></iframe>
```
```javascript
// 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:
```javascript
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)