- 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>
182 lines
7.7 KiB
Markdown
182 lines
7.7 KiB
Markdown
# 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/
|