Production deployment setup + feature complete
- Dockerfile + deploy.sh for Hetzner server - Email verification via Better Auth + Resend - Invite code flow (6-digit OTP, generate/join) - Settlement share percent fix (payer vs debtor) - OCR scanner fixes (date display, retry, viewfinder) - app.json icon/splash/adaptive-icon configured - iOS deployment target 15.5 (ML Kit requirement) - DB migration 0014: household_invitations table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
181
CLAUDE.md
Normal file
181
CLAUDE.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# HaushaltsApp — Claude Instructions
|
||||
|
||||
## Projektübersicht
|
||||
|
||||
Mobile-SaaS App für Haushaltsverwaltung: Haushaltsbuch, Urlaubsbudget, Echtzeit-Einkaufsliste.
|
||||
|
||||
**Stack:**
|
||||
- **Monorepo:** Bun Workspaces + Turborepo
|
||||
- **Backend:** Hono (Bun) — `apps/server/`
|
||||
- **Mobile:** React Native + Expo + expo-router — `apps/native/`
|
||||
- **Web (Dashboard):** React + TanStack Router + Vite — `apps/web/`
|
||||
- **Database:** PostgreSQL + Drizzle ORM — `packages/db/`
|
||||
- **Auth:** Better Auth — `packages/auth/`
|
||||
- **Shared Types/Schemas:** Zod — `packages/shared/`
|
||||
- **UI (Web):** shadcn/ui (base-lyra) — `packages/ui/`
|
||||
- **UI (Native):** HeroUI Native + Uniwind
|
||||
|
||||
## Architektur-Entscheidungen
|
||||
|
||||
- **Multi-Tenant via `householdId`:** Jeder Haushalt ist ein Tenant. Alle Daten (Transaktionen, Einkaufslisten etc.) sind an eine `householdId` gebunden. `householdId` wird via `x-household-id` HTTP-Header übergeben.
|
||||
- **Subscription Plans:** `free`, `pro`, `family` — Feature-Gates in `plan.middleware.ts`, Definitionen in `packages/shared/src/constants/plans.ts`.
|
||||
- **Better Auth** übernimmt User/Session-Management. Die `packages/auth/` enthält die Server-seitige Konfiguration.
|
||||
- **Shared Package (`@haushaltsApp/shared`):** Zod-Schemas und TypeScript-Types werden im shared package definiert und sowohl vom Backend als auch vom Frontend verwendet.
|
||||
- **Drizzle Schema:** Alle App-Tabellen in `packages/db/src/schema/app.ts`, Auth-Tabellen in `packages/db/src/schema/auth.ts`.
|
||||
|
||||
## Development starten
|
||||
|
||||
```bash
|
||||
# Alle Services starten
|
||||
bun run dev
|
||||
|
||||
# Einzelne Services
|
||||
bun run dev:server # API auf http://localhost:3000
|
||||
bun run dev:web # Web auf http://localhost:3001
|
||||
bun run dev:native # Expo (Metro Bundler)
|
||||
|
||||
# Datenbank
|
||||
bun run db:start # PostgreSQL via Docker starten
|
||||
bun run db:generate # Drizzle Migrationen generieren
|
||||
bun run db:migrate # Migrationen anwenden
|
||||
bun run db:studio # Drizzle Studio öffnen
|
||||
```
|
||||
|
||||
## Testing-Konventionen
|
||||
|
||||
### Backend (`apps/server/`)
|
||||
- **Framework:** `bun:test` (built-in, kein extra Package)
|
||||
- **Ort:** `apps/server/src/__tests__/routes/` und `apps/server/src/__tests__/services/`
|
||||
- **Konvention:** Route-Tests testen den HTTP-Layer direkt via `app.request()`. Services werden unit-getestet.
|
||||
- **Ausführen:** `bun run test:api` oder `bun test apps/server/src/__tests__`
|
||||
|
||||
```typescript
|
||||
// Beispiel Route-Test
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import app from "../../index";
|
||||
|
||||
describe("GET /health", () => {
|
||||
it("returns 200 with status ok", async () => {
|
||||
const res = await app.request("/health");
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Mobile (`apps/native/`)
|
||||
- **Framework:** `bun:test` — kein Jest/Babel/jest-expo (Jest + Bun's .bun/ Store sind inkompatibel mit RN's ESM setup files)
|
||||
- **Was wird getestet:** Stores, Hooks, Utils, API-Client — reine Business Logic, kein React Native Rendering
|
||||
- **Was NICHT getestet wird:** UI-Komponenten, Screens — Rendering wird manuell via Expo Go verifiziert
|
||||
- **Ort:** `apps/native/src/__tests__/` spiegelt `apps/native/src/` Struktur
|
||||
- **Ausführen:** `bun run test:mobile` oder `bun test apps/native/src/__tests__`
|
||||
|
||||
```typescript
|
||||
// Beispiel Store-Test
|
||||
import { describe, expect, it, beforeEach } from "bun:test";
|
||||
import { useAuthStore } from "../../stores/auth.store";
|
||||
|
||||
describe("authStore", () => {
|
||||
beforeEach(() => {
|
||||
useAuthStore.setState({ user: null, token: null, isAuthenticated: false });
|
||||
});
|
||||
|
||||
it("setUser authenticates the user", () => {
|
||||
useAuthStore.getState().setUser({ id: "1", name: "Test", email: "t@t.com" });
|
||||
expect(useAuthStore.getState().isAuthenticated).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Coding-Konventionen
|
||||
|
||||
### Allgemein
|
||||
- **TypeScript strict mode** überall — kein `any`, kein type-casting ohne Kommentar
|
||||
- **Zod-first:** Alle API-Inputs werden mit Zod-Schemas aus `@haushaltsApp/shared` validiert
|
||||
- **Named exports** bevorzugen (default exports nur bei Expo/React Router Screens)
|
||||
|
||||
### Naming
|
||||
- **Files:** `kebab-case.ts` / `PascalCase.tsx` für React-Komponenten
|
||||
- **Variables/Functions:** `camelCase`
|
||||
- **Types/Interfaces:** `PascalCase`
|
||||
- **Database tables:** `snake_case` (Drizzle convention)
|
||||
- **Zod schemas:** `camelCaseSchema` (z.B. `createTransactionSchema`)
|
||||
- **Route files:** `feature.routes.ts`
|
||||
- **Middleware files:** `feature.middleware.ts`
|
||||
- **Service files:** `feature.service.ts`
|
||||
|
||||
### API-Design
|
||||
- Alle Endpoints unter `/api/` prefix
|
||||
- Auth-Check via `authMiddleware` + `requireAuth`
|
||||
- Tenant-Check via `tenantMiddleware` + `requireHousehold`
|
||||
- Plan-Gates via `requireFeature('featureName')`
|
||||
- HTTP-Status-Codes: 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found
|
||||
|
||||
### Ordnerstruktur (Backend)
|
||||
```
|
||||
apps/server/src/
|
||||
├── routes/ # HTTP-Layer, minimale Logik
|
||||
├── services/ # Business Logic, kein direkter DB-Code in routes
|
||||
├── middleware/ # Auth, Tenant, Plan Feature Gates
|
||||
├── websocket/ # WebSocket Handler
|
||||
├── lib/ # DB-Instance, Utilities
|
||||
└── __tests__/ # Tests spiegeln src/-Struktur
|
||||
```
|
||||
|
||||
### State Management (Mobile)
|
||||
- **Zustand** für globalen Client-State (User, aktueller Haushalt)
|
||||
- **TanStack Query** für Server-State (Daten aus der API)
|
||||
- Kein direktes Fetch in Komponenten — immer Custom Hooks oder TanStack Query
|
||||
|
||||
## Known Constraints
|
||||
|
||||
### React Native Testing
|
||||
Jest + Bun's Content-Addressable Store (`.bun/`) sind strukturell inkompatibel für RN Rendering-Tests. `jest-expo` setzt `setupFiles` mit absoluten `.bun/` Pfaden — kein Resolver oder `transformIgnorePatterns` kann das abfangen.
|
||||
|
||||
**Entscheidung:** `bun:test` für Business Logic (Stores, Hooks, Utils, Services). Rendering wird via Expo Go verifiziert. Rendering-Tests werden nachgereicht wenn Bun/jest-expo das nativ lösen.
|
||||
|
||||
### Web App (`apps/web`)
|
||||
TanStack Router generiert `routeTree.gen.ts` erst bei `vite dev`. `check-types` für web daher nur nach einmaligem dev-Run aussagekräftig.
|
||||
|
||||
### Better Auth Mobile Setup
|
||||
- `bearer` Plugin ist Pflicht in `packages/auth/src/index.ts`
|
||||
- Mobile Clients nutzen `Authorization: Bearer <token>` Header
|
||||
- Ohne `bearer` Plugin: `getSession()` gibt `null` zurück für alle Mobile Requests — stiller Auth-Fehler, alle Requests landen als 401
|
||||
|
||||
### Expo Router — Redirect Pattern (FINAL)
|
||||
**FALSCH:** Session-Guards in mehreren Layouts gleichzeitig → Ping-Pong Loop zwischen `(auth)` und `(app)` Layout
|
||||
|
||||
**RICHTIG:**
|
||||
- `index.tsx`: statischer `<Redirect href="/(auth)/login" />` — kein useSession, kein useEffect
|
||||
- `(auth)/_layout.tsx`: **KEIN Guard**, nur Stack-Definition
|
||||
- `(app)/_layout.tsx`: **EINZIGER Guard**
|
||||
- kein session → `/(auth)/login`
|
||||
- kein householdId → `/(auth)/onboarding`
|
||||
- sonst: render children
|
||||
|
||||
Ein Guard, eine Quelle der Wahrheit.
|
||||
|
||||
### Household / Organization Bridge
|
||||
- `households.id === Better Auth organization.id` (gleiche UUID)
|
||||
- Reihenfolge beim Onboarding:
|
||||
1. `organization.create()` → `organizationId`
|
||||
2. `INSERT INTO households { id: organizationId, ... }` (via `/api/households/setup`)
|
||||
3. `seedDefaultCategories(organizationId)`
|
||||
- Kein harter DB-FK von `households` zu `organization` — application-level check reicht, Better Auth Schema ist extern
|
||||
|
||||
## Projektstand
|
||||
|
||||
| Phase | Status | Details |
|
||||
|-------|--------|---------|
|
||||
| Phase 1 — Foundation | ✅ | Monorepo, DB Schema, Shared Types, Stubs |
|
||||
| Phase 2 — Auth Flow | ✅ | Better Auth, Organization Plugin, Apple, Auth Screens |
|
||||
| Phase 3 — Transactions Full Stack | ✅ | 9 API Tests, 7 Mobile Tests, Tenant Isolation bestätigt |
|
||||
| Phase 4 — Dashboard | ⬜ | |
|
||||
| Phase 5 — Urlaubsbudget | ⬜ | |
|
||||
| Phase 6 — OCR Scanner | ⬜ | |
|
||||
| Phase 7 — Einkaufsliste (WebSockets) | ⬜ | |
|
||||
|
||||
## Skills
|
||||
|
||||
Projekt-spezifische Skills unter `apps/api/.agents/skills/` (noch leer, wird befüllt).
|
||||
|
||||
@apps/api/.agents/skills/
|
||||
Reference in New Issue
Block a user