feat: pictures table, Hetzner S3 upload/delete, auto-migration

- pictures table with UUID, status enum, timestamps, blurhash, design
- Auto-trigger updates updated_at on every row change
- POST /api/pictures/:id/upload  → upload file to Hetzner snakkimo bucket
- DELETE /api/pictures/:id       → removes DB row + Hetzner file
- PATCH /api/pictures/:id        → auto-sets published/blocked timestamps
- Migration runs on every server start (idempotent)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 13:39:16 +02:00
parent b82a468197
commit 0f35459b86
6 changed files with 1145 additions and 8 deletions

40
src/db-migrate.js Normal file
View File

@@ -0,0 +1,40 @@
const { query } = require('./db');
async function migrate() {
await query(`
CREATE TABLE IF NOT EXISTS pictures (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
status VARCHAR(20) NOT NULL DEFAULT 'uploaded'
CHECK (status IN ('uploaded', 'published', 'blocked')),
blocked_reason VARCHAR(20) CHECK (blocked_reason IN ('regenerate', 'not_to_use')),
generation_prompt TEXT,
generation_timestamp TIMESTAMPTZ,
generation_duration_s NUMERIC(10,3),
published_timestamp TIMESTAMPTZ,
blocked_timestamp TIMESTAMPTZ,
blurhash TEXT,
picture_link TEXT,
design TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
await query(`
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN NEW.updated_at = NOW(); RETURN NEW; END;
$$ LANGUAGE plpgsql
`);
await query(`
DROP TRIGGER IF EXISTS pictures_updated_at ON pictures;
CREATE TRIGGER pictures_updated_at
BEFORE UPDATE ON pictures
FOR EACH ROW EXECUTE FUNCTION update_updated_at()
`);
console.log('Migration complete: pictures table ready');
}
module.exports = migrate;