feat(anhaenge): ATTACH-Zeile im erzeugten ICS

Jeder Anhang wird als ATTACH mit FMTTYPE, SIZE und FILENAME ausgegeben,
als URI statt inline base64 -- bei 10 MB je Datei wuerde sonst bei jeder
Synchronisation der ganze Anhang im ICS-Rumpf mitgeschleppt.

Die absolute URL kommt aus dav_util.public_base, das PUBLIC_BASE_URL und
die Forwarded-Header schon behandelt; ohne Basis-URL wird die Zeile
weggelassen, weil eine relative ATTACH-URL einem externen Client nichts
nuetzt. Abschaltbar ueber ATTACHMENT_PUBLIC_LINKS=0.

Der CalDAV-PUT ist geprueft: ein externer Client, der den Termin
zurueckschreibt, laesst die Anhaenge unberuehrt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-14 10:19:14 +02:00
co-authored by Claude Opus 5
parent 4b9c09df74
commit a024fa54b9
6 changed files with 207 additions and 21 deletions
+1 -8
View File
@@ -10,7 +10,6 @@ Two routers live here:
send a bearer token. See the README for the tradeoff.
"""
import os
from urllib.parse import quote
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
@@ -29,12 +28,6 @@ from database import get_db
router = APIRouter()
public_router = APIRouter()
# Deployments that would rather lose attachments in external calendar clients
# than hand out unauthenticated URLs can set this to 0: the capability endpoint
# then 404s and no ATTACH line is emitted into any ICS.
PUBLIC_LINKS_ENABLED = os.environ.get("ATTACHMENT_PUBLIC_LINKS", "1") != "0"
def _to_dict(att: models.EventAttachment) -> dict:
uploader = None
if att.uploader is not None:
@@ -261,7 +254,7 @@ def public_attachment(token: str, name: str = "", db: Session = Depends(get_db))
Never distinguish "wrong token" from "deleted": both are 404.
"""
if not PUBLIC_LINKS_ENABLED:
if not attachments_store.PUBLIC_LINKS_ENABLED:
raise HTTPException(404, "Not found")
att = (
db.query(models.EventAttachment)