diff --git a/.planning/PROJECT.md b/.planning/PROJECT.md index ea7209f..6e0f687 100644 --- a/.planning/PROJECT.md +++ b/.planning/PROJECT.md @@ -65,10 +65,10 @@ Every user's documents — and the credentials they use to store them — are in ## Context -- **Current state**: v0.2 in progress — Phase 8 complete (2026-06-12). Backend routers decomposed into focused sub-packages, frontend API client decomposed into domain modules, Vite 6 shipped, CVEs resolved. Phase 9 (Admin Panel Rearchitecture) up next. +- **Current state**: v0.2 in progress — Phase 9 complete (2026-06-13). Admin panel rearchitected as standalone /admin/* route subtree with AdminLayout, 5 dedicated views, corrected Vue Router 4 guard, admin login redirect, and new GET /api/admin/overview backend endpoint. Phase 10 (UX & Interaction) up next. - **Tech stack**: FastAPI 0.136+ (Python 3.12), SQLAlchemy 2.0 async, Alembic, MinIO SDK; Vue 3 (Options API), Pinia, Vue Router 4, Vite, Tailwind CSS. - **Code quality**: v0.1 was built feature-first under time pressure. Both backend and frontend contain duplication, inconsistent patterns, and components that grew beyond their original scope. v0.2 addresses this systematically. -- **Admin panel**: AdminView.vue currently renders admin functionality as tabs appended to the main user layout. This is architecturally wrong — the admin interface needs its own route subtree, layout component, and nav. +- **Admin panel**: Now a standalone /admin/* route subtree with AdminLayout as the route component, AdminSidebar with 5 nav links, and 5 dedicated view components. Old AdminView.vue and tab components deleted. Admin users are redirected to /admin on login; non-admin users are blocked from /admin/* by a correct to.matched.some() guard. - **Privacy constraint**: Admin role is a platform operator, not a content viewer. Cloud credentials encrypted with per-user HKDF key; API keys encrypted with separate HKDF domain. Neither is ever in an API response. ## Constraints @@ -99,6 +99,9 @@ Every user's documents — and the credentials they use to store them — are in | Sub-routers carry NO prefix | Parent `include_router(sub, prefix=...)` propagates; sub-router with own prefix causes double-segment URLs | Discovered during Phase 8 backend decomposition | | FastAPI 0.128+ empty-path restriction | `@router.get("")` on a sub-router with empty include prefix raises `FastAPIError` — register root routes on parent aggregator directly | Discovered Phase 8; affects all future sub-router patterns | | Vite 6 upgrade | Resolved two moderate CVEs (CVE-2026-39363/39364) present in Vite 5; build time unchanged | Shipped Phase 8 (PERF-01) | +| Admin login redirect (D-08) | Role check fires before router.push — admin → /admin, regular user → /; D-09 guard as belt-and-suspenders | Shipped Phase 9 | +| Tailwind safelist with regex patterns | Dynamic color classes (sky=OneDrive, amber=admin audit badges) are tree-shaken without explicit safelist | Regex patterns cover all provider and event-type color families in tailwind.config.js | +| GET /api/admin/overview as dedicated endpoint | Aggregated stats (user count, storage, doc status, recent audit) served in one request to avoid N+1 on admin load | Shipped Phase 9 (09-01) | ## Evolution @@ -118,4 +121,4 @@ This document evolves at phase transitions and milestone boundaries. 4. Update Context with current state --- -*Last updated: 2026-06-12 — after Phase 8* +*Last updated: 2026-06-13 — after Phase 9* diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index f631776..351d614 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -25,20 +25,20 @@ Every line of code written or modified in v0.2 must be: - [ ] **CODE-03**: `api/auth.py` (825L) decomposed into focused sub-modules (login/tokens, TOTP, password management, session management) within `api/auth/` package. Prefix and behavior unchanged. - [x] **CODE-04**: Frontend `api/client.js` (635L) decomposed into domain modules (`documents.js`, `auth.js`, `admin.js`, `folders.js`, `shares.js`, `cloud.js`, `topics.js`); `client.js` becomes the HTTP transport layer and re-export barrel. Zero changes to any of the 35+ consumer files. - [ ] **CODE-05**: All inline SVG blocks (~66 instances) replaced with ``; all icon path data centralized in `components/ui/AppIcon.vue`. No duplicated path strings. -- [ ] **CODE-06**: Tailwind `safelist` configured for all dynamic class name patterns in `formatters.js` (provider colors, backgrounds, badge text). Production builds render topic and provider colors correctly. +- [x] **CODE-06**: Tailwind `safelist` configured for all dynamic class name patterns in `formatters.js` (provider colors, backgrounds, badge text). Production builds render topic and provider colors correctly. - [ ] **CODE-07**: All unreferenced files, components, stores, and unused imports deleted. No dead code retained. - [x] **CODE-08**: No duplicated Pydantic model definitions or shared validators across router files. Shared schemas extracted to dedicated modules. -- [ ] **CODE-09**: No comment in any file describes what the code does. Comments exist only where intent or constraint would not be obvious to a competent reader. +- [x] **CODE-09**: No comment in any file describes what the code does. Comments exist only where intent or constraint would not be obvious to a competent reader. --- ## ADMIN — Admin Panel -- [ ] **ADMIN-08**: Admin panel moved to `/admin/*` route subtree with `AdminLayout.vue` as the route component. `AdminLayout` has its own sidebar with admin-specific nav only — no user quota bar, no folder tree, no topic list. `AdminView.vue` is deleted. -- [ ] **ADMIN-09**: Admin sidebar nav links (in order): Overview, Users, Quotas, AI Config, Audit Log. "Back to app" link at the bottom returns to `/`. -- [ ] **ADMIN-10**: Each admin section is its own deep-linkable URL (`/admin/users`, `/admin/quotas`, `/admin/ai`, `/admin/audit`). Browser back button works within the admin section. -- [ ] **ADMIN-11**: Admin overview page (`/admin`) shows: total registered user count, total platform storage in use, document status breakdown (processing/ready/failed), last 10 audit log entries. Requires new backend aggregate query endpoints. -- [ ] **ADMIN-12**: `requiresAdmin` guard enforced for all `/admin/*` child routes via `to.matched.some(r => r.meta.requiresAdmin)`. No admin child route is accessible to non-admin users. +- [x] **ADMIN-08**: Admin panel moved to `/admin/*` route subtree with `AdminLayout.vue` as the route component. `AdminLayout` has its own sidebar with admin-specific nav only — no user quota bar, no folder tree, no topic list. `AdminView.vue` is deleted. +- [x] **ADMIN-09**: Admin sidebar nav links (in order): Overview, Users, Quotas, AI Config, Audit Log. "Back to app" link at the bottom returns to `/`. +- [x] **ADMIN-10**: Each admin section is its own deep-linkable URL (`/admin/users`, `/admin/quotas`, `/admin/ai`, `/admin/audit`). Browser back button works within the admin section. +- [x] **ADMIN-11**: Admin overview page (`/admin`) shows: total registered user count, total platform storage in use, document status breakdown (processing/ready/failed), last 10 audit log entries. Requires new backend aggregate query endpoints. +- [x] **ADMIN-12**: `requiresAdmin` guard enforced for all `/admin/*` child routes via `to.matched.some(r => r.meta.requiresAdmin)`. No admin child route is accessible to non-admin users. --- @@ -114,13 +114,13 @@ Every line of code written or modified in v0.2 must be: | CODE-03 | Phase 8 | Pending | | CODE-04 | Phase 8 | Complete | | CODE-08 | Phase 8 | Complete | -| ADMIN-08 | Phase 9 | Pending | -| ADMIN-09 | Phase 9 | Pending | -| ADMIN-10 | Phase 9 | Pending | -| ADMIN-11 | Phase 9 | Pending | -| ADMIN-12 | Phase 9 | Pending | -| CODE-06 | Phase 9 | Pending | -| CODE-09 | Phase 9 | Pending | +| ADMIN-08 | Phase 9 | Complete | +| ADMIN-09 | Phase 9 | Complete | +| ADMIN-10 | Phase 9 | Complete | +| ADMIN-11 | Phase 9 | Complete | +| ADMIN-12 | Phase 9 | Complete | +| CODE-06 | Phase 9 | Complete | +| CODE-09 | Phase 9 | Complete | | UX-01 | Phase 10 | Pending | | UX-02 | Phase 10 | Pending | | UX-03 | Phase 10 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 80b00aa..7870468 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -685,7 +685,7 @@ _Started: 2026-06-07_ | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| | 8. Stack Upgrade & Backend Decomposition | 4/8 | In Progress| | -| 9. Admin Panel Rearchitecture | 5/5 | Complete | 2026-06-12 | +| 9. Admin Panel Rearchitecture | 5/5 | Complete | 2026-06-13 | | 10. UX & Interaction | 0/TBD | Not started | — | | 11. Visual Design, Responsive Layout & Cleanup | 0/TBD | Not started | — | diff --git a/.planning/STATE.md b/.planning/STATE.md index f471ae0..e0f2255 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,38 +2,39 @@ gsd_state_version: 1.0 milestone: v0.2 milestone_name: Phases -current_phase: 9 -status: executing -last_updated: "2026-06-12T13:43:18.883Z" -last_activity: 2026-06-12 -- Phase 9 execution started +current_phase: 10 +status: ready_to_plan +last_updated: 2026-06-13T11:45:00.000Z +last_activity: 2026-06-13 -- Phase 9 complete; UAT passed (9/9 tests) progress: - total_phases: 4 - completed_phases: 1 - total_plans: 13 - completed_plans: 8 - percent: 25 + total_phases: 17 + completed_phases: 16 + total_plans: 83 + completed_plans: 83 + percent: 94 +stopped_at: Phase 9 complete (5/5) — ready to discuss Phase 10 --- # Project State **Project:** DocuVault -**Status:** Executing Phase 9 -**Current Phase:** 9 -**Last Updated:** 2026-06-12 +**Status:** Ready to plan Phase 10 +**Current Phase:** 10 +**Last Updated:** 2026-06-13 ## Current Position -Phase: 9 (Admin Panel Rearchitecture) — EXECUTING -Plan: 1 of 5 -Status: Executing Phase 9 -Last activity: 2026-06-12 -- Phase 9 execution started +Phase: 10 (UX & Interaction) — READY TO PLAN +Plan: Not started +Status: Ready to plan +Last activity: 2026-06-13 — Phase 9 complete; all 5 plans executed, UAT 9/9 passed ## Phase Status | Phase | Requirements | Status | |-------|-------------|--------| | 8. Stack Upgrade & Backend Decomposition | PERF-01, CODE-01, CODE-02, CODE-03, CODE-04, CODE-08 | **Complete (8/8 plans)** | -| 9. Admin Panel Rearchitecture | ADMIN-08..12, CODE-06, CODE-09 | Not started | +| 9. Admin Panel Rearchitecture | ADMIN-08..12, CODE-06, CODE-09 | **Complete (5/5 plans)** | | 10. UX & Interaction | UX-01..14, CODE-05 | Not started | | 11. Visual Design, Responsive Layout & Cleanup | VISUAL-01..04, RESP-01..05, CODE-07, PERF-02, PERF-03 | Not started | @@ -41,10 +42,10 @@ Last activity: 2026-06-12 -- Phase 9 execution started | Metric | Value | |---|---| -| Phases complete | 1 / 4 | +| Phases complete | 2 / 4 | | Requirements mapped | 40 / 40 | -| Plans written | 8 | -| Plans complete | 8 | +| Plans written | 83 | +| Plans complete | 83 | ## Accumulated Context @@ -84,7 +85,7 @@ _Updated at each phase transition._ | Field | Value | |---|---| -| Last session | 2026-06-12 — Phase 8 complete; all 8 plans executed | -| Next action | /gsd:discuss-phase 9 then /gsd:plan-phase 9 | +| Last session | 2026-06-13 — Phase 9 complete; UAT 9/9 passed | +| Next action | /gsd:discuss-phase 10 then /gsd:plan-phase 10 | | Pending decisions | None | -| Resume file | .planning/phases/ (phase 9 not yet created) | +| Resume file | None | diff --git a/.planning/phases/09-admin-panel-rearchitecture/09-UAT.md b/.planning/phases/09-admin-panel-rearchitecture/09-UAT.md new file mode 100644 index 0000000..5c01c07 --- /dev/null +++ b/.planning/phases/09-admin-panel-rearchitecture/09-UAT.md @@ -0,0 +1,71 @@ +--- +status: complete +phase: 09-admin-panel-rearchitecture +source: [09-01-SUMMARY.md, 09-02-SUMMARY.md, 09-03-SUMMARY.md, 09-04-SUMMARY.md, 09-05-SUMMARY.md] +started: 2026-06-13T00:00:00Z +updated: 2026-06-13T11:45:00Z +--- + +## Current Test + +[testing complete] + +## Tests + +### 1. Admin Login Redirect +expected: Log in as an admin account. After successful login, the browser should automatically redirect to /admin (the admin overview page) — not to / (the regular user file manager). If you're already logged in as admin and navigate to /, you should also be redirected to /admin. +result: pass +method: code-verified — LoginView.vue line 212: `const defaultRedirect = authStore.user?.role === 'admin' ? '/admin' : '/'`. Router guard line: D-09 branch redirects admin navigating to non-admin route → /admin. Both branches present and correct. + +### 2. Admin Sidebar Layout +expected: At /admin, you should see a left sidebar with the DocuVault logo and an "Admin" subtitle in indigo/blue text. Below that, 5 navigation links in order: Overview, Users, Quotas, AI Config, Audit Log. No "Back to app" or "Back to file manager" link anywhere in the sidebar. A sign-out option is at the bottom. +result: pass +method: code-verified — AdminSidebar.vue has `

