Files
gartenmanager/docs/project-structure.md
Faultier314 834a3bf4d5 feat: Phase 1 complete – full working application
Backend (FastAPI):
- REST API: auth, plants, beds, plantings
- CRUD layer with CRUDBase
- Pydantic v2 schemas for all entities
- Alembic migration: complete schema + all enums
- Seed data: 28 global plants + 15 compatibilities

Frontend (Vue 3 + PrimeVue):
- Axios client with JWT interceptor + auto-refresh
- Pinia stores: auth, beds, plants
- Views: Login, Beds, BedDetail, PlantLibrary
- Components: AppLayout, BedForm, PlantingForm, PlantForm

Docker:
- docker-compose.yml (production)
- docker-compose.dev.yml (development with hot-reload)
- Nginx config with SPA fallback + API proxy
- Multi-stage frontend Dockerfile
- .env.example, .gitignore

Version: 1.0.0-alpha
2026-04-06 07:45:00 +02:00

7.9 KiB
Raw Blame History

Projektstruktur & Modulreferenz

Token-Sparmaßnahme: Dieses Dokument ist die erste Anlaufstelle. Vor dem Öffnen von Quellcode hier nachschlagen. Bei jeder Änderung an Funktionen, Modulen oder der Verzeichnisstruktur sofort aktualisieren.


Verzeichnisstruktur

gartenmanager/
├── .claude/                        # Claude-Tooling (kein Projektcode)
│   ├── scripts/bump.sh             # Version bumpen + commit + push
│   ├── scripts/new-feature.sh      # Feature-Branch erstellen
│   └── session-context.md          # Sessionstart-Kontext
├── .gitea/PULL_REQUEST_TEMPLATE.md
├── backend/                        # FastAPI Python Backend
│   ├── app/
│   │   ├── main.py                 # FastAPI App, CORS, Router-Include, /health
│   │   ├── core/
│   │   │   ├── config.py           # pydantic-settings (DATABASE_URL, SECRET_KEY, ...)
│   │   │   ├── security.py         # JWT erstellen/prüfen, Passwort-Hashing
│   │   │   └── deps.py             # FastAPI-Dependencies: get_current_user, get_tenant_context, require_min_role()
│   │   ├── db/
│   │   │   ├── base.py             # DeclarativeBase + alle Model-Imports für Alembic
│   │   │   └── session.py          # async Engine, AsyncSessionLocal, get_session()
│   │   ├── models/                 # SQLAlchemy ORM (alle UUID-PKs, async)
│   │   │   ├── user.py             # User, UserTenant (+ TenantRole Enum)
│   │   │   ├── tenant.py           # Tenant
│   │   │   ├── plant.py            # PlantFamily, Plant, PlantCompatibility
│   │   │   ├── bed.py              # Bed (+ LocationType, SoilType Enums)
│   │   │   └── planting.py         # BedPlanting
│   │   ├── schemas/                # Pydantic v2 (Create/Update/Read)
│   │   │   ├── auth.py             # LoginRequest, RefreshRequest, TokenResponse, AccessTokenResponse
│   │   │   ├── user.py             # UserCreate, UserUpdate, UserRead
│   │   │   ├── tenant.py           # TenantCreate, TenantUpdate, TenantRead
│   │   │   ├── plant.py            # PlantCreate/Update/Read, PlantFamilyRead, PlantCompatibilityRead
│   │   │   ├── bed.py              # BedCreate/Update/Read/DetailRead, PlantingInBed
│   │   │   └── planting.py         # PlantingCreate/Update/Read
│   │   ├── crud/                   # DB-Zugriff, keine Business-Logik
│   │   │   ├── base.py             # CRUDBase[Model, Create, Update]: get, get_multi, create, update, remove
│   │   │   ├── user.py             # get_by_email, authenticate, get_tenants
│   │   │   ├── plant.py            # get_multi_for_tenant (global+tenant), create_for_tenant
│   │   │   ├── bed.py              # get_multi_for_tenant, get_with_plantings, create_for_tenant
│   │   │   └── planting.py         # get_multi_for_bed, create_for_bed
│   │   ├── api/v1/
│   │   │   ├── router.py           # Alle Sub-Router unter /api/v1
│   │   │   ├── auth.py             # POST /login, POST /refresh, GET /me
│   │   │   ├── plants.py           # GET/POST/PUT/DELETE /plants, GET /plant-families
│   │   │   ├── beds.py             # GET/POST/PUT/DELETE /beds
│   │   │   └── plantings.py        # GET/POST /beds/{id}/plantings, PUT/DELETE /plantings/{id}
│   │   └── seeds/
│   │       └── initial_data.py     # 28 globale Pflanzen + 15 Kompatibilitäten (idempotent)
│   ├── alembic/
│   │   ├── env.py                  # Async Alembic-Config, liest DATABASE_URL aus Settings
│   │   └── versions/001_initial.py # Vollständiges initiales Schema (alle Tabellen + Enums)
│   ├── requirements.txt
│   └── Dockerfile                  # python:3.11-slim, uvicorn
├── frontend/                       # Vue 3 SPA
│   ├── src/
│   │   ├── main.js                 # App-Bootstrap: PrimeVue, Pinia, Router
│   │   ├── App.vue                 # Root: AppLayout (eingeloggt) / router-view (Login)
│   │   ├── api/
│   │   │   ├── client.js           # Axios-Instanz, JWT-Interceptor, Auto-Refresh bei 401
│   │   │   └── index.js            # authApi, plantsApi, bedsApi, plantingsApi
│   │   ├── stores/
│   │   │   ├── auth.js             # user, tenants, activeTenantId, login(), logout(), setActiveTenant()
│   │   │   ├── beds.js             # beds, currentBed, fetchBeds/Bed, createBed/Planting, deleteBed/Planting
│   │   │   └── plants.js           # plants, families, fetchPlants/Families, create/update/deletePlant
│   │   ├── router/index.js         # /login, /beete, /beete/:id, /pflanzen  auth guard
│   │   ├── views/
│   │   │   ├── LoginView.vue       # Email+Passwort Formular
│   │   │   ├── BedsView.vue        # DataTable aller Beete, Create/Edit-Dialog
│   │   │   ├── BedDetailView.vue   # Beet-Infos + Bepflanzungs-Tabelle + Add-Dialog
│   │   │   └── PlantsView.vue      # Pflanzenbibliothek DataTable, Filter, eigene Pflanze anlegen
│   │   └── components/
│   │       ├── AppLayout.vue       # Navbar (Logo, Nav-Links, Tenant-Selector, Logout)
│   │       ├── BedForm.vue         # Formular für Beet anlegen/bearbeiten
│   │       ├── PlantingForm.vue    # Formular für Bepflanzung hinzufügen
│   │       └── PlantForm.vue       # Formular für eigene Pflanze anlegen
│   ├── nginx.conf                  # SPA fallback + API-Proxy → backend:8000
│   ├── Dockerfile                  # Multi-stage: node:20 build → nginx:alpine
│   └── package.json
├── docker-compose.yml              # Produktion: db + backend + frontend
├── docker-compose.dev.yml          # Entwicklung: db + backend (reload) + Frontend lokal via npm run dev
├── .env.example                    # Vorlage für .env
├── .gitignore
├── CHANGELOG.md
├── CLAUDE.md
├── README.md
└── VERSION

