Setup initial project
This commit is contained in:
commit
db79af06f7
119 changed files with 20761 additions and 0 deletions
222
docs/phases/phase-0-foundation.md
Normal file
222
docs/phases/phase-0-foundation.md
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
# 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue