-- Klarbild — Initiales Schema (siehe 03-datenmodell-api.md) -- Wird beim Start automatisch angewandt (nummerierte SQL-Dateien, keine ORM-Magie). CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- Nutzerinnen ------------------------------------------------------------- CREATE TABLE IF NOT EXISTS users ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), username text UNIQUE NOT NULL, password_hash text NOT NULL, role text NOT NULL DEFAULT 'user' CHECK (role IN ('user','admin')), display_name text, created_at timestamptz NOT NULL DEFAULT now() ); -- Rezepte (gespeicherte Voreinstellungen) -------------------------------- CREATE TABLE IF NOT EXISTS recipes ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, tasks jsonb NOT NULL DEFAULT '[]'::jsonb, -- z.B. ["clean","format","deliver"] output_format text, orientation text, crop_mode text, dpi int NOT NULL DEFAULT 300, contour_mm numeric, model_key text, delivery text NOT NULL DEFAULT 'library' CHECK (delivery IN ('library','picdrop','both')), picdrop_gallery text, custom_instruction text, is_default bool NOT NULL DEFAULT false, created_by uuid REFERENCES users(id) ON DELETE SET NULL, created_at timestamptz NOT NULL DEFAULT now() ); -- Aufträge (ein Stapel) --------------------------------------------------- CREATE TABLE IF NOT EXISTS jobs ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), created_by uuid REFERENCES users(id) ON DELETE SET NULL, origin text NOT NULL DEFAULT 'web' CHECK (origin IN ('web','telegram')), recipe_snapshot jsonb NOT NULL, status text NOT NULL DEFAULT 'queued' CHECK (status IN ('queued','running','paused','done','cancelled')), total int NOT NULL DEFAULT 0, done_count int NOT NULL DEFAULT 0, failed_count int NOT NULL DEFAULT 0, created_at timestamptz NOT NULL DEFAULT now(), finished_at timestamptz ); -- Ordner ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS folders ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, picdrop_gallery text, created_at timestamptz NOT NULL DEFAULT now() ); -- Positionen (ein Bild in einem Auftrag) --------------------------------- CREATE TABLE IF NOT EXISTS items ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), job_id uuid REFERENCES jobs(id) ON DELETE CASCADE, position int NOT NULL DEFAULT 0, status text NOT NULL DEFAULT 'queued' CHECK (status IN ('queued','running','done','failed','skipped')), attempts int NOT NULL DEFAULT 0, error_message text, source_path text, result_path text, filename text, output_px text, source_quality text CHECK (source_quality IN ('original','compressed')), dpi int, has_alpha bool NOT NULL DEFAULT false, model_used text, prompt_used text, cost numeric, -- tatsächliche Kosten aus usage.cost folder_id uuid REFERENCES folders(id) ON DELETE SET NULL, delivery_status text NOT NULL DEFAULT 'none' CHECK (delivery_status IN ('none','pending','delivered','failed')), delivered_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS items_job_idx ON items(job_id); CREATE INDEX IF NOT EXISTS items_created_idx ON items(created_at DESC); -- Druckaufträge ----------------------------------------------------------- CREATE TABLE IF NOT EXISTS print_jobs ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), created_by uuid REFERENCES users(id) ON DELETE SET NULL, note text, status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','done')), created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS print_job_items ( print_job_id uuid REFERENCES print_jobs(id) ON DELETE CASCADE, item_id uuid REFERENCES items(id) ON DELETE CASCADE, PRIMARY KEY (print_job_id, item_id) ); -- Freigabelinks ----------------------------------------------------------- CREATE TABLE IF NOT EXISTS share_links ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), token text UNIQUE NOT NULL, scope_type text NOT NULL CHECK (scope_type IN ('selection','folder')), scope_ref jsonb NOT NULL, expires_at timestamptz, password_hash text, revoked bool NOT NULL DEFAULT false, created_at timestamptz NOT NULL DEFAULT now() ); -- Telegram --------------------------------------------------------------- CREATE TABLE IF NOT EXISTS telegram_links ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), chat_id bigint UNIQUE NOT NULL, user_id uuid REFERENCES users(id) ON DELETE CASCADE, default_recipe_id uuid REFERENCES recipes(id) ON DELETE SET NULL, active bool NOT NULL DEFAULT true, linked_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS telegram_pairing_codes ( code text PRIMARY KEY, user_id uuid REFERENCES users(id) ON DELETE CASCADE, expires_at timestamptz NOT NULL, used_at timestamptz ); CREATE TABLE IF NOT EXISTS telegram_drafts ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), chat_id bigint NOT NULL, media_group_id text, file_refs jsonb NOT NULL DEFAULT '[]'::jsonb, last_received_at timestamptz NOT NULL DEFAULT now(), status text NOT NULL DEFAULT 'collecting' CHECK (status IN ('collecting','awaiting_recipe','dispatched','discarded')), notice_message_id bigint ); -- Einstellungen (eine Zeile) --------------------------------------------- CREATE TABLE IF NOT EXISTS settings ( id int PRIMARY KEY DEFAULT 1 CHECK (id = 1), openrouter_key_enc text, picdrop_host text, picdrop_protocol text CHECK (picdrop_protocol IN ('ftps','sftp')), picdrop_port int, picdrop_user text, picdrop_password_enc text, picdrop_base_path text, default_dpi int NOT NULL DEFAULT 300, default_crop_mode text NOT NULL DEFAULT 'crop', concurrency int NOT NULL DEFAULT 2, cricut_sheet_cm text NOT NULL DEFAULT '17.1x23.5', monthly_budget numeric, n8n_webhook_url text, telegram_bot_token_enc text, telegram_webhook_secret text ); -- Modelle ----------------------------------------------------------------- CREATE TABLE IF NOT EXISTS models ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), model_id text NOT NULL, label text NOT NULL, description text, active bool NOT NULL DEFAULT true, is_default bool NOT NULL DEFAULT false, supports_alpha bool NOT NULL DEFAULT false, sort int NOT NULL DEFAULT 0 );