-- +goose Up -- +goose StatementBegin CREATE TABLE IF NOT EXISTS public.request_settings ( id boolean PRIMARY KEY DEFAULT true, requests_enabled boolean NOT NULL DEFAULT false, global_max_requests integer NOT NULL DEFAULT 5, global_window_days integer NOT NULL DEFAULT 7, global_auto_approval_enabled boolean NOT NULL DEFAULT false, created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT request_settings_singleton CHECK (id), CONSTRAINT request_settings_global_max_nonnegative CHECK (global_max_requests >= 0), CONSTRAINT request_settings_global_window_positive CHECK (global_window_days > 0) ); INSERT INTO public.request_settings (id) VALUES (true) ON CONFLICT (id) DO NOTHING; CREATE TABLE IF NOT EXISTS public.request_user_limits ( user_id integer PRIMARY KEY REFERENCES public.users(id) ON DELETE CASCADE, limit_mode text NOT NULL DEFAULT 'inherit', max_requests integer, window_days integer, approval_mode text NOT NULL DEFAULT 'inherit', updated_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT request_user_limits_limit_mode_check CHECK (limit_mode IN ('inherit', 'custom', 'unlimited', 'blocked')), CONSTRAINT request_user_limits_approval_mode_check CHECK (approval_mode IN ('inherit', 'manual', 'auto', 'blocked')), CONSTRAINT request_user_limits_max_nonnegative CHECK (max_requests IS NULL OR max_requests >= 0), CONSTRAINT request_user_limits_window_positive CHECK (window_days IS NULL OR window_days > 0) ); CREATE TABLE IF NOT EXISTS public.request_integrations ( kind text PRIMARY KEY, enabled boolean NOT NULL DEFAULT false, base_url text NOT NULL DEFAULT '', api_key_ref text NOT NULL DEFAULT '', root_folder text NOT NULL DEFAULT '', quality_profile_id integer, tags integer[] NOT NULL DEFAULT '{}', options jsonb NOT NULL DEFAULT '{}'::jsonb, last_check_at timestamp with time zone, last_check_status text NOT NULL DEFAULT '', last_check_error text NOT NULL DEFAULT '', created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT request_integrations_kind_check CHECK (kind IN ('radarr', 'sonarr')) ); CREATE TABLE IF NOT EXISTS public.media_requests ( id text PRIMARY KEY, provider text NOT NULL DEFAULT 'tmdb', media_type text NOT NULL, tmdb_id integer NOT NULL, tvdb_id integer, imdb_id text NOT NULL DEFAULT '', title text NOT NULL, year integer, overview text NOT NULL DEFAULT '', poster_path text NOT NULL DEFAULT '', backdrop_path text NOT NULL DEFAULT '', status text NOT NULL, outcome text NOT NULL DEFAULT 'active', requested_by_user_id integer NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, requested_by_profile_id text NOT NULL DEFAULT '', integration_kind text NOT NULL DEFAULT '', external_id text NOT NULL DEFAULT '', external_status text NOT NULL DEFAULT '', last_error text NOT NULL DEFAULT '', created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, approved_at timestamp with time zone, completed_at timestamp with time zone, CONSTRAINT media_requests_provider_check CHECK (provider IN ('tmdb')), CONSTRAINT media_requests_media_type_check CHECK (media_type IN ('movie', 'series')), CONSTRAINT media_requests_status_check CHECK (status IN ('pending', 'approved', 'queued', 'downloading', 'completed')), CONSTRAINT media_requests_outcome_check CHECK (outcome IN ('active', 'declined', 'cancelled', 'failed')), CONSTRAINT media_requests_tmdb_positive CHECK (tmdb_id > 0) ); CREATE UNIQUE INDEX IF NOT EXISTS idx_media_requests_active_tmdb ON public.media_requests (media_type, provider, tmdb_id) WHERE outcome = 'active' AND status <> 'completed'; CREATE INDEX IF NOT EXISTS idx_media_requests_user_created ON public.media_requests (requested_by_user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_media_requests_profile_created ON public.media_requests (requested_by_profile_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_media_requests_status_created ON public.media_requests (status, created_at DESC) WHERE outcome = 'active'; CREATE INDEX IF NOT EXISTS idx_media_requests_outcome_created ON public.media_requests (outcome, created_at DESC); CREATE TABLE IF NOT EXISTS public.media_request_events ( id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, request_id text NOT NULL REFERENCES public.media_requests(id) ON DELETE CASCADE, event_type text NOT NULL, actor_user_id integer REFERENCES public.users(id) ON DELETE SET NULL, actor_profile_id text NOT NULL DEFAULT '', message text NOT NULL DEFAULT '', metadata jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamp with time zone DEFAULT now() NOT NULL ); CREATE INDEX IF NOT EXISTS idx_media_request_events_request_created ON public.media_request_events (request_id, created_at DESC); -- +goose StatementEnd -- +goose Down -- +goose StatementBegin DROP TABLE IF EXISTS public.media_request_events; DROP INDEX IF EXISTS public.idx_media_requests_outcome_created; DROP INDEX IF EXISTS public.idx_media_requests_status_created; DROP INDEX IF EXISTS public.idx_media_requests_profile_created; DROP INDEX IF EXISTS public.idx_media_requests_user_created; DROP INDEX IF EXISTS public.idx_media_requests_active_tmdb; DROP TABLE IF EXISTS public.media_requests; DROP TABLE IF EXISTS public.request_integrations; DROP TABLE IF EXISTS public.request_user_limits; DROP TABLE IF EXISTS public.request_settings; -- +goose StatementEnd