15 Commits
Author SHA1 Message Date
Gallus-maintanance c0d6a1c5de Update events
ci/woodpecker/push/woodpecker Pipeline was successful
2026-09-15 09:51:18 +00:00
Gallus-maintanance d678d80116 Update events
ci/woodpecker/push/woodpecker Pipeline was successful
2026-08-31 11:54:15 +00:00
Gallus-maintanance bce973679b Update events
ci/woodpecker/push/woodpecker Pipeline was successful
2026-08-31 11:39:35 +00:00
Gallus-maintanance 45f705a104 Update events
ci/woodpecker/push/woodpecker Pipeline was successful
2026-08-31 11:20:26 +00:00
Gallus-maintanance c4f17d5f78 Update events
ci/woodpecker/push/woodpecker Pipeline was successful
2026-08-31 10:51:05 +00:00
Gallus-maintanance 247f6ea272 Update events
ci/woodpecker/push/woodpecker Pipeline was successful
2026-08-28 19:34:40 +00:00
Gallus-maintanance ebcfec4ec7 Update events
ci/woodpecker/push/woodpecker Pipeline was successful
2026-08-16 20:13:03 +00:00
Kenzo e22ccfeda3 Update package-lock.json with latest dependency versions.
ci/woodpecker/push/woodpecker Pipeline was successful
2026-08-13 19:46:21 +02:00
Kenzo 3729639596 Update package-lock.json with latest dependency versions.
ci/woodpecker/push/woodpecker Pipeline failed
2026-08-13 19:39:16 +02:00
Kenzo 4887f599fd Update package-lock.json with latest dependency versions. 2026-08-13 19:27:16 +02:00
Gallus-maintanance 69db8de6d7 Update events 2026-08-13 13:23:33 +00:00
Gallus-maintanance 7bb5f339c0 Update events 2026-08-13 13:15:54 +00:00
Gallus-maintanance 5982a1bfd1 Update events
ci/woodpecker/push/woodpecker Pipeline was successful
2026-08-11 13:15:58 +00:00
KenzoandClaude Opus 5 f46a24ee4e fix(upload): Groessengrenzen realistisch setzen, PDFs eigener Wert
ci/woodpecker/push/woodpecker Pipeline was successful
Der Upload lief gegen einen einzigen globalen Wert von 5 MB, der fuer alle
Dateitypen galt. Eine Getraenkekarte liegt aber schnell bei 15-20 MB, das
Hochladen scheiterte deshalb zuverlaessig.

- MAX_FILE_SIZE (Bilder) von 5 auf 20 MB. Bilder werden ohnehin auf 1600px
  heruntergerechnet, die Grenze muss nur ein unbearbeitetes Handyfoto
  durchlassen - 5 MB reichten dafuer schon nicht.
- MAX_PDF_SIZE neu, 40 MB. PDFs werden unveraendert abgelegt.
- Beides ueber Umgebungsvariablen uebersteuerbar.

Die Registrierung von multipart nimmt den groesseren der beiden Werte als
Obergrenze, die Routen setzen ihn per request.file({ limits }) auf ihren
eigenen herunter. Die Fehlermeldung im PDF-Zweig nannte bisher die
Bildgrenze.

