init
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS gifts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
slug VARCHAR(100) NOT NULL UNIQUE,
|
||||
recipient_name TEXT NOT NULL,
|
||||
sender_name TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
intro_message TEXT NOT NULL,
|
||||
reveal_message TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'published',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT gifts_status_check CHECK (status IN ('draft', 'published', 'archived'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS gift_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
gift_id UUID NOT NULL REFERENCES gifts(id) ON DELETE CASCADE,
|
||||
type TEXT NOT NULL,
|
||||
title TEXT,
|
||||
text TEXT,
|
||||
media_url TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT gift_items_type_check CHECK (length(trim(type)) > 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS gift_items_gift_id_sort_order_idx
|
||||
ON gift_items (gift_id, sort_order, id);
|
||||
@@ -0,0 +1,91 @@
|
||||
INSERT INTO gifts (
|
||||
id,
|
||||
slug,
|
||||
recipient_name,
|
||||
sender_name,
|
||||
title,
|
||||
intro_message,
|
||||
reveal_message,
|
||||
status
|
||||
)
|
||||
VALUES (
|
||||
'11111111-1111-4111-8111-111111111111',
|
||||
'demo',
|
||||
'Anna',
|
||||
'Alex',
|
||||
'A little surprise for you',
|
||||
'I made something for you.',
|
||||
'You make ordinary days feel like something worth remembering. Here is to all the little adventures still ahead.',
|
||||
'published'
|
||||
)
|
||||
ON CONFLICT (slug) DO UPDATE SET
|
||||
recipient_name = EXCLUDED.recipient_name,
|
||||
sender_name = EXCLUDED.sender_name,
|
||||
title = EXCLUDED.title,
|
||||
intro_message = EXCLUDED.intro_message,
|
||||
reveal_message = EXCLUDED.reveal_message,
|
||||
status = EXCLUDED.status,
|
||||
updated_at = NOW();
|
||||
|
||||
INSERT INTO gift_items (id, gift_id, type, title, text, media_url, sort_order, metadata)
|
||||
SELECT
|
||||
'22222222-2222-4222-8222-222222222222',
|
||||
id,
|
||||
'image',
|
||||
'The good kind of ordinary.',
|
||||
'The tiny moments are usually the ones that stay with us the longest.',
|
||||
'https://images.unsplash.com/photo-1511988617509-8e73b0f3b7d2?auto=format&fit=crop&w=1400&q=85',
|
||||
1,
|
||||
'{"caption":"A little snapshot of a very good day.","accent":"coral"}'::jsonb
|
||||
FROM gifts
|
||||
WHERE slug = 'demo'
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
gift_id = EXCLUDED.gift_id,
|
||||
type = EXCLUDED.type,
|
||||
title = EXCLUDED.title,
|
||||
text = EXCLUDED.text,
|
||||
media_url = EXCLUDED.media_url,
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
metadata = EXCLUDED.metadata;
|
||||
|
||||
INSERT INTO gift_items (id, gift_id, type, title, text, media_url, sort_order, metadata)
|
||||
SELECT
|
||||
'33333333-3333-4333-8333-333333333333',
|
||||
id,
|
||||
'text',
|
||||
'A note for your next chapter.',
|
||||
'Keep choosing the places, people, and tiny rituals that make you feel most like yourself. I will always be cheering for you.',
|
||||
NULL,
|
||||
2,
|
||||
'{"eyebrow":"A note from me","accent":"lavender"}'::jsonb
|
||||
FROM gifts
|
||||
WHERE slug = 'demo'
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
gift_id = EXCLUDED.gift_id,
|
||||
type = EXCLUDED.type,
|
||||
title = EXCLUDED.title,
|
||||
text = EXCLUDED.text,
|
||||
media_url = EXCLUDED.media_url,
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
metadata = EXCLUDED.metadata;
|
||||
|
||||
INSERT INTO gift_items (id, gift_id, type, title, text, media_url, sort_order, metadata)
|
||||
SELECT
|
||||
'44444444-4444-4444-8444-444444444444',
|
||||
id,
|
||||
'text',
|
||||
'One more thing.',
|
||||
'There is nowhere else I would rather be than somewhere in the middle of the next story with you.',
|
||||
NULL,
|
||||
3,
|
||||
'{"eyebrow":"P.S.","accent":"gold"}'::jsonb
|
||||
FROM gifts
|
||||
WHERE slug = 'demo'
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
gift_id = EXCLUDED.gift_id,
|
||||
type = EXCLUDED.type,
|
||||
title = EXCLUDED.title,
|
||||
text = EXCLUDED.text,
|
||||
media_url = EXCLUDED.media_url,
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
metadata = EXCLUDED.metadata;
|
||||
@@ -0,0 +1,92 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS users_email_lower_idx ON users (LOWER(email));
|
||||
|
||||
CREATE TABLE IF NOT EXISTS galleries (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
slug VARCHAR(160) NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
client_name TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
password_hash TEXT,
|
||||
downloads_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
favorites_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
download_all_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
watermark_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
expires_at TIMESTAMPTZ,
|
||||
cover_media_id UUID,
|
||||
theme_config JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
branding_config JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
published_at TIMESTAMPTZ,
|
||||
CONSTRAINT galleries_status_check CHECK (status IN ('draft', 'published', 'archived'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS galleries_user_id_created_at_idx ON galleries (user_id, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS media (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
gallery_id UUID NOT NULL REFERENCES galleries(id) ON DELETE CASCADE,
|
||||
original_filename TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
storage_key TEXT NOT NULL,
|
||||
external_url TEXT,
|
||||
thumbnail_key TEXT,
|
||||
preview_key TEXT,
|
||||
processing_status TEXT NOT NULL DEFAULT 'UPLOADING',
|
||||
processing_error TEXT,
|
||||
width INTEGER,
|
||||
height INTEGER,
|
||||
duration_seconds DOUBLE PRECISION,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT media_processing_status_check CHECK (processing_status IN ('UPLOADING', 'PROCESSING', 'READY', 'FAILED'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_gallery_sort_order_idx ON media (gallery_id, sort_order, id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS favorites (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
gallery_id UUID NOT NULL REFERENCES galleries(id) ON DELETE CASCADE,
|
||||
media_id UUID NOT NULL REFERENCES media(id) ON DELETE CASCADE,
|
||||
visitor_id TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (gallery_id, media_id, visitor_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS downloads (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
gallery_id UUID NOT NULL REFERENCES galleries(id) ON DELETE CASCADE,
|
||||
media_id UUID REFERENCES media(id) ON DELETE SET NULL,
|
||||
visitor_id TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS downloads_gallery_created_at_idx ON downloads (gallery_id, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS download_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
gallery_id UUID NOT NULL REFERENCES galleries(id) ON DELETE CASCADE,
|
||||
visitor_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'QUEUED',
|
||||
storage_key TEXT,
|
||||
error TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ,
|
||||
CONSTRAINT download_jobs_status_check CHECK (status IN ('QUEUED', 'PROCESSING', 'READY', 'FAILED'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS download_jobs_gallery_visitor_idx ON download_jobs (gallery_id, visitor_id, created_at DESC);
|
||||
@@ -0,0 +1,29 @@
|
||||
CREATE TABLE IF NOT EXISTS gifts (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
recipient_name TEXT NOT NULL,
|
||||
sender_name TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
intro_message TEXT NOT NULL,
|
||||
reveal_message TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'published',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT gifts_status_check CHECK (status IN ('draft', 'published', 'archived'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS gift_items (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
gift_id TEXT NOT NULL REFERENCES gifts(id) ON DELETE CASCADE,
|
||||
type TEXT NOT NULL,
|
||||
title TEXT,
|
||||
text TEXT,
|
||||
media_url TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
metadata TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT gift_items_type_check CHECK (length(trim(type)) > 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS gift_items_gift_id_sort_order_idx
|
||||
ON gift_items (gift_id, sort_order, id);
|
||||
@@ -0,0 +1,91 @@
|
||||
INSERT INTO gifts (
|
||||
id,
|
||||
slug,
|
||||
recipient_name,
|
||||
sender_name,
|
||||
title,
|
||||
intro_message,
|
||||
reveal_message,
|
||||
status
|
||||
)
|
||||
VALUES (
|
||||
'11111111-1111-4111-8111-111111111111',
|
||||
'demo',
|
||||
'Anna',
|
||||
'Alex',
|
||||
'A little surprise for you',
|
||||
'I made something for you.',
|
||||
'You make ordinary days feel like something worth remembering. Here is to all the little adventures still ahead.',
|
||||
'published'
|
||||
)
|
||||
ON CONFLICT (slug) DO UPDATE SET
|
||||
recipient_name = excluded.recipient_name,
|
||||
sender_name = excluded.sender_name,
|
||||
title = excluded.title,
|
||||
intro_message = excluded.intro_message,
|
||||
reveal_message = excluded.reveal_message,
|
||||
status = excluded.status,
|
||||
updated_at = CURRENT_TIMESTAMP;
|
||||
|
||||
INSERT INTO gift_items (id, gift_id, type, title, text, media_url, sort_order, metadata)
|
||||
SELECT
|
||||
'22222222-2222-4222-8222-222222222222',
|
||||
id,
|
||||
'image',
|
||||
'The good kind of ordinary.',
|
||||
'The tiny moments are usually the ones that stay with us the longest.',
|
||||
'https://images.unsplash.com/photo-1511988617509-8e73b0f3b7d2?auto=format&fit=crop&w=1400&q=85',
|
||||
1,
|
||||
'{"caption":"A little snapshot of a very good day.","accent":"coral"}'
|
||||
FROM gifts
|
||||
WHERE slug = 'demo'
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
gift_id = excluded.gift_id,
|
||||
type = excluded.type,
|
||||
title = excluded.title,
|
||||
text = excluded.text,
|
||||
media_url = excluded.media_url,
|
||||
sort_order = excluded.sort_order,
|
||||
metadata = excluded.metadata;
|
||||
|
||||
INSERT INTO gift_items (id, gift_id, type, title, text, media_url, sort_order, metadata)
|
||||
SELECT
|
||||
'33333333-3333-4333-8333-333333333333',
|
||||
id,
|
||||
'text',
|
||||
'A note for your next chapter.',
|
||||
'Keep choosing the places, people, and tiny rituals that make you feel most like yourself. I will always be cheering for you.',
|
||||
NULL,
|
||||
2,
|
||||
'{"eyebrow":"A note from me","accent":"lavender"}'
|
||||
FROM gifts
|
||||
WHERE slug = 'demo'
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
gift_id = excluded.gift_id,
|
||||
type = excluded.type,
|
||||
title = excluded.title,
|
||||
text = excluded.text,
|
||||
media_url = excluded.media_url,
|
||||
sort_order = excluded.sort_order,
|
||||
metadata = excluded.metadata;
|
||||
|
||||
INSERT INTO gift_items (id, gift_id, type, title, text, media_url, sort_order, metadata)
|
||||
SELECT
|
||||
'44444444-4444-4444-8444-444444444444',
|
||||
id,
|
||||
'text',
|
||||
'One more thing.',
|
||||
'There is nowhere else I would rather be than somewhere in the middle of the next story with you.',
|
||||
NULL,
|
||||
3,
|
||||
'{"eyebrow":"P.S.","accent":"gold"}'
|
||||
FROM gifts
|
||||
WHERE slug = 'demo'
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
gift_id = excluded.gift_id,
|
||||
type = excluded.type,
|
||||
title = excluded.title,
|
||||
text = excluded.text,
|
||||
media_url = excluded.media_url,
|
||||
sort_order = excluded.sort_order,
|
||||
metadata = excluded.metadata;
|
||||
@@ -0,0 +1,92 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS users_email_lower_idx ON users (lower(email));
|
||||
|
||||
CREATE TABLE IF NOT EXISTS galleries (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
client_name TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
password_hash TEXT,
|
||||
downloads_enabled INTEGER NOT NULL DEFAULT 1,
|
||||
favorites_enabled INTEGER NOT NULL DEFAULT 1,
|
||||
download_all_enabled INTEGER NOT NULL DEFAULT 1,
|
||||
watermark_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
expires_at TEXT,
|
||||
cover_media_id TEXT,
|
||||
theme_config TEXT NOT NULL DEFAULT '{}',
|
||||
branding_config TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
published_at TEXT,
|
||||
CONSTRAINT galleries_status_check CHECK (status IN ('draft', 'published', 'archived'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS galleries_user_id_created_at_idx ON galleries (user_id, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS media (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
gallery_id TEXT NOT NULL REFERENCES galleries(id) ON DELETE CASCADE,
|
||||
original_filename TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
file_size INTEGER NOT NULL DEFAULT 0,
|
||||
storage_key TEXT NOT NULL,
|
||||
external_url TEXT,
|
||||
thumbnail_key TEXT,
|
||||
preview_key TEXT,
|
||||
processing_status TEXT NOT NULL DEFAULT 'UPLOADING',
|
||||
processing_error TEXT,
|
||||
width INTEGER,
|
||||
height INTEGER,
|
||||
duration_seconds REAL,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT media_processing_status_check CHECK (processing_status IN ('UPLOADING', 'PROCESSING', 'READY', 'FAILED'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_gallery_sort_order_idx ON media (gallery_id, sort_order, id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS favorites (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
gallery_id TEXT NOT NULL REFERENCES galleries(id) ON DELETE CASCADE,
|
||||
media_id TEXT NOT NULL REFERENCES media(id) ON DELETE CASCADE,
|
||||
visitor_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE (gallery_id, media_id, visitor_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS downloads (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
gallery_id TEXT NOT NULL REFERENCES galleries(id) ON DELETE CASCADE,
|
||||
media_id TEXT REFERENCES media(id) ON DELETE SET NULL,
|
||||
visitor_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS downloads_gallery_created_at_idx ON downloads (gallery_id, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS download_jobs (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
gallery_id TEXT NOT NULL REFERENCES galleries(id) ON DELETE CASCADE,
|
||||
visitor_id TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'QUEUED',
|
||||
storage_key TEXT,
|
||||
error TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at TEXT,
|
||||
CONSTRAINT download_jobs_status_check CHECK (status IN ('QUEUED', 'PROCESSING', 'READY', 'FAILED'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS download_jobs_gallery_visitor_idx ON download_jobs (gallery_id, visitor_id, created_at DESC);
|
||||
Reference in New Issue
Block a user