Bilder, die vor der Umstellung hochgeladen wurden, liegen unverkleinert im
Originalformat im Repo - damals war sharp im Container kaputt, es gab also
weder Verkleinerung noch Formatwandlung. Das Skript schickt sie durch
dieselbe Verarbeitung wie einen frischen Upload.
node dist/scripts/convert-images-to-avif.js # nur anzeigen
node dist/scripts/convert-images-to-avif.js --apply # wirklich tun
Ohne --apply wird nichts geschrieben.
Ablauf: Workspace auf den Repo-Stand bringen, jedes referenzierte Bild
umwandeln und nach seinem Inhalt benennen (Event-Titel, Alt-Text, Zweck),
die Verweise in Events, Gallery und Textbereichen nachziehen, die
Vorgaenger wegraeumen und committen.
Weggeraeumt wird nur, was das CMS selbst angelegt hat. Ein handgepflegtes
event_karaoke.jpg bekommt zwar eine AVIF-Fassung und der Verweis zeigt
darauf, das Original bleibt aber liegen und wird im Bericht genannt.
saveImageBuffer aus upload.service.ts herausgeloest, damit Upload und
Skript nachweislich dieselbe Verarbeitung benutzen statt zweier Kopien.
replaceImageUrls ersetzt Pfade in den Textbereichen, ohne die restliche
JSON-Struktur anzufassen.
Durchgespielt an vier Bildern zu je ~656 KB: 2.56 MB -> 0.16 MB. Die
Vorschau schreibt nichts, ein zweiter Lauf findet nichts mehr, die
Highlights-Liste bleibt unversehrt und alle Bilder werden danach als
image/avif ausgeliefert.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Uploads werden nach AVIF gewandelt statt nach WebP und heissen jetzt nach
dem Inhalt statt nach Zeitstempel und Zufall:
- Events uebernehmen den Event-Titel, die Gallery den Alt-Text, Bilder in
den Textbereichen ihren Zweck, PDFs den urspruenglichen Dateinamen.
- Ohne solche Angabe wird der Dateiname vor dem Upload verwendet.
- Umlaute werden ausgeschrieben (Getraenkekarte, nicht Getrnkekarte),
Sonderzeichen und Pfadanteile fallen weg.
- Gleiche Namen werden durchnummeriert, damit zwei Events namens
"Karaoke" sich nicht gegenseitig ueberschreiben.
Besitz statt Namensmuster:
Die Loeschsicherheit haing bisher am Namensmuster <zeitstempel>-<zufall>.
Mit sprechenden Namen traegt der Name diese Information nicht mehr - ein
hochgeladenes karaoke-abend.avif ist von einem handgepflegten
event_karaoke.jpg nicht zu unterscheiden. Deshalb fuehrt das CMS jetzt in
managed_assets Buch darueber, welche Dateien es selbst angelegt hat, und
loescht ausschliesslich diese. Uploads von vor der Umstellung werden
weiterhin am alten Muster erkannt, damit sie aufraeumbar bleiben.
initDatabase() legte Tabellen nur an, wenn users noch fehlte. Auf einer
bestehenden Datenbank waere managed_assets damit nie entstanden. Der
Block laeuft jetzt bei jedem Start; alle Anweisungen sind IF NOT EXISTS.
Nebenbei repariert: die Formulare schickten den Alt-Text NACH der Datei.
Zu dem Zeitpunkt hat der Server ihn noch nicht geparst, in der Gallery
landete deshalb immer der Dateiname als Alt-Text. Textfelder gehen jetzt
vor der Datei raus.
Gemessen an einem 4032x3024-Bild: 0.90 MB rein, 36 KB AVIF bei 1600x1200
raus, 579 ms. Das Testbild ist synthetisch und komprimiert besser als ein
echtes Foto - die Verkleinerung auf 1600px greift aber immer.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Nur bauen was sich geaendert hat:
- deploy_backend laeuft nur bei Aenderungen unter backend/, deploy_frontend
nur bei src/, public/ und den Frontend-Konfigdateien. Die haeufigsten
Commits sind Inhaltsaenderungen aus dem CMS und fassen nur src/ und
public/ an - die brauchen kein Backend-Deployment mehr.
- Der Abhaengigkeits-Audit laeuft nur noch, wenn sich package.json oder
package-lock.json aendern. Sonst kam bei jedem Inhalts-Commit derselbe
Bericht nach Discord.
- "[ALL]" in der Commit-Message erzwingt weiterhin den vollen Durchlauf.
Build-Kontext:
- Es gab kein .dockerignore im Root. Bei jedem Frontend-Deploy wanderte
das komplette Repo zum Fly-Remote-Builder, inklusive 50 MB Git-Historie
und des gesamten Backends. 98 MB -> 47 MB.
- Zwei tote Zeilen im Frontend-Dockerfile entfernt: ein Root-styles/ gibt
es nicht und /styles/ wird als URL nirgends verwendet.
Backend-Image:
- npm ci lief in beiden Stages, better-sqlite3 wurde also doppelt
uebersetzt. Jetzt einmal im Builder, danach npm prune --omit=dev und die
fertigen node_modules wandern weiter.
- Die Build-Werkzeuge sind raus aus der Laufzeit-Stage. Das apk del vorher
hat sie nur unsichtbar gemacht, die Layer blieben im Image.
- Ohne Cache lokal: 1:31 -> 0:29. Image: 845 MB -> 312 MB.
sharp war die ganze Zeit kaputt:
- backend/package-lock.json enthielt als einziges Plattform-Binary
@img/sharp-win32-x64, das Lockfile stammt von einer Windows-Maschine.
npm ci installiert strikt nach Lockfile, auf Alpine kam damit gar kein
sharp-Binary an. Der Upload fiel jedes Mal auf den Fallback zurueck:
keine Verkleinerung auf 1600px, keine WebP-Wandlung, das Originalbild
landete unveraendert im Repo. Genau deshalb sind die Bilder so gross -
in der Historie liegen entsprechend .jpeg statt .webp.
- Lockfile mit allen Plattformvarianten neu aufgeloest. Keine einzige
bestehende Paketversion aendert sich dabei; dazugekommen sind die
Binaries, weggefallen sind 12 Postgres-Pakete, die drizzle-kit optional
mitzieht und die hier niemand benutzt.
- vips aus dem Image entfernt, sharp bringt seit 0.33 sein eigenes libvips
mit.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Bilder wurden nie geloescht - weder beim Loeschen eines Datensatzes noch
beim Austauschen. Dazu waren die Textbereiche zwar im Backend vorhanden,
aber ohne Oberflaeche, und die Getraenkekarte hing hartkodiert im Markup.
Aufraeumen:
- asset.service.ts loescht ausschliesslich Dateien, die das CMS selbst
angelegt hat (Muster <zeitstempel>-<zufall>.<ext>) und nur innerhalb der
Upload-Ordner. Handgepflegte Assets wie event_karaoke.jpg oder Welcome.png
bleiben unangetastet, auch wenn sie nirgends referenziert sind.
- image-refs.service.ts sammelt alle benutzten Bild-URLs, inklusive der
Pfade aus den Textbereichen. Geloescht wird nur, was wirklich niemand
mehr benutzt - ein von zwei Events geteiltes Bild bleibt liegen.
- Events und Gallery raeumen beim Loeschen und beim Bildwechsel mit auf.
- Der Publish entfernt zusaetzlich Verwaiste. Die Referenzliste deckt
bewusst alle Zeilen ab, auch unveroeffentlichte, sonst verlieren die ihr
Bild. Die Loeschungen werden mitcommittet, das Repo schrumpft also.
Texte:
- Neuer Adminbereich fuer Hero, Willkommen und Drinks ueber die schon
vorhandenen /api/content-Endpunkte, samt Highlights-Liste und Bildern.
- Der Generator maskiert Texte jetzt. Ohne das haette ein "<" oder "&" in
einem Feld gereicht, um den Astro-Build und damit den Deploy zu killen.
PDF:
- POST /api/pdf/drinks nimmt die Getraenkekarte entgegen, hinterlegt die
URL in der drinks-Section und raeumt den Vorgaenger weg. Der Generator
verlinkt sie, statt den Pfad fest im Markup zu haben.
Ausserdem:
- Vorschaubilder im Admin auf eine feste Box gezwungen. Uploads sind bis
1600px breit und haben das Layout je nach Seitenverhaeltnis zerrissen.
- git.service.ts sichert public/pdf beim Neu-Clone mit weg, nicht nur
public/images.
- Der Publish scheitert nicht mehr, wenn nur das Audit-Log nicht
geschrieben werden kann - der Push ist da laengst durch.
- Upload-Logik lag dreifach kopiert vor, jetzt in upload.service.ts. Der
Helfer prueft auch auf abgeschnittene Dateien; bisher landete bei zu
grossen Uploads ein kaputtes Bild auf der Platte.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Die Bild-URLs in der DB sind /images/events/... bzw. /images/gallery/... -
das ist der Pfad auf der publizierten Astro-Seite. Das Backend lieferte die
Dateien aber nur unter /static/ mit Root auf dem Workspace aus, die Datei lag
also unter /static/public/images/... Ein Aufruf von /images/... traf auf gar
keine Route, kam als JSON-404 zurueck und wurde vom Browser als
OpaqueResponseBlocking verworfen. Dadurch waren im Adminbereich saemtliche
Event- und Gallery-Bilder kaputt.
Zusaetzlicher statischer Mount /images/ -> <workspace>/public/images. Die DB
bleibt unveraendert und die URLs stimmen weiterhin fuer die publizierte Seite.
/static/ bleibt zusaetzlich bestehen.
Beide Verzeichnisse werden vorab angelegt - @fastify/static verweigert sonst
die Registrierung, wenn der Workspace noch nicht geklont ist.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Die Pipeline hatte nur einen Deploy-Step fuer das Frontend (App gallus-pub).
Die Backend-App gallus-cms-backend wurde von der CI nie ausgerollt, sondern
haing am letzten manuellen fly deploy. Ein Push auf main hat damit zwar die
Admin-Seite aktualisiert, aber nie die API dahinter.
Step laeuft vor deploy_frontend und nutzt denselben FLY_API_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Publish schlug bei jedem Versuch mit "spawn git ENOENT" fehl, obwohl git im
Container installiert ist. Node meldet ENOENT auch dann, wenn das cwd des
Kindprozesses nicht existiert: initialize() setzte simple-git auf den
Workspace, loeschte diesen per rm -rf und startete den Clone anschliessend
aus dem geloeschten Verzeichnis heraus.
Nebeneffekt davon: da Uploads unter GIT_WORKSPACE_DIR/public/images liegen,
hat jeder fehlgeschlagene Publish die hochgeladenen Bilder mitgeloescht.
git.service.ts:
- Clone laeuft aus dem Elternverzeichnis statt aus dem geloeschten Ziel
- Re-Clone nur noch wenn kein brauchbares Repo vorhanden ist
- Uploads werden ueber den Re-Clone hinweg gesichert (Repo-Stand gewinnt)
- commitAndPush scheitert nicht mehr, wenn es nichts zu committen gibt
- reset() nur bei echtem Repo, mit Ausnahme fuer public/images
- Token wird in Fehlermeldungen maskiert
Dockerfile:
- COPY von src/db/migrations entfernt. Das Verzeichnis war nie im Git und
liess den Image-Build scheitern. Zur Laufzeit wird es nicht gebraucht,
das Schema legt initDatabase() selbst an.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
- Removed redundant `/tmp/` paths for audit result and output files.
- Ensured consistent file access in vulnerability checks and Discord notifications.
- Added workspace file listing for better debugging in case of missing audit results.
- Refined npm audit process to generate detailed JSON and text outputs.
- Improved Discord notifications with comprehensive vulnerability details and formatting.
- Replaced `apt-get` with `apk` for faster lightweight image handling.
- Simplified payload preparation by redirecting commit messages to a temporary file.
- Ensured cleanup with `rm -f` for improved reliability and maintainability.
- Implemented success and failure notifications using `jq` for secure payload formatting.
- Enhanced YAML to manage build alerts and improve CI visibility.
- Updated `loadBanner` to wait for DOM readiness with `DOMContentLoaded` support.
- Added comprehensive debug logs for banner loading, response status, and DOM interactions.
- Streamlined CORS logic by consolidating `allowedOrigins` checks and improving readability.
- Updated callback invocations for consistency and clarity.
- Updated `fly.toml` to allow multiple CORS origins.
- Refactored CORS logic in `index.ts` to validate and support multiple origins, including handling requests with no origin.
- Adjusted server logic in `/banners/active` to resolve timezone issues and ensure consistent date handling.
- Sorted active banners by creation date in descending order for better relevance.
- Integrated `Banner.astro` component into the homepage layout for displaying active banners.
- Introduced `banners.ts` with CRUD operations for managing banners.
- Added `/banners/active` endpoint to fetch active banners.
- Secured admin-only routes for banner creation, update, and deletion.
- Created `Banner.css` for banner styling.
- Implemented `Banner.astro` component to retrieve active banners from the CMS.
- Integrated styling via `Banner.css`.
- Handles errors gracefully during banner fetch.
- Introduced a new "Banners" feature, enabling banner creation, management, and display across the admin panel and frontend.
- Enhanced image handling for events and gallery by converting images to optimized webp format.
- Added `banners` table in the database schema for storing announcements.
- Integrated new `/api/banners` route in backend for banner operations.
- Updated `index.astro` to include banner display component.
- Added supporting UI and APIs in the admin panel for banner management.
- Introduced a gallery management section in `admin.astro` for uploading, listing, and deleting gallery images.
- Added dynamic fetching of events and gallery images from the backend in `index.astro`.
- Updated authentication to handle gallery-related UI visibility and actions.
- Introduced `/events/upload` endpoint for securely uploading and processing event images.
- Added image validation, resizing, and conversion to WebP with fallback support for original formats.
- Updated `uploadImage` to `uploadEventImage` and introduced `uploadGalleryImage` in `admin.astro`.
- Create standalone migration script that works in production
- Include migration script and images in Docker build
- Images will be copied to /app/data/images on container start
- Can be run with: node migrate-production.js
- Add migration script to convert and copy images
- Include 7 events (Karaoke, Pub Quiz, etc.) in WebP format
- Include 9 gallery images in WebP format
- Update .gitignore to allow images in data/images/
- Add migration documentation in MIGRATION_README.md
Images are stored in backend/data/images/ which maps to
the persistent Fly.io volume at /app/data/
- Implemented public `/gallery/public` and `/events/public` endpoints for fetching published data without authentication.
- Updated persistent volume configuration for Fly.io across backend and static file serving.
- Adjusted frontend to dynamically fetch events and gallery images from backend API.
- Refined Woodpecker pipeline for clearer separation of backend and frontend deployments.
- Updated frontend to use `https://cms.gallus-pub.ch` as the API base URL.
- Configured cookies with `SameSite=None` and `Secure` for production in `auth.ts`.
- Enhanced `fly.toml` to include `FRONTEND_URL`, `CORS_ORIGIN`, and `GITEA_REDIRECT_URI`.
- Adjusted `.gitignore` to ignore `/ai/` directory.
- Introduced Caddy server for serving frontend and API backend.
- Implemented admin dashboard for creating, editing, and managing events.
- Replaced session-based authentication with token-based OAuth using Gitea.
- Added support for drag-and-drop event reordering in the admin interface.
- Standardized Fastify route validation with JSON schemas.
- Enhanced authentication flow with cookie-based state and secure token storage.
- Reworked backend routes to handle publishing, event management, and content updates.
- Updated `Dockerfile.caddy` and `fly.toml` for deployment configuration.
- Introduced Caddy server for serving frontend and API backend.
- Implemented admin dashboard for creating, editing, and managing events.
- Replaced session-based authentication with token-based OAuth using Gitea.
- Added support for drag-and-drop event reordering in the admin interface.
- Standardized Fastify route validation with JSON schemas.
- Enhanced authentication flow with cookie-based state and secure token storage.
- Reworked backend routes to handle publishing, event management, and content updates.
- Updated `Dockerfile.caddy` and `fly.toml` for deployment configuration.
- Introduced Caddy server for serving frontend and API backend.
- Implemented admin dashboard for creating, editing, and managing events.
- Replaced session-based authentication with token-based OAuth using Gitea.
- Added support for drag-and-drop event reordering in the admin interface.
- Standardized Fastify route validation with JSON schemas.
- Enhanced authentication flow with cookie-based state and secure token storage.
- Reworked backend routes to handle publishing, event management, and content updates.
- Updated `Dockerfile.caddy` and `fly.toml` for deployment configuration.
- **Hover support refinement**: Limited hover effects to devices with pointer precision and hover capability.
- **Active state improvements**: Added visual feedback for tap and ensured consistent card toggling on mobile, including outside-click handling.
- **Styling additions**: Introduced a tappable hint for better user guidance and refined cursor styles.
- **Script update**: Prevented multiple active cards and ensured seamless closing on external clicks.