Implements Tasks 23 & 24: Backend and Frontend Dockerfiles
Backend Dockerfiles:
- Dockerfile.dev: Development with dotnet watch hot reload
- Base: sdk:10.0, installs dotnet-ef tool
- Layer caching: csproj files copied before source
- Entry: dotnet watch run with --no-restore
- Dockerfile: Production multi-stage build
- Build stage: sdk:10.0, restore + build + publish
- Runtime stage: aspnet:10.0-alpine (~110MB)
- Health check: /health/live endpoint
- Non-root: USER app (built-in)
Frontend Dockerfiles:
- Dockerfile.dev: Development with Bun hot reload
- Base: node:22-alpine, installs Bun globally
- Layer caching: package.json + bun.lock before source
- Command: bun run dev
- Dockerfile: Production standalone 3-stage build
- Deps stage: Install with --frozen-lockfile
- Build stage: bun run build → standalone output
- Runner stage: node:22-alpine with non-root nextjs user
- Copies: .next/standalone, .next/static, public
- Health check: Node.js HTTP GET to port 3000
- Entry: node server.js (~240MB)
All Dockerfiles use layer caching optimization and security best practices.
Note: Docker build verification skipped (Docker daemon not running).
Implement Task 16: Club + Member API endpoints with MemberSyncService
Services:
- ClubService: GetMyClubsAsync (user's clubs), GetCurrentClubAsync (tenant club)
- MemberService: GetMembersAsync (list), GetMemberByIdAsync, GetCurrentMemberAsync
- MemberSyncService: Auto-creates Member records from JWT on first request
Middleware:
- MemberSyncMiddleware: Runs after auth, calls MemberSyncService
Endpoints:
- GET /api/clubs/me (list user's clubs)
- GET /api/clubs/current (current tenant's club)
- GET /api/members (list members, RLS filtered)
- GET /api/members/{id} (member detail)
- GET /api/members/me (current user's membership)
Tests: 14 integration tests (6 club + 8 member)
- Club filtering by user membership
- Multi-tenant isolation via RLS
- Member auto-sync on first request
- Cross-tenant access blocked
- Role-based authorization
Build: 0 errors, all tests compile
Pattern: TypedResults, RequireAuthorization policies, TDD approach
- Use consolidated Finbuckle.MultiTenant namespace instead of separate imports
- Switch TenantProvider to use untyped IMultiTenantContextAccessor (Finbuckle 9.x pattern)
- Register TenantDbConnectionInterceptor and SaveChangesTenantInterceptor as singletons
- Add interceptors to DbContext configuration for RLS tenant context support
- Update evidence files for Task 7 and Task 8 verification
- Create SeedDataService in Infrastructure/Seed with idempotent seeding
- Seed 2 clubs: Sunrise Tennis Club, Valley Cycling Club
- Seed 7 member records (5 unique Keycloak test users)
- Seed 8 work items covering all status states
- Seed 5 shifts with date variety (past, today, future)
- Seed shift signups for realistic partial capacity
- Register SeedDataService in Program.cs with development-only guard
- Use deterministic GUID generation from club names
- Ensure all tenant IDs match for RLS compliance
- Track in learnings.md and evidence files for Task 22 QA