Add photo galleries API and frontend for public and admin viewing.

Introduces the Go photo-api service, nginx/systemd deploy wiring, and Next.js gallery/lightbox pages so event photos can be managed and browsed.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Michilis
2026-07-25 17:32:23 +00:00
co-authored by Cursor
parent 4772b85f3d
commit c9a600b6d6
61 changed files with 6912 additions and 7 deletions
+46
View File
@@ -0,0 +1,46 @@
CREATE TABLE IF NOT EXISTS photos_galleries (
id uuid PRIMARY KEY,
slug varchar(160) NOT NULL UNIQUE,
title varchar(200) NOT NULL,
title_es varchar(200),
description text,
description_es text,
event_id uuid,
visibility varchar(10) NOT NULL DEFAULT 'private'
CHECK (visibility IN ('public','private','link','ticket')),
share_token varchar(64) NOT NULL UNIQUE,
cover_photo_id uuid,
created_by uuid,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL
);
CREATE INDEX IF NOT EXISTS photos_galleries_event_idx ON photos_galleries (event_id);
CREATE INDEX IF NOT EXISTS photos_galleries_visibility_idx ON photos_galleries (visibility);
CREATE TABLE IF NOT EXISTS photos_photos (
id uuid PRIMARY KEY,
gallery_id uuid NOT NULL REFERENCES photos_galleries(id) ON DELETE CASCADE,
position integer NOT NULL DEFAULT 0,
original_key text NOT NULL,
original_filename text,
content_type varchar(50) NOT NULL,
size_bytes bigint NOT NULL,
width integer,
height integer,
thumb_key text,
preview_key text,
taken_at timestamptz,
status varchar(12) NOT NULL DEFAULT 'queued'
CHECK (status IN ('queued','processing','ready','failed')),
attempts integer NOT NULL DEFAULT 0,
next_attempt_at timestamptz NOT NULL,
last_error text,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL
);
CREATE INDEX IF NOT EXISTS photos_photos_gallery_pos_idx ON photos_photos (gallery_id, position);
CREATE INDEX IF NOT EXISTS photos_photos_queue_idx ON photos_photos (status, next_attempt_at) WHERE status IN ('queued','failed');
+46
View File
@@ -0,0 +1,46 @@
CREATE TABLE IF NOT EXISTS photos_galleries (
id text PRIMARY KEY,
slug text NOT NULL UNIQUE,
title text NOT NULL,
title_es text,
description text,
description_es text,
event_id text,
visibility text NOT NULL DEFAULT 'private'
CHECK (visibility IN ('public','private','link','ticket')),
share_token text NOT NULL UNIQUE,
cover_photo_id text,
created_by text,
created_at text NOT NULL,
updated_at text NOT NULL
);
CREATE INDEX IF NOT EXISTS photos_galleries_event_idx ON photos_galleries (event_id);
CREATE INDEX IF NOT EXISTS photos_galleries_visibility_idx ON photos_galleries (visibility);
CREATE TABLE IF NOT EXISTS photos_photos (
id text PRIMARY KEY,
gallery_id text NOT NULL REFERENCES photos_galleries(id) ON DELETE CASCADE,
position integer NOT NULL DEFAULT 0,
original_key text NOT NULL,
original_filename text,
content_type text NOT NULL,
size_bytes integer NOT NULL,
width integer,
height integer,
thumb_key text,
preview_key text,
taken_at text,
status text NOT NULL DEFAULT 'queued'
CHECK (status IN ('queued','processing','ready','failed')),
attempts integer NOT NULL DEFAULT 0,
next_attempt_at text NOT NULL,
last_error text,
created_at text NOT NULL,
updated_at text NOT NULL
);
CREATE INDEX IF NOT EXISTS photos_photos_gallery_pos_idx ON photos_photos (gallery_id, position);
CREATE INDEX IF NOT EXISTS photos_photos_queue_idx ON photos_photos (status, next_attempt_at) WHERE status IN ('queued','failed');
+8
View File
@@ -0,0 +1,8 @@
// Package migrations embeds the dual-dialect SQL migrations owned by
// photo-api. Drizzle (backend) never sees these tables.
package migrations
import "embed"
//go:embed *.sql
var FS embed.FS