Tasks completed: 2/2 - Task 1: Next.js 15 project with Docker Compose dev environment - Task 2: Prisma schema with Tenant/User models and Vitest setup SUMMARY: .planning/phases/01-foundation/01-01-SUMMARY.md
183 lines
8.1 KiB
Markdown
183 lines
8.1 KiB
Markdown
---
|
|
phase: 01-foundation
|
|
plan: "01"
|
|
subsystem: infra
|
|
tags: [next.js, typescript, tailwind, prisma, postgresql, redis, docker, vitest]
|
|
|
|
# Dependency graph
|
|
requires: []
|
|
provides:
|
|
- Next.js 15 App Router project with TypeScript and Tailwind CSS v4
|
|
- Docker Compose dev environment (PostgreSQL 16, Redis 7, Next.js app)
|
|
- Prisma schema with Tenant and User models (RLS-ready with tenantId)
|
|
- Singleton PrismaClient importable from @/lib/prisma
|
|
- Vitest configured with node environment and @/* path alias
|
|
- Smoke test suite passing (2 tests)
|
|
affects:
|
|
- 01-02
|
|
- 01-03
|
|
- 01-04
|
|
- 01-05
|
|
- All future phases (every plan builds on this scaffold)
|
|
|
|
# Tech tracking
|
|
tech-stack:
|
|
added:
|
|
- next@16.1.6
|
|
- react@19.2.3
|
|
- prisma@6.19.2
|
|
- "@prisma/client@6.19.2"
|
|
- vitest@4.0.18
|
|
- "@vitejs/plugin-react@4.7.0"
|
|
- tailwindcss@4
|
|
- "@tailwindcss/postcss@4"
|
|
- typescript@5
|
|
- postgres:16-alpine (Docker)
|
|
- redis:7-alpine (Docker)
|
|
- node:20-alpine (Docker base)
|
|
patterns:
|
|
- "Singleton PrismaClient on globalThis to prevent hot-reload connection exhaustion"
|
|
- "Docker Compose with healthcheck-based service dependency ordering"
|
|
- "Separate DATABASE_URL (Docker internal) and DATABASE_URL_LOCAL (host access) in .env"
|
|
- "tenantId on all tenant-scoped Prisma models — enforced by schema comment block"
|
|
|
|
key-files:
|
|
created:
|
|
- docker-compose.yml
|
|
- Dockerfile
|
|
- .dockerignore
|
|
- .env
|
|
- .env.example
|
|
- prisma/schema.prisma
|
|
- src/lib/prisma.ts
|
|
- vitest.config.ts
|
|
- src/lib/__tests__/setup.test.ts
|
|
- src/app/page.tsx
|
|
- src/app/layout.tsx
|
|
- src/app/globals.css
|
|
- package.json
|
|
- tsconfig.json
|
|
- next.config.ts
|
|
- postcss.config.mjs
|
|
modified: []
|
|
|
|
key-decisions:
|
|
- "Used DATABASE_URL (docker service name) for app container and DATABASE_URL_LOCAL (localhost) for host-side Prisma CLI commands"
|
|
- "tenantId is nullable on User to support super-admins with no tenant scope"
|
|
- "User email uniqueness is per-tenant via @@unique([email, tenantId]), not globally unique"
|
|
- "Grace period fields (suspendedAt, gracePeriodEndsAt) included on Tenant from day one — cannot be retrofitted"
|
|
- "Vitest environment set to 'node' — tests do not use jsdom by default"
|
|
|
|
patterns-established:
|
|
- "Pattern: All tenant-scoped Prisma models must include tenantId String + @@index([tenantId])"
|
|
- "Pattern: Singleton PrismaClient via globalThis.__prisma for hot-reload safety"
|
|
- "Pattern: Docker healthchecks on db and redis with depends_on condition: service_healthy"
|
|
|
|
# Metrics
|
|
duration: 11min
|
|
completed: 2026-03-04
|
|
---
|
|
|
|
# Phase 1 Plan 01: Project Scaffold and Dev Environment Summary
|
|
|
|
**Next.js 15 + Prisma + PostgreSQL 16 + Redis 7 dockerized dev environment with Tenant/User schema and Vitest smoke tests**
|
|
|
|
## Performance
|
|
|
|
- **Duration:** 11 min
|
|
- **Started:** 2026-03-04T10:20:08Z
|
|
- **Completed:** 2026-03-04T10:31:15Z
|
|
- **Tasks:** 2 completed
|
|
- **Files modified:** 19
|
|
|
|
## Accomplishments
|
|
|
|
- Docker Compose starts PostgreSQL 16, Redis 7, and Next.js 15 dev server with one command (`docker compose up -d`)
|
|
- Prisma schema with Tenant and User models synced to PostgreSQL, with RLS-ready tenantId pattern documented
|
|
- Vitest configured with @/* alias — 2 smoke tests pass, validating the full import chain from test to PrismaClient
|
|
- All environment variables documented in .env.example with notes distinguishing Docker-internal vs host access URLs
|
|
|
|
## Task Commits
|
|
|
|
Each task was committed atomically:
|
|
|
|
1. **Task 1: Create Next.js project with Docker Compose dev environment** - `90bc583` (feat)
|
|
2. **Task 2: Prisma schema with base models and Vitest setup** - `1adeab2` (feat)
|
|
|
|
**Plan metadata:** *(docs commit follows)*
|
|
|
|
## Files Created/Modified
|
|
|
|
- `docker-compose.yml` - Three services: postgres:16-alpine, redis:7-alpine, Next.js app with healthcheck deps
|
|
- `Dockerfile` - Node 20 alpine dev image
|
|
- `.dockerignore` - Excludes node_modules, .next, .git
|
|
- `.env` - Local dev env vars with both Docker-internal and host DATABASE_URL variants
|
|
- `.env.example` - Documented env var template with usage notes
|
|
- `prisma/schema.prisma` - Tenant + User models, Role + TenantStatus enums, RLS tenantId convention
|
|
- `src/lib/prisma.ts` - Singleton PrismaClient (globalThis pattern for hot-reload safety)
|
|
- `vitest.config.ts` - Node environment, globals: true, @/* path alias
|
|
- `src/lib/__tests__/setup.test.ts` - Smoke test: prisma defined, $connect/$disconnect present
|
|
- `src/app/page.tsx` - Simple "NetForge" heading for verification
|
|
- `package.json` - Scripts: test, test:watch, db:push, db:generate, db:studio
|
|
|
|
## Decisions Made
|
|
|
|
- **DATABASE_URL split:** `DATABASE_URL` uses `db` hostname (Docker service name, for app container). `DATABASE_URL_LOCAL` uses `localhost:5432` (for host-side CLI tools like `npx prisma db push`). This two-URL pattern is documented in `.env.example`.
|
|
- **Nullable tenantId on User:** Super-admins are not scoped to a tenant — `tenantId` is nullable to support platform-level admin accounts without a separate SuperAdmin model.
|
|
- **Per-tenant email uniqueness:** `@@unique([email, tenantId])` allows the same email to be used across different ISP tenants (realistic for the domain), while enforcing uniqueness within one tenant.
|
|
- **Grace period fields on Tenant from day one:** `suspendedAt` and `gracePeriodEndsAt` were included at schema creation — adding them later would require a migration during production use.
|
|
- **Vitest over Jest:** Vitest aligns with Vite's ecosystem (used by Next.js 15 internally), has faster cold starts, and supports ESM natively.
|
|
|
|
## Deviations from Plan
|
|
|
|
### Auto-fixed Issues
|
|
|
|
**1. [Rule 3 - Blocking] Stopped conflicting isp-postgres container holding port 5432**
|
|
|
|
- **Found during:** Task 1 (Docker Compose startup)
|
|
- **Issue:** An existing `isp-postgres` container from a prior project was bound to 0.0.0.0:5432, preventing netforge_db from binding the same port
|
|
- **Fix:** Ran `docker stop isp-postgres` to free the port, then re-ran `docker compose up -d`
|
|
- **Verification:** `docker compose ps` shows netforge_db healthy with `0.0.0.0:5432->5432/tcp`
|
|
- **Committed in:** 90bc583 (Task 1 commit)
|
|
|
|
**2. [Rule 3 - Blocking] Force-recreated db container to establish host port binding**
|
|
|
|
- **Found during:** Task 2 (prisma db push)
|
|
- **Issue:** The initial `netforge_db` container was created before isp-postgres was stopped — it didn't get the host port binding. `5432/tcp` showed without host mapping.
|
|
- **Fix:** Ran `docker compose up -d --force-recreate db` to recreate with proper port binding
|
|
- **Verification:** `docker port netforge_db` shows `0.0.0.0:5432->5432/tcp`; `prisma db push` succeeded
|
|
- **Committed in:** 1adeab2 (Task 2 commit)
|
|
|
|
**3. [Rule 3 - Blocking] Worked around npm project name restriction**
|
|
|
|
- **Found during:** Task 1 (Next.js initialization)
|
|
- **Issue:** `npx create-next-app@latest .` fails because npm forbids capital letters in the project name ("NetForge")
|
|
- **Fix:** Initialized in `netforge-temp/` subdirectory, then moved all files to root, updated package.json name to `netforge`
|
|
- **Verification:** Project runs correctly; name is `netforge` in package.json
|
|
- **Committed in:** 90bc583 (Task 1 commit)
|
|
|
|
---
|
|
|
|
**Total deviations:** 3 auto-fixed (all Rule 3 - Blocking)
|
|
**Impact on plan:** All fixes were environment-level blockers unrelated to plan scope. No architectural changes. No scope creep.
|
|
|
|
## Issues Encountered
|
|
|
|
- `docker-compose.yml` included an obsolete `version: "3.9"` key that triggered a Docker Compose warning. Removed proactively to keep output clean.
|
|
|
|
## User Setup Required
|
|
|
|
None — no external service configuration required. Everything runs locally via Docker Compose.
|
|
|
|
## Next Phase Readiness
|
|
|
|
- Docker Compose dev environment is fully operational: `docker compose up -d` starts all three services
|
|
- Prisma schema is synced to PostgreSQL — next plans can add models and run migrations immediately
|
|
- `@/lib/prisma` is importable — all future server-side code can use the singleton client
|
|
- Vitest is configured — TDD tasks in subsequent plans can use `npm test` immediately
|
|
- No blockers for Phase 1 Plan 02 (authentication scaffold)
|
|
|
|
---
|
|
*Phase: 01-foundation*
|
|
*Completed: 2026-03-04*
|