Commit Graph

4 Commits

Author SHA1 Message Date
kevin-asprec
69eac9ffaa feat(01-03): Prisma tenant middleware, PostgreSQL RLS, and isolation tests
- Create src/lib/prisma-tenant.ts:
  - withTenantContext(tenantId) / createTenantPrisma — Prisma $extends client
  - Intercepts findMany, findFirst, findUnique, create, createMany, update,
    updateMany, delete, deleteMany, upsert, count, aggregate, groupBy on User
  - Auto-injects tenantId filter on all reads, writes, and deletes
  - setTenantRLS() helper for explicit RLS enforcement in transactions
  - TENANT_SCOPED_MODELS constant for future extensibility
- Create prisma/migrations/20260304104214_initial_schema — baseline migration
  capturing schema created by initial db push
- Create prisma/migrations/20260304104245_add_rls_policies:
  - ALTER TABLE User ENABLE ROW LEVEL SECURITY
  - CREATE POLICY tenant_isolation_user USING app.current_tenant_id session var
  - Defense-in-depth architecture comments explaining primary vs secondary enforcement
- Create src/lib/__tests__/tenant-isolation.test.ts (6 tests, all passing):
  - Test 1: Tenant A context returns only Tenant A's users (zero from B)
  - Test 2: Tenant B context returns only Tenant B's users (zero from A)
  - Test 3: create() auto-sets tenantId, invisible to other tenant
  - Test 4: findUnique by Tenant B's ID under Tenant A context returns null
  - Additional: findFirst cross-tenant blocked, count() is tenant-scoped
2026-03-04 18:45:13 +08:00
kevin-asprec
3c37cb1866 feat(01-02): login page UI, logout flow, seed script, and auth unit tests
- src/app/(auth)/layout.tsx: centered auth layout for login page
- src/app/(auth)/login/page.tsx: login form with error/loading states, sign up link
- src/components/providers.tsx: SessionProvider wrapper for client-side session
- src/components/layout/header.tsx: authenticated header with Sign out button
- src/app/(dashboard)/layout.tsx: dashboard layout wrapping Header component
- src/app/(dashboard)/dashboard/page.tsx: basic dashboard page post-login
- src/app/layout.tsx: wrap root with SessionProvider via Providers component
- prisma/seed.ts: idempotent seed for Demo ISP tenant + admin + super-admin users
- package.json: add db:seed script and prisma.seed config, add tsx devDep
- src/lib/__tests__/auth.test.ts: 8 unit tests for authOptions callbacks
2026-03-04 18:42:53 +08:00
kevin-asprec
43761d94ee feat(01-03): tenant signup API, service, and UI
- Add businessAddress and contactPhone fields to Tenant schema
- Create src/lib/tenant.ts with createTenant() function:
  - Validates input, slugifies business name, hashes password (bcrypt 12)
  - Prisma transaction creates Tenant + admin User atomically
  - Custom EmailAlreadyExistsError for 409 Conflict responses
- Create POST /api/tenants/signup route returning 201/400/409/500
- Create /signup page with full form (business name, owner info, password, optional fields)
  - Client-side validation: required fields, email format, password match
  - Redirects to /login?registered=true on success
- Update /login page to show success banner when ?registered=true
2026-03-04 18:40:40 +08:00
kevin-asprec
1adeab2fbc feat(01-01): Prisma schema with Tenant/User models and Vitest test setup
- prisma/schema.prisma with Tenant, User, Role, TenantStatus models
  - tenantId on all tenant-scoped models (RLS-ready convention)
  - @@unique([email, tenantId]) and @@index([tenantId]) on User
  - Grace period fields on Tenant (suspendedAt, gracePeriodEndsAt)
  - RLS comment block documenting tenantId convention for future models
- src/lib/prisma.ts singleton PrismaClient pattern (hot-reload safe)
- vitest.config.ts with node environment and @/* path alias
- src/lib/__tests__/setup.test.ts smoke test (2 tests passing)
- package.json scripts: test, test:watch, db:push, db:generate, db:studio
- Schema synced to PostgreSQL 16 via prisma db push
2026-03-04 18:31:04 +08:00