Geprueft am laufenden Container, auch mit --memory=512m wie auf Fly: 18-MB-
PDF und 18-MB-Foto gehen durch, 45 MB bzw. 25 MB werden mit 413 und
passender Meldung abgelehnt, kein OOM.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-11 14:42:56 +02:00
Gallus-maintanance 76c4d5c9bf Update Menu
ci/woodpecker/push/woodpecker Pipeline was successful
2026-08-11 12:13:25 +00:00
88 changed files with 1774 additions and 2566 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
FROM node:20-alpine AS build FROM node:22-alpine AS build
WORKDIR /app WORKDIR /app
COPY package*.json ./ COPY package*.json ./
@@ -7,7 +7,7 @@ RUN npm ci || npm install
COPY . . COPY . .
RUN npm run build RUN npm run build
FROM node:20-alpine AS production FROM node:22-alpine AS production
WORKDIR /app WORKDIR /app
RUN npm install -g serve RUN npm install -g serve
+6 -1
View File
@@ -28,7 +28,12 @@ export const env = {
FRONTEND_URL: process.env.FRONTEND_URL || 'http://localhost:5173', FRONTEND_URL: process.env.FRONTEND_URL || 'http://localhost:5173',
// Upload // Upload
MAX_FILE_SIZE: parseInt(process.env.MAX_FILE_SIZE || '5242880', 10), // Bilder werden ohnehin auf 1600px heruntergerechnet, die Grenze muss nur
// gross genug fuer ein unbearbeitetes Handyfoto sein.
MAX_FILE_SIZE: parseInt(process.env.MAX_FILE_SIZE || String(20 * 1024 * 1024), 10),
// PDFs werden unveraendert abgelegt. Eine Getraenkekarte mit Bildern liegt
// schnell bei 15-20 MB, deshalb ein eigener, groesserer Wert.
MAX_PDF_SIZE: parseInt(process.env.MAX_PDF_SIZE || String(40 * 1024 * 1024), 10),
}; };
// Validate required environment variables // Validate required environment variables
+3 -1
View File
@@ -73,9 +73,11 @@ fastify.register(jwt, {
}, },
}); });
// Der globale Wert ist die Obergrenze fuer alles. Die Routen setzen ihn per
// request.file({ limits }) auf ihren eigenen, engeren Wert herunter.
fastify.register(multipart, { fastify.register(multipart, {
limits: { limits: {
fileSize: env.MAX_FILE_SIZE, fileSize: Math.max(env.MAX_FILE_SIZE, env.MAX_PDF_SIZE),
}, },
}); });
+2 -1
View File
@@ -5,6 +5,7 @@ import { contentSections } from '../db/schema.js';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { saveUploadedImage } from '../services/upload.service.js'; import { saveUploadedImage } from '../services/upload.service.js';
import { dropImageIfUnused, extractImageUrls } from '../services/image-refs.service.js'; import { dropImageIfUnused, extractImageUrls } from '../services/image-refs.service.js';
import { env } from '../config/env.js';
// Fastify JSON schema for content section body // Fastify JSON schema for content section body
const contentBodyJsonSchema = { const contentBodyJsonSchema = {
@@ -106,7 +107,7 @@ const contentRoute: FastifyPluginAsync = async (fastify) => {
preHandler: [fastify.authenticate], preHandler: [fastify.authenticate],
}, async (request, reply) => { }, async (request, reply) => {
try { try {
const file = await (request as any).file(); const file = await (request as any).file({ limits: { fileSize: env.MAX_FILE_SIZE } });
if (!file) { if (!file) {
return reply.code(400).send({ error: 'No file uploaded' }); return reply.code(400).send({ error: 'No file uploaded' });
} }
+2 -1
View File
@@ -4,6 +4,7 @@ import { events } from '../db/schema.js';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { dropImageIfUnused } from '../services/image-refs.service.js'; import { dropImageIfUnused } from '../services/image-refs.service.js';
import { saveUploadedImage } from '../services/upload.service.js'; import { saveUploadedImage } from '../services/upload.service.js';
import { env } from '../config/env.js';
/** Raeumt eine Bilddatei weg, ohne dass ein Fehler die Antwort kippt. */ /** Raeumt eine Bilddatei weg, ohne dass ein Fehler die Antwort kippt. */
async function dropUnusedImage(fastify: any, url: string | null | undefined, reason: string) { async function dropUnusedImage(fastify: any, url: string | null | undefined, reason: string) {
@@ -102,7 +103,7 @@ const eventsRoute: FastifyPluginAsync = async (fastify) => {
}, async (request, reply) => { }, async (request, reply) => {
try { try {
// Expect a single file field named "file" // Expect a single file field named "file"
const file = await (request as any).file(); const file = await (request as any).file({ limits: { fileSize: env.MAX_FILE_SIZE } });
if (!file) { if (!file) {
return reply.code(400).send({ error: 'No file uploaded' }); return reply.code(400).send({ error: 'No file uploaded' });
} }
+2 -1
View File
@@ -5,6 +5,7 @@ import { galleryImages } from '../db/schema.js';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { dropImageIfUnused } from '../services/image-refs.service.js'; import { dropImageIfUnused } from '../services/image-refs.service.js';
import { saveUploadedImage } from '../services/upload.service.js'; import { saveUploadedImage } from '../services/upload.service.js';
import { env } from '../config/env.js';
/** Raeumt eine Bilddatei weg, ohne dass ein Fehler die Antwort kippt. */ /** Raeumt eine Bilddatei weg, ohne dass ein Fehler die Antwort kippt. */
async function dropUnusedImage(fastify: any, url: string | null | undefined, reason: string) { async function dropUnusedImage(fastify: any, url: string | null | undefined, reason: string) {
@@ -81,7 +82,7 @@ const galleryRoute: FastifyPluginAsync = async (fastify) => {
}, async (request, reply) => { }, async (request, reply) => {
try { try {
// Expect a single file field named "file" // Expect a single file field named "file"
const file = await (request as any).file(); const file = await (request as any).file({ limits: { fileSize: env.MAX_FILE_SIZE } });
if (!file) { if (!file) {
return reply.code(400).send({ error: 'No file uploaded' }); return reply.code(400).send({ error: 'No file uploaded' });
} }
+3 -3
View File
@@ -45,7 +45,7 @@ const pdfRoute: FastifyPluginAsync = async (fastify) => {
} }
try { try {
const file = await (request as any).file(); const file = await (request as any).file({ limits: { fileSize: env.MAX_PDF_SIZE } });
if (!file) { if (!file) {
return reply.code(400).send({ error: 'No file uploaded' }); return reply.code(400).send({ error: 'No file uploaded' });
} }
@@ -63,7 +63,7 @@ const pdfRoute: FastifyPluginAsync = async (fastify) => {
const buffer = Buffer.concat(chunks); const buffer = Buffer.concat(chunks);
if ((file.file as any)?.truncated) { if ((file.file as any)?.truncated) {
const limit = Math.round(env.MAX_FILE_SIZE / 1024 / 1024); const limit = Math.round(env.MAX_PDF_SIZE / 1024 / 1024);
return reply.code(413).send({ error: `PDF too large. Maximum is ${limit} MB` }); return reply.code(413).send({ error: `PDF too large. Maximum is ${limit} MB` });
} }
@@ -113,7 +113,7 @@ const pdfRoute: FastifyPluginAsync = async (fastify) => {
} catch (err: any) { } catch (err: any) {
if (err?.code === 'FST_REQ_FILE_TOO_LARGE') { if (err?.code === 'FST_REQ_FILE_TOO_LARGE') {
const limit = Math.round(env.MAX_FILE_SIZE / 1024 / 1024); const limit = Math.round(env.MAX_PDF_SIZE / 1024 / 1024);
return reply.code(413).send({ error: `PDF too large. Maximum is ${limit} MB` }); return reply.code(413).send({ error: `PDF too large. Maximum is ${limit} MB` });
} }
fastify.log.error({ err }, 'PDF upload failed'); fastify.log.error({ err }, 'PDF upload failed');
+1696 -2522
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -9,6 +9,6 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^5.12.0" "astro": "^7.2.1"
} }
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 567 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1021 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1021 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 488 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.
+8 -8
View File
@@ -7,19 +7,19 @@ const { id } = Astro.props;
<h2 class="title">Drinks</h2> <h2 class="title">Drinks</h2>
<p class="note"> <p class="note">
Ob frisch gezapftes Pint, edler Whisky oder ein gemütliches Glas Wein bei uns genießt du das Leben in entspannter Atmosphäre. Auch Cocktails dürfen nicht fehlen: Vieles kreieren wir selbst. Sláinte! Ob ein frisch gezapftes Pint, ein edler Tropfen Whiskey oder ein gemütliches Glas Wein hier kannst du in entspannter Atmosphäre das Leben genießen.
</p> </p>
<a href="/pdf/Getraenke_Gallus_2025.pdf" class="card-link" target="_blank" rel="noopener noreferrer">Getränkekarte</a> <a href="/pdf/gallus-getraenkekarte-2026.pdf" class="card-link" target="_blank" rel="noopener noreferrer">Getränkekarte</a>
<h3 class="monats-hit">Monats Hit</h3> <h3 class="monats-hit">Monats Hit</h3>
<div class="mate-vodka"> <div class="mate-vodka">
<div class="circle" title="Mate Vodka"> <div class="circle" title="mit oder ohne Schuss ;)">
<img src="/images/MonthlyHit.png" alt="Monats Hit" class="circle-image" /> <img src="/images/content/mit-oder-ohne-schuss.avif" alt="Monats Hit" class="circle-image" />
<span class="circle-label"></span> <span class="circle-label"></span>
</div> </div>
<div>Mate Vodka</div> <div>mit oder ohne Schuss ;)</div>
</div> </div>
<p class="note"> <p class="note">
@@ -28,15 +28,15 @@ const { id } = Astro.props;
<div class="circle-row"> <div class="circle-row">
<div class="circle whiskey-circle" title="Whiskey 1"> <div class="circle whiskey-circle" title="Whiskey 1">
<img src="/images/whiskey/Whiskey1.png" alt="Whiskey 1" class="circle-image" /> <img src="/images/content/whiskey-1.avif" alt="Whiskey 1" class="circle-image" />
<span class="circle-label"></span> <span class="circle-label"></span>
</div> </div>
<div class="circle whiskey-circle" title="Whiskey 2"> <div class="circle whiskey-circle" title="Whiskey 2">
<img src="/images/whiskey/Whiskey2.png" alt="Whiskey 2" class="circle-image" /> <img src="/images/content/whiskey-2.avif" alt="Whiskey 2" class="circle-image" />
<span class="circle-label"></span> <span class="circle-label"></span>
</div> </div>
<div class="circle whiskey-circle" title="Whiskey 3"> <div class="circle whiskey-circle" title="Whiskey 3">
<img src="/images/whiskey/Whiskey3.png" alt="Whiskey 3" class="circle-image" /> <img src="/images/content/whiskey-3.avif" alt="Whiskey 3" class="circle-image" />
<span class="circle-label"></span> <span class="circle-label"></span>
</div> </div>
</div> </div>
+48 -24
View File
@@ -28,27 +28,20 @@ Plätze sind begrenzt! Jetzt reservieren unter 🍀WA 077 232 27 70
`, `,
}, },
{ {
image: "/images/events/msogsj4l-bmlcxc.jpeg", image: "/images/events/noeg.avif",
title: "SG Fest", title: "NOeg",
date: "2026-08-14", date: "2026-09-16",
description: ` description: `
Das Stadt-Fest steht vor der Tür! Wir haben OFFEN ab 17:00Uhr mit Karaoke! Wir freuen uns auf euch! 🎤 Singe die größten Hits auf Spanisch, Italienisch, Französisch, Türkisch oder vielleicht sogar Japanisch?!
✨ Lass das Gallus zum Leben erwachen bei unserer multilingualen Karaoke-Nacht!
`, `,
}, },
{ {
image: "/images/events/mr82c7eh-yp87hy.jpeg", image: "/images/events/yutaro.avif",
title: "Richie Live", title: "Yutaro",
date: "2026-08-27", date: "2026-09-17",
description: ` description: `
SOLD OUT! - Richie hat das Image eines Weltenbummlers und Musikkünstlers. In der Ostschweiz ist er bereits bestens bekannt.🍀Warteliste: WA 077 232 27 70 Mit seiner Gitarre und seiner markanten Stimme verbindet er Singer-Songwriter-Sound mit Einflüssen aus Rock, Blues und Progressive Rock. 🍀Reservationen🍀via WA 077 232 27 70
`,
},
{
image: "/images/events/mrnpux59-b3075p.jpeg",
title: "Hannes LIVE",
date: "2026-09-10",
description: `
Er ist zurück!🥰🍀Johannes ist wieder bei uns! Eine weitere unvergessliche Irish-Night!!🍀Sei dabei! Reservationen🍀via WA 077 232 27 70
`, `,
}, },
{ {
@@ -59,21 +52,52 @@ Plätze sind begrenzt! Jetzt reservieren unter 🍀WA 077 232 27 70
SOLD OUT! - First time on stage und das direkt im Gallus Pub!🎤✨ SOLD OUT! - First time on stage und das direkt im Gallus Pub!🎤✨
Danielle & Nina bringen Irish Folk & liebevolle Cover Songs.🍀Warteliste: WA 077 232 27 70 Danielle & Nina bringen Irish Folk & liebevolle Cover Songs.🍀Warteliste: WA 077 232 27 70
`, `,
},
{
image: "/images/events/dirty-karaoke.avif",
title: "Dirty Karaoke",
date: "2026-10-07",
description: `
*Nicht für alle Ohren geeignet! Ob eindeutig zweideutig, hemmungslos daneben oder einfach nur zum Fremdschämen an diesem Abend gilt: Was gesungen wird, bleibt auf der Bühne.😉
`,
},
{
image: "/images/events/voices.avif",
title: "Voices",
date: "2026-10-22",
description: `
Ein Abend vol­ler Songs, Stim­men & Live Mu­sic.
Mit besonderen Stimmen, guter Stimmung und ganz viel musikalischer Energie. 🍀Reservationen🍀via WA 077 232 27 70
`,
},
{
image: "/images/events/disney-karaoke.avif",
title: "Disney Karaoke",
date: "2026-10-28",
description: `
Die nächste Disney Karaoke Night steht an und passend zur Gruselweek: wer mit Grusel-Kostüm kommt, kriegt einen Glitzer-Shot auf's Haus.🎤✨
`,
},
{
image: "/images/events/katy-elias.avif",
title: "Katy & Elias",
date: "2026-11-21",
description: `
ACHTUNG Kurz-Konzert 19:30-20:30! Danach gehts für die beiden direkt weiter zu einem zweiten Konzert in der Lukas Bar in St. Georgen.
🍀Reservationen🍀via WA 077 232 27 70
`,
} }
]; ];
const images = [ const images = [
{ src: "/images/gallery/mndhrrtl-s4o8dn.png", alt: "mndhrrtl-s4o8dn.png" },
{ src: "/images/gallery/mndhtjvz-965yra.jpeg", alt: "mndhtjvz-965yra.jpeg" }, { src: "/images/gallery/mndhtjvz-965yra.jpeg", alt: "mndhtjvz-965yra.jpeg" },
{ src: "/images/gallery/mndi3625-lnnggc.jpeg", alt: "mndi3625-lnnggc.jpeg" },
{ src: "/images/gallery/mndi7upa-qxukqg.jpeg", alt: "mndi7upa-qxukqg.jpeg" }, { src: "/images/gallery/mndi7upa-qxukqg.jpeg", alt: "mndi7upa-qxukqg.jpeg" },
{ src: "/images/gallery/mndi8kzg-oazjp8.jpeg", alt: "mndi8kzg-oazjp8.jpeg" },
{ src: "/images/gallery/mndiabuo-m29xj0.jpeg", alt: "mndiabuo-m29xj0.jpeg" },
{ src: "/images/gallery/mndicr59-n8suoy.jpeg", alt: "mndicr59-n8suoy.jpeg" },
{ src: "/images/gallery/mndieqns-mjs7xe.jpeg", alt: "mndieqns-mjs7xe.jpeg" },
{ src: "/images/gallery/mndif8nv-aptjf6.jpeg", alt: "mndif8nv-aptjf6.jpeg" }, { src: "/images/gallery/mndif8nv-aptjf6.jpeg", alt: "mndif8nv-aptjf6.jpeg" },
{ src: "/images/gallery/mndiggta-hnldkv.jpeg", alt: "mndiggta-hnldkv.jpeg" }, { src: "/images/gallery/halloween.avif", alt: "Halloween" },
{ src: "/images/gallery/mndih8lp-woprt4.webp", alt: "mndih8lp-woprt4.webp" } { src: "/images/gallery/sangallerruum.avif", alt: "Sangallerruum" },
{ src: "/images/gallery/welovegalluspub.avif", alt: "WeLoveGallusPub" },
{ src: "/images/gallery/raphi-sabrina.jpeg", alt: "Raphi&Sabrina" },
{ src: "/images/gallery/pubquiz.avif", alt: "PubQuiz" }
]; ];
--- ---