MeshiTrack/docs/phases/phase-0-foundation.md

223 lines
7 KiB
Markdown
Raw Permalink Normal View History

2026-03-27 14:50:34 +09:00
# Phase 0 — Foundation & Infrastructure
**Goal**: Repo scaffolding, Docker environment, and authentication. After this phase, a developer can clone the repo, run `docker compose up`, and see a working authenticated "Hello World" page.
---
## Deliverables
1. Monorepo initialized with Turborepo
2. NestJS API with health endpoint
3. Next.js web app with landing page
4. Shared types package building and importable
5. Docker Compose with all services running
6. Keycloak configured with realm, roles, and test users
7. Auth guard protecting API routes
8. User and Household schemas with basic CRUD
9. Dev seed script
10. Linting and formatting
---
## Tasks
### 0.1 — Monorepo Setup
- Initialize root `package.json` with workspaces: `packages/*`
- Add Turborepo config (`turbo.json`) with pipelines: `build`, `dev`, `lint`, `test`
- Create three packages:
- `packages/shared` — TypeScript library, compiled with `tsc`
- `packages/api` — NestJS app (`@nestjs/cli` scaffold)
- `packages/web` — Next.js app (`create-next-app` with App Router, TypeScript, Tailwind CSS)
- Root `tsconfig.base.json` with strict settings, extended by each package
- Configure package references so `api` and `web` depend on `shared`
### 0.2 — Shared Types (Initial)
Define in `packages/shared/src/`:
```typescript
// enums/roles.ts
export enum HouseholdRole {
OWNER = 'owner',
ADMIN = 'admin',
MEMBER = 'member',
}
// types/user.ts
export interface User {
id: string;
keycloakId: string;
displayName: string;
email: string;
householdIds: string[];
defaultHouseholdId: string;
createdAt: Date;
updatedAt: Date;
}
// types/household.ts
export interface Household {
id: string;
name: string;
ownerUserId: string;
members: HouseholdMember[];
inviteCode: string;
settings: HouseholdSettings;
createdAt: Date;
updatedAt: Date;
}
export interface HouseholdMember {
userId: string;
role: HouseholdRole;
joinedAt: Date;
}
export interface HouseholdSettings {
timezone: string;
currency: string;
language: string;
}
```
- Add Zod validation schemas for create/update DTOs
- Export everything from `index.ts`
### 0.3 — Docker Compose
Create `docker/docker-compose.yml`:
```yaml
services:
mongodb:
image: mongo:7
ports: ['27017:27017']
volumes: [mongo-data:/data/db]
environment:
MONGO_INITDB_ROOT_USERNAME: meshitrack
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}
keycloak:
image: quay.io/keycloak/keycloak:24.0
ports: ['8080:8080']
environment:
KC_DB: dev-mem # Dev mode, in-memory DB
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
command: start-dev --import-realm
volumes:
- ./keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json
api:
build:
context: ..
dockerfile: docker/Dockerfile.api
ports: ['3001:3001']
depends_on: [mongodb, keycloak]
environment:
MONGODB_URI: mongodb://meshitrack:${MONGO_PASSWORD}@mongodb:27017/meshitrack?authSource=admin
KEYCLOAK_URL: http://keycloak:8080
KEYCLOAK_REALM: meshitrack
KEYCLOAK_CLIENT_ID: meshitrack-api
web:
build:
context: ..
dockerfile: docker/Dockerfile.web
ports: ['3000:3000']
depends_on: [api]
environment:
NEXT_PUBLIC_API_URL: http://localhost:3001/api/v1
NEXT_PUBLIC_KEYCLOAK_URL: http://localhost:8080
NEXT_PUBLIC_KEYCLOAK_REALM: meshitrack
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID: meshitrack-web
mongo-express:
image: mongo-express
ports: ['8081:8081']
depends_on: [mongodb]
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: meshitrack
ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_PASSWORD}
ME_CONFIG_MONGODB_URL: mongodb://meshitrack:${MONGO_PASSWORD}@mongodb:27017/
profiles: [dev]
volumes:
mongo-data:
```
- Create `.env.example` with all required variables
- Create `Dockerfile.api` and `Dockerfile.web` (multi-stage builds)
### 0.4 — Keycloak Configuration
- Export a Keycloak realm JSON (`docker/keycloak/realm-export.json`) with:
- Realm: `meshitrack`
- Client: `meshitrack-web` (public, PKCE) for frontend
- Client: `meshitrack-api` (bearer-only) for backend validation
- Realm roles: `admin`, `member`
- Custom protocol mapper: `household-mapper` that maps user attribute `householdIds` to JWT claim
- Test users: `testuser1` / `testuser2` with passwords
### 0.5 — NestJS API Bootstrap
- `packages/api/src/main.ts`: bootstrap NestJS with:
- Global prefix `/api/v1`
- CORS configured for `http://localhost:3000`
- Swagger/OpenAPI docs at `/api/docs`
- Validation pipe (class-validator + class-transformer)
- Modules:
- `AuthModule`: Keycloak strategy (`passport-openidconnect` or `nest-keycloak-connect`), `@AuthGuard` decorator, extract user + householdId from JWT
- `UsersModule`: `User` Mongoose schema, sync user on first login (upsert from Keycloak token)
- `HouseholdsModule`: `Household` Mongoose schema, CRUD endpoints
- `POST /households` — create (creator becomes owner)
- `GET /households/:householdId` — get (members only)
- `POST /households/:householdId/invite` — generate invite code
- `POST /households/join` — join via invite code
- `PATCH /households/:householdId` — update settings (admin/owner)
- `HealthModule`: `GET /api/v1/health` — returns `{ status: 'ok', version, uptime }`
- Common:
- `HouseholdPlugin`: Fastify preHandler hook that reads `:householdId` from the URI, validates it against the user's `householdIds[]` JWT claim, and injects it into `request.householdId`
- `CurrentUser` param decorator
- `CurrentHousehold` param decorator
- Global exception filter with consistent error response shape
### 0.6 — Next.js Web Bootstrap
- Configure `next-auth` or `keycloak-js` for OIDC login flow
- Pages:
- `/login` — redirects to Keycloak
- `/` — dashboard (protected, shows "Welcome, {name}" + household selector)
- `/settings` — household management (create, invite, switch)
- Layout: navigation sidebar (placeholder links for future phases), top bar with user avatar + household switcher
- API client service (`packages/web/src/services/api-client.ts`): fetch wrapper that attaches the JWT `Authorization` header; household context is passed via URL (e.g. `/api/v1/households/:householdId/products`)
### 0.7 — Dev Seed Script
- `packages/api/src/scripts/seed.ts`:
- Create 2 test users (matching Keycloak test users)
- Create 1 household with both users as members
- Log credentials and household ID to console
---
## Acceptance Criteria
- [ ] `docker compose up` starts all services without errors
- [ ] Navigating to `http://localhost:3000` redirects to Keycloak login
- [ ] After login, dashboard shows user name and household
- [ ] `GET /api/v1/health` returns 200
- [ ] `GET /api/v1/households/:id` returns 401 without token, 200 with valid token
- [ ] `packages/shared` types are importable from both `api` and `web`
---
## Dependencies
None — this is the foundation phase.
## Estimated Effort
Medium-large. Mostly boilerplate and configuration, but Keycloak setup requires careful attention.