electric-horses-infra/stacks/eh-search
Benjamin Weinlich b3813ed6ac feat(stacks/eh-search): add site-search FastAPI service
Mirrors /opt/ai-apps/eh-search/ on the server, including the full
FastAPI app (intent routing, FTS+fuzzy+substring hybrid, multi-source
federation across vehicles + blog + brands + pages + static + tag
bridge), SQL schema (Postgres materialized view with german_unaccent
text search, pg_trgm for fuzzy), Dockerfile and compose.

Sanitized the hardcoded password in sql/01_init.sql — replaced with
REPLACE_ME_BEFORE_APPLYING placeholder since this repo is public.

The eh-search service binds only on the private network (10.0.0.8:8200)
and is reachable only via Pegasus nginx proxy at /api/search.

Refs OP#1094 OP#1105 OP#1112 OP#1116 OP#1117
2026-04-11 22:19:39 +02:00
..
app feat(stacks/eh-search): add site-search FastAPI service 2026-04-11 22:19:39 +02:00
.env.example feat(stacks/eh-search): add site-search FastAPI service 2026-04-11 22:19:39 +02:00
Agent.md feat(stacks/eh-search): add site-search FastAPI service 2026-04-11 22:19:39 +02:00
docker-compose.yml feat(stacks/eh-search): add site-search FastAPI service 2026-04-11 22:19:39 +02:00
Dockerfile feat(stacks/eh-search): add site-search FastAPI service 2026-04-11 22:19:39 +02:00
README.md feat(stacks/eh-search): add site-search FastAPI service 2026-04-11 22:19:39 +02:00
requirements.txt feat(stacks/eh-search): add site-search FastAPI service 2026-04-11 22:19:39 +02:00

eh-search — Site-Search für electric-horses.de

Live: Via Nginx-Proxy auf https://www.electric-horses.de/api/search?q=... Interne Adresse: http://10.0.0.8:8200 (privat, nicht public!) Stack-Pfad: /opt/ai-apps/eh-search/ auf ai-apps

Was macht das?

Ein FastAPI-Service der eine Cmd+K Command Palette auf der Electric-Horses-Website mit Suchergebnissen versorgt. Durchsucht:

  • 75 Fahrzeuge (Postgres FTS + Fuzzy + Komm-Nr/DVN Exact Lookup)
  • 28 Blog-Posts (mit Tags + neue SEO-Descriptions)
  • 24 Marken
  • 6 Legal-Pages (Impressum, Datenschutz, ...)
  • 8 Static Pages (Werkstatt, Ersatzteile, Kontakt, ...)
  • 60 Tag → Page Bridges für semantische Verbindungen

Killer-Feature: Eingabe einer 4-stelligen Komm-Nr (oder D9094 etc.) → Direct Redirect zur Fahrzeug-Detailseite. Latenz <50ms. Ideal für Mitarbeiter am Telefon.

Architektur

Browser Cmd+K
    ↓
www.electric-horses.de/api/search
    ↓ (Nginx Proxy auf Pegasus)
10.0.0.8:8200 (eh-search auf ai-apps, privat)
    ↓
├─ Postgres eh_vehicles (loco-replica-db Container, Port 5433)
├─ Redis Cache (Stack-intern)
├─ Directus REST API (in-memory Content Index)
└─ Static Page Registry (hardcoded in Python)

Sicherheit

  • Bind NUR auf privates Netz (10.0.0.8:8200) — nicht über Public-IP erreichbar
  • Read-Only DB-User (search_read) — kann nur SELECT auf Materialized View
  • Same-Origin via Pegasus Nginx — kein CORS nötig, kein neues Cert, kein neues DNS
  • Rate Limit via Nginx (30 req/s pro IP)

Files

  • docker-compose.yml — Stack
  • Dockerfile — Python 3.12 + FastAPI + asyncpg + redis-py
  • requirements.txt — Python-Deps
  • .env.example — Template für .env (niemals echte .env committen!)
  • app/ — FastAPI-Anwendung
  • sql/ — Postgres-Schema und Migrations (search_vehicles Materialized View, pg_trgm, unaccent)
  • Agent.md — AI-Briefing
  • README.md — diese Datei

Live-Update-Workflow

Wenn sich Fahrzeugdaten ändern (vehicle_sync.py nächtlich auf Pegasus), wird am Ende des Syncs automatisch die Materialized View refreshed UND der Redis-Cache invalidiert. Directus-Edits an Blog/Brands/Pages feuern einen Webhook an eh-search → sofortige Cache-Invalidation und Content-Index-Refresh.

OpenProject Phasen (alle abgeschlossen)

  • M6 — Postgres FTS + Fuzzy + Exact + Command Palette (Phase 1)
  • M6.2 — Federated Multi-Source (Phase 1.5)
  • M6.3 — UX Refinement (sleek, mobile-first, smart group ordering)
  • M6.4 — Komm-Nr Datenmodell-Fix (D9094 korrekt statt falsche interne Nummer)
  • M6.5 — Sync-Fix für deaktivierte Fahrzeuge

Out of Scope (mögliche Phase 2)

  • Semantic Vector Search via Qdrant + bge-Embeddings
  • Voice Input via Whisper
  • LLM Filter-Extraktion via lokales Mistral/Gemma
  • Image Search / OCR
  • Cross-Encoder Reranking