Phase 01: Foundation - 5 plan(s) in 4 wave(s) - 2 parallel (wave 2: auth + tenant provisioning), 3 sequential - Ready for execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
10 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-foundation | 05 | execute | 4 |
|
|
true |
|
Purpose: The platform owner needs to manage ISP tenants (view, suspend, monitor) without being scoped to any single tenant. This closes the super-admin requirement and validates the entire foundation with comprehensive tests. Output: Working super-admin panel with tenant management, plus full test suite covering auth, isolation, and RBAC.
<execution_context> @C:\Users\KevinAsprec.claude/get-shit-done/workflows/execute-plan.md @C:\Users\KevinAsprec.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/01-foundation/01-CONTEXT.md @.planning/phases/01-foundation/01-02-SUMMARY.md @.planning/phases/01-foundation/01-03-SUMMARY.md @.planning/phases/01-foundation/01-04-SUMMARY.md @src/lib/auth-options.ts @src/lib/casl/ability.ts @src/lib/prisma-tenant.ts @prisma/schema.prisma Task 1: Super-admin API routes and middleware guard src/lib/middleware/super-admin.ts src/app/api/admin/tenants/route.ts src/app/api/admin/tenants/[id]/route.ts src/app/api/admin/tenants/[id]/suspend/route.ts prisma/seed.ts Create `src/lib/middleware/super-admin.ts`: - Export `withSuperAdmin(handler)` — wraps an API route handler - Gets current user session via getCurrentUser() - If no session: return 401 - If session.user.isSuperAdmin is not true: return 403 { error: "Super-admin access required" } - If authorized: call handler with user contextCreate `src/app/api/admin/tenants/route.ts`:
- GET handler wrapped with withSuperAdmin
- Queries ALL tenants (no tenant scoping — super-admin sees everything)
- Returns array of: { id, name, slug, status, ownerEmail, createdAt, userCount, subscriberCount (0 for now — no Subscriber model yet) }
- userCount: count of users per tenant via Prisma _count
- Sort by createdAt descending
Create `src/app/api/admin/tenants/[id]/route.ts`:
- GET handler wrapped with withSuperAdmin
- Returns single tenant detail: all fields plus users list (id, email, firstName, lastName, roles, isActive)
- Return 404 if tenant not found
Create `src/app/api/admin/tenants/[id]/suspend/route.ts`:
- POST handler wrapped with withSuperAdmin
- Accepts JSON body: { action: "suspend" | "activate" }
- For suspend:
- Set status to PENDING_SUSPENSION
- Set suspendedAt to now()
- Set gracePeriodEndsAt to 7 days from now
- Return { message: "Tenant suspension initiated. Grace period ends on {date}." }
- For activate:
- Set status to ACTIVE
- Clear suspendedAt and gracePeriodEndsAt
- Return { message: "Tenant activated." }
- Return 404 if tenant not found
Update `prisma/seed.ts`:
- Add a second demo tenant: "Test ISP 2" with a different admin user (admin2@demo.com) for testing cross-tenant isolation
- Keep existing demo tenant and super-admin user
Run `npm run db:seed` — seed runs cleanly with both tenants. Start the app. Use curl or an API client to:
1. GET /api/admin/tenants without auth — expect 401
2. Log in as super-admin, GET /api/admin/tenants — expect 200 with both tenants
3. POST /api/admin/tenants/{id}/suspend with { action: "suspend" } — expect tenant status changes to PENDING_SUSPENSION
Super-admin API routes list all tenants and can suspend/activate them. withSuperAdmin middleware blocks non-super-admin access with 403. Grace period suspension works.
Task 2: Super-admin UI panel and comprehensive test harness
src/app/(super-admin)/layout.tsx
src/app/(super-admin)/admin/page.tsx
src/app/(super-admin)/admin/tenants/page.tsx
src/middleware.ts
src/lib/__tests__/super-admin.test.ts
src/lib/__tests__/tenant-isolation.test.ts
Create `src/app/(super-admin)/layout.tsx`:
- Server component that checks session
- If user is not super-admin, redirect to /login or show 403 page
- Simple layout with sidebar navigation: "Dashboard", "Tenants"
- Header shows "NetForge Admin" and the super-admin's name
- Sign out button
Create `src/app/(super-admin)/admin/page.tsx`:
- Simple admin dashboard showing: total tenants, active tenants, suspended tenants
- Fetches stats from /api/admin/tenants
Create `src/app/(super-admin)/admin/tenants/page.tsx`:
- Table listing all tenants: Name, Status (badge with color: green=Active, yellow=Pending Suspension, red=Suspended), Owner Email, Users, Created Date
- Each row has actions: View details, Suspend/Activate toggle button
- Suspend button triggers confirmation dialog then calls the suspend API
- Activate button calls the suspend API with action: "activate"
- Clicking tenant name navigates to a detail view (or opens a modal showing users)
- Use Tailwind for styling — clean data table with hover states
Update `src/middleware.ts`:
- Add /admin/* routes to be protected
- These routes should check for super-admin status specifically (or rely on the layout redirect + API guards)
- Public routes remain: /login, /signup, /api/auth/*
Create `src/lib/__tests__/super-admin.test.ts`:
- Test: super-admin user can access admin routes (withSuperAdmin allows)
- Test: regular admin user cannot access admin routes (withSuperAdmin returns 403)
- Test: tenant suspension sets correct status and grace period dates
- Test: tenant activation clears suspension fields
Enhance `src/lib/__tests__/tenant-isolation.test.ts` (if needed):
- Ensure the existing cross-tenant isolation test still passes with the two-tenant seed data
- Add test: super-admin query for tenants returns ALL tenants (not scoped)
- Add test: regular user query does NOT return other tenants' data
Run full test suite: `npx vitest run` — all tests must pass.
Run `npx vitest run` — all tests pass (auth, RBAC, tenant isolation, super-admin). Start the app, log in as superadmin@netforge.com, navigate to /admin/tenants — see both demo tenants listed. Suspend a tenant — status badge updates. Activate it — status reverts. Log in as admin@demo.com — navigating to /admin returns forbidden.
Super-admin can log in and manage all tenants via /admin panel (TENANT-03). Non-super-admin users are blocked from admin routes. Full test harness covers auth, RBAC, tenant isolation, and super-admin access. Phase 1 success criteria are met.
1. Super-admin logs in and sees tenant list at /admin/tenants
2. Tenant table shows status, owner, user count, creation date
3. Suspend action sets PENDING_SUSPENSION with 7-day grace period
4. Activate action restores ACTIVE status
5. Regular admin user gets 403 when accessing /admin routes
6. `npx vitest run` passes ALL tests across the entire phase:
- Auth config tests
- RBAC permission tests for all 5 roles
- Tenant isolation tests (zero cross-tenant leakage)
- Super-admin access tests
<success_criteria>
- Super-admin can log in and view all tenants (TENANT-03)
- Super-admin sees tenant status, user count, creation date per tenant
- Tenant suspension with grace period works (not immediate lockout)
- Non-super-admin blocked from admin panel
- Full test suite validates Phase 1 foundation (INFRA-02 partial — unit tests for auth/RBAC/isolation)
- All Phase 1 requirements met: TENANT-01, TENANT-02, TENANT-03, AUTH-01, AUTH-02, AUTH-03, AUTH-04, INFRA-01, INFRA-02 </success_criteria>