🌲 Crumbforest Code Tour

Dies ist dein "Reiseführer" durch den Code. Ziel ist es, jede Komponente zu verstehen, damit du sie als solide Basis für zukünftige Projekte nutzen kannst.

🗺️ High-Level Architektur (Der Wald von oben)

Der CrumbCore basiert auf FastAPI (Python) und folgt einer modularen Struktur:

  • app/main.py: Der Eingang (Root). Hier startet alles.
  • app/routers/: Die Wegweiser. Jede Datei bedient einen URL-Bereich (z.B. /admin, /api).
  • app/services/: Die Arbeiter. Logik für RAG, Übersetzungen, Config-Loading.
  • app/models/: Die Datenbank-Struktur (SQLAlchemy / raw SQL Schemas).
  • app/templates/: Das Gesicht (Jinja2 HTML Templates) & Design.

🚀 1. Der Einstieg: app/main.py

Hier atmet das System.

Wichtige Konzepte:

  1. FastAPI Instanz: app = FastAPI() ist der Kern.
  2. Middleware: SessionMiddleware (für Login-Cookies) und CORSMiddleware (Sicherheit).
  3. Template Engine: init_templates(app) lädt Jinja2. Unsere render-Funktion injiziert automatisch User, Sprache und Flash-Nachrichten in jedes Template.
  4. Router Mounting: Am Ende der Datei werden die Module aus routers/ eingebunden (app.include_router(...)). Das hält main.py schlank.

Code-Lupe:

# app/main.py

# ... Imports ...

# Security Secret (wichtig für Cookies!)
SECRET = os.getenv("APP_SECRET", "dev-secret-change-me")

app = FastAPI()

# 1. Statische Dateien (CSS, Bilder) verfügbar machen
app.mount("/static", StaticFiles(directory="static"), name="static")

# 2. Session Middleware (Das Gedächtnis des Browsers)
app.add_middleware(SessionMiddleware, secret_key=SECRET, ...)

# 3. Template Renderer (Unsere "View Engine")
def init_templates(app: FastAPI):
    # ... lädt "templates"-Ordner ...
    # Definiert die "render"-Funktion, die wir überall nutzen
    def render(req: Request, template: str, **ctx):
        # ... Logik für Sprache (lang) und User-Context ...
        return HTMLResponse(tpl.render(**base_ctx))

# 4. Router einbinden (Modularisierung)
app.include_router(home_router, prefix="/{lang}") # Multi-Language Support!
# ...

🚦 2. Das Routing: app/routers/

Hier entscheidet sich, wohin die Reise geht.

  • home.py: Öffentliche Seiten (/, /about, /crew). Lädt Inhalte multiligual.
  • crumbforest_roles.py: Das Herzstück Phase 1. Zeigt Rollen-Dashboard und Chat. Prüft user_group-Zugriff.
  • auth.py (bzw. Login in Hauptdatei/Home): Verwaltet Login/Logout.
  • diary_rag.py: API für das Tagebuch-RAG (Vector Search).

🧠 3. Die Intelligenz: app/services/

  • rag_service.py: Verbindet Qdrant (Vektordatenbank) mit LLMs. Hier passiert die Magie ("Suche Igel im Wald").
  • localization.py: Lädt characters.de.json usw. und merget sie live in die Config.

💾 4. Die Daten: crumbforest_config.json

Unsere "Single Source of Truth" für Rollen und Gruppen.
Statt Hardcoding in Python definieren wir hier:
* Wer darf was sehen? (group_access)
* Welche Farbe hat der Fuchs?
* Welches LLM nutzt die Eule?


Dies ist der Startpunkt. Welchen Bereich wollen wir als nächstes zerlegen? 🧐

🧠 5. Deep Dive: Der RAG Service (app/services/rag_service.py)

Hier wird Text zu Wissen. Wenn wir "Wald" sagen, müssen wir nicht nur Keyword-Matches finden, sondern Bedeutung.

Der Indexing-Workflow (index_post)

Wie kommt ein Blogpost oder Tagebucheintrag in das Gehirn?

  1. Hash-Check: Wir erstellen einen MD5-Hash des Inhalts. Ist er gleich wie in der DB? Überspringen! (Spart KI-Kosten).
  2. Chunking: Der Text wird mit EmbeddingService in Häppchen geteilt (z.B. 1000 Zeichen).
  3. Embedding: Jedes Häppchen wird durch ein Embedding-Modell (OpenAI/Jina) geschickt. Das Ergebnis ist ein Vektor (eine Liste von Zahlen, z.B. [0.1, -0.5, ...]).
  4. Upsert in Qdrant: Wir speichern den Vektor + Metadaten (Textinhalt, Titel, Slug) in Qdrant.
  5. Tracking: In MariaDB (post_vectors Tabelle) merken wir uns, welcher Post welche Vektoren hat (für Löschung/Updates).

Die Suche (query_with_rag)

