Token-gescoper iCal-Feed (/absences/ical/<token>.ics), abonnierbar in Outlook/Apple/Google. Anders als der CalDAV-Client (Push nach Nextcloud) pollt der Kalender die URL selbst. Feed zeigt nur die eigenen bestätigten Abwesenheiten des Token-Inhabers. - users.ical_token_hash (SHA-256, rotierbar) + Migration 0041 (nur Spalte, keine RLS-Aenderung; users-Policy deckt neue nullable Spalte ab) - Router ical.py: oeffentlicher Feed (kein JWT) + Token-Verwaltung POST/GET/DELETE /users/me/ical-token (authentifiziert) - ProfilePage: Sektion "Kalender-Abo (iCal)" mit Erzeugen/Rotieren/ Deaktivieren, URL-Anzeige einmalig + Kopieren - test_ical.py: Token-Lifecycle + oeffentlicher Feed (3 Tests) Deployed auf 137 (Migration 0041, 196/196 Tests gruen). 164 ausstehend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
30 lines
846 B
Python
30 lines
846 B
Python
"""iCal-Abo: users.ical_token_hash
|
|
|
|
Revision ID: 0041
|
|
Revises: 0040
|
|
Create Date: 2026-07-08
|
|
|
|
Gehashtes, rotierbares Token für den read-only iCal-Kalender-Feed pro Nutzer
|
|
(/absences/ical/<token>.ics). NULL = Feed deaktiviert. Keine RLS-Änderung
|
|
(nur neue nullable Spalte auf bestehender users-Policy).
|
|
"""
|
|
from alembic import op
|
|
|
|
revision = "0041"
|
|
down_revision = "0040"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.execute("ALTER TABLE users ADD COLUMN IF NOT EXISTS ical_token_hash TEXT")
|
|
op.execute(
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS ix_users_ical_token_hash "
|
|
"ON users (ical_token_hash) WHERE ical_token_hash IS NOT NULL"
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.execute("DROP INDEX IF EXISTS ix_users_ical_token_hash")
|
|
op.execute("ALTER TABLE users DROP COLUMN IF EXISTS ical_token_hash")
|