feat: Login-Name vs. Anzeigename (Server)

- Neue Spalte users.display_name (Original-Schreibweise); username bleibt der
  lowercase Login-Name. Setup/Create setzen display_name aus der Eingabe.
- Login bleibt case-insensitive (Anzeigename eingeben funktioniert -> wird
  lowercased -> trifft den Login-Namen).
- Profil: PUT /api/profile/ kann display_name UND username (Login-Name) aendern;
  bei Login-Namen-Wechsel kommt ein frischer Token zurueck (JWT sub haengt am
  Namen). Stabile interne ID (Integer-PK) traegt alle Verweise -> Umbenennen
  bricht Shares/Gruppen/creator_id nicht.
- display_name ueberall ausgeliefert/genutzt (me, profile, users, directory,
  shares, Gruppen-Mitglieder, creator/owner, ORGANIZER-Export).
- Migration + Backfill (display_name = username). Tests ergaenzt (17 gruen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-05-31 17:40:38 +02:00
parent 28a7cbe94e
commit f9923b022e
10 changed files with 103 additions and 15 deletions

View File

@@ -134,7 +134,7 @@ def list_calendars(
owner = db.query(models.User).filter(models.User.id == cal.user_id).first()
d = _cal_dict(
cal, owned=False,
shared_by=owner.username if owner else None,
shared_by=(owner.display_name or owner.username) if owner else None,
permission=share.permission,
)
if cal.id in group_cal_map:
@@ -330,7 +330,7 @@ def list_shares(
u = db.query(models.User).filter(models.User.id == s.user_id).first()
out.append({
"user_id": s.user_id,
"display_name": u.username if u else None,
"display_name": (u.display_name or u.username) if u else None,
"permission": s.permission,
"created_at": s.created_at,
})
@@ -501,7 +501,7 @@ def export_calendar(
.all()
)
# Resolve creator display names for ORGANIZER.
name_cache = {u.id: u.username for u in db.query(models.User).all()}
name_cache = {u.id: (u.display_name or u.username) for u in db.query(models.User).all()}
ics = ical_io.build_ics(cal, events, name_cache=name_cache)
safe_name = "".join(c for c in cal.name if c.isalnum() or c in (" ", "-", "_")).strip() or "calendar"
return Response(