Der magische Moment, wenn der User eine Frage stellt:

  1. Embed Query: Die Frage "Wer wohnt im Wald?" wird ebenfalls in einen Vektor verwandelt.
  2. Vektor-Suche (Semantic Search): Wir fragen Qdrant: "Welche Vektoren liegen in der Nähe dieses Frage-Vektors?". Dank Cosine-Similarity findet er Inhalte, die inhaltlich passen, auch ohne exakte Worte.
  3. Context Assembly: Wir nehmen die Top-3 gefundenen Text-Chunks und kleben sie zusammen.
  4. LLM Generation: Wir bauen einen Prompt für die KI:
    text Context: [Der Igel wohnt im Unterholz...] Frage: Wer wohnt im Wald? Antworte basierend auf dem Context.
  5. Antwort: Die KI generiert die fertige Antwort.

Warum rag_service.py wichtig ist

Er entkoppelt die Logik:
* Der Router (diary_rag.py) weiß nur: "Indexiere dies" oder "Frage das".
* Der Service kümmert sich um die Details (Qdrant API, Datenbank-Sync, Hash-Prüfung).
So bleibt der Controller sauber! 🧹✨

🔐 6. Deep Dive: Sicherheit & Auth (app/deps.py)

Wie unterscheiden wir Freund von Feind? Das "Dependency Injection" System von FastAPI regelt das elegant.

Der Wächter: current_user

In app/deps.py definieren wir Funktionen, die FastAPI vor jedem Request ausführt.

# app/deps.py

def current_user(req: Request):
    # Holt das User-Objekt aus dem signierten Session-Cookie
    return req.session.get("user")

def admin_required(user = Depends(current_user)):
    # 1. Ist ein User da?
    if not user:
        raise HTTPException(status_code=401) # -> Login Page

    # 2. Ist es ein Admin?
    if user.get("role") != "admin":
        raise HTTPException(status_code=403) # -> Verboten!

    return user

Die Anwendung in Routern

Wir müssen Sicherheitschecks nicht in jede Funktion kopieren. Wir "injizieren" sie einfach:

# app/routers/admin.py

@router.get("/")
def admin_dashboard(
    user: dict = Depends(admin_required) # <--- Hier passiert der Check!
):
    # Wenn wir hier sind, IST der User garantiert ein Admin.
    return render(..., user=user)

Session Security (in main.py)

Das Login-System basiert auf SessionMiddleware.
* Cookie: Der Browser bekommt ein Cookie namens session.
* Signierung: Das Cookie ist mit APP_SECRET kryptografisch signiert. Der User kann den Inhalt lesen (Base64), aber nicht verändern. Wenn er role: admin reinschummelt, wird die Signatur ungültig und der Server ignoriert das Cookie.
* HttpOnly: JavaScript kann das Cookie nicht stehlen.
* SameSite: Schützt vor CSRF-Attacken.

🎨 7. Das Frontend (app/templates/)

Wir nutzen Jinja2 (klassisches Server-Side Rendering) gepaart mit PicoCSS (minimalistisches CSS Framework).

Struktur

  • base.html: Das Skelett. Enthält <head>, Navigation und Footer. Alle anderen Seiten erben hiervon ({% extends "base.html" %}).
  • crumbforest/: Templates für die Rollen-Ansicht und den Chat.
  • pages/: Allgemeine Seiten (Login, Home).

Datenfluss

Wenn ein Router render(req, "login.html", error="Falsches PW") aufruft:
1. Jinja2 lädt login.html.
2. Es sieht {% extends "base.html" %} und lädt erst das Skelett.
3. Es füllt die Blöcke ({% block content %}) mit dem Login-Formular.
4. Es injiziert die Variable error an der passenden Stelle.

💾 8. Die Daten (app/models/ & SQL)

Hier wird es interessant. Wir nutzen Hybrid-Ansatz:

API-Validierung (Pydantic)

In app/models/ liegen Klassen wie QueryRequest oder User.
Das sind keine Datenbank-Tabellen! Sie dienen nur der Validierung von Input/Output.
Wenn jemand JSON an die API schickt, prüft FastAPI automatisch: "Fehlt das Feld 'question'? Ist 'limit' eine Zahl?".

Datenbank (Raw SQL)

Wir nutzen kein schwergewichtiges ORM (wie SQLAlchemy) für die Queries, sondern rohes SQL via pymysql.
Warum? Performance & Kontrolle.
* Zu sehen in: app/services/rag_service.py oder main.py.
* Codeschnipsel:
python cur.execute("SELECT * FROM users WHERE email=%s", (email,))
Das macht den Code extrem transparent. SQL ist die Wahrheit.


🎉 Herzlichen Glückwunsch! Du hast den Wald einmal durchquert.
Du kennst jetzt:
1. Den Einstieg (main.py)
2. Die Logik (routers & services)
3. Die Sicherheit (deps.py)
4. Das Gesicht (templates)
5. Das Gedächtnis (models & SQL)

Bereit für Phase 2? 🚀