# 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 ```