diff --git a/MIGRATION_README.md b/MIGRATION_README.md new file mode 100644 index 0000000..194b1c6 --- /dev/null +++ b/MIGRATION_README.md @@ -0,0 +1,142 @@ +# Migration der alten Events und Gallery-Bilder + +## ✅ Was wurde migriert? + +### Events (7 Stück): +- Karaoke (wiederkehrend) +- Pub Quiz (wiederkehrend) +- Schlager Hüttenzauber Karaoke +- Adventskalender +- Santa Karaoke-Party +- Weihnachtsferien +- Neujahrs-Apero + +### Gallery-Bilder (9 Stück): +- Gallery1.webp bis Gallery9.webp + +## 📁 Wo liegen die Bilder? + +Alle Bilder wurden konvertiert und liegen jetzt in: +- **Events:** `backend/data/images/events/` +- **Gallery:** `backend/data/images/gallery/` + +Die Bilder wurden automatisch: +- Von PNG/JPG/JPEG zu WebP konvertiert +- Auf max. 1600px Breite skaliert +- Mit 85% Qualität optimiert + +## 🚀 Deployment-Schritte + +### 1. Lokale Vorbereitung (bereits erledigt ✓) +- ✓ Migrations-Script erstellt +- ✓ Bilder konvertiert und in `backend/data/images/` kopiert +- ✓ Public API-Endpunkte erstellt (`/api/events/public`, `/api/gallery/public`) +- ✓ Frontend aktualisiert, um Events und Gallery dynamisch zu laden + +### 2. Auf Fly.io deployen + +Alle Änderungen committen und pushen: +```bash +git add . +git commit -m "feat: Migrate old events and gallery images to CMS" +git push origin main +``` + +Woodpecker CI wird automatisch beide Services deployen. + +### 3. Nach dem ersten Deploy - Datenbank initialisieren + +**Wichtig:** Die Bilder sind bereits im Repository in `backend/data/images/`, aber die Datenbank muss noch mit den Event- und Gallery-Einträgen befüllt werden. + +#### Via fly ssh (Empfohlen): + +```bash +# In das Backend einloggen +fly ssh console -a gallus-cms-backend + +# Prüfen ob Bilder da sind +ls -la /app/data/images/events/ +ls -la /app/data/images/gallery/ + +# Migrations-Script ausführen +cd /app +npm run migrate:old-data +``` + +#### Alternative: Manuell via Admin-Panel + +1. Gehe zu https://gallus-pub.ch/admin +2. Melde dich an +3. Für jedes Event: + - Klicke auf "Neues Event" + - Gib Titel, Datum und Beschreibung ein + - Statt Bild hochzuladen, trage manuell die imageUrl ein: + - z.B. `/images/events/event_karaoke.webp` + - Speichere das Event + +## 🔍 Verifikation + +Nach dem Deployment prüfen: + +1. **Frontend:** https://gallus-pub.ch/ + - Events sollten angezeigt werden + - Gallery sollte Bilder zeigen + +2. **Admin:** https://gallus-pub.ch/admin + - Events können bearbeitet werden + - Neue Events können hinzugefügt werden + +3. **Backend Health:** https://cms.gallus-pub.ch/health + - Status sollte "ok" sein + +## 📝 Event-Daten für manuelles Einfügen + +Falls du die Events manuell via Admin-Panel einfügen möchtest: + +### Karaoke +- **Titel:** Karaoke +- **Datum:** 2025-12-31 +- **Beschreibung:** Bei uns gibt es Karaoke Mi-Sa!!
Seid ihr eine Gruppe und lieber unter euch? ..unseren 2.Stock kannst du auch mieten ;)
Reserviere am besten gleich per Whatsapp 077 232 27 70 +- **Bild-URL:** `/images/events/event_karaoke.webp` + +### Pub Quiz +- **Titel:** Pub Quiz +- **Datum:** 2025-12-31 +- **Beschreibung:** Jeden Freitag findet unser Pub Quiz statt. Gespielt wird tischweise in 3-4 Runden.
Jede Woche gibt es ein anderes Thema. Es geht um Ruhm und Ehre und zusätzlich werden die Sieger der Herzen durch das Publikum gekürt! <3
Auch Einzelpersonen sind herzlich willkommen!
*zum mitmachen minimum 1 Getränk konsumieren oder 5CHF +- **Bild-URL:** `/images/events/event_pub-quiz.webp` + +### Schlager Hüttenzauber Karaoke +- **Titel:** Schlager Hüttenzauber Karaoke +- **Datum:** 2025-11-27 +- **Beschreibung:** Ab 19:00 Uhr Eintritt ist Frei! Reservieren unter 077 232 27 70 +- **Bild-URL:** `/images/events/event_schlager-karaoke.webp` + +### Adventskalender +- **Titel:** Adventskalender +- **Datum:** 2025-12-20 +- **Beschreibung:** Jeden Tag neue Überraschungen! Check unsere Social Media Stories! +- **Bild-URL:** `/images/events/event_advents-kalender.webp` + +### Santa Karaoke-Party +- **Titel:** Santa Karaoke-Party +- **Datum:** 2025-12-06 +- **Beschreibung:** 🤶🏻🎅🏻Komme als Weihnachts-Mann/-Frau und bekomme einen Shot auf's Haus!🤶🏻🎅🏻 +- **Bild-URL:** `/images/events/event_santa_karaoke.webp` + +### Weihnachtsferien +- **Titel:** Weihnachtsferien +- **Datum:** 2025-12-21 +- **Beschreibung:** Wir sind ab 02.01.2026 wieder wie gewohnt für euch da! 🍀.
Für Anfragen WA 077 232 27 70 Antwort innerhalb 48h +- **Bild-URL:** `/images/events/event_ferien.webp` + +### Neujahrs-Apero +- **Titel:** Neujahrs-Apero +- **Datum:** 2026-01-02 +- **Beschreibung:** 18:00-20:00 Uhr +- **Bild-URL:** `/images/events/event_neujahrs-apero.webp` + +## ⚠️ Wichtige Hinweise + +1. **Bilder sind im Volume persistent:** Alle Bilder in `/app/data/` bleiben bei Restarts erhalten +2. **Datenbank ist persistent:** Die SQLite-DB in `/app/data/gallus_cms.db` bleibt erhalten +3. **Alte Bilder in `public/images/`:** Die alten Original-Bilder bleiben im Frontend-Repository, werden aber nicht mehr verwendet diff --git a/backend/.gitignore b/backend/.gitignore index e2f0e3e..7814f45 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -4,7 +4,9 @@ dist *.log .DS_Store /tmp -/data -*.db -*.db-wal -*.db-shm +/data/*.db +/data/*.db-wal +/data/*.db-shm +/data/workspace +# Allow images to be committed +!/data/images diff --git a/backend/data/images/events/event_advents-kalender.webp b/backend/data/images/events/event_advents-kalender.webp new file mode 100644 index 0000000..35f826a Binary files /dev/null and b/backend/data/images/events/event_advents-kalender.webp differ diff --git a/backend/data/images/events/event_ferien.webp b/backend/data/images/events/event_ferien.webp new file mode 100644 index 0000000..c282cd7 Binary files /dev/null and b/backend/data/images/events/event_ferien.webp differ diff --git a/backend/data/images/events/event_karaoke.webp b/backend/data/images/events/event_karaoke.webp new file mode 100644 index 0000000..4912672 Binary files /dev/null and b/backend/data/images/events/event_karaoke.webp differ diff --git a/backend/data/images/events/event_neujahrs-apero.webp b/backend/data/images/events/event_neujahrs-apero.webp new file mode 100644 index 0000000..4e1c1f2 Binary files /dev/null and b/backend/data/images/events/event_neujahrs-apero.webp differ diff --git a/backend/data/images/events/event_pub-quiz.webp b/backend/data/images/events/event_pub-quiz.webp new file mode 100644 index 0000000..4936ecb Binary files /dev/null and b/backend/data/images/events/event_pub-quiz.webp differ diff --git a/backend/data/images/events/event_santa_karaoke.webp b/backend/data/images/events/event_santa_karaoke.webp new file mode 100644 index 0000000..129cf5a Binary files /dev/null and b/backend/data/images/events/event_santa_karaoke.webp differ diff --git a/backend/data/images/events/event_schlager-karaoke.webp b/backend/data/images/events/event_schlager-karaoke.webp new file mode 100644 index 0000000..2ca22d9 Binary files /dev/null and b/backend/data/images/events/event_schlager-karaoke.webp differ diff --git a/backend/data/images/gallery/Gallery1.webp b/backend/data/images/gallery/Gallery1.webp new file mode 100644 index 0000000..cc23860 Binary files /dev/null and b/backend/data/images/gallery/Gallery1.webp differ diff --git a/backend/data/images/gallery/Gallery2.webp b/backend/data/images/gallery/Gallery2.webp new file mode 100644 index 0000000..75e5de4 Binary files /dev/null and b/backend/data/images/gallery/Gallery2.webp differ diff --git a/backend/data/images/gallery/Gallery3.webp b/backend/data/images/gallery/Gallery3.webp new file mode 100644 index 0000000..8641d8d Binary files /dev/null and b/backend/data/images/gallery/Gallery3.webp differ diff --git a/backend/data/images/gallery/Gallery4.webp b/backend/data/images/gallery/Gallery4.webp new file mode 100644 index 0000000..bfb6c4e Binary files /dev/null and b/backend/data/images/gallery/Gallery4.webp differ diff --git a/backend/data/images/gallery/Gallery5.webp b/backend/data/images/gallery/Gallery5.webp new file mode 100644 index 0000000..ba13919 Binary files /dev/null and b/backend/data/images/gallery/Gallery5.webp differ diff --git a/backend/data/images/gallery/Gallery6.webp b/backend/data/images/gallery/Gallery6.webp new file mode 100644 index 0000000..6cdb18f Binary files /dev/null and b/backend/data/images/gallery/Gallery6.webp differ diff --git a/backend/data/images/gallery/Gallery7.webp b/backend/data/images/gallery/Gallery7.webp new file mode 100644 index 0000000..77f19f7 Binary files /dev/null and b/backend/data/images/gallery/Gallery7.webp differ diff --git a/backend/data/images/gallery/Gallery8.webp b/backend/data/images/gallery/Gallery8.webp new file mode 100644 index 0000000..7926875 Binary files /dev/null and b/backend/data/images/gallery/Gallery8.webp differ diff --git a/backend/data/images/gallery/Gallery9.webp b/backend/data/images/gallery/Gallery9.webp new file mode 100644 index 0000000..3d2860c Binary files /dev/null and b/backend/data/images/gallery/Gallery9.webp differ diff --git a/backend/package.json b/backend/package.json index 9c21032..7f5e169 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,7 +9,8 @@ "start": "node dist/index.js", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", - "db:studio": "drizzle-kit studio" + "db:studio": "drizzle-kit studio", + "migrate:old-data": "tsx src/scripts/migrate-old-data.ts" }, "dependencies": { "@fastify/cookie": "^9.3.1", diff --git a/backend/src/scripts/migrate-old-data.ts b/backend/src/scripts/migrate-old-data.ts new file mode 100644 index 0000000..029a0ab --- /dev/null +++ b/backend/src/scripts/migrate-old-data.ts @@ -0,0 +1,190 @@ +import { db } from '../config/database.js'; +import { events, galleryImages } from '../db/schema.js'; +import fs from 'fs'; +import path from 'path'; +import sharp from 'sharp'; + +// Old events data +const oldEvents = [ + { + image: "/images/events/event_karaoke.jpg", + title: "Karaoke", + date: "2025-12-31", // Set as ongoing event + description: `Bei uns gibt es Karaoke Mi-Sa!!
+Seid ihr eine Gruppe und lieber unter euch? ..unseren 2.Stock kannst du auch mieten ;)
+Reserviere am besten gleich per Whatsapp 077 232 27 70`, + displayOrder: 0, + }, + { + image: "/images/events/event_pub-quiz.jpg", + title: "Pub Quiz", + date: "2025-12-31", // Set as ongoing event + description: `Jeden Freitag findet unser Pub Quiz statt. Gespielt wird tischweise in 3-4 Runden.
+Jede Woche gibt es ein anderes Thema. Es geht um Ruhm und Ehre und zusätzlich werden die Sieger der Herzen durch das Publikum gekürt! <3
+Auch Einzelpersonen sind herzlich willkommen!
+*zum mitmachen minimum 1 Getränk konsumieren oder 5CHF`, + displayOrder: 1, + }, + { + image: "/images/events/event_schlager-karaoke.jpeg", + title: "Schlager Hüttenzauber Karaoke", + date: "2025-11-27", + description: `Ab 19:00 Uhr Eintritt ist Frei! Reservieren unter 077 232 27 70`, + displayOrder: 2, + }, + { + image: "/images/events/event_advents-kalender.jpeg", + title: "Adventskalender", + date: "2025-12-20", + description: `Jeden Tag neue Überraschungen! Check unsere Social Media Stories!`, + displayOrder: 3, + }, + { + image: "/images/events/event_santa_karaoke.jpeg", + title: "Santa Karaoke-Party", + date: "2025-12-06", + description: `🤶🏻🎅🏻Komme als Weihnachts-Mann/-Frau und bekomme einen Shot auf's Haus!🤶🏻🎅🏻`, + displayOrder: 4, + }, + { + image: "/images/events/event_ferien.jpeg", + title: "Weihnachtsferien", + date: "2025-12-21", + description: `Wir sind ab 02.01.2026 wieder wie gewohnt für euch da! 🍀.
Für Anfragen WA 077 232 27 70 Antwort innerhalb 48h`, + displayOrder: 5, + }, + { + image: "/images/events/event_neujahrs-apero.jpeg", + title: "Neujahrs-Apero", + date: "2026-01-02", + description: `18:00-20:00 Uhr`, + displayOrder: 6, + }, +]; + +// Old gallery images +const oldGalleryImages = [ + { src: "/images/gallery/Gallery7.png", alt: "Gallery 7" }, + { src: "/images/gallery/Gallery8.png", alt: "Gallery 8" }, + { src: "/images/gallery/Gallery9.png", alt: "Gallery 9" }, + { src: "/images/gallery/Gallery6.png", alt: "Gallery 6" }, + { src: "/images/gallery/Gallery1.png", alt: "Gallery 1" }, + { src: "/images/gallery/Gallery2.png", alt: "Gallery 2" }, + { src: "/images/gallery/Gallery3.png", alt: "Gallery 3" }, + { src: "/images/gallery/Gallery4.png", alt: "Gallery 4" }, + { src: "/images/gallery/Gallery5.png", alt: "Gallery 5" }, +]; + +async function copyAndConvertImage( + sourcePath: string, + destDir: string, + filename: string +): Promise { + const projectRoot = path.join(process.cwd(), '..'); + const fullSourcePath = path.join(projectRoot, 'public', sourcePath); + + // Ensure destination directory exists + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, { recursive: true }); + } + + const ext = path.extname(filename); + const baseName = path.basename(filename, ext); + const webpFilename = `${baseName}.webp`; + const destPath = path.join(destDir, webpFilename); + + console.log(`Processing: ${fullSourcePath} -> ${destPath}`); + + // Check if source exists + if (!fs.existsSync(fullSourcePath)) { + console.error(`Source file not found: ${fullSourcePath}`); + throw new Error(`Source file not found: ${fullSourcePath}`); + } + + // Convert to webp and copy + await sharp(fullSourcePath) + .rotate() // Auto-rotate based on EXIF + .resize({ width: 1600, withoutEnlargement: true }) + .webp({ quality: 85 }) + .toFile(destPath); + + return `/images/${path.relative(destDir, destPath).replace(/\\/g, '/')}`; +} + +async function migrateEvents() { + console.log('\n=== Migrating Events ===\n'); + + const dataDir = process.env.GIT_WORKSPACE_DIR || path.join(process.cwd(), 'data'); + const eventsImageDir = path.join(dataDir, 'images', 'events'); + + for (const event of oldEvents) { + try { + const filename = path.basename(event.image); + const newImageUrl = await copyAndConvertImage( + event.image, + eventsImageDir, + filename + ); + + const [newEvent] = await db.insert(events).values({ + title: event.title, + date: event.date, + description: event.description, + imageUrl: newImageUrl, + displayOrder: event.displayOrder, + isPublished: true, + }).returning(); + + console.log(`✓ Migrated event: ${newEvent.title}`); + } catch (error) { + console.error(`✗ Failed to migrate event "${event.title}":`, error); + } + } +} + +async function migrateGallery() { + console.log('\n=== Migrating Gallery Images ===\n'); + + const dataDir = process.env.GIT_WORKSPACE_DIR || path.join(process.cwd(), 'data'); + const galleryImageDir = path.join(dataDir, 'images', 'gallery'); + + for (let i = 0; i < oldGalleryImages.length; i++) { + const img = oldGalleryImages[i]; + try { + const filename = path.basename(img.src); + const newImageUrl = await copyAndConvertImage( + img.src, + galleryImageDir, + filename + ); + + const [newImage] = await db.insert(galleryImages).values({ + imageUrl: newImageUrl, + altText: img.alt, + displayOrder: i, + isPublished: true, + }).returning(); + + console.log(`✓ Migrated gallery image: ${newImage.altText}`); + } catch (error) { + console.error(`✗ Failed to migrate gallery image "${img.alt}":`, error); + } + } +} + +async function main() { + console.log('Starting migration of old data...\n'); + console.log('Working directory:', process.cwd()); + console.log('Data directory:', process.env.GIT_WORKSPACE_DIR || path.join(process.cwd(), 'data')); + + try { + await migrateEvents(); + await migrateGallery(); + console.log('\n✓ Migration completed successfully!'); + } catch (error) { + console.error('\n✗ Migration failed:', error); + process.exit(1); + } +} + +main();