Berechtigungslogik

is_superadmin=True  → alles erlaubt, Tenant-Prüfung wird übersprungen
TENANT_ADMIN        → alles im eigenen Tenant (inkl. Beet löschen)
READ_WRITE          → lesen + schreiben, kein Beet löschen
READ_ONLY           → nur GET-Endpoints

require_min_role(TenantRole.READ_WRITE) in deps.py gibt (user, tenant_id, role) zurück.


Datenbankschema (Kurzform)

users           → id, email, hashed_password, is_superadmin
tenants         → id, name, slug
user_tenants    → user_id, tenant_id, role
plant_families  → id, name, latin_name
plants          → id, tenant_id(nullable=global), family_id, nutrient_demand, water_demand, rest_years, ...
plant_compat.   → plant_id_a, plant_id_b, rating, reason
beds            → id, tenant_id, width_m, length_m, location, soil_type
bed_plantings   → id, bed_id, plant_id, area_m2, count, planted_date, removed_date

API-Routen Übersicht

POST   /api/v1/auth/login
POST   /api/v1/auth/refresh
GET    /api/v1/auth/me
GET    /api/v1/plant-families
GET    /api/v1/plants
GET    /api/v1/plants/{id}
POST   /api/v1/plants             (READ_WRITE+)
PUT    /api/v1/plants/{id}        (READ_WRITE+, global nur Superadmin)
DELETE /api/v1/plants/{id}        (READ_WRITE+, global nur Superadmin)
GET    /api/v1/beds
GET    /api/v1/beds/{id}          (mit Bepflanzungen)
POST   /api/v1/beds               (READ_WRITE+)
PUT    /api/v1/beds/{id}          (READ_WRITE+)
DELETE /api/v1/beds/{id}          (TENANT_ADMIN+)
GET    /api/v1/beds/{id}/plantings
POST   /api/v1/beds/{id}/plantings (READ_WRITE+)
PUT    /api/v1/plantings/{id}      (READ_WRITE+)
DELETE /api/v1/plantings/{id}      (READ_WRITE+)
GET    /health