Gleicher Dateiname erzeugt keine neue Datei, sondern eine neue Fassung -
gleiche ID, gleiche Vorschaubild-Adresse. Die darf einen Tag im Browser
liegen, also zeigte die Kachel nach dem erneuten Hochladen bis zu 24
Stunden das alte Bild. Das erklaert, warum eine Drehung erst nach
mehreren Versuchen "ankam". Die Adresse traegt jetzt ?v=<Fassung>.
Vorschaubilder entstehen im Hintergrund; die Kachel fragt jetzt bis zu
fuenfmal nach, statt bis zum naechsten Neuladen ihr Ersatzsymbol zu
zeigen. Nur bei Bildern juenger als zwei Minuten - sonst fragte jede
dauerhaft fehlgeschlagene Datei bei jedem Oeffnen erneut. Gemessen: 608
ms bis zur Anzeige, ohne Neuladen.
Das Anheben der Kachel beim Ueberfahren ist raus: Steht der Zeiger auf
der Unterkante, rutscht sie unter ihm weg, mouseleave, sie faellt
zurueck, mouseenter - im Kreis, sichtbar als Flackern.
Dazu liest die EXIF-Auswertung jetzt 256 statt 64 KB und gibt bei einem
abgeschnittenen Block nicht mehr auf: Die Drehung steht vorn, das
eingebettete Vorschaubild dahinter.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
130 Zeilen fuer einen einmaligen Bedarf - die Haelfte davon eine Karte
im Admin-Bereich, die sonst dauerhaft dort steht. Die EXIF-Behebung
selbst bleibt: Neu hochgeladene Bilder stehen richtig herum.
Bestehende Vorschaubilder werden damit nicht mehr nachgezogen. Wer sie
gerade ruecken will, laedt die Datei erneut hoch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Kameras speichern die Bildpunkte fast immer quer und legen die Drehung
als EXIF-Notiz daneben. Browser befolgen sie von selbst - Go nicht.
Deshalb stand dasselbe Foto in der grossen Ansicht aufrecht und lag im
Vorschaubild auf der Seite.
Neues Paket internal/imageutil liest genau ein Feld (0x0112) aus dem
Anfang der Datei und dreht danach. Von Hand statt mit einer Bibliothek:
Der Weg dorthin ist kurz und liegt fest, und gebraucht wird ein Feld.
Gelesen werden nur die ersten 64 KB, damit ein 40-MB-Foto nicht zweimal
im Speicher liegt.
Gedreht wird vor dem Verkleinern - andersherum bekaeme ein hochkantes
Foto den Zuschnitt eines Querformats.
Bestehende Vorschaubilder aendern sich davon nicht. Dafuer neu:
POST /admin/thumbnails/rebuild samt Knopf im Admin-Bereich.
Auch Profilbilder aus OIDC laufen jetzt durch dieselbe Auswertung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Adresse im <script>-Tag stand fest in der index.html, waehrend die CSP
aus HONEYBAR_ORIGIN kam. Wer die Leiste woanders betreibt, bekam genau den
Widerspruch: die CSP erlaubte den eigenen Ursprung, geladen wurde der alte.
Der erste Versuch, das ueber ein Bau-Argument zu loesen, half beim fertigen
Abbild nicht — dort ist der Bau vorbei, und ein Umzug haette einen Neubau
verlangt. Ursprung und Schluessel stehen jetzt in /api/v1/public/config, die
Oberflaeche haengt das Skript selbst ein. Beides speist sich aus derselben
Variablen wie die CSP, sie koennen also nicht auseinanderlaufen, und eine
Aenderung wirkt beim Neustart.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Leiste kommt als Skript von der HoneyOS-Domain und holt sich von dort
ihre App-Liste. Beides blockte die CSP bisher (script-src / connect-src
fielen auf 'self' zurueck) – ohne Ausnahme erschien sie gar nicht.
- HONEYBAR_ORIGIN nimmt genau diesen einen Ursprung in script-src und
connect-src auf. Leer heisst: keine Leiste, und die Regel bleibt so eng
wie bisher. Frame-Freigabe erbt sie ausdruecklich nicht.
- SecurityHeaders nimmt die Ausnahmen jetzt als CSP-Struct statt als
variadische frameSrc-Liste – mit drei Richtungen wurde das sonst unklar.
- Das <script>-Tag steht in der index.html und nicht serverseitig
eingespritzt: der Service Worker legt die index.html in seinen Vorrat
(navigateFallback), was zur Bauzeit fehlt, kaeme im Browser nie an.
- defer, damit ein langsames oder abgeschaltetes HoneyOS den Start von
Aether nicht ausbremst.
Tests fuer die neue Regel: eng ohne Ausnahme, beide Richtungen mit,
Pfad wird auf den Ursprung gekuerzt, Adresse ohne Schema faellt raus.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Schickt der Anbieter ein `picture`, wird es bei der Anmeldung geholt,
mittig auf 256x256 zugeschnitten und oben rechts angezeigt. Fehlt es
oder scheitert das Holen, bleibt es beim Buchstabenkreis - die
Anmeldung selbst hängt nicht davon ab.
Die Bildadresse kommt aus einem Token, also von außen. Erlaubt ist
deshalb ausschließlich der Ursprung aus OIDC_ISSUER, Weiterleitungen
eingeschlossen; dazu 4 MB und 5 Sekunden als Deckel. Ohne das wäre es
eine Aufforderung an den Server, für einen Fremden beliebige Adressen
im internen Netz anzufragen.
Neu: Migration 000019 (avatar_key/-source/-updated_at), Paket
internal/avatar, Route GET /api/v1/users/{id}/avatar. Der GC durchsucht
"avatars/" mit und kennt die Schlüssel - sonst räumte er die Bilder
nach einem Tag weg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Knopf hiess fest "Sign in with SSO / OIDC". Der Anbieter heisst bei
jedem anders; "Anmelden mit PocketID" sagt dem, der davorsteht, mehr.
Vorgabe unveraendert. Der Wert geht nur ueber /public/config, wenn OIDC
ueberhaupt an ist.
Beim Testen dabei selbst einen Fehler gebaut und eingefangen: Ein
Kommentar hinter einem LEEREN Wert wird in einer .env zum Wert.
OIDC_ISSUER= # z. B. https://... startete den OIDC-Aufbau mit dem
Kommentartext als Adresse, und der Start brach ab.
In deploy/.env.example steckten vier weitere solche Zeilen, darunter
OFFICE_PROVIDER= # collabora | onlyoffice - Office waere mit Unsinn als
Anbieter eingeschaltet worden. Alle umgestellt, Warnung an den Dateikopf.
Dazu im README belegt, was beim Ausschalten der Versionierung passiert:
Fassungen bleiben, auch im Papierkorb; erst das Leeren entfernt sie samt
Blobs, und zwar sofort statt erst beim GC.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
OIDC_ISSUER=PocketID (der Name statt der Adresse) endete in
unsupported protocol scheme "" – tief aus der Bibliothek, ohne Hinweis
auf die Ursache. Und weil ein Fehlschlag hier den Start abbricht, stand
die ganze Cloud wegen einer Zeile in der .env.
Jetzt wird vorher geprueft und die Zeile beim Namen genannt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Zahl stand doppelt im Quelltext: als const im Backend und noch einmal
im Verlaufs-Dialog, verbunden nur durch den Kommentar "muss zu
maxVersionsKept im Backend passen". Ein Satz ist keine Zusicherung - beim
ersten Aendern haette der Dialog eine andere Zahl genannt als die, die der
Server anwendet.
Jetzt eine .env-Variable (Vorgabe 5), die der Server ueber
/api/v1/public/config mitliefert. Werte unter 1 werden verworfen: Die Zahl
geht als OFFSET in die Aufraeumabfrage, und OFFSET 0 loeschte jede Fassung -
aus "keine Grenze" wuerde "kein Verlauf".
Gemessen: MAX_VERSIONS_KEPT=3, Datei siebenmal ueberschrieben, uebrig
bleiben v6, v5, v4 bei aktueller v7.
Dazu ein README-Abschnitt "Versionen" - Standard aus, warum das so ist,
Kontingent, und dass jedes Strg+S in Office eine Fassung anlegt. Bisher
stand zu alldem nichts im README.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein $ im Wert wird von Docker Compose als Variable gelesen und schneidet
ab: Sicher$Passwort2026 kommt als Sicher an (gemessen). Angelegt wird ein
Konto mit einem Passwort, das niemand kennt; die Anmeldung scheitert mit
'invalid credentials', obwohl alles richtig eingetippt ist. Die Datenbank
laeuft dabei weiter, weil Postgres denselben abgeschnittenen Wert bekommt.
Der Start nennt jetzt die Laenge des Passworts (nicht den Wert) und warnt
unter 12 Zeichen mit dem Hinweis auf $$. Ohne diese Zahl war der Fehler
von aussen nicht zu sehen: Compose meldet zwar 'variable is not set', aber
auf stderr beim up, nicht im Container-Log.
Escape-Tabelle in README und beiden .env.example. Ein Backslash hilft
nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Verbindungszeichenkette wurde mit fmt.Sprintf zusammengeklebt. Ein
Passwort mit % und @ ergab eine kaputte URL:
parse "postgres://aether:8UoNj%CoWJ7ze@qy@localhost:5432/aether":
invalid URL escape "%Co"
Der Server kam nicht über die Migrationen hinaus. Die key=value-Form hätte
dasselbe Problem mit einem Leerzeichen gehabt, nur stiller.
config.DSN() liefert jetzt eine postgres://-URL, gebaut mit net/url und
net.JoinHostPort (klammert IPv6). pgxpool und golang-migrate nehmen sie
beide unverändert – dsnToURL, splitDSN, indexByte und orDefault in pkg/db
sind ersatzlos entfallen. Test deckt %, @, :, /, Leerzeichen und IPv6 ab.
Dazu: DB_HOST und DB_PORT fehlten in der schlanken .env.example, weshalb
localhost statt postgres gegriffen hat. APP_ENV ergänzt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA.
Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb,
Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming,
Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office
(Collabora/OnlyOffice).
Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort)
sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>