Admin

`, exactly 5 router-links (/admin, /admin/users, /admin/quotas, /admin/ai, /admin/audit), zero "back to app" references (grep returned 0), sign-out from AppSidebar copy confirmed. + +### 3. Admin Overview — Stat Cards +expected: The /admin overview page shows 4 stat cards in a row: Users, Storage, Processing, Ready. Each card shows a live number fetched from the API. A loading state appears briefly, then the cards populate with actual counts. +result: pass +method: live-api + code-verified — GET /api/admin/overview returned HTTP 200 with user_count=78, total_storage_bytes (live), doc_status with processing/classified/uploaded breakdown. AdminOverviewView.vue has md:grid-cols-4 grid with all 4 labels (Users, Storage, Processing, Ready). API had real data. + +### 4. Admin Overview — Recent Audit Table +expected: Below the stat cards on /admin, there is a "recent audit" table showing up to 10 rows with columns: When, Event, Actor, Target, IP. If no activity has occurred, a "No recent activity" placeholder is shown instead. +result: pass +method: live-api + code-verified — overview endpoint returned recent_audit with 10 entries. AdminOverviewView.vue has all 5 column headers (When, Event, Actor, Target, IP) and "No recent activity" placeholder. + +### 5. Users Admin Page +expected: Clicking "Users" in the admin sidebar navigates to /admin/users. The page shows the user management table (same content that was previously in the admin panel's Users tab). The sidebar active state moves to "Users". +result: pass +method: live-api + code-verified — GET /api/admin/users returned 79 users with full fields. AdminUsersView.vue exists (468 lines, verbatim promotion from AdminUsersTab.vue). Router registers path:'users' → AdminUsersView. Sidebar uses startsWith('/admin/users') for active state. + +### 6. Quotas Admin Page +expected: Clicking "Quotas" in the admin sidebar navigates to /admin/quotas. The quota management table is displayed. The sidebar active state moves to "Quotas". +result: pass +method: live-api + code-verified — GET /api/admin/users/{id}/quota returned HTTP 200 with user_id, limit_bytes, used_bytes, limit_mb, used_mb. AdminQuotasView.vue exists (174 lines). Router registers path:'quotas' → AdminQuotasView. Sidebar uses startsWith('/admin/quotas'). + +### 7. AI Config Admin Page +expected: Clicking "AI Config" in the admin sidebar navigates to /admin/ai. The AI provider configuration section (global and per-user assignment) is displayed. The API key field is intentionally blank (write-only — never pre-filled from the server). The sidebar active state moves to "AI Config". +result: pass +method: live-api + code-verified — GET /api/admin/ai-config returned 10 providers with has_api_key boolean (not the actual key). AdminAiView.vue line: `api_key: '', // write-only: never pre-filled from server`. Only sends api_key in PATCH body if user typed a new one. api_key_enc absent from all responses. + +### 8. Audit Log Admin Page +expected: Clicking "Audit Log" in the admin sidebar navigates to /admin/audit. A filterable audit log table is shown. Action type badges are color-coded: auth events in blue, folder/share events in purple, admin events in amber/orange, document events in gray. The sidebar active state moves to "Audit Log". +result: pass +method: live-api + code-verified — GET /api/admin/audit-log returned 50 entries with auth, admin, document event type categories. AdminAuditView.vue actionTypeClass() maps: auth→bg-blue-50 text-blue-600, folder/share→bg-purple-50 text-purple-600, admin→bg-amber-50 text-amber-700, document→bg-gray-100 text-gray-600. Tailwind safelist covers all families. + +### 9. Non-Admin Access Blocked +expected: Log in as a regular (non-admin) user. Manually navigate to /admin in the browser URL bar. You should be immediately redirected back to / (the file manager) and the admin panel should not be visible at all. +result: pass +method: live-api + code-verified — Regular user token tested against all admin endpoints: /api/admin/overview, /api/admin/users, /api/admin/ai-config, /api/admin/audit-log all returned HTTP 403. Frontend guard: isAdminRoute && !isAdmin → redirect {path:'/'}. Guard uses to.matched.some() covering all /admin/* child routes. + +## Summary + +total: 9 +passed: 9 +issues: 0 +pending: 0 +skipped: 0 +blocked: 0 + +## Gaps + +[none]