Files
NetForge/.planning/phases/01-foundation/01-05-PLAN.md
kevin-asprec 7e6d286fca docs(01): create phase plan
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>
2026-03-04 18:13:27 +08:00

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
01-04
src/app/(super-admin)/layout.tsx
src/app/(super-admin)/admin/page.tsx
src/app/(super-admin)/admin/tenants/page.tsx
src/app/api/admin/tenants/route.ts
src/app/api/admin/tenants/[id]/route.ts
src/app/api/admin/tenants/[id]/suspend/route.ts
src/lib/middleware/super-admin.ts
prisma/seed.ts
src/lib/__tests__/super-admin.test.ts
src/lib/__tests__/tenant-isolation.test.ts
true
truths artifacts key_links
Super-admin can log in and view all tenants without being scoped to any single tenant
Super-admin can see tenant status, creation date, and subscriber count for each tenant
Super-admin can suspend a tenant with a grace period
Super-admin cannot access tenant-scoped data (no impersonation)
Non-super-admin users cannot access the admin panel
Test harness validates core isolation and auth behaviors
path provides min_lines
src/app/(super-admin)/admin/tenants/page.tsx Tenant management list UI 40
path provides exports
src/app/api/admin/tenants/route.ts List all tenants API
GET
path provides exports
src/app/api/admin/tenants/[id]/suspend/route.ts Tenant suspension API
POST
path provides exports
src/lib/middleware/super-admin.ts Super-admin route guard
withSuperAdmin
path provides min_lines
src/lib/__tests__/super-admin.test.ts Super-admin access and tenant management tests 40
from to via pattern
src/app/api/admin/tenants/route.ts src/lib/middleware/super-admin.ts withSuperAdmin guard on route withSuperAdmin
from to via pattern
src/app/(super-admin)/admin/tenants/page.tsx src/app/api/admin/tenants/route.ts fetch tenant list fetch.*api/admin/tenants
from to via pattern
src/lib/middleware/super-admin.ts src/lib/auth.ts Checks isSuperAdmin from session isSuperAdmin
Implement the super-admin panel: a separate auth context for platform-level management, tenant listing with status/metrics, tenant suspension with grace period, and the consolidated test harness proving all Phase 1 behaviors work together.

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 context
Create `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>
After completion, create `.planning/phases/01-foundation/01-05-SUMMARY.md`