Fix: korrigiere Code-Bugs und Dokumentation

- api/main.py: index() Route übernimmt Request-Objekt korrekt (statt {}),
  calendar_label Query-Parameter implementiert (dynamische WHERE-Klausel)
- docker-compose.yml: DB_BOOTSTRAP/DB_ROOT_USER/DB_ROOT_PASSWORD Variablen
  für calendar-sync Service hinzugefügt (waren dokumentiert, aber nie übergeben)
- .env.example: DB_BOOTSTRAP, API_PORT, TIMEZONE hinzugefügt
- AGENTS.md: Zeilennummer sync.py:91→83, gemischte Deutsch/China-Sprache
  bereinigt
- SPEC.md: --entrypoint→command, API_HOST entfernt (nicht implementiert),
  TIMEZONE hinzugefügt, JSON-Beispiele um timezone-Feld erweitert,
  Docker-Compose-Beispiel und Projektstruktur aktualisiert,
  Search als implementiert markiert
- README.md: TIMEZONE und API_PORT in Konfigtationstabelle,
  timezone im JSON-Beispiel
This commit is contained in:
2026-08-04 19:28:39 +02:00
parent f717d37d07
commit 5343896653
6 changed files with 61 additions and 32 deletions
+11
View File
@@ -16,3 +16,14 @@ LOG_LEVEL=INFO
# Optional: Healthchecks.io / Uptime Kuma URL (wird nach jedem Sync gepingt)
# HEALTHCHECK_URL=https://hc-ping.com/DEINE_UUID
# Optional: Database Bootstrap (erstellt DB und User beim Start)
# DB_BOOTSTRAP=true
# DB_ROOT_USER=root
# DB_ROOT_PASSWORD=dein_root_passwort
# API-Server Port (für Web-UI + REST API)
API_PORT=8000
# Zeitzone für die API/Web-UI (Default: UTC)
# TIMEZONE=Europe/Berlin
+2 -2
View File
@@ -48,7 +48,7 @@ Die API (`api/main.py`) ist eine separarte FastAPI-App die als eigenständiger C
### Datenbank-Schema
Schema wird in `ensure_schema()` per `CREATE TABLE IF NOT EXISTS` erstellt. Bei Schema-Änderungen:
- `ensure_schema()` in `sync.py:91` anpassen
- `ensure_schema()` in `sync.py:83` anpassen
- MariaDB-kompatibles SQL verwenden (kein PostgreSQL-Specific)
- Indexe für Performance bedenken
@@ -56,7 +56,7 @@ Schema wird in `ensure_schema()` per `CREATE TABLE IF NOT EXISTS` erstellt. Bei
Verwendet `recurring_ical_events` Bibliothek für RRULE/EXDATE/RECURRENCE-ID Expansion. Fenster wird über `WINDOW_PAST_DAYS`/`WINDOW_FUTURE_DAYS` gesteuert.
### UTC-Normalisierung
Alle Zeiten werden in naive UTC datetime konvertiert (`to_naive_utc()`). Bei Datumsänderungen sicherstellen, dass Zeitzone korrekt处理 wird.
Alle Zeiten werden in naive UTC datetime konvertiert (`to_naive_utc()`). Bei Datumsänderungen sicherstellen, dass die Zeitzone korrekt verarbeitet wird.
### Soft-Delete
Events werden nicht gelöscht, sondern mit `deleted=1` markiert (`mark_missing_as_deleted()`).
+6 -2
View File
@@ -70,6 +70,8 @@ Läuft als Docker Container, pollt periodisch einen privaten Google Calendar ICS
| `DB_BOOTSTRAP` | `false` | DB + User beim Start erstellen |
| `DB_ROOT_USER` | - | Root-User fürs Bootstrap |
| `DB_ROOT_PASSWORD` | - | Root-Passwort fürs Bootstrap |
| `API_PORT` | `8000` | Port für den API/Web-UI Container |
| `TIMEZONE` | `UTC` | Zeitzone für API/Web-UI Anzeige (z.B. `Europe/Berlin`) |
## Datenbank-Schema
@@ -133,11 +135,13 @@ curl http://localhost:8000/api/events/42
"start_at": "2025-01-15T10:00:00",
"end_at": "2025-01-15T11:00:00",
"all_day": false,
"status": "CONFIRMED"
"status": "CONFIRMED",
"timezone": "Europe/Berlin"
}
],
"count": 1,
"query_time": "2025-01-15T09:30:00Z"
"query_time": "2025-01-15T09:30:00Z",
"timezone": "Europe/Berlin"
}
```
+14 -6
View File
@@ -24,7 +24,7 @@ Python-basiertes System zur Synchronisation eines Google Calendar ICS-Feeds nach
└─────────────────┘
```
**Entscheidung:** Gleicher Docker Build (ein Dockerfile), zwei verschiedene Container/Services via `docker-compose.yml`. Das Image wird mit einem `--entrypoint` Parameter gesteuert.
**Entscheidung:** Gleicher Docker Build (ein Dockerfile), zwei verschiedene Container/Services via `docker-compose.yml`. Der jeweilige Service wird via `command` Parameter gesteuert (`python sync.py` vs. `uvicorn api.main:app`).
## 3. Bestehendes System (Sync Tool)
@@ -90,11 +90,13 @@ Gibt die nächsten N Termine zurück.
"start_at": "2025-01-15T10:00:00",
"end_at": "2025-01-15T11:00:00",
"all_day": false,
"status": "CONFIRMED"
"status": "CONFIRMED",
"timezone": "Europe/Berlin"
}
],
"count": 10,
"query_time": "2025-01-14T14:30:00Z"
"query_time": "2025-01-14T14:30:00Z",
"timezone": "Europe/Berlin"
}
```
@@ -123,12 +125,12 @@ Healthcheck Endpoint für den API Container.
### 4.3 Technologie-Stack (API)
- **Framework:** FastAPI
- **Templating:** Jinja2 (server-side rendering)
- **DB-Zugriff:** mysql-connector-python (gleicher Connection-Pool wie Sync)
- **DB-Zugriff:** mysql-connector-python (shared `get_connection()` aus `api/database.py`)
- **Port:** 8000 (konfigurierbar via `API_PORT`)
### 4.4 Additional Environment Variablen (API)
- `API_PORT` - Port für den API Server (default: 8000)
- `API_HOST` - Bind Address (default: 0.0.0.0)
- `TIMEZONE` - Zeitzone für die Anzeige von Zeiten (default: UTC)
- `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` - Identisch zum Sync
## 5. Docker Setup
@@ -174,6 +176,9 @@ services:
- WINDOW_FUTURE_DAYS=${WINDOW_FUTURE_DAYS:-365}
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- HEALTHCHECK_URL=${HEALTHCHECK_URL:-}
- DB_BOOTSTRAP=${DB_BOOTSTRAP:-false}
- DB_ROOT_USER=${DB_ROOT_USER:-}
- DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD:-}
networks:
- docker-backend
@@ -191,6 +196,7 @@ services:
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- TIMEZONE=${TIMEZONE:-UTC}
labels:
- "com.centurylinklabs.watchtower.enable=true"
networks:
@@ -218,6 +224,8 @@ networks:
├── mariadb-setup.sql # Manuelles DB-Setup Script
├── .env.example # Beispiel-Umgebungsvariablen (erweitert)
├── SPEC.md # Diese Spezifikation
├── PLAN.md # Implementierungsplan
├── AGENTS.md # Richtlinien für AI-Agenten
└── .github/workflows/ # CI/CD (Docker Build + Push)
```
@@ -269,4 +277,4 @@ Bestehender GitHub Actions Workflow erweitern:
## 11. Future Enhancements (nicht im Scope)
- [ ] Kalender-Filter UI (nach calendar_label)
- [ ] Suchfunktion nach Event-Titel
- [x] Suchfunktion nach Event-Titel (implementiert)
+17 -14
View File
@@ -4,7 +4,7 @@ from datetime import datetime, timezone
from pathlib import Path
from zoneinfo import ZoneInfo
from fastapi import FastAPI, HTTPException, Query
from fastapi import FastAPI, HTTPException, Query, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
@@ -76,24 +76,25 @@ def row_to_event(row) -> EventResponse:
)
def fetch_events(limit: int = 10, search: str | None = None) -> list[dict]:
def fetch_events(limit: int = 10, search: str | None = None, calendar_label: str | None = None) -> list[dict]:
conn = get_connection()
try:
cur = conn.cursor()
try:
conditions = ["deleted = 0", "start_at >= NOW()"]
params: list = []
if search:
conditions.append("summary LIKE %s")
params.append(f"%{search}%")
if calendar_label:
conditions.append("calendar_label = %s")
params.append(calendar_label)
params.append(limit)
where = " WHERE " + " AND ".join(conditions)
cur.execute(
f"SELECT {SELECT_COLUMNS} FROM calendar_events "
"WHERE deleted = 0 AND start_at >= NOW() AND summary LIKE %s "
"ORDER BY start_at ASC LIMIT %s",
(f"%{search}%", limit),
)
else:
cur.execute(
f"SELECT {SELECT_COLUMNS} FROM calendar_events "
"WHERE deleted = 0 AND start_at >= NOW() "
"ORDER BY start_at ASC LIMIT %s",
(limit,),
f"{where} ORDER BY start_at ASC LIMIT %s",
tuple(params),
)
return cur.fetchall()
finally:
@@ -111,9 +112,10 @@ def health():
def get_events(
limit: int = Query(default=10, ge=1, le=50),
search: str | None = Query(default=None),
calendar_label: str | None = Query(default=None),
):
try:
rows = fetch_events(limit=limit, search=search)
rows = fetch_events(limit=limit, search=search, calendar_label=calendar_label)
except Exception:
log.exception("DB-Fehler bei /api/events")
raise HTTPException(status_code=500, detail="Database error")
@@ -156,6 +158,7 @@ def get_event(event_id: int):
@app.get("/", response_class=HTMLResponse)
def index(
request: Request,
search: str | None = Query(default=None),
limit: int = Query(default=10, ge=1, le=50),
):
@@ -181,5 +184,5 @@ def index(
return templates.TemplateResponse(
"index.html",
{"request": {}, "events": events, "search": search or ""},
{"request": request, "events": events, "search": search or ""},
)
+3
View File
@@ -22,6 +22,9 @@ services:
- WINDOW_FUTURE_DAYS=${WINDOW_FUTURE_DAYS:-365}
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- HEALTHCHECK_URL=${HEALTHCHECK_URL:-}
- DB_BOOTSTRAP=${DB_BOOTSTRAP:-false}
- DB_ROOT_USER=${DB_ROOT_USER:-}
- DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD:-}
networks:
- docker-backend