docs(36): capture phase context .planning/phases/36-admin-role-panel-foundation/36-CONTEXT.md .planning/phases/36-admin-role-panel-foundation/36-DISCUSSION-LOG.md

This commit is contained in:
2026-04-19 20:34:10 +02:00
parent 8f4bb5096d
commit 38c0382f64
3 changed files with 181 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
# Phase 36: Admin Role & Panel Foundation - Context
**Gathered:** 2026-04-19
**Status:** Ready for planning
<domain>
## Phase Boundary
Add an `isAdmin` boolean to the users table, protect the `/admin` route (server middleware + client guard), build a structured admin shell with sidebar navigation, and surface `isAdmin` to the client via `/api/auth/me`. Admin status is granted directly via SQL/Drizzle Studio — no CLI script needed.
</domain>
<decisions>
## Implementation Decisions
### Schema
- **D-01:** Add `isAdmin boolean NOT NULL DEFAULT false` to the `users` table via Drizzle migration.
- **D-02:** No Logto role claims — isAdmin lives entirely in the GearBox database.
### Admin Grant Mechanism
- **D-03:** No CLI script. Developers grant/revoke admin status via direct SQL (`UPDATE users SET is_admin = true WHERE ...`) or Drizzle Studio. This is acceptable for a single-admin app.
### Route Protection
- **D-04:** New `requireAdmin` middleware (extends `requireAuth`) — returns 403 JSON for any non-admin hitting `/api/admin/*` endpoints.
- **D-05:** TanStack Router `beforeLoad` guard on the `/admin` client route — redirects non-admin users to home (`/`). Belt-and-suspenders: server 403 + client redirect.
### Admin Nav Link
- **D-06:** Show a conditional "Admin" link for admin users in the **user avatar/menu area** of the top nav (not a top-level nav item). Keeps it scoped to account-level actions.
- **D-07:** `isAdmin` is surfaced to the client by adding it to the `/api/auth/me` response. No separate query needed.
### Admin Panel Layout
- **D-08:** `/admin` renders a structured shell with a sidebar nav — not an empty placeholder. The shell has two nav items: **Items** and **Tags** (matching phases 37 and 38 respectively). Both are disabled/coming-soon in this phase.
- **D-09:** This layout is the reusable admin frame — phases 37 and 38 replace the placeholder content areas without reworking the shell.
### Claude's Discretion
- Exact visual styling of the admin shell (consistent with app's light/minimal aesthetic)
- Whether to add a dedicated `/admin` server-side route handler or reuse the SPA catch-all
- How to structure the `requireAdmin` middleware relative to `requireAuth` (wrapping vs. separate)
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### Auth & Middleware
- `src/server/middleware/auth.ts` — existing `requireAuth` middleware; `requireAdmin` should follow the same pattern
- `src/server/services/auth.service.ts``getOrCreateUser` and user DB patterns
### Database Schema
- `src/db/schema.ts``users` table definition; add `isAdmin` here
- `src/server/routes/auth.ts``/api/auth/me` endpoint; add `isAdmin` to response
### Client Routing & Nav
- `src/client/routes/__root.tsx` — root layout with top nav + user menu; add conditional Admin link
- `src/client/routes/` — TanStack Router file-based routes; create `admin.tsx` and `admin/` directory
### Requirements
- `.planning/REQUIREMENTS.md` — ROLE-01, ROLE-02, ADMN-01
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- `requireAuth` middleware (`src/server/middleware/auth.ts`): `requireAdmin` follows the same Context/Next signature — call `requireAuth` first, then check `isAdmin` from the resolved user record
- Existing Hono route patterns in `src/server/routes/` — admin routes follow the same structure
- TanStack Router file-based routing — `/admin` becomes `src/client/routes/admin.tsx` (or `admin/index.tsx`)
### Established Patterns
- Auth middleware sets `userId` on Hono context; `requireAdmin` reads the `users` record to check `isAdmin`
- `/api/auth/me` already returns `{ user, authenticated }` — add `isAdmin` to the `user` object
- Light/airy design aesthetic — admin shell should match app visual style (white, minimal, no visual clutter)
### Integration Points
- `src/server/index.ts`: Register new `/api/admin/*` routes behind `requireAdmin`
- `src/client/routes/__root.tsx`: Conditional Admin link in user menu (reads `isAdmin` from auth query)
- `src/db/schema.ts` + migration: `isAdmin` column on `users` table
</code_context>
<specifics>
## Specific Ideas
- Admin sidebar: two sections "Items" (phase 37) and "Tags" (phase 38) — both greyed out / "Coming soon" in this phase
- The admin shell is the persistent frame; phases 37/38 inject content into a `<Outlet>` or equivalent
</specifics>
<deferred>
## Deferred Ideas
- Logto UI-based admin management — not possible without switching to Logto role claims (explicitly ruled out)
- Users section in admin sidebar — not in current roadmap, deferred to a future milestone if needed
- Formal CLI tool for admin grant — deemed unnecessary given direct SQL access
</deferred>
---
*Phase: 36-admin-role-panel-foundation*
*Context gathered: 2026-04-19*

View File

@@ -0,0 +1,77 @@
# Phase 36: Admin Role & Panel Foundation - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-04-19
**Phase:** 36 — Admin Role & Panel Foundation
**Areas discussed:** Admin panel layout, Admin nav link, Non-admin response, CLI grant interface
---
## Admin Panel Layout
| Option | Description | Selected |
|--------|-------------|----------|
| Structured shell | Full admin layout with sidebar nav (Items, Tags) — phases 37/38 slot in | ✓ |
| Minimal placeholder | Just a heading + coming soon text | |
**User's choice:** Structured shell with Items + Tags sidebar sections (both disabled in this phase)
**Notes:** User confirmed Items + Tags as the two nav sections matching phases 37 and 38.
---
## Admin Nav Link
| Option | Description | Selected |
|--------|-------------|----------|
| Yes — conditionally shown | Top nav shows 'Admin' only for admin users, isAdmin in /me response | ✓ |
| No — navigate directly | No nav link, admin accesses by URL | |
**Placement:**
| Option | Description | Selected |
|--------|-------------|----------|
| User avatar/menu area | Admin link in user dropdown near avatar | ✓ |
| Top-level nav item | Standalone nav item alongside main nav | |
| You decide | — | |
**User's choice:** Conditionally shown in the user avatar/menu area.
---
## Non-Admin Response
| Option | Description | Selected |
|--------|-------------|----------|
| Server 403 + client redirect | requireAdmin middleware + TanStack Router beforeLoad redirect | ✓ |
| Client redirect only | TanStack Router beforeLoad only | |
| Server 403 only | 403 response, no redirect | |
**User's choice:** Belt-and-suspenders: server 403 on API routes + client redirect on browser navigation.
---
## CLI Grant Interface
| Option | Description | Selected |
|--------|-------------|----------|
| bun run admin:grant \<email\> | Script looks up user by email | |
| bun run admin:grant \<logto-sub\> | Uses Logto sub identifier | |
| Direct SQL / Drizzle Studio | No script — UPDATE SQL directly | ✓ |
**Context:** User initially asked about doing it via the Logto UI. Clarified that since isAdmin lives in the GearBox DB (not Logto), the Logto UI cannot set it. User settled on direct SQL / Drizzle Studio — no CLI script needed for a single-admin app.
---
## Claude's Discretion
- Exact admin shell visual styling
- Whether `/admin` needs a dedicated server route or uses the SPA catch-all
- Internal structure of `requireAdmin` relative to `requireAuth`
## Deferred Ideas
- Logto UI-based admin management (requires Logto role claims — ruled out)
- Users section in admin sidebar (not in current roadmap)
- Formal CLI grant tool (deemed unnecessary)