From a047ffc0408559e303fb97b7fed6be49050a2c13 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 5 Sep 2026 14:09:45 +0000 Subject: [PATCH] chore: keep migrations inside deployment archive --- 000001_s1_s3.up.sql | 81 ------- 000002_s4_s7.up.sql | 64 ----- 000003_user_management.up.sql | 21 -- 000004_access_revocation.up.sql | 2 - 000005_internal_auth_nonces.up.sql | 6 - 000006_e_contract_playback_decisions.up.sql | 22 -- 000007_external_auth_license_cache.up.sql | 16 -- 000008_gateway_routing.up.sql | 86 ------- 000009_gateway_operations.up.sql | 19 -- 000010_license_entitlements.up.sql | 10 - 000011_e_address_directory.up.sql | 75 ------ 000012_remove_gateway.up.sql | 48 ---- 000013_e_address_observability.up.sql | 22 -- 000014_operations_closure.up.sql | 218 ------------------ 000015_e_address_refresh_rate_limit.up.sql | 2 - 000016_system_operations.up.sql | 90 -------- ...17_notifications_security_lifecycle.up.sql | 124 ---------- 000018_initial_setup.up.sql | 9 - 000019_e_address_group_rules.up.sql | 6 - 000020_playback_session_context.up.sql | 17 -- 000021_subtitle_sources.up.sql | 55 ----- 000022_metadata_provider_credentials.up.sql | 10 - 000023_user_engagement_and_bot.up.sql | 80 ------- 000024_daily_checkins.up.sql | 20 -- 000025_user_store_calendar_monitoring.up.sql | 34 --- 000026_telegram_brand_and_chat_state.up.sql | 9 - 000027_site_brand_settings.up.sql | 17 -- 000028_media_request_metadata.up.sql | 13 -- 000029_support_attachments.up.sql | 15 -- 000030_user_password_and_banner.up.sql | 14 -- 000031_login_background.up.sql | 3 - 000032_branding_image_limits.up.sql | 9 - 000033_negative_point_balances.up.sql | 5 - ...embership_limits_points_red_packets.up.sql | 54 ----- 000035_plugin_management_permission.up.sql | 17 -- 000036_plugin_service_actor.up.sql | 14 -- 000037_user_groups.up.sql | 34 --- 000038_store_settings.up.sql | 12 - 000039_telegram_command_auto_delete.up.sql | 3 - ...enable_telegram_command_auto_delete.up.sql | 9 - 000041_web_logins_not_playback_devices.up.sql | 10 - 000042_login_protection.up.sql | 11 - 000043_plugin_v11.up.sql | 104 --------- 000044_telegram_membership_signup.up.sql | 14 -- 000045_plugin_telegram_inbound_media.up.sql | 8 - 000046_plugin_telegram_chat_type.up.sql | 2 - 000047_plugin_telegram_send_message.up.sql | 3 - 000048_telegram_pro_user_duration.up.sql | 3 - 000049_telegram_group_cleanup.up.sql | 21 -- 000050_telegram_rebind_after_unbind.up.sql | 7 - 000051_storage_webhook_events.up.sql | 13 -- 000052_user_agent_security_rules.up.sql | 3 - 000053_precise_client_access_rules.up.sql | 3 - 000054_api_keys.up.sql | 13 -- 000055_playback_traffic.up.sql | 3 - 000056_playback_estimate.up.sql | 1 - 000057_admin_agent.up.sql | 60 ----- s-migrations/000001_s1_s3.up.sql | 81 ------- s-migrations/000002_s4_s7.up.sql | 64 ----- s-migrations/000003_user_management.up.sql | 21 -- s-migrations/000004_access_revocation.up.sql | 2 - .../000005_internal_auth_nonces.up.sql | 6 - ...00006_e_contract_playback_decisions.up.sql | 22 -- .../000007_external_auth_license_cache.up.sql | 16 -- s-migrations/000008_gateway_routing.up.sql | 86 ------- s-migrations/000009_gateway_operations.up.sql | 19 -- .../000010_license_entitlements.up.sql | 10 - .../000011_e_address_directory.up.sql | 75 ------ s-migrations/000012_remove_gateway.up.sql | 48 ---- .../000013_e_address_observability.up.sql | 22 -- s-migrations/000014_operations_closure.up.sql | 218 ------------------ ...000015_e_address_refresh_rate_limit.up.sql | 2 - s-migrations/000016_system_operations.up.sql | 90 -------- ...17_notifications_security_lifecycle.up.sql | 124 ---------- s-migrations/000018_initial_setup.up.sql | 9 - .../000019_e_address_group_rules.up.sql | 6 - .../000020_playback_session_context.up.sql | 17 -- s-migrations/000021_subtitle_sources.up.sql | 55 ----- ...00022_metadata_provider_credentials.up.sql | 10 - .../000023_user_engagement_and_bot.up.sql | 80 ------- s-migrations/000024_daily_checkins.up.sql | 20 -- ...0025_user_store_calendar_monitoring.up.sql | 34 --- ...00026_telegram_brand_and_chat_state.up.sql | 9 - .../000027_site_brand_settings.up.sql | 17 -- .../000028_media_request_metadata.up.sql | 13 -- .../000029_support_attachments.up.sql | 15 -- .../000030_user_password_and_banner.up.sql | 14 -- s-migrations/000031_login_background.up.sql | 3 - .../000032_branding_image_limits.up.sql | 9 - .../000033_negative_point_balances.up.sql | 5 - ...embership_limits_points_red_packets.up.sql | 54 ----- ...000035_plugin_management_permission.up.sql | 17 -- .../000036_plugin_service_actor.up.sql | 14 -- s-migrations/000037_user_groups.up.sql | 34 --- s-migrations/000038_store_settings.up.sql | 12 - ...000039_telegram_command_auto_delete.up.sql | 3 - ...enable_telegram_command_auto_delete.up.sql | 9 - ...041_web_logins_not_playback_devices.up.sql | 10 - s-migrations/000042_login_protection.up.sql | 11 - s-migrations/000043_plugin_v11.up.sql | 104 --------- .../000044_telegram_membership_signup.up.sql | 14 -- ...00045_plugin_telegram_inbound_media.up.sql | 8 - .../000046_plugin_telegram_chat_type.up.sql | 2 - ...000047_plugin_telegram_send_message.up.sql | 3 - .../000048_telegram_pro_user_duration.up.sql | 3 - .../000049_telegram_group_cleanup.up.sql | 21 -- ...000050_telegram_rebind_after_unbind.up.sql | 7 - .../000051_storage_webhook_events.up.sql | 13 -- .../000052_user_agent_security_rules.up.sql | 3 - .../000053_precise_client_access_rules.up.sql | 3 - s-migrations/000054_api_keys.up.sql | 13 -- s-migrations/000055_playback_traffic.up.sql | 3 - s-migrations/000056_playback_estimate.up.sql | 1 - s-migrations/000057_admin_agent.up.sql | 60 ----- 114 files changed, 3288 deletions(-) delete mode 100644 000001_s1_s3.up.sql delete mode 100644 000002_s4_s7.up.sql delete mode 100644 000003_user_management.up.sql delete mode 100644 000004_access_revocation.up.sql delete mode 100644 000005_internal_auth_nonces.up.sql delete mode 100644 000006_e_contract_playback_decisions.up.sql delete mode 100644 000007_external_auth_license_cache.up.sql delete mode 100644 000008_gateway_routing.up.sql delete mode 100644 000009_gateway_operations.up.sql delete mode 100644 000010_license_entitlements.up.sql delete mode 100644 000011_e_address_directory.up.sql delete mode 100644 000012_remove_gateway.up.sql delete mode 100644 000013_e_address_observability.up.sql delete mode 100644 000014_operations_closure.up.sql delete mode 100644 000015_e_address_refresh_rate_limit.up.sql delete mode 100644 000016_system_operations.up.sql delete mode 100644 000017_notifications_security_lifecycle.up.sql delete mode 100644 000018_initial_setup.up.sql delete mode 100644 000019_e_address_group_rules.up.sql delete mode 100644 000020_playback_session_context.up.sql delete mode 100644 000021_subtitle_sources.up.sql delete mode 100644 000022_metadata_provider_credentials.up.sql delete mode 100644 000023_user_engagement_and_bot.up.sql delete mode 100644 000024_daily_checkins.up.sql delete mode 100644 000025_user_store_calendar_monitoring.up.sql delete mode 100644 000026_telegram_brand_and_chat_state.up.sql delete mode 100644 000027_site_brand_settings.up.sql delete mode 100644 000028_media_request_metadata.up.sql delete mode 100644 000029_support_attachments.up.sql delete mode 100644 000030_user_password_and_banner.up.sql delete mode 100644 000031_login_background.up.sql delete mode 100644 000032_branding_image_limits.up.sql delete mode 100644 000033_negative_point_balances.up.sql delete mode 100644 000034_membership_limits_points_red_packets.up.sql delete mode 100644 000035_plugin_management_permission.up.sql delete mode 100644 000036_plugin_service_actor.up.sql delete mode 100644 000037_user_groups.up.sql delete mode 100644 000038_store_settings.up.sql delete mode 100644 000039_telegram_command_auto_delete.up.sql delete mode 100644 000040_enable_telegram_command_auto_delete.up.sql delete mode 100644 000041_web_logins_not_playback_devices.up.sql delete mode 100644 000042_login_protection.up.sql delete mode 100644 000043_plugin_v11.up.sql delete mode 100644 000044_telegram_membership_signup.up.sql delete mode 100644 000045_plugin_telegram_inbound_media.up.sql delete mode 100644 000046_plugin_telegram_chat_type.up.sql delete mode 100644 000047_plugin_telegram_send_message.up.sql delete mode 100644 000048_telegram_pro_user_duration.up.sql delete mode 100644 000049_telegram_group_cleanup.up.sql delete mode 100644 000050_telegram_rebind_after_unbind.up.sql delete mode 100644 000051_storage_webhook_events.up.sql delete mode 100644 000052_user_agent_security_rules.up.sql delete mode 100644 000053_precise_client_access_rules.up.sql delete mode 100644 000054_api_keys.up.sql delete mode 100644 000055_playback_traffic.up.sql delete mode 100644 000056_playback_estimate.up.sql delete mode 100644 000057_admin_agent.up.sql delete mode 100644 s-migrations/000001_s1_s3.up.sql delete mode 100644 s-migrations/000002_s4_s7.up.sql delete mode 100644 s-migrations/000003_user_management.up.sql delete mode 100644 s-migrations/000004_access_revocation.up.sql delete mode 100644 s-migrations/000005_internal_auth_nonces.up.sql delete mode 100644 s-migrations/000006_e_contract_playback_decisions.up.sql delete mode 100644 s-migrations/000007_external_auth_license_cache.up.sql delete mode 100644 s-migrations/000008_gateway_routing.up.sql delete mode 100644 s-migrations/000009_gateway_operations.up.sql delete mode 100644 s-migrations/000010_license_entitlements.up.sql delete mode 100644 s-migrations/000011_e_address_directory.up.sql delete mode 100644 s-migrations/000012_remove_gateway.up.sql delete mode 100644 s-migrations/000013_e_address_observability.up.sql delete mode 100644 s-migrations/000014_operations_closure.up.sql delete mode 100644 s-migrations/000015_e_address_refresh_rate_limit.up.sql delete mode 100644 s-migrations/000016_system_operations.up.sql delete mode 100644 s-migrations/000017_notifications_security_lifecycle.up.sql delete mode 100644 s-migrations/000018_initial_setup.up.sql delete mode 100644 s-migrations/000019_e_address_group_rules.up.sql delete mode 100644 s-migrations/000020_playback_session_context.up.sql delete mode 100644 s-migrations/000021_subtitle_sources.up.sql delete mode 100644 s-migrations/000022_metadata_provider_credentials.up.sql delete mode 100644 s-migrations/000023_user_engagement_and_bot.up.sql delete mode 100644 s-migrations/000024_daily_checkins.up.sql delete mode 100644 s-migrations/000025_user_store_calendar_monitoring.up.sql delete mode 100644 s-migrations/000026_telegram_brand_and_chat_state.up.sql delete mode 100644 s-migrations/000027_site_brand_settings.up.sql delete mode 100644 s-migrations/000028_media_request_metadata.up.sql delete mode 100644 s-migrations/000029_support_attachments.up.sql delete mode 100644 s-migrations/000030_user_password_and_banner.up.sql delete mode 100644 s-migrations/000031_login_background.up.sql delete mode 100644 s-migrations/000032_branding_image_limits.up.sql delete mode 100644 s-migrations/000033_negative_point_balances.up.sql delete mode 100644 s-migrations/000034_membership_limits_points_red_packets.up.sql delete mode 100644 s-migrations/000035_plugin_management_permission.up.sql delete mode 100644 s-migrations/000036_plugin_service_actor.up.sql delete mode 100644 s-migrations/000037_user_groups.up.sql delete mode 100644 s-migrations/000038_store_settings.up.sql delete mode 100644 s-migrations/000039_telegram_command_auto_delete.up.sql delete mode 100644 s-migrations/000040_enable_telegram_command_auto_delete.up.sql delete mode 100644 s-migrations/000041_web_logins_not_playback_devices.up.sql delete mode 100644 s-migrations/000042_login_protection.up.sql delete mode 100644 s-migrations/000043_plugin_v11.up.sql delete mode 100644 s-migrations/000044_telegram_membership_signup.up.sql delete mode 100644 s-migrations/000045_plugin_telegram_inbound_media.up.sql delete mode 100644 s-migrations/000046_plugin_telegram_chat_type.up.sql delete mode 100644 s-migrations/000047_plugin_telegram_send_message.up.sql delete mode 100644 s-migrations/000048_telegram_pro_user_duration.up.sql delete mode 100644 s-migrations/000049_telegram_group_cleanup.up.sql delete mode 100644 s-migrations/000050_telegram_rebind_after_unbind.up.sql delete mode 100644 s-migrations/000051_storage_webhook_events.up.sql delete mode 100644 s-migrations/000052_user_agent_security_rules.up.sql delete mode 100644 s-migrations/000053_precise_client_access_rules.up.sql delete mode 100644 s-migrations/000054_api_keys.up.sql delete mode 100644 s-migrations/000055_playback_traffic.up.sql delete mode 100644 s-migrations/000056_playback_estimate.up.sql delete mode 100644 s-migrations/000057_admin_agent.up.sql diff --git a/000001_s1_s3.up.sql b/000001_s1_s3.up.sql deleted file mode 100644 index 7139849..0000000 --- a/000001_s1_s3.up.sql +++ /dev/null @@ -1,81 +0,0 @@ -CREATE EXTENSION IF NOT EXISTS pgcrypto; - -CREATE TABLE users ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - username text NOT NULL, - username_normalized text NOT NULL UNIQUE, - email text NOT NULL, - email_normalized text NOT NULL UNIQUE, - role text NOT NULL DEFAULT 'user' CHECK (role IN ('user','admin')), - status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','disabled','deleted')), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - deleted_at timestamptz -); -CREATE TABLE credentials ( - user_id uuid PRIMARY KEY REFERENCES users(id), - password_hash text NOT NULL, - password_changed_at timestamptz NOT NULL DEFAULT now(), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE memberships ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), - status text NOT NULL CHECK (status IN ('active','expired','revoked')), - starts_at timestamptz NOT NULL, ends_at timestamptz NOT NULL, - source text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), - CHECK (ends_at > starts_at) -); -CREATE INDEX memberships_active_user_idx ON memberships(user_id, ends_at DESC) WHERE status = 'active'; -CREATE TABLE refresh_token_families ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), - revoked_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE refresh_tokens ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), family_id uuid NOT NULL REFERENCES refresh_token_families(id), - token_hash bytea NOT NULL UNIQUE, expires_at timestamptz NOT NULL, consumed_at timestamptz, - replaced_by uuid, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX refresh_tokens_family_idx ON refresh_tokens(family_id); -CREATE TABLE card_batches ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, card_type text NOT NULL CHECK (card_type IN ('registration','renewal')), - duration_days integer NOT NULL CHECK (duration_days > 0), created_by uuid REFERENCES users(id), - revoked_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE cards ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), batch_id uuid NOT NULL REFERENCES card_batches(id), - code_hash bytea NOT NULL UNIQUE, remaining_uses integer NOT NULL DEFAULT 1 CHECK (remaining_uses >= 0), - status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','revoked','exhausted')), - expires_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE card_usages ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), card_id uuid NOT NULL REFERENCES cards(id), user_id uuid NOT NULL REFERENCES users(id), - used_at timestamptz NOT NULL DEFAULT now(), UNIQUE(card_id, user_id) -); -CREATE INDEX card_usages_user_idx ON card_usages(user_id, used_at DESC); -CREATE TABLE audit_events ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, actor_user_id uuid REFERENCES users(id), action text NOT NULL, - target_type text NOT NULL, target_id text NOT NULL, detail jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX audit_events_target_idx ON audit_events(target_type, target_id, created_at DESC); -CREATE TABLE policy_rules ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), subject_type text NOT NULL CHECK (subject_type IN ('global','membership','user')), - subject_id uuid, priority integer NOT NULL DEFAULT 0, enabled boolean NOT NULL DEFAULT true, - max_devices integer, max_online integer, max_playbacks integer, max_device_playbacks integer, max_bitrate_kbps integer, - downloads_allowed boolean, proxy_allowed boolean, transcode_allowed boolean, library_id text, - allowed_cidrs text[] NOT NULL DEFAULT '{}', countries text[] NOT NULL DEFAULT '{}', regions text[] NOT NULL DEFAULT '{}', - asns bigint[] NOT NULL DEFAULT '{}', carriers text[] NOT NULL DEFAULT '{}', route_id text, created_at timestamptz NOT NULL DEFAULT now(), - CHECK ((subject_type = 'global' AND subject_id IS NULL) OR (subject_type <> 'global' AND subject_id IS NOT NULL)) -); -CREATE INDEX policy_rules_subject_idx ON policy_rules(subject_type, subject_id, priority DESC) WHERE enabled; -CREATE TABLE devices ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), fingerprint_hash bytea NOT NULL, - first_seen_at timestamptz NOT NULL DEFAULT now(), last_seen_at timestamptz NOT NULL DEFAULT now(), revoked_at timestamptz, - UNIQUE(user_id, fingerprint_hash) -); -CREATE TABLE playback_sessions ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), device_id uuid REFERENCES devices(id), - e_session_id text NOT NULL UNIQUE, library_id text NOT NULL, route_id text, state text NOT NULL CHECK (state IN ('reserved','active','released','expired')), - lease_token_hash bytea NOT NULL UNIQUE, expires_at timestamptz NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), released_at timestamptz -); -CREATE INDEX playback_sessions_active_user_idx ON playback_sessions(user_id, expires_at) WHERE state IN ('reserved','active'); -CREATE INDEX playback_sessions_active_device_idx ON playback_sessions(device_id, expires_at) WHERE state IN ('reserved','active'); diff --git a/000002_s4_s7.up.sql b/000002_s4_s7.up.sql deleted file mode 100644 index 664dfea..0000000 --- a/000002_s4_s7.up.sql +++ /dev/null @@ -1,64 +0,0 @@ -CREATE TABLE e_command_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), actor_user_id uuid NOT NULL REFERENCES users(id), - command text NOT NULL, target_id text NOT NULL, idempotency_key text NOT NULL UNIQUE, - state text NOT NULL CHECK (state IN ('accepted','completed','failed')), created_at timestamptz NOT NULL DEFAULT now(), completed_at timestamptz -); -CREATE INDEX e_command_requests_actor_idx ON e_command_requests(actor_user_id, created_at DESC); -CREATE TABLE e_event_inbox ( - event_id uuid PRIMARY KEY, event_type text NOT NULL, occurred_at timestamptz NOT NULL, - payload jsonb NOT NULL, received_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE invite_codes ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), creator_user_id uuid NOT NULL REFERENCES users(id), - code_hash bytea NOT NULL UNIQUE, max_uses integer NOT NULL DEFAULT 1 CHECK (max_uses > 0), used_count integer NOT NULL DEFAULT 0 CHECK (used_count >= 0), - expires_at timestamptz, revoked_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE invite_relations ( - parent_user_id uuid NOT NULL REFERENCES users(id), child_user_id uuid NOT NULL REFERENCES users(id), invite_code_id uuid NOT NULL REFERENCES invite_codes(id), - risk_state text NOT NULL DEFAULT 'normal' CHECK (risk_state IN ('normal','review','blocked')), detached_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY(parent_user_id, child_user_id), CHECK (parent_user_id <> child_user_id) -); -CREATE UNIQUE INDEX invite_relations_child_active_idx ON invite_relations(child_user_id) WHERE detached_at IS NULL; -CREATE TABLE media_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), query_text text NOT NULL, - e_media_id uuid, availability text NOT NULL CHECK (availability IN ('unknown','available','unavailable')), status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','planned','fulfilled','rejected','cancelled')), - created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX media_requests_status_idx ON media_requests(status, created_at DESC); -CREATE TABLE ticket_types (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL UNIQUE, enabled boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now()); -CREATE TABLE tickets ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), owner_user_id uuid NOT NULL REFERENCES users(id), type_id uuid REFERENCES ticket_types(id), subject text NOT NULL, - status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','pending','resolved','closed')), priority text NOT NULL DEFAULT 'normal' CHECK (priority IN ('low','normal','high')), - created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), closed_at timestamptz -); -CREATE INDEX tickets_owner_idx ON tickets(owner_user_id, updated_at DESC); -CREATE INDEX tickets_status_idx ON tickets(status, updated_at DESC); -CREATE TABLE ticket_replies (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), ticket_id uuid NOT NULL REFERENCES tickets(id), author_user_id uuid NOT NULL REFERENCES users(id), body text NOT NULL, created_at timestamptz NOT NULL DEFAULT now()); -CREATE INDEX ticket_replies_ticket_idx ON ticket_replies(ticket_id, created_at); -CREATE TABLE ticket_assignments (ticket_id uuid PRIMARY KEY REFERENCES tickets(id), admin_user_id uuid NOT NULL REFERENCES users(id), assigned_at timestamptz NOT NULL DEFAULT now()); -CREATE TABLE telegram_accounts (user_id uuid PRIMARY KEY REFERENCES users(id), telegram_user_id bigint NOT NULL UNIQUE, username text, bound_at timestamptz NOT NULL DEFAULT now(), disabled_at timestamptz); -CREATE TABLE telegram_bind_codes (code_hash bytea PRIMARY KEY, user_id uuid NOT NULL REFERENCES users(id), expires_at timestamptz NOT NULL, consumed_at timestamptz, created_at timestamptz NOT NULL DEFAULT now()); - -CREATE TABLE license_products ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, edition text NOT NULL, max_s_instances integer NOT NULL CHECK (max_s_instances > 0), - max_e_nodes integer NOT NULL CHECK (max_e_nodes >= 0), feature_flags text[] NOT NULL DEFAULT '{}', valid_days integer NOT NULL CHECK (valid_days > 0), offline_days integer NOT NULL DEFAULT 60 CHECK (offline_days BETWEEN 0 AND 60), created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE license_activations ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), product_id uuid NOT NULL REFERENCES license_products(id), activation_hash bytea NOT NULL UNIQUE, - status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','revoked','exhausted')), max_instances integer NOT NULL DEFAULT 1 CHECK (max_instances > 0), created_at timestamptz NOT NULL DEFAULT now(), revoked_at timestamptz -); -CREATE TABLE license_instances ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), activation_id uuid NOT NULL REFERENCES license_activations(id), instance_id uuid NOT NULL, - fingerprint_hash bytea NOT NULL, signed_license text NOT NULL, expires_at timestamptz NOT NULL, offline_until timestamptz NOT NULL, last_seen_at timestamptz NOT NULL DEFAULT now(), unbound_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(activation_id, instance_id) -); -CREATE INDEX license_instances_active_idx ON license_instances(activation_id) WHERE unbound_at IS NULL; - -CREATE TABLE outbox_events ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), topic text NOT NULL, aggregate_type text NOT NULL, aggregate_id text NOT NULL, - payload jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), published_at timestamptz, attempts integer NOT NULL DEFAULT 0, last_error text - , locked_by text, locked_until timestamptz -); -CREATE INDEX outbox_events_pending_idx ON outbox_events(created_at) WHERE published_at IS NULL; -CREATE TABLE runtime_config (key text PRIMARY KEY, value jsonb NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), updated_by uuid REFERENCES users(id)); diff --git a/000003_user_management.up.sql b/000003_user_management.up.sql deleted file mode 100644 index f597795..0000000 --- a/000003_user_management.up.sql +++ /dev/null @@ -1,21 +0,0 @@ -ALTER TABLE users - ADD COLUMN ban_reason text, - ADD COLUMN banned_until timestamptz; - -CREATE TABLE user_permissions ( - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - permission text NOT NULL CHECK (permission IN ( - 'users.read', - 'users.write', - 'cards.manage', - 'operations.manage', - 'e.manage', - 'audit.read', - 'licenses.manage' - )), - granted_by uuid REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (user_id, permission) -); - -CREATE INDEX user_permissions_permission_idx ON user_permissions(permission, user_id); diff --git a/000004_access_revocation.up.sql b/000004_access_revocation.up.sql deleted file mode 100644 index 0bbe54e..0000000 --- a/000004_access_revocation.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users - ADD COLUMN auth_version bigint NOT NULL DEFAULT 1 CHECK (auth_version > 0); diff --git a/000005_internal_auth_nonces.up.sql b/000005_internal_auth_nonces.up.sql deleted file mode 100644 index 5f20f0a..0000000 --- a/000005_internal_auth_nonces.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -CREATE TABLE internal_auth_nonces ( - nonce_hash bytea PRIMARY KEY, - expires_at timestamptz NOT NULL -); - -CREATE INDEX internal_auth_nonces_expiry_idx ON internal_auth_nonces(expires_at); diff --git a/000006_e_contract_playback_decisions.up.sql b/000006_e_contract_playback_decisions.up.sql deleted file mode 100644 index 1859a1b..0000000 --- a/000006_e_contract_playback_decisions.up.sql +++ /dev/null @@ -1,22 +0,0 @@ -CREATE TABLE playback_decisions ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id), - media_id uuid NOT NULL, - library_id uuid NOT NULL, - source_id uuid NOT NULL, - device_id text NOT NULL, - route_id text, - allowed boolean NOT NULL, - deny_code text, - max_bitrate bigint NOT NULL DEFAULT 0 CHECK (max_bitrate >= 0), - allow_download boolean NOT NULL DEFAULT false, - max_playbacks integer NOT NULL DEFAULT 0 CHECK (max_playbacks >= 0), - max_device_playbacks integer NOT NULL DEFAULT 0 CHECK (max_device_playbacks >= 0), - policy_revision bigint NOT NULL CHECK (policy_revision >= 0), - expires_at timestamptz NOT NULL, - consumed_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX playback_decisions_expiry_idx ON playback_decisions(expires_at); -CREATE INDEX playback_decisions_user_idx ON playback_decisions(user_id, created_at DESC); diff --git a/000007_external_auth_license_cache.up.sql b/000007_external_auth_license_cache.up.sql deleted file mode 100644 index 72d99de..0000000 --- a/000007_external_auth_license_cache.up.sql +++ /dev/null @@ -1,16 +0,0 @@ -CREATE TABLE commercial_license_cache ( - instance_id text PRIMARY KEY, - license_id text NOT NULL, - signed_license text NOT NULL, - edition text NOT NULL, - features text[] NOT NULL DEFAULT '{}', - max_s_instances integer NOT NULL CHECK (max_s_instances > 0), - max_e_nodes integer NOT NULL CHECK (max_e_nodes > 0), - expires_at timestamptz NOT NULL, - offline_until timestamptz NOT NULL, - status text NOT NULL CHECK (status IN ('active','revoked','expired')), - last_verified_at timestamptz NOT NULL DEFAULT now(), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX commercial_license_cache_status_expiry_idx ON commercial_license_cache(status,expires_at); diff --git a/000008_gateway_routing.up.sql b/000008_gateway_routing.up.sql deleted file mode 100644 index e105676..0000000 --- a/000008_gateway_routing.up.sql +++ /dev/null @@ -1,86 +0,0 @@ -CREATE TABLE gateway_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - public_origin text NOT NULL DEFAULT '', - trusted_proxy_cidrs text[] NOT NULL DEFAULT '{}', - active_revision bigint, - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO gateway_settings(singleton) VALUES (true); - -CREATE TABLE gateway_service_pools ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - code text NOT NULL UNIQUE CHECK (code ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$'), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - enabled boolean NOT NULL DEFAULT true, - is_default boolean NOT NULL DEFAULT false, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE UNIQUE INDEX gateway_service_pools_one_default_idx ON gateway_service_pools(is_default) WHERE is_default; -INSERT INTO gateway_service_pools(code,name,is_default) VALUES ('default','Default',true); - -CREATE TABLE gateway_nodes ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - node_id text NOT NULL UNIQUE CHECK (node_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$'), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - gateway_url text NOT NULL CHECK (length(gateway_url) BETWEEN 8 AND 2048), - pool_id uuid NOT NULL REFERENCES gateway_service_pools(id), - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - city text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0 CHECK (asn BETWEEN 0 AND 4294967295), - weight integer NOT NULL DEFAULT 100 CHECK (weight BETWEEN 1 AND 10000), - max_connections bigint NOT NULL DEFAULT 0 CHECK (max_connections >= 0), - max_playback_sessions bigint NOT NULL DEFAULT 0 CHECK (max_playback_sessions >= 0), - max_transcode_sessions bigint NOT NULL DEFAULT 0 CHECK (max_transcode_sessions >= 0), - bandwidth_capacity_mbps bigint NOT NULL DEFAULT 0 CHECK (bandwidth_capacity_mbps >= 0), - library_ids text[] NOT NULL DEFAULT '{}', - storage_ids text[] NOT NULL DEFAULT '{}', - enabled boolean NOT NULL DEFAULT true, - allow_login boolean NOT NULL DEFAULT true, - allow_new_playback boolean NOT NULL DEFAULT true, - maintenance boolean NOT NULL DEFAULT false, - draining boolean NOT NULL DEFAULT false, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX gateway_nodes_pool_idx ON gateway_nodes(pool_id,enabled,node_id); -CREATE INDEX gateway_nodes_route_idx ON gateway_nodes(enabled,maintenance,draining,country,region,carrier,asn); - -CREATE TABLE gateway_user_routes ( - user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, - forced_node_id text REFERENCES gateway_nodes(node_id) ON UPDATE CASCADE, - forced_pool_id uuid REFERENCES gateway_service_pools(id), - preferred_country text NOT NULL DEFAULT '', - preferred_region text NOT NULL DEFAULT '', - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE gateway_config_versions ( - revision bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - snapshot jsonb NOT NULL CHECK (jsonb_typeof(snapshot)='object'), - source_revision bigint REFERENCES gateway_config_versions(revision), - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX gateway_config_versions_created_idx ON gateway_config_versions(created_at DESC); -ALTER TABLE gateway_settings ADD CONSTRAINT gateway_settings_active_revision_fk FOREIGN KEY(active_revision) REFERENCES gateway_config_versions(revision); - -CREATE TABLE gateway_route_events ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - request_id text NOT NULL, - node_id text, - outcome text NOT NULL CHECK (outcome IN ('routed','rejected','upstream_failed')), - reason text NOT NULL DEFAULT '', - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0, - latency_ms integer NOT NULL DEFAULT 0 CHECK (latency_ms >= 0), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX gateway_route_events_recent_idx ON gateway_route_events(created_at DESC); -CREATE INDEX gateway_route_events_node_idx ON gateway_route_events(node_id,created_at DESC); diff --git a/000009_gateway_operations.up.sql b/000009_gateway_operations.up.sql deleted file mode 100644 index 946e637..0000000 --- a/000009_gateway_operations.up.sql +++ /dev/null @@ -1,19 +0,0 @@ -ALTER TABLE gateway_settings - ADD COLUMN routing_weights jsonb NOT NULL DEFAULT '{"country":1000,"region":100,"city":20,"carrier":10,"asn":10}'::jsonb, - ADD COLUMN recovery_ramp_seconds integer NOT NULL DEFAULT 300 CHECK (recovery_ramp_seconds BETWEEN 0 AND 3600), - ADD CONSTRAINT gateway_settings_routing_weights_object CHECK (jsonb_typeof(routing_weights) = 'object'); - -ALTER TABLE gateway_route_events - ADD COLUMN username text NOT NULL DEFAULT '', - ADD COLUMN device_hash text NOT NULL DEFAULT '', - ADD COLUMN resource_id text NOT NULL DEFAULT '', - ADD COLUMN path_class text NOT NULL DEFAULT 'other' - CHECK (path_class IN ('login','browse','image','playback','transcode','websocket','other')), - ADD COLUMN selection_source text NOT NULL DEFAULT ''; - -CREATE INDEX gateway_route_events_outcome_created_idx - ON gateway_route_events(outcome,created_at DESC); -CREATE INDEX gateway_route_events_location_created_idx - ON gateway_route_events(country,region,carrier,created_at DESC); -CREATE INDEX gateway_route_events_username_created_idx - ON gateway_route_events(username,created_at DESC) WHERE username <> ''; diff --git a/000010_license_entitlements.up.sql b/000010_license_entitlements.up.sql deleted file mode 100644 index 5d97a47..0000000 --- a/000010_license_entitlements.up.sql +++ /dev/null @@ -1,10 +0,0 @@ -ALTER TABLE commercial_license_cache - ADD COLUMN customer_id text NOT NULL DEFAULT '', - ADD COLUMN product text NOT NULL DEFAULT 'overcooked', - ADD COLUMN max_users integer NOT NULL DEFAULT 0 CHECK (max_users >= 0), - ADD COLUMN max_service_pools integer NOT NULL DEFAULT 0 CHECK (max_service_pools >= 0), - ADD COLUMN not_before timestamptz NOT NULL DEFAULT now(), - ADD COLUMN maintenance_until timestamptz; - -CREATE INDEX commercial_license_cache_product_status_idx - ON commercial_license_cache(product,status,offline_until); diff --git a/000011_e_address_directory.up.sql b/000011_e_address_directory.up.sql deleted file mode 100644 index e1dd3be..0000000 --- a/000011_e_address_directory.up.sql +++ /dev/null @@ -1,75 +0,0 @@ -CREATE TABLE e_address_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - trusted_proxy_cidrs text[] NOT NULL DEFAULT '{}', - default_address_id uuid, - rule_revision bigint NOT NULL DEFAULT 1, - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO e_address_settings(singleton) VALUES (true); - -CREATE TABLE e_addresses ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - public_url text NOT NULL CHECK (public_url ~ '^https?://[^/?#[:space:]]+'), - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - city text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0 CHECK (asn BETWEEN 0 AND 4294967295), - node_group text NOT NULL DEFAULT '', - priority integer NOT NULL DEFAULT 100 CHECK (priority BETWEEN -100000 AND 100000), - enabled boolean NOT NULL DEFAULT true, - healthy boolean NOT NULL DEFAULT false, - license_valid boolean NOT NULL DEFAULT false, - allow_user_publish boolean NOT NULL DEFAULT true, - maintenance boolean NOT NULL DEFAULT false, - description text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE UNIQUE INDEX e_addresses_public_url_idx ON e_addresses(public_url); -CREATE INDEX e_addresses_selection_idx ON e_addresses(enabled,healthy,license_valid,allow_user_publish,maintenance,priority); - -ALTER TABLE e_address_settings - ADD CONSTRAINT e_address_settings_default_fk FOREIGN KEY(default_address_id) REFERENCES e_addresses(id) ON DELETE SET NULL; - -CREATE TABLE e_address_rules ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - priority integer NOT NULL DEFAULT 100 CHECK (priority BETWEEN -100000 AND 100000), - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0 CHECK (asn BETWEEN 0 AND 4294967295), - node_group text NOT NULL DEFAULT '', - address_id uuid NOT NULL REFERENCES e_addresses(id) ON DELETE CASCADE, - enabled boolean NOT NULL DEFAULT true, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX e_address_rules_match_idx ON e_address_rules(enabled,priority,country,region,carrier,asn,node_group); - -CREATE TABLE e_user_assignments ( - user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, - address_id uuid NOT NULL REFERENCES e_addresses(id), - fixed boolean NOT NULL DEFAULT false, - rule_revision bigint NOT NULL DEFAULT 1, - reason text NOT NULL DEFAULT '', - source_ip_hash text NOT NULL DEFAULT '', - assigned_at timestamptz NOT NULL DEFAULT now(), - expires_at timestamptz, - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX e_user_assignments_expiry_idx ON e_user_assignments(expires_at); - -CREATE TABLE e_assignment_history ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - address_id uuid REFERENCES e_addresses(id) ON DELETE SET NULL, - source_ip_hash text NOT NULL DEFAULT '', - reason text NOT NULL DEFAULT '', - rule_revision bigint NOT NULL, - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX e_assignment_history_user_idx ON e_assignment_history(user_id,created_at DESC); diff --git a/000012_remove_gateway.up.sql b/000012_remove_gateway.up.sql deleted file mode 100644 index 53c7c6b..0000000 --- a/000012_remove_gateway.up.sql +++ /dev/null @@ -1,48 +0,0 @@ -INSERT INTO e_addresses ( - id, name, public_url, country, region, city, carrier, asn, node_group, - priority, enabled, healthy, license_valid, allow_user_publish, - maintenance, description, created_at, updated_at -) -SELECT - node.id, - node.name, - rtrim(node.gateway_url, '/'), - node.country, - node.region, - node.city, - node.carrier, - node.asn, - pool.code, - node.weight, - node.enabled, - false, - false, - false, - node.maintenance OR node.draining, - 'Migrated from retired proxy node ' || node.node_id, - node.created_at, - node.updated_at -FROM gateway_nodes AS node -JOIN gateway_service_pools AS pool ON pool.id = node.pool_id -WHERE node.gateway_url ~ '^https?://[^/?#[:space:]]+' -ON CONFLICT (public_url) DO UPDATE SET - name = EXCLUDED.name, - country = EXCLUDED.country, - region = EXCLUDED.region, - city = EXCLUDED.city, - carrier = EXCLUDED.carrier, - asn = EXCLUDED.asn, - node_group = EXCLUDED.node_group, - priority = EXCLUDED.priority, - description = CASE - WHEN e_addresses.description = '' THEN EXCLUDED.description - ELSE e_addresses.description - END, - updated_at = GREATEST(e_addresses.updated_at, EXCLUDED.updated_at); - -DROP TABLE gateway_route_events; -DROP TABLE gateway_settings; -DROP TABLE gateway_config_versions; -DROP TABLE gateway_user_routes; -DROP TABLE gateway_nodes; -DROP TABLE gateway_service_pools; diff --git a/000013_e_address_observability.up.sql b/000013_e_address_observability.up.sql deleted file mode 100644 index 2aa9254..0000000 --- a/000013_e_address_observability.up.sql +++ /dev/null @@ -1,22 +0,0 @@ -ALTER TABLE e_address_rules - ADD COLUMN match_count bigint NOT NULL DEFAULT 0 CHECK (match_count >= 0), - ADD COLUMN last_matched_at timestamptz; - -CREATE TABLE e_address_selection_events ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - user_id uuid REFERENCES users(id) ON DELETE SET NULL, - address_id uuid REFERENCES e_addresses(id) ON DELETE SET NULL, - rule_id uuid REFERENCES e_address_rules(id) ON DELETE SET NULL, - outcome text NOT NULL CHECK (outcome IN ('selected','unavailable','access_denied')), - reason text NOT NULL DEFAULT '', - source_ip_hash text NOT NULL DEFAULT '', - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0 CHECK (asn BETWEEN 0 AND 4294967295), - created_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX e_address_selection_events_created_idx ON e_address_selection_events(created_at DESC); -CREATE INDEX e_address_selection_events_outcome_idx ON e_address_selection_events(outcome,created_at DESC); -CREATE INDEX e_address_selection_events_user_idx ON e_address_selection_events(user_id,created_at DESC); diff --git a/000014_operations_closure.up.sql b/000014_operations_closure.up.sql deleted file mode 100644 index 05487c8..0000000 --- a/000014_operations_closure.up.sql +++ /dev/null @@ -1,218 +0,0 @@ -ALTER TABLE users DROP CONSTRAINT users_role_check; -ALTER TABLE users ADD CONSTRAINT users_role_check CHECK (role IN ('user','owner','admin','operator','support','auditor')); - -ALTER TABLE user_permissions DROP CONSTRAINT user_permissions_permission_check; -ALTER TABLE user_permissions ADD CONSTRAINT user_permissions_permission_check CHECK (permission IN ( - 'users.read','users.write','memberships.manage','cards.manage','invites.manage', - 'requests.manage','tickets.manage','notifications.manage','security.manage', - 'operations.manage','e.manage','audit.read','logs.read','backups.manage', - 'updates.manage','settings.manage','licenses.manage' -)); - -CREATE TABLE membership_plans ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - code text NOT NULL UNIQUE CHECK (code ~ '^[a-z0-9][a-z0-9._-]{0,63}$'), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - duration_days integer NOT NULL CHECK (duration_days > 0), - max_playbacks integer NOT NULL DEFAULT 0 CHECK (max_playbacks >= 0), - max_devices integer NOT NULL DEFAULT 0 CHECK (max_devices >= 0), - max_bitrate_kbps integer NOT NULL DEFAULT 0 CHECK (max_bitrate_kbps >= 0), - enabled boolean NOT NULL DEFAULT true, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -ALTER TABLE memberships DROP CONSTRAINT memberships_status_check; -ALTER TABLE memberships ADD CONSTRAINT memberships_status_check CHECK (status IN ('active','paused','expired','revoked')); -ALTER TABLE memberships - ADD COLUMN plan_id uuid REFERENCES membership_plans(id) ON DELETE SET NULL, - ADD COLUMN paused_at timestamptz, - ADD COLUMN pause_reason text NOT NULL DEFAULT '', - ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); - -ALTER TABLE media_requests DROP CONSTRAINT media_requests_status_check; -UPDATE media_requests SET status='submitted' WHERE status='open'; -UPDATE media_requests SET status='accepted' WHERE status='planned'; -ALTER TABLE media_requests ADD CONSTRAINT media_requests_status_check CHECK (status IN ( - 'submitted','reviewing','accepted','existing','downloading','fulfilled','failed','rejected','cancelled' -)); -ALTER TABLE media_requests - ADD COLUMN media_type text NOT NULL DEFAULT 'other' CHECK (media_type IN ('movie','series','season','episode','other')), - ADD COLUMN release_year integer CHECK (release_year BETWEEN 1800 AND 3000), - ADD COLUMN priority text NOT NULL DEFAULT 'normal' CHECK (priority IN ('low','normal','high','urgent')), - ADD COLUMN assignee_user_id uuid REFERENCES users(id) ON DELETE SET NULL, - ADD COLUMN public_reply text NOT NULL DEFAULT '', - ADD COLUMN internal_note text NOT NULL DEFAULT '', - ADD COLUMN merged_into uuid REFERENCES media_requests(id) ON DELETE SET NULL, - ADD COLUMN closed_at timestamptz; - -CREATE TABLE media_request_messages ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - request_id uuid NOT NULL REFERENCES media_requests(id) ON DELETE CASCADE, - author_user_id uuid REFERENCES users(id) ON DELETE SET NULL, - visibility text NOT NULL CHECK (visibility IN ('user','internal')), - body text NOT NULL CHECK (length(btrim(body)) BETWEEN 1 AND 5000), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX media_request_messages_request_idx ON media_request_messages(request_id,created_at); - -ALTER TABLE tickets DROP CONSTRAINT tickets_status_check; -UPDATE tickets SET status='in_progress' WHERE status='pending'; -ALTER TABLE tickets ADD CONSTRAINT tickets_status_check CHECK (status IN ('open','in_progress','waiting_user','resolved','closed')); -ALTER TABLE tickets DROP CONSTRAINT tickets_priority_check; -ALTER TABLE tickets ADD CONSTRAINT tickets_priority_check CHECK (priority IN ('low','normal','high','urgent')); -ALTER TABLE tickets - ADD COLUMN sla_due_at timestamptz, - ADD COLUMN last_user_reply_at timestamptz, - ADD COLUMN last_staff_reply_at timestamptz, - ADD COLUMN reopened_at timestamptz; -ALTER TABLE ticket_replies - ADD COLUMN visibility text NOT NULL DEFAULT 'user' CHECK (visibility IN ('user','internal')); - -CREATE TABLE announcements ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - title text NOT NULL CHECK (length(btrim(title)) BETWEEN 1 AND 200), - body text NOT NULL CHECK (length(btrim(body)) BETWEEN 1 AND 20000), - status text NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','scheduled','published','withdrawn')), - audience text NOT NULL DEFAULT 'all' CHECK (audience IN ('all','active_members','staff')), - pinned boolean NOT NULL DEFAULT false, - publish_at timestamptz, - expires_at timestamptz, - created_by uuid NOT NULL REFERENCES users(id), - updated_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - CHECK (expires_at IS NULL OR publish_at IS NULL OR expires_at > publish_at) -); -CREATE INDEX announcements_status_publish_idx ON announcements(status,publish_at DESC); - -CREATE TABLE announcement_reads ( - announcement_id uuid NOT NULL REFERENCES announcements(id) ON DELETE CASCADE, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - read_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (announcement_id,user_id) -); - -CREATE TABLE notification_events ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - event_type text NOT NULL, - target_user_id uuid REFERENCES users(id) ON DELETE CASCADE, - title text NOT NULL CHECK (length(btrim(title)) BETWEEN 1 AND 200), - body text NOT NULL CHECK (length(body) <= 10000), - link_path text NOT NULL DEFAULT '' CHECK (link_path='' OR link_path ~ '^/'), - related_type text NOT NULL DEFAULT '', - related_id text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX notification_events_target_idx ON notification_events(target_user_id,created_at DESC); - -CREATE TABLE notification_deliveries ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - event_id uuid NOT NULL REFERENCES notification_events(id) ON DELETE CASCADE, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - channel text NOT NULL CHECK (channel IN ('in_app','email','telegram')), - status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','sending','sent','failed','cancelled')), - attempts integer NOT NULL DEFAULT 0 CHECK (attempts >= 0), - next_attempt_at timestamptz, - last_error text NOT NULL DEFAULT '', - read_at timestamptz, - deleted_at timestamptz, - sent_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(event_id,user_id,channel) -); -CREATE INDEX notification_deliveries_user_idx ON notification_deliveries(user_id,channel,created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX notification_deliveries_pending_idx ON notification_deliveries(next_attempt_at,created_at) WHERE status IN ('pending','failed'); - -CREATE TABLE risk_rules ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - event_type text NOT NULL CHECK (event_type IN ('concurrency','multi_region','device_churn','auth_failures','manual')), - window_seconds integer NOT NULL CHECK (window_seconds BETWEEN 60 AND 2592000), - threshold integer NOT NULL CHECK (threshold > 0), - action text NOT NULL DEFAULT 'review' CHECK (action IN ('observe','review','revoke_sessions','disable_user')), - enabled boolean NOT NULL DEFAULT true, - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE risk_events ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - rule_id uuid REFERENCES risk_rules(id) ON DELETE SET NULL, - user_id uuid REFERENCES users(id) ON DELETE SET NULL, - event_type text NOT NULL, - severity text NOT NULL CHECK (severity IN ('info','low','medium','high','critical')), - status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','reviewing','resolved','dismissed','appealed')), - evidence jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(evidence)='object'), - resolution text NOT NULL DEFAULT '', - resolved_by uuid REFERENCES users(id) ON DELETE SET NULL, - resolved_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX risk_events_status_idx ON risk_events(status,severity,created_at DESC); - -ALTER TABLE refresh_token_families - ADD COLUMN device_name text NOT NULL DEFAULT '', - ADD COLUMN client_name text NOT NULL DEFAULT '', - ADD COLUMN platform text NOT NULL DEFAULT '', - ADD COLUMN ip_hash text NOT NULL DEFAULT '', - ADD COLUMN last_seen_at timestamptz NOT NULL DEFAULT now(); - -ALTER TABLE devices - ADD COLUMN device_name text NOT NULL DEFAULT '', - ADD COLUMN client_name text NOT NULL DEFAULT '', - ADD COLUMN platform text NOT NULL DEFAULT '', - ADD COLUMN client_version text NOT NULL DEFAULT '', - ADD COLUMN last_ip_hash text NOT NULL DEFAULT '', - ADD COLUMN trusted boolean NOT NULL DEFAULT false, - ADD COLUMN blocked_at timestamptz, - ADD COLUMN blocked_by uuid REFERENCES users(id) ON DELETE SET NULL; - -CREATE TABLE configuration_versions ( - revision bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - domain text NOT NULL CHECK (domain IN ('s','e')), - values jsonb NOT NULL CHECK (jsonb_typeof(values)='object'), - state text NOT NULL DEFAULT 'draft' CHECK (state IN ('draft','published','superseded','failed')), - source_revision bigint REFERENCES configuration_versions(revision), - created_by uuid NOT NULL REFERENCES users(id), - published_at timestamptz, - failure_reason text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE UNIQUE INDEX configuration_versions_published_domain_idx ON configuration_versions(domain) WHERE state='published'; - -CREATE TABLE operation_jobs ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - job_type text NOT NULL, - resource_type text NOT NULL DEFAULT '', - resource_id text NOT NULL DEFAULT '', - idempotency_key text NOT NULL UNIQUE, - state text NOT NULL DEFAULT 'queued' CHECK (state IN ('queued','running','pausing','paused','cancelling','cancelled','succeeded','partial_failed','failed')), - progress_total bigint NOT NULL DEFAULT 0 CHECK (progress_total >= 0), - progress_completed bigint NOT NULL DEFAULT 0 CHECK (progress_completed >= 0), - progress_failed bigint NOT NULL DEFAULT 0 CHECK (progress_failed >= 0), - error_code text NOT NULL DEFAULT '', - error_summary text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - started_at timestamptz, - updated_at timestamptz NOT NULL DEFAULT now(), - finished_at timestamptz, - CHECK (progress_completed + progress_failed <= progress_total OR progress_total=0) -); -CREATE INDEX operation_jobs_state_idx ON operation_jobs(state,created_at DESC); - -CREATE TABLE api_idempotency_keys ( - actor_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - operation text NOT NULL, - idempotency_key text NOT NULL, - resource_id text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now(), - expires_at timestamptz NOT NULL DEFAULT now() + interval '24 hours', - PRIMARY KEY (actor_user_id,operation,idempotency_key) -); -CREATE INDEX api_idempotency_keys_expiry_idx ON api_idempotency_keys(expires_at); diff --git a/000015_e_address_refresh_rate_limit.up.sql b/000015_e_address_refresh_rate_limit.up.sql deleted file mode 100644 index 2bbc67f..0000000 --- a/000015_e_address_refresh_rate_limit.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE e_address_selection_events DROP CONSTRAINT e_address_selection_events_outcome_check; -ALTER TABLE e_address_selection_events ADD CONSTRAINT e_address_selection_events_outcome_check CHECK (outcome IN ('selected','unavailable','access_denied','rate_limited')); diff --git a/000016_system_operations.up.sql b/000016_system_operations.up.sql deleted file mode 100644 index 19ae0d7..0000000 --- a/000016_system_operations.up.sql +++ /dev/null @@ -1,90 +0,0 @@ -ALTER TABLE audit_events - ADD COLUMN request_id text NOT NULL DEFAULT ''; -CREATE INDEX audit_events_request_idx ON audit_events(request_id, created_at DESC) WHERE request_id <> ''; - -CREATE TABLE runtime_log_events ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - level text NOT NULL CHECK (level IN ('debug','info','warn','error')), - message text NOT NULL CHECK (length(message) BETWEEN 1 AND 256), - request_id text NOT NULL DEFAULT '', - method text NOT NULL DEFAULT '', - path text NOT NULL DEFAULT '', - status_code integer NOT NULL DEFAULT 0 CHECK (status_code BETWEEN 0 AND 599), - duration_ms bigint NOT NULL DEFAULT 0 CHECK (duration_ms >= 0), - detail jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(detail)='object'), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX runtime_log_events_time_idx ON runtime_log_events(created_at DESC); -CREATE INDEX runtime_log_events_request_idx ON runtime_log_events(request_id,created_at DESC) WHERE request_id <> ''; - -ALTER TABLE operation_jobs - ADD COLUMN schema_version integer NOT NULL DEFAULT 1, - ADD COLUMN stage text NOT NULL DEFAULT 'queued', - ADD COLUMN priority integer NOT NULL DEFAULT 0 CHECK (priority BETWEEN -100 AND 100), - ADD COLUMN progress_skipped bigint NOT NULL DEFAULT 0 CHECK (progress_skipped >= 0), - ADD COLUMN retryable boolean NOT NULL DEFAULT true, - ADD COLUMN next_retry_at timestamptz, - ADD COLUMN claimed_by text NOT NULL DEFAULT '', - ADD COLUMN payload jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(payload)='object'); - -CREATE TABLE backup_policies ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - scope text NOT NULL CHECK (scope IN ('s','e','auth','all')), - enabled boolean NOT NULL DEFAULT true, - interval_hours integer NOT NULL DEFAULT 24 CHECK (interval_hours BETWEEN 1 AND 720), - retention_count integer NOT NULL DEFAULT 14 CHECK (retention_count BETWEEN 1 AND 365), - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(scope) -); - -CREATE TABLE backup_artifacts ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - scope text NOT NULL CHECK (scope IN ('s','e','auth','all')), - state text NOT NULL DEFAULT 'queued' CHECK (state IN ('queued','running','succeeded','failed','expired','deleted')), - storage_key text NOT NULL, - size_bytes bigint NOT NULL DEFAULT 0 CHECK (size_bytes >= 0), - sha256 text NOT NULL DEFAULT '' CHECK (sha256 = '' OR sha256 ~ '^[0-9a-f]{64}$'), - job_id uuid REFERENCES operation_jobs(id) ON DELETE SET NULL, - error_code text NOT NULL DEFAULT '', - error_summary text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - completed_at timestamptz, - deleted_at timestamptz -); -CREATE INDEX backup_artifacts_scope_idx ON backup_artifacts(scope,created_at DESC); - -CREATE TABLE system_runtime_state ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - maintenance boolean NOT NULL DEFAULT false, - maintenance_reason text NOT NULL DEFAULT '', - maintenance_job_id uuid REFERENCES operation_jobs(id) ON DELETE SET NULL, - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO system_runtime_state(singleton) VALUES (true) ON CONFLICT DO NOTHING; - -CREATE TABLE update_artifacts ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - product text NOT NULL CHECK (product IN ('s','e','auth')), - platform text NOT NULL, - version text NOT NULL, - channel text NOT NULL CHECK (channel IN ('stable','preview')), - download_url text NOT NULL DEFAULT '', - sha256 text NOT NULL CHECK (sha256 ~ '^[0-9a-f]{64}$'), - signature text NOT NULL DEFAULT '', - min_s_version text NOT NULL DEFAULT '', - min_e_version text NOT NULL DEFAULT '', - state text NOT NULL DEFAULT 'authorized' CHECK (state IN ('authorized','downloaded','verified','staged','applied','rolled_back','failed')), - storage_key text NOT NULL DEFAULT '', - job_id uuid REFERENCES operation_jobs(id) ON DELETE SET NULL, - error_code text NOT NULL DEFAULT '', - error_summary text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(product,platform,version,channel) -); -CREATE INDEX update_artifacts_state_idx ON update_artifacts(product,state,updated_at DESC); diff --git a/000017_notifications_security_lifecycle.up.sql b/000017_notifications_security_lifecycle.up.sql deleted file mode 100644 index 8688762..0000000 --- a/000017_notifications_security_lifecycle.up.sql +++ /dev/null @@ -1,124 +0,0 @@ -ALTER TABLE users - ADD COLUMN deleted_by uuid REFERENCES users(id) ON DELETE SET NULL, - ADD COLUMN deletion_reason text NOT NULL DEFAULT '', - ADD COLUMN restored_at timestamptz; - -ALTER TABLE card_batches - ADD COLUMN membership_plan_id uuid REFERENCES membership_plans(id) ON DELETE SET NULL, - ADD COLUMN edition text NOT NULL DEFAULT '' CHECK (char_length(edition) <= 64), - ADD COLUMN max_uses integer NOT NULL DEFAULT 1 CHECK (max_uses > 0); - -ALTER TABLE card_batches DROP CONSTRAINT card_batches_card_type_check; -ALTER TABLE card_batches ADD CONSTRAINT card_batches_card_type_check CHECK (card_type IN ('registration','renewal','membership')); - -ALTER TABLE cards - ADD COLUMN label text NOT NULL DEFAULT '' CHECK (char_length(label) <= 128); - -CREATE TABLE notification_channel_configs ( - channel text PRIMARY KEY CHECK (channel IN ('smtp','telegram')), - enabled boolean NOT NULL DEFAULT false, - settings jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(settings)='object'), - secret_cipher bytea, - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO notification_channel_configs(channel) VALUES ('smtp'),('telegram'); - -CREATE TABLE notification_templates ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - template_key text NOT NULL CHECK (template_key ~ '^[a-z0-9][a-z0-9._-]{0,127}$'), - channel text NOT NULL CHECK (channel IN ('email','telegram')), - subject text NOT NULL DEFAULT '' CHECK (char_length(subject) <= 300), - body text NOT NULL CHECK (char_length(body) BETWEEN 1 AND 20000), - enabled boolean NOT NULL DEFAULT true, - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(template_key,channel) -); - -CREATE TABLE notification_delivery_attempts ( - id bigserial PRIMARY KEY, - delivery_id uuid NOT NULL REFERENCES notification_deliveries(id) ON DELETE CASCADE, - attempt integer NOT NULL CHECK (attempt > 0), - status text NOT NULL CHECK (status IN ('sent','failed','skipped')), - error_code text NOT NULL DEFAULT '', - error_summary text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(delivery_id,attempt) -); - -CREATE TABLE telegram_runtime_state ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - update_offset bigint NOT NULL DEFAULT 0, - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO telegram_runtime_state(singleton) VALUES(true); - -CREATE TABLE user_ip_history ( - id bigserial PRIMARY KEY, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - ip_hash text NOT NULL CHECK (char_length(ip_hash)=64), - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint, - first_seen_at timestamptz NOT NULL DEFAULT now(), - last_seen_at timestamptz NOT NULL DEFAULT now(), - seen_count bigint NOT NULL DEFAULT 1 CHECK (seen_count > 0), - UNIQUE(user_id,ip_hash) -); -CREATE INDEX user_ip_history_user_time_idx ON user_ip_history(user_id,last_seen_at DESC); - -CREATE TABLE security_list_entries ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - subject_type text NOT NULL CHECK (subject_type IN ('ip','cidr','device','user')), - mode text NOT NULL CHECK (mode IN ('allow','deny')), - subject_value text NOT NULL CHECK (char_length(subject_value) BETWEEN 1 AND 512), - reason text NOT NULL CHECK (char_length(btrim(reason)) BETWEEN 1 AND 1000), - expires_at timestamptz, - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - revoked_at timestamptz, - revoked_by uuid REFERENCES users(id) ON DELETE SET NULL, - UNIQUE(subject_type,mode,subject_value) -); -CREATE INDEX security_list_active_idx ON security_list_entries(subject_type,mode,expires_at) WHERE revoked_at IS NULL; - -CREATE TABLE user_appeals ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - risk_event_id uuid REFERENCES risk_events(id) ON DELETE SET NULL, - body text NOT NULL CHECK (char_length(btrim(body)) BETWEEN 1 AND 5000), - status text NOT NULL DEFAULT 'submitted' CHECK (status IN ('submitted','reviewing','accepted','rejected')), - resolution text NOT NULL DEFAULT '', - resolved_by uuid REFERENCES users(id) ON DELETE SET NULL, - resolved_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE media_request_callbacks ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL CHECK (char_length(btrim(name)) BETWEEN 1 AND 128), - callback_url text NOT NULL CHECK (callback_url ~ '^https://'), - secret_cipher bytea NOT NULL, - enabled boolean NOT NULL DEFAULT true, - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE media_request_callback_events ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - callback_id uuid NOT NULL REFERENCES media_request_callbacks(id) ON DELETE CASCADE, - external_event_id text NOT NULL CHECK (char_length(external_event_id) BETWEEN 1 AND 256), - request_id uuid REFERENCES media_requests(id) ON DELETE SET NULL, - payload jsonb NOT NULL CHECK (jsonb_typeof(payload)='object'), - signature_valid boolean NOT NULL, - applied boolean NOT NULL DEFAULT false, - error_summary text NOT NULL DEFAULT '', - received_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(callback_id,external_event_id) -); diff --git a/000018_initial_setup.up.sql b/000018_initial_setup.up.sql deleted file mode 100644 index 78e1ffc..0000000 --- a/000018_initial_setup.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE system_installation ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - installation_id uuid NOT NULL DEFAULT gen_random_uuid(), - setup_completed_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO system_installation(singleton) VALUES(true); diff --git a/000019_e_address_group_rules.up.sql b/000019_e_address_group_rules.up.sql deleted file mode 100644 index 0133fb3..0000000 --- a/000019_e_address_group_rules.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE e_address_rules - ALTER COLUMN address_id DROP NOT NULL; - -ALTER TABLE e_address_rules - ADD CONSTRAINT e_address_rules_target_check - CHECK (address_id IS NOT NULL OR length(btrim(node_group)) > 0); diff --git a/000020_playback_session_context.up.sql b/000020_playback_session_context.up.sql deleted file mode 100644 index 3fed86b..0000000 --- a/000020_playback_session_context.up.sql +++ /dev/null @@ -1,17 +0,0 @@ -ALTER TABLE playback_decisions - ADD COLUMN device_name text NOT NULL DEFAULT '', - ADD COLUMN client_name text NOT NULL DEFAULT '', - ADD COLUMN client_version text NOT NULL DEFAULT '', - ADD COLUMN remote_ip_hash text NOT NULL DEFAULT ''; - -ALTER TABLE playback_sessions - ADD COLUMN media_id uuid, - ADD COLUMN source_id uuid, - ADD COLUMN node_id uuid, - ADD COLUMN position_ticks bigint NOT NULL DEFAULT 0 CHECK (position_ticks >= 0), - ADD COLUMN device_name text NOT NULL DEFAULT '', - ADD COLUMN client_name text NOT NULL DEFAULT '', - ADD COLUMN client_version text NOT NULL DEFAULT ''; - -CREATE INDEX playback_sessions_recent_idx - ON playback_sessions(updated_at DESC, id DESC); diff --git a/000021_subtitle_sources.up.sql b/000021_subtitle_sources.up.sql deleted file mode 100644 index 827dfbf..0000000 --- a/000021_subtitle_sources.up.sql +++ /dev/null @@ -1,55 +0,0 @@ -CREATE TABLE subtitle_sources ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL CHECK (char_length(btrim(name)) BETWEEN 1 AND 128), - source_type text NOT NULL DEFAULT 'json_rest_v1' CHECK (source_type IN ('json_rest_v1')), - base_url text NOT NULL CHECK (base_url ~ '^https://'), - api_key_cipher bytea, - enabled boolean NOT NULL DEFAULT true, - languages text[] NOT NULL DEFAULT '{}', - priority integer NOT NULL DEFAULT 100 CHECK (priority BETWEEN 1 AND 10000), - timeout_seconds integer NOT NULL DEFAULT 10 CHECK (timeout_seconds BETWEEN 2 AND 30), - created_by uuid NOT NULL REFERENCES users(id), - updated_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE (name) -); - -CREATE INDEX subtitle_sources_enabled_idx - ON subtitle_sources(priority,id) WHERE enabled; - -CREATE TABLE subtitle_search_candidates ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - source_id uuid NOT NULL REFERENCES subtitle_sources(id) ON DELETE CASCADE, - media_item_id uuid NOT NULL, - media_source_id uuid NOT NULL, - external_id text NOT NULL CHECK (char_length(external_id) BETWEEN 1 AND 512), - title text NOT NULL CHECK (char_length(title) BETWEEN 1 AND 512), - language text NOT NULL DEFAULT '' CHECK (char_length(language) <= 64), - codec text NOT NULL CHECK (codec IN ('srt','ass','ssa','vtt','sup','sub','idx')), - release_group text NOT NULL DEFAULT '' CHECK (char_length(release_group) <= 256), - score double precision NOT NULL DEFAULT 0 CHECK (score >= 0 AND score <= 1), - download_path text NOT NULL CHECK (char_length(download_path) BETWEEN 1 AND 2048), - hearing_impaired boolean NOT NULL DEFAULT false, - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - expires_at timestamptz NOT NULL DEFAULT (now() + interval '1 hour') -); - -CREATE INDEX subtitle_search_candidates_actor_idx - ON subtitle_search_candidates(created_by,created_at DESC); -CREATE INDEX subtitle_search_candidates_expiry_idx - ON subtitle_search_candidates(expires_at); - -CREATE TABLE subtitle_imports ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - candidate_id uuid NOT NULL REFERENCES subtitle_search_candidates(id) ON DELETE CASCADE, - actor_user_id uuid NOT NULL REFERENCES users(id), - idempotency_key text NOT NULL CHECK (char_length(idempotency_key) BETWEEN 16 AND 128), - state text NOT NULL DEFAULT 'processing' CHECK (state IN ('processing','succeeded','failed')), - result jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(result)='object'), - error_code text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(actor_user_id,idempotency_key) -); diff --git a/000022_metadata_provider_credentials.up.sql b/000022_metadata_provider_credentials.up.sql deleted file mode 100644 index c547c2f..0000000 --- a/000022_metadata_provider_credentials.up.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE metadata_provider_credentials ( - provider text PRIMARY KEY CHECK (provider IN ('tmdb','bangumi')), - token_cipher bytea, - enabled boolean NOT NULL DEFAULT true, - updated_by uuid NOT NULL REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX metadata_provider_credentials_enabled_idx - ON metadata_provider_credentials(provider) WHERE enabled; diff --git a/000023_user_engagement_and_bot.up.sql b/000023_user_engagement_and_bot.up.sql deleted file mode 100644 index a083d93..0000000 --- a/000023_user_engagement_and_bot.up.sql +++ /dev/null @@ -1,80 +0,0 @@ -CREATE TABLE point_rules ( - rule_key text PRIMARY KEY CHECK (rule_key ~ '^[a-z0-9][a-z0-9._-]{0,63}$'), - name text NOT NULL CHECK (char_length(btrim(name)) BETWEEN 1 AND 128), - points integer NOT NULL CHECK (points BETWEEN -100000 AND 100000), - enabled boolean NOT NULL DEFAULT true, - description text NOT NULL DEFAULT '' CHECK (char_length(description) <= 1000), - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO point_rules(rule_key,name,points,description) VALUES - ('playback.completed','有效观看',5,'单次观看达到 10 分钟后奖励,每个播放会话仅奖励一次'), - ('feedback.accepted','有效反馈',10,'管理员确认媒体问题后奖励'); - -CREATE TABLE point_accounts ( - user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, - balance bigint NOT NULL DEFAULT 0 CHECK (balance >= 0), - lifetime_earned bigint NOT NULL DEFAULT 0 CHECK (lifetime_earned >= 0), - lifetime_spent bigint NOT NULL DEFAULT 0 CHECK (lifetime_spent >= 0), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE point_ledger ( - id bigserial PRIMARY KEY, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - delta integer NOT NULL CHECK (delta <> 0 AND delta BETWEEN -100000 AND 100000), - balance_after bigint NOT NULL CHECK (balance_after >= 0), - reason text NOT NULL CHECK (char_length(btrim(reason)) BETWEEN 1 AND 200), - source_type text NOT NULL DEFAULT 'manual' CHECK (source_type IN ('manual','playback_session','media_feedback','redemption')), - source_id text NOT NULL DEFAULT '' CHECK (char_length(source_id) <= 128), - actor_user_id uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(user_id,source_type,source_id) -); -CREATE INDEX point_ledger_user_time_idx ON point_ledger(user_id,created_at DESC,id DESC); - -CREATE TABLE media_feedback ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - media_id uuid, - source_id uuid, - playback_session_id uuid REFERENCES playback_sessions(id) ON DELETE SET NULL, - category text NOT NULL CHECK (category IN ('playback','subtitle','audio','metadata','quality','other')), - title text NOT NULL CHECK (char_length(btrim(title)) BETWEEN 1 AND 200), - detail text NOT NULL CHECK (char_length(btrim(detail)) BETWEEN 1 AND 5000), - status text NOT NULL DEFAULT 'submitted' CHECK (status IN ('submitted','reviewing','resolved','rejected')), - priority text NOT NULL DEFAULT 'normal' CHECK (priority IN ('low','normal','high','urgent')), - resolution text NOT NULL DEFAULT '' CHECK (char_length(resolution) <= 5000), - assigned_to uuid REFERENCES users(id) ON DELETE SET NULL, - resolved_by uuid REFERENCES users(id) ON DELETE SET NULL, - resolved_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX media_feedback_user_time_idx ON media_feedback(user_id,created_at DESC); -CREATE INDEX media_feedback_queue_idx ON media_feedback(status,priority,updated_at DESC); - -CREATE TABLE telegram_bot_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - enabled boolean NOT NULL DEFAULT true, - commands_enabled boolean NOT NULL DEFAULT true, - rate_limit_per_minute integer NOT NULL DEFAULT 12 CHECK (rate_limit_per_minute BETWEEN 1 AND 120), - welcome_message text NOT NULL DEFAULT '欢迎使用站点助手,请先绑定账户。' CHECK (char_length(welcome_message) BETWEEN 1 AND 1000), - maintenance_message text NOT NULL DEFAULT '' CHECK (char_length(maintenance_message) <= 1000), - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO telegram_bot_settings(singleton) VALUES(true); - -CREATE TABLE telegram_command_logs ( - id bigserial PRIMARY KEY, - telegram_user_id bigint NOT NULL, - user_id uuid REFERENCES users(id) ON DELETE SET NULL, - command text NOT NULL CHECK (char_length(command) BETWEEN 1 AND 64), - result text NOT NULL CHECK (char_length(result) BETWEEN 1 AND 64), - duration_ms integer NOT NULL DEFAULT 0 CHECK (duration_ms >= 0), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX telegram_command_logs_rate_idx ON telegram_command_logs(telegram_user_id,created_at DESC); -CREATE INDEX telegram_command_logs_admin_idx ON telegram_command_logs(created_at DESC,id DESC); diff --git a/000024_daily_checkins.up.sql b/000024_daily_checkins.up.sql deleted file mode 100644 index 86b22f7..0000000 --- a/000024_daily_checkins.up.sql +++ /dev/null @@ -1,20 +0,0 @@ -ALTER TABLE point_ledger DROP CONSTRAINT point_ledger_source_type_check; -ALTER TABLE point_ledger ADD CONSTRAINT point_ledger_source_type_check - CHECK (source_type IN ('manual','playback_session','media_feedback','redemption','daily_checkin')); - -INSERT INTO point_rules(rule_key,name,points,description) VALUES - ('daily_checkin.base','每日签到',5,'每天首次签到获得的积分'), - ('daily_checkin.streak_7','连续签到 7 天',10,'连续签到第 7 天的额外积分'), - ('daily_checkin.streak_30','连续签到 30 天',30,'连续签到第 30 天的额外积分') -ON CONFLICT(rule_key) DO NOTHING; - -CREATE TABLE daily_checkins ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - checkin_date date NOT NULL, - streak integer NOT NULL CHECK(streak > 0), - reward_points integer NOT NULL CHECK(reward_points >= 0), - created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(user_id,checkin_date) -); -CREATE INDEX daily_checkins_user_time_idx ON daily_checkins(user_id,checkin_date DESC); diff --git a/000025_user_store_calendar_monitoring.up.sql b/000025_user_store_calendar_monitoring.up.sql deleted file mode 100644 index 3fe88d1..0000000 --- a/000025_user_store_calendar_monitoring.up.sql +++ /dev/null @@ -1,34 +0,0 @@ -ALTER TABLE membership_plans - ADD COLUMN price_points integer NOT NULL DEFAULT 0 CHECK(price_points >= 0), - ADD COLUMN self_service boolean NOT NULL DEFAULT false; - -INSERT INTO membership_plans(code,name,duration_days,max_playbacks,max_devices,max_bitrate_kbps,price_points,self_service,enabled) -VALUES('points-30d','30 天会员',30,1,3,0,100,true,true) -ON CONFLICT(code) DO NOTHING; - -ALTER TABLE point_ledger DROP CONSTRAINT point_ledger_source_type_check; -ALTER TABLE point_ledger ADD CONSTRAINT point_ledger_source_type_check - CHECK (source_type IN ('manual','playback_session','media_feedback','redemption','daily_checkin','membership_purchase')); - -CREATE TABLE membership_purchases ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - plan_id uuid NOT NULL REFERENCES membership_plans(id), - price_points integer NOT NULL CHECK(price_points >= 0), - membership_id uuid REFERENCES memberships(id), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX membership_purchases_user_time_idx ON membership_purchases(user_id,created_at DESC); - -CREATE TABLE watch_calendar_entries ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - title text NOT NULL CHECK(length(btrim(title)) BETWEEN 1 AND 200), - episode_label text NOT NULL DEFAULT '' CHECK(length(episode_label) <= 100), - next_episode_at timestamptz NOT NULL, - notes text NOT NULL DEFAULT '' CHECK(length(notes) <= 1000), - completed boolean NOT NULL DEFAULT false, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX watch_calendar_user_time_idx ON watch_calendar_entries(user_id,completed,next_episode_at); diff --git a/000026_telegram_brand_and_chat_state.up.sql b/000026_telegram_brand_and_chat_state.up.sql deleted file mode 100644 index 63c49a4..0000000 --- a/000026_telegram_brand_and_chat_state.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -ALTER TABLE telegram_bot_settings - ADD COLUMN server_name text NOT NULL DEFAULT 'OverCooked' CHECK(char_length(btrim(server_name)) BETWEEN 1 AND 80), - ADD COLUMN icon text NOT NULL DEFAULT '🎬' CHECK(char_length(btrim(icon)) BETWEEN 1 AND 16); - -CREATE TABLE telegram_chat_states ( - chat_id bigint PRIMARY KEY, - message_id bigint NOT NULL CHECK(message_id > 0), - updated_at timestamptz NOT NULL DEFAULT now() -); diff --git a/000027_site_brand_settings.up.sql b/000027_site_brand_settings.up.sql deleted file mode 100644 index c7cacb8..0000000 --- a/000027_site_brand_settings.up.sql +++ /dev/null @@ -1,17 +0,0 @@ -CREATE TABLE site_brand_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - server_name text NOT NULL DEFAULT 'OverCooked' CHECK (char_length(btrim(server_name)) BETWEEN 1 AND 80), - emby_display_name text NOT NULL DEFAULT 'OverCooked' CHECK (char_length(btrim(emby_display_name)) BETWEEN 1 AND 80), - icon_data_url text NOT NULL DEFAULT '' CHECK (char_length(icon_data_url) <= 700000), - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO site_brand_settings(singleton) VALUES(true); - -ALTER TABLE telegram_bot_settings - ADD COLUMN official_chat_id bigint NOT NULL DEFAULT 0, - ADD COLUMN official_chat_title text NOT NULL DEFAULT '' CHECK (char_length(official_chat_title) <= 120); - -ALTER TABLE telegram_chat_states - ADD COLUMN message_kind text NOT NULL DEFAULT 'text' CHECK (message_kind IN ('text','photo')); diff --git a/000028_media_request_metadata.up.sql b/000028_media_request_metadata.up.sql deleted file mode 100644 index 7e72cc9..0000000 --- a/000028_media_request_metadata.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -ALTER TABLE media_requests - ADD COLUMN provider text NOT NULL DEFAULT '', - ADD COLUMN external_id text NOT NULL DEFAULT '', - ADD COLUMN original_title text NOT NULL DEFAULT ''; - -ALTER TABLE media_requests - ADD CONSTRAINT media_requests_provider_check CHECK (provider IN ('', 'tmdb', 'bangumi')), - ADD CONSTRAINT media_requests_external_id_length CHECK (length(external_id) <= 512), - ADD CONSTRAINT media_requests_original_title_length CHECK (length(original_title) <= 300); - -CREATE INDEX media_requests_external_metadata_idx - ON media_requests(provider, external_id) - WHERE provider <> '' AND external_id <> ''; diff --git a/000029_support_attachments.up.sql b/000029_support_attachments.up.sql deleted file mode 100644 index 05e2585..0000000 --- a/000029_support_attachments.up.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE TABLE support_attachments ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - ticket_id uuid REFERENCES tickets(id) ON DELETE CASCADE, - media_feedback_id uuid REFERENCES media_feedback(id) ON DELETE CASCADE, - uploader_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - filename text NOT NULL CHECK (length(filename) BETWEEN 1 AND 255), - mime_type text NOT NULL CHECK (length(mime_type) BETWEEN 1 AND 128), - size_bytes bigint NOT NULL CHECK (size_bytes BETWEEN 1 AND 8388608), - content bytea NOT NULL, - created_at timestamptz NOT NULL DEFAULT now(), - CHECK ((ticket_id IS NOT NULL)::int + (media_feedback_id IS NOT NULL)::int = 1) -); - -CREATE INDEX support_attachments_ticket_idx ON support_attachments(ticket_id,created_at,id) WHERE ticket_id IS NOT NULL; -CREATE INDEX support_attachments_feedback_idx ON support_attachments(media_feedback_id,created_at,id) WHERE media_feedback_id IS NOT NULL; diff --git a/000030_user_password_and_banner.up.sql b/000030_user_password_and_banner.up.sql deleted file mode 100644 index a7e53c1..0000000 --- a/000030_user_password_and_banner.up.sql +++ /dev/null @@ -1,14 +0,0 @@ -ALTER TABLE site_brand_settings - ADD COLUMN banner_enabled boolean NOT NULL DEFAULT false, - ADD COLUMN banner_image_data_url text NOT NULL DEFAULT '' CHECK (char_length(banner_image_data_url) <= 2800000), - ADD COLUMN banner_link_url text NOT NULL DEFAULT '' CHECK (char_length(banner_link_url) <= 2000); - -CREATE TABLE telegram_password_flows ( - telegram_user_id bigint PRIMARY KEY, - stage text NOT NULL CHECK (stage IN ('new_password','confirm_password')), - password_hash text NOT NULL DEFAULT '', - expires_at timestamptz NOT NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX telegram_password_flows_expiry_idx ON telegram_password_flows(expires_at); diff --git a/000031_login_background.up.sql b/000031_login_background.up.sql deleted file mode 100644 index 0ac3f59..0000000 --- a/000031_login_background.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE site_brand_settings - ADD COLUMN login_background_data_url text NOT NULL DEFAULT '' - CHECK (char_length(login_background_data_url) <= 5600000); diff --git a/000032_branding_image_limits.up.sql b/000032_branding_image_limits.up.sql deleted file mode 100644 index 1955a14..0000000 --- a/000032_branding_image_limits.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -ALTER TABLE site_brand_settings - DROP CONSTRAINT IF EXISTS site_brand_settings_banner_image_data_url_check, - DROP CONSTRAINT IF EXISTS site_brand_settings_login_background_data_url_check; - -ALTER TABLE site_brand_settings - ADD CONSTRAINT site_brand_settings_banner_image_data_url_check - CHECK (char_length(banner_image_data_url) <= 28000000), - ADD CONSTRAINT site_brand_settings_login_background_data_url_check - CHECK (char_length(login_background_data_url) <= 28000000); diff --git a/000033_negative_point_balances.up.sql b/000033_negative_point_balances.up.sql deleted file mode 100644 index b287298..0000000 --- a/000033_negative_point_balances.up.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE point_accounts - DROP CONSTRAINT IF EXISTS point_accounts_balance_check; - -ALTER TABLE point_ledger - DROP CONSTRAINT IF EXISTS point_ledger_balance_after_check; diff --git a/000034_membership_limits_points_red_packets.up.sql b/000034_membership_limits_points_red_packets.up.sql deleted file mode 100644 index 905a360..0000000 --- a/000034_membership_limits_points_red_packets.up.sql +++ /dev/null @@ -1,54 +0,0 @@ -ALTER TABLE site_brand_settings - ADD COLUMN IF NOT EXISTS points_name text NOT NULL DEFAULT '积分' - CHECK (char_length(btrim(points_name)) BETWEEN 1 AND 20); - -UPDATE memberships -SET ends_at = timestamptz '9999-12-31 23:59:59+00' -WHERE ends_at > timestamptz '9999-12-31 23:59:59+00'; - -UPDATE card_batches SET duration_days = 365000 WHERE duration_days > 365000; -UPDATE membership_plans SET duration_days = 365000 WHERE duration_days > 365000; - -ALTER TABLE card_batches - ADD CONSTRAINT card_batches_duration_days_limit CHECK (duration_days BETWEEN 1 AND 365000); -ALTER TABLE membership_plans - ADD CONSTRAINT membership_plans_duration_days_limit CHECK (duration_days BETWEEN 1 AND 365000); - -ALTER TABLE point_ledger DROP CONSTRAINT IF EXISTS point_ledger_source_type_check; -ALTER TABLE point_ledger - ADD CONSTRAINT point_ledger_source_type_check CHECK (source_type IN ('manual','playback_session','media_feedback','redemption','daily_checkin','membership_purchase','telegram_red_packet')); - -CREATE TABLE telegram_red_packets ( - id uuid PRIMARY KEY, - chat_id bigint NOT NULL, - creator_user_id uuid NOT NULL REFERENCES users(id) ON DELETE RESTRICT, - creator_telegram_id bigint NOT NULL, - allocation_mode text NOT NULL CHECK (allocation_mode IN ('normal','random')), - claim_mode text NOT NULL CHECK (claim_mode IN ('button','password')), - total_points integer NOT NULL CHECK (total_points BETWEEN 1 AND 100000), - packet_count integer NOT NULL CHECK (packet_count BETWEEN 1 AND 1000), - remaining_points integer NOT NULL CHECK (remaining_points >= 0), - remaining_count integer NOT NULL CHECK (remaining_count >= 0), - password_hash text NOT NULL DEFAULT '', - message text NOT NULL DEFAULT '' CHECK (char_length(message) <= 200), - status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','expired','closed')), - message_id bigint NOT NULL DEFAULT 0, - expires_at timestamptz NOT NULL, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - CHECK (remaining_count <= packet_count), - CHECK (remaining_points <= total_points) -); - -CREATE INDEX telegram_red_packets_chat_open_idx ON telegram_red_packets(chat_id, expires_at DESC) WHERE status = 'open'; - -CREATE TABLE telegram_red_packet_claims ( - id bigserial PRIMARY KEY, - packet_id uuid NOT NULL REFERENCES telegram_red_packets(id) ON DELETE CASCADE, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE RESTRICT, - telegram_user_id bigint NOT NULL, - amount integer NOT NULL CHECK (amount > 0), - claimed_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(packet_id, user_id), - UNIQUE(packet_id, telegram_user_id) -); diff --git a/000035_plugin_management_permission.up.sql b/000035_plugin_management_permission.up.sql deleted file mode 100644 index 0777f90..0000000 --- a/000035_plugin_management_permission.up.sql +++ /dev/null @@ -1,17 +0,0 @@ -ALTER TABLE user_permissions DROP CONSTRAINT user_permissions_permission_check; -ALTER TABLE user_permissions ADD CONSTRAINT user_permissions_permission_check CHECK (permission IN ( - 'users.read','users.write','memberships.manage','cards.manage','invites.manage', - 'requests.manage','tickets.manage','notifications.manage','security.manage', - 'operations.manage','e.manage','plugins.manage','audit.read','logs.read','backups.manage', - 'updates.manage','settings.manage','licenses.manage' -)); - -INSERT INTO user_permissions (user_id, permission, granted_by) -SELECT id, 'plugins.manage', id -FROM users -WHERE role = 'owner' -ON CONFLICT (user_id, permission) DO NOTHING; - -ALTER TABLE e_command_requests - ADD COLUMN response_status integer CHECK (response_status BETWEEN 100 AND 599), - ADD COLUMN response_body jsonb; diff --git a/000036_plugin_service_actor.up.sql b/000036_plugin_service_actor.up.sql deleted file mode 100644 index fe0f235..0000000 --- a/000036_plugin_service_actor.up.sql +++ /dev/null @@ -1,14 +0,0 @@ --- The plugin control plane is authenticated by E, but existing S transactions --- require a UUID actor for foreign keys and role checks. This principal has no --- credential row and is excluded from all user-management views. -INSERT INTO users (id, username, username_normalized, email, email_normalized, role, status) -VALUES ( - '00000000-0000-0000-0000-000000000036', - '__plugin_service__', - '__plugin_service__', - 'plugin-service@internal.invalid', - 'plugin-service@internal.invalid', - 'admin', - 'active' -) -ON CONFLICT (id) DO NOTHING; diff --git a/000037_user_groups.up.sql b/000037_user_groups.up.sql deleted file mode 100644 index 415d1b4..0000000 --- a/000037_user_groups.up.sql +++ /dev/null @@ -1,34 +0,0 @@ -CREATE TABLE user_groups ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL, - name_normalized text NOT NULL UNIQUE, - description text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id), - updated_by uuid REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - CHECK (char_length(btrim(name)) BETWEEN 1 AND 80), - CHECK (char_length(description) <= 1000) -); - -CREATE TABLE user_group_members ( - group_id uuid NOT NULL REFERENCES user_groups(id) ON DELETE CASCADE, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - created_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (group_id, user_id) -); -CREATE INDEX user_group_members_user_idx ON user_group_members(user_id, group_id); - -CREATE TABLE user_group_permissions ( - group_id uuid NOT NULL REFERENCES user_groups(id) ON DELETE CASCADE, - permission text NOT NULL, - granted_by uuid REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (group_id, permission), - CHECK (permission IN ( - 'users.read','users.write','memberships.manage','cards.manage','invites.manage', - 'requests.manage','tickets.manage','notifications.manage','security.manage', - 'operations.manage','e.manage','plugins.manage','audit.read','logs.read', - 'backups.manage','updates.manage','settings.manage','licenses.manage' - )) -); diff --git a/000038_store_settings.up.sql b/000038_store_settings.up.sql deleted file mode 100644 index 47759b5..0000000 --- a/000038_store_settings.up.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE store_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - title text NOT NULL DEFAULT '会员商店', - introduction text NOT NULL DEFAULT '', - notice text NOT NULL DEFAULT '', - enabled boolean NOT NULL DEFAULT true, - allow_point_purchase boolean NOT NULL DEFAULT true, - updated_by text NOT NULL DEFAULT '', - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO store_settings(singleton) VALUES(true) ON CONFLICT (singleton) DO NOTHING; diff --git a/000039_telegram_command_auto_delete.up.sql b/000039_telegram_command_auto_delete.up.sql deleted file mode 100644 index 7854c4a..0000000 --- a/000039_telegram_command_auto_delete.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE telegram_bot_settings - ADD COLUMN command_auto_delete_seconds integer NOT NULL DEFAULT 0 - CHECK (command_auto_delete_seconds BETWEEN 0 AND 3600); diff --git a/000040_enable_telegram_command_auto_delete.up.sql b/000040_enable_telegram_command_auto_delete.up.sql deleted file mode 100644 index 5358114..0000000 --- a/000040_enable_telegram_command_auto_delete.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -ALTER TABLE telegram_bot_settings - ALTER COLUMN command_auto_delete_seconds SET DEFAULT 60; - --- The preceding migration introduced this setting with a disabled default. --- Enable the requested cleanup for existing installations while retaining 0 --- as the explicit opt-out value in the administration UI. -UPDATE telegram_bot_settings -SET command_auto_delete_seconds = 60 -WHERE singleton AND command_auto_delete_seconds = 0; diff --git a/000041_web_logins_not_playback_devices.up.sql b/000041_web_logins_not_playback_devices.up.sql deleted file mode 100644 index 6ad86db..0000000 --- a/000041_web_logins_not_playback_devices.up.sql +++ /dev/null @@ -1,10 +0,0 @@ --- WebUI refresh-token sessions are account sessions, not playback devices. --- Retire legacy rows created by the old login path only when they have never --- been used to reserve or play media, preserving real browser playback data. -UPDATE devices device -SET revoked_at = COALESCE(device.revoked_at, now()) -WHERE device.revoked_at IS NULL - AND lower(device.client_name) LIKE '%webui%' - AND NOT EXISTS ( - SELECT 1 FROM playback_sessions session WHERE session.device_id = device.id - ); diff --git a/000042_login_protection.up.sql b/000042_login_protection.up.sql deleted file mode 100644 index 0aeacc0..0000000 --- a/000042_login_protection.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE auth_login_failures ( - subject_type text NOT NULL CHECK (subject_type IN ('account','ip')), - subject_hash text NOT NULL, - failed_count integer NOT NULL DEFAULT 0 CHECK (failed_count >= 0), - window_started_at timestamptz NOT NULL DEFAULT now(), - locked_until timestamptz, - updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (subject_type, subject_hash) -); - -CREATE INDEX auth_login_failures_cleanup_idx ON auth_login_failures(updated_at); diff --git a/000043_plugin_v11.up.sql b/000043_plugin_v11.up.sql deleted file mode 100644 index a946153..0000000 --- a/000043_plugin_v11.up.sql +++ /dev/null @@ -1,104 +0,0 @@ -CREATE TABLE IF NOT EXISTS plugin_user_policies ( - user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, - is_disabled boolean NOT NULL DEFAULT false, - is_administrator boolean NOT NULL DEFAULT false, - allow_video_playback boolean NOT NULL DEFAULT true, - allow_audio_playback boolean NOT NULL DEFAULT true, - allow_content_downloading boolean NOT NULL DEFAULT false, - enable_remote_access boolean NOT NULL DEFAULT true, - enabled_library_ids text[] NOT NULL DEFAULT '{}', - blocked_library_ids text[] NOT NULL DEFAULT '{}', - max_concurrent_streams integer NOT NULL DEFAULT 0 CHECK (max_concurrent_streams >= 0), - updated_at timestamptz NOT NULL DEFAULT now() -); - -ALTER TABLE playback_decisions - ADD COLUMN IF NOT EXISTS media_type text NOT NULL DEFAULT '' CHECK (media_type IN ('','video','audio')); -ALTER TABLE playback_sessions - ADD COLUMN IF NOT EXISTS media_type text NOT NULL DEFAULT '' CHECK (media_type IN ('','video','audio')); -CREATE INDEX IF NOT EXISTS playback_sessions_plugin_report_idx ON playback_sessions(created_at DESC,user_id); - -CREATE TABLE IF NOT EXISTS user_group_media_policies ( - group_id uuid PRIMARY KEY REFERENCES user_groups(id) ON DELETE CASCADE, - enabled_library_ids text[] NOT NULL DEFAULT '{}', - blocked_library_ids text[] NOT NULL DEFAULT '{}', - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now(), - CHECK (cardinality(enabled_library_ids) <= 500), - CHECK (cardinality(blocked_library_ids) <= 500) -); - -ALTER TABLE invite_codes - ADD COLUMN IF NOT EXISTS owner_reward_points integer NOT NULL DEFAULT 0 CHECK (owner_reward_points >= 0), - ADD COLUMN IF NOT EXISTS owner_reward_membership_days integer NOT NULL DEFAULT 0 CHECK (owner_reward_membership_days >= 0), - ADD COLUMN IF NOT EXISTS redeemer_reward_points integer NOT NULL DEFAULT 0 CHECK (redeemer_reward_points >= 0), - ADD COLUMN IF NOT EXISTS redeemer_reward_membership_days integer NOT NULL DEFAULT 0 CHECK (redeemer_reward_membership_days >= 0); - -CREATE TABLE IF NOT EXISTS invite_redemptions ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - invite_id uuid NOT NULL REFERENCES invite_codes(id) ON DELETE CASCADE, - redeemed_by_user_id uuid NOT NULL REFERENCES users(id), - idempotency_key text NOT NULL CHECK (char_length(idempotency_key) BETWEEN 1 AND 256), - owner_reward_points integer NOT NULL DEFAULT 0, - owner_reward_membership_days integer NOT NULL DEFAULT 0, - redeemer_reward_points integer NOT NULL DEFAULT 0, - redeemer_reward_membership_days integer NOT NULL DEFAULT 0, - redeemed_at timestamptz NOT NULL DEFAULT now(), - UNIQUE (invite_id, redeemed_by_user_id), - UNIQUE (invite_id, idempotency_key) -); -CREATE INDEX IF NOT EXISTS invite_redemptions_user_time_idx ON invite_redemptions(redeemed_by_user_id,redeemed_at DESC); - -INSERT INTO membership_plans(code,name,duration_days,max_playbacks,max_devices,max_bitrate_kbps,price_points,self_service,enabled) -VALUES('points-7d','7 天会员',7,1,3,0,30,true,true) -ON CONFLICT(code) DO NOTHING; - -ALTER TABLE point_ledger DROP CONSTRAINT IF EXISTS point_ledger_source_type_check; -ALTER TABLE point_ledger ADD CONSTRAINT point_ledger_source_type_check CHECK (source_type IN ( - 'manual','playback_session','media_feedback','redemption','daily_checkin', - 'membership_purchase','telegram_red_packet','invite_reward' -)); - -CREATE TABLE IF NOT EXISTS plugin_telegram_contexts ( - token_hash bytea PRIMARY KEY, - plugin_id text NOT NULL, - tenant_id text NOT NULL, - telegram_user_id bigint NOT NULL, - chat_id bigint NOT NULL, - telegram_username text NOT NULL DEFAULT '', - expires_at timestamptz NOT NULL, - consumed_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - CHECK (expires_at > created_at) -); -CREATE INDEX IF NOT EXISTS plugin_telegram_contexts_expiry_idx ON plugin_telegram_contexts(expires_at) WHERE consumed_at IS NULL; - -CREATE TABLE IF NOT EXISTS plugin_telegram_sends ( - plugin_id text NOT NULL, - tenant_id text NOT NULL, - action text NOT NULL CHECK (action IN ('send_photo','send_document')), - idempotency_key text NOT NULL CHECK (char_length(idempotency_key) BETWEEN 1 AND 256), - status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','sent')), - message_id bigint NOT NULL DEFAULT 0, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (plugin_id,tenant_id,action,idempotency_key) -); -CREATE INDEX IF NOT EXISTS plugin_telegram_sends_pending_idx ON plugin_telegram_sends(updated_at) WHERE status='pending'; - -CREATE TABLE IF NOT EXISTS plugin_audit_records ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - plugin_id text NOT NULL, - plugin_version text NOT NULL, - tenant_id text NOT NULL, - actor_type text NOT NULL, - actor_id text NOT NULL, - action text NOT NULL, - target_type text NOT NULL, - target_id text NOT NULL DEFAULT '', - request_id text NOT NULL DEFAULT '', - trace_id text NOT NULL DEFAULT '', - result text NOT NULL, - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX IF NOT EXISTS plugin_audit_records_plugin_time_idx ON plugin_audit_records(plugin_id,tenant_id,created_at DESC,id DESC); diff --git a/000044_telegram_membership_signup.up.sql b/000044_telegram_membership_signup.up.sql deleted file mode 100644 index 385bce2..0000000 --- a/000044_telegram_membership_signup.up.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE TABLE telegram_membership_signup_flows ( - token_hash text PRIMARY KEY, - actor_user_id uuid NOT NULL REFERENCES users(id), - telegram_user_id bigint NOT NULL, - telegram_username text NOT NULL DEFAULT '', - started_at timestamptz, - consumed_at timestamptz, - expires_at timestamptz NOT NULL, - created_at timestamptz NOT NULL DEFAULT now() -); - -CREATE UNIQUE INDEX telegram_membership_signup_flows_active_target_idx - ON telegram_membership_signup_flows(telegram_user_id) - WHERE consumed_at IS NULL; diff --git a/000045_plugin_telegram_inbound_media.up.sql b/000045_plugin_telegram_inbound_media.up.sql deleted file mode 100644 index d79fc81..0000000 --- a/000045_plugin_telegram_inbound_media.up.sql +++ /dev/null @@ -1,8 +0,0 @@ -ALTER TABLE plugin_telegram_contexts - ADD COLUMN IF NOT EXISTS message_id bigint NOT NULL DEFAULT 0, - ADD COLUMN IF NOT EXISTS chat_type text NOT NULL DEFAULT 'private', - ADD COLUMN IF NOT EXISTS inbound_media jsonb NOT NULL DEFAULT '[]'::jsonb; - -ALTER TABLE plugin_telegram_sends DROP CONSTRAINT IF EXISTS plugin_telegram_sends_action_check; -ALTER TABLE plugin_telegram_sends ADD CONSTRAINT plugin_telegram_sends_action_check - CHECK (action IN ('send_message','send_photo','send_document','resend_inbound_media','bot_api','chat_api')); diff --git a/000046_plugin_telegram_chat_type.up.sql b/000046_plugin_telegram_chat_type.up.sql deleted file mode 100644 index e45c991..0000000 --- a/000046_plugin_telegram_chat_type.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE plugin_telegram_contexts - ADD COLUMN IF NOT EXISTS chat_type text NOT NULL DEFAULT 'private'; diff --git a/000047_plugin_telegram_send_message.up.sql b/000047_plugin_telegram_send_message.up.sql deleted file mode 100644 index 4fea51b..0000000 --- a/000047_plugin_telegram_send_message.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE plugin_telegram_sends DROP CONSTRAINT IF EXISTS plugin_telegram_sends_action_check; -ALTER TABLE plugin_telegram_sends ADD CONSTRAINT plugin_telegram_sends_action_check - CHECK (action IN ('send_message','send_photo','send_document','resend_inbound_media','bot_api','chat_api')); diff --git a/000048_telegram_pro_user_duration.up.sql b/000048_telegram_pro_user_duration.up.sql deleted file mode 100644 index f0f739f..0000000 --- a/000048_telegram_pro_user_duration.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE telegram_membership_signup_flows - ADD COLUMN membership_duration_days integer NOT NULL DEFAULT 30 - CHECK (membership_duration_days BETWEEN 1 AND 365000); diff --git a/000049_telegram_group_cleanup.up.sql b/000049_telegram_group_cleanup.up.sql deleted file mode 100644 index f90493d..0000000 --- a/000049_telegram_group_cleanup.up.sql +++ /dev/null @@ -1,21 +0,0 @@ -CREATE TABLE telegram_group_members ( - chat_id bigint NOT NULL, - telegram_user_id bigint NOT NULL, - username text NOT NULL DEFAULT '', - first_seen_at timestamptz NOT NULL DEFAULT now(), - last_seen_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (chat_id, telegram_user_id) -); - -CREATE TABLE telegram_group_cleanup_flows ( - token_hash text PRIMARY KEY, - actor_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - chat_id bigint NOT NULL, - expires_at timestamptz NOT NULL, - consumed_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX telegram_group_cleanup_flows_active_idx - ON telegram_group_cleanup_flows(actor_user_id, chat_id) - WHERE consumed_at IS NULL; diff --git a/000050_telegram_rebind_after_unbind.up.sql b/000050_telegram_rebind_after_unbind.up.sql deleted file mode 100644 index ead635a..0000000 --- a/000050_telegram_rebind_after_unbind.up.sql +++ /dev/null @@ -1,7 +0,0 @@ --- A disabled row records binding history only. It must not reserve the numeric --- Telegram identity forever, otherwise a user who unbinds cannot bind a newly --- registered panel account. -ALTER TABLE telegram_accounts DROP CONSTRAINT IF EXISTS telegram_accounts_telegram_user_id_key; -CREATE UNIQUE INDEX IF NOT EXISTS telegram_accounts_telegram_user_id_active_idx - ON telegram_accounts(telegram_user_id) - WHERE disabled_at IS NULL; diff --git a/000051_storage_webhook_events.up.sql b/000051_storage_webhook_events.up.sql deleted file mode 100644 index 171a107..0000000 --- a/000051_storage_webhook_events.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE storage_webhook_events ( - event_id text PRIMARY KEY, - library_id uuid NOT NULL, - event_type text NOT NULL, - paths jsonb NOT NULL, - task_ids jsonb NOT NULL DEFAULT '[]'::jsonb, - state text NOT NULL CHECK (state IN ('processing','accepted','failed')), - error_code text NOT NULL DEFAULT '', - received_at timestamptz NOT NULL DEFAULT now(), - completed_at timestamptz -); - -CREATE INDEX storage_webhook_events_received_idx ON storage_webhook_events(received_at DESC); diff --git a/000052_user_agent_security_rules.up.sql b/000052_user_agent_security_rules.up.sql deleted file mode 100644 index 4120f08..0000000 --- a/000052_user_agent_security_rules.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE security_list_entries DROP CONSTRAINT security_list_entries_subject_type_check; -ALTER TABLE security_list_entries ADD CONSTRAINT security_list_entries_subject_type_check - CHECK (subject_type IN ('ip','cidr','device','user','user_agent')); diff --git a/000053_precise_client_access_rules.up.sql b/000053_precise_client_access_rules.up.sql deleted file mode 100644 index c572ac6..0000000 --- a/000053_precise_client_access_rules.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE security_list_entries DROP CONSTRAINT security_list_entries_subject_type_check; -ALTER TABLE security_list_entries ADD CONSTRAINT security_list_entries_subject_type_check - CHECK (subject_type IN ('ip','cidr','device','user','user_agent','client_name','client_version')); diff --git a/000054_api_keys.up.sql b/000054_api_keys.up.sql deleted file mode 100644 index bd280aa..0000000 --- a/000054_api_keys.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE api_keys ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - prefix text NOT NULL UNIQUE CHECK (prefix ~ '^oc_[a-z0-9]{8}$'), - key_hash bytea NOT NULL UNIQUE, - expires_at timestamptz, - enabled boolean NOT NULL DEFAULT true, - created_at timestamptz NOT NULL DEFAULT now(), - last_used_at timestamptz -); -CREATE INDEX api_keys_user_idx ON api_keys(user_id,created_at DESC); -CREATE INDEX api_keys_active_idx ON api_keys(prefix) WHERE enabled; diff --git a/000055_playback_traffic.up.sql b/000055_playback_traffic.up.sql deleted file mode 100644 index 97b640d..0000000 --- a/000055_playback_traffic.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE playback_sessions ADD COLUMN IF NOT EXISTS bytes_served bigint NOT NULL DEFAULT 0 CHECK (bytes_served >= 0); -ALTER TABLE playback_sessions ADD COLUMN IF NOT EXISTS estimated_bytes bigint NOT NULL DEFAULT 0 CHECK (estimated_bytes >= 0); -CREATE INDEX IF NOT EXISTS playback_sessions_traffic_user_idx ON playback_sessions(user_id, updated_at DESC); diff --git a/000056_playback_estimate.up.sql b/000056_playback_estimate.up.sql deleted file mode 100644 index dc0bca1..0000000 --- a/000056_playback_estimate.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE playback_sessions ADD COLUMN IF NOT EXISTS estimated_bytes bigint NOT NULL DEFAULT 0 CHECK (estimated_bytes >= 0); diff --git a/000057_admin_agent.up.sql b/000057_admin_agent.up.sql deleted file mode 100644 index ef635a7..0000000 --- a/000057_admin_agent.up.sql +++ /dev/null @@ -1,60 +0,0 @@ -CREATE TABLE agent_settings ( - id smallint PRIMARY KEY DEFAULT 1 CHECK (id = 1), - enabled boolean NOT NULL DEFAULT false, - provider_name text NOT NULL DEFAULT '', - provider_type text NOT NULL DEFAULT 'openai_compatible' CHECK (provider_type IN ('openai_compatible','openai','custom_http')), - base_url text NOT NULL DEFAULT '', - model text NOT NULL DEFAULT '', - encrypted_api_key bytea, - timeout_ms integer NOT NULL DEFAULT 30000 CHECK (timeout_ms BETWEEN 1000 AND 120000), - max_tokens integer NOT NULL DEFAULT 2048 CHECK (max_tokens BETWEEN 1 AND 32768), - temperature double precision NOT NULL DEFAULT 0.2 CHECK (temperature BETWEEN 0 AND 2), - system_prompt text NOT NULL DEFAULT '', - max_history integer NOT NULL DEFAULT 20 CHECK (max_history BETWEEN 1 AND 100), - rate_limit integer NOT NULL DEFAULT 30 CHECK (rate_limit BETWEEN 1 AND 600), - request_logging boolean NOT NULL DEFAULT false, - tool_enabled boolean NOT NULL DEFAULT true, - confirmation_required boolean NOT NULL DEFAULT true, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - updated_by uuid REFERENCES users(id) -); - -CREATE TABLE agent_sessions ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - admin_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - title text NOT NULL DEFAULT 'OverCooked Admin Agent', - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - expires_at timestamptz NOT NULL DEFAULT (now() + interval '24 hours') -); -CREATE INDEX agent_sessions_admin_idx ON agent_sessions(admin_user_id, updated_at DESC); - -CREATE TABLE agent_messages ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - session_id uuid NOT NULL REFERENCES agent_sessions(id) ON DELETE CASCADE, - role text NOT NULL CHECK (role IN ('system','user','assistant','tool')), - content text NOT NULL, - tool_name text, - tool_call_id text, - confirmation_state text NOT NULL DEFAULT 'none' CHECK (confirmation_state IN ('none','pending','confirmed','cancelled','expired')), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX agent_messages_session_idx ON agent_messages(session_id, created_at); - -CREATE TABLE agent_audit ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - session_id uuid REFERENCES agent_sessions(id) ON DELETE SET NULL, - admin_user_id uuid NOT NULL REFERENCES users(id) ON DELETE RESTRICT, - capability text NOT NULL, - action text NOT NULL, - target text NOT NULL DEFAULT '', - request_summary text NOT NULL DEFAULT '', - result jsonb NOT NULL DEFAULT '{}'::jsonb, - status text NOT NULL CHECK (status IN ('planned','pending_confirmation','success','cancelled','failed')), - error_code text NOT NULL DEFAULT '', - trace_id text NOT NULL DEFAULT '', - latency_ms bigint NOT NULL DEFAULT 0, - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX agent_audit_admin_idx ON agent_audit(admin_user_id, created_at DESC); diff --git a/s-migrations/000001_s1_s3.up.sql b/s-migrations/000001_s1_s3.up.sql deleted file mode 100644 index 7139849..0000000 --- a/s-migrations/000001_s1_s3.up.sql +++ /dev/null @@ -1,81 +0,0 @@ -CREATE EXTENSION IF NOT EXISTS pgcrypto; - -CREATE TABLE users ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - username text NOT NULL, - username_normalized text NOT NULL UNIQUE, - email text NOT NULL, - email_normalized text NOT NULL UNIQUE, - role text NOT NULL DEFAULT 'user' CHECK (role IN ('user','admin')), - status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','disabled','deleted')), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - deleted_at timestamptz -); -CREATE TABLE credentials ( - user_id uuid PRIMARY KEY REFERENCES users(id), - password_hash text NOT NULL, - password_changed_at timestamptz NOT NULL DEFAULT now(), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE memberships ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), - status text NOT NULL CHECK (status IN ('active','expired','revoked')), - starts_at timestamptz NOT NULL, ends_at timestamptz NOT NULL, - source text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), - CHECK (ends_at > starts_at) -); -CREATE INDEX memberships_active_user_idx ON memberships(user_id, ends_at DESC) WHERE status = 'active'; -CREATE TABLE refresh_token_families ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), - revoked_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE refresh_tokens ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), family_id uuid NOT NULL REFERENCES refresh_token_families(id), - token_hash bytea NOT NULL UNIQUE, expires_at timestamptz NOT NULL, consumed_at timestamptz, - replaced_by uuid, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX refresh_tokens_family_idx ON refresh_tokens(family_id); -CREATE TABLE card_batches ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, card_type text NOT NULL CHECK (card_type IN ('registration','renewal')), - duration_days integer NOT NULL CHECK (duration_days > 0), created_by uuid REFERENCES users(id), - revoked_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE cards ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), batch_id uuid NOT NULL REFERENCES card_batches(id), - code_hash bytea NOT NULL UNIQUE, remaining_uses integer NOT NULL DEFAULT 1 CHECK (remaining_uses >= 0), - status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','revoked','exhausted')), - expires_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE card_usages ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), card_id uuid NOT NULL REFERENCES cards(id), user_id uuid NOT NULL REFERENCES users(id), - used_at timestamptz NOT NULL DEFAULT now(), UNIQUE(card_id, user_id) -); -CREATE INDEX card_usages_user_idx ON card_usages(user_id, used_at DESC); -CREATE TABLE audit_events ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, actor_user_id uuid REFERENCES users(id), action text NOT NULL, - target_type text NOT NULL, target_id text NOT NULL, detail jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX audit_events_target_idx ON audit_events(target_type, target_id, created_at DESC); -CREATE TABLE policy_rules ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), subject_type text NOT NULL CHECK (subject_type IN ('global','membership','user')), - subject_id uuid, priority integer NOT NULL DEFAULT 0, enabled boolean NOT NULL DEFAULT true, - max_devices integer, max_online integer, max_playbacks integer, max_device_playbacks integer, max_bitrate_kbps integer, - downloads_allowed boolean, proxy_allowed boolean, transcode_allowed boolean, library_id text, - allowed_cidrs text[] NOT NULL DEFAULT '{}', countries text[] NOT NULL DEFAULT '{}', regions text[] NOT NULL DEFAULT '{}', - asns bigint[] NOT NULL DEFAULT '{}', carriers text[] NOT NULL DEFAULT '{}', route_id text, created_at timestamptz NOT NULL DEFAULT now(), - CHECK ((subject_type = 'global' AND subject_id IS NULL) OR (subject_type <> 'global' AND subject_id IS NOT NULL)) -); -CREATE INDEX policy_rules_subject_idx ON policy_rules(subject_type, subject_id, priority DESC) WHERE enabled; -CREATE TABLE devices ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), fingerprint_hash bytea NOT NULL, - first_seen_at timestamptz NOT NULL DEFAULT now(), last_seen_at timestamptz NOT NULL DEFAULT now(), revoked_at timestamptz, - UNIQUE(user_id, fingerprint_hash) -); -CREATE TABLE playback_sessions ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), device_id uuid REFERENCES devices(id), - e_session_id text NOT NULL UNIQUE, library_id text NOT NULL, route_id text, state text NOT NULL CHECK (state IN ('reserved','active','released','expired')), - lease_token_hash bytea NOT NULL UNIQUE, expires_at timestamptz NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), released_at timestamptz -); -CREATE INDEX playback_sessions_active_user_idx ON playback_sessions(user_id, expires_at) WHERE state IN ('reserved','active'); -CREATE INDEX playback_sessions_active_device_idx ON playback_sessions(device_id, expires_at) WHERE state IN ('reserved','active'); diff --git a/s-migrations/000002_s4_s7.up.sql b/s-migrations/000002_s4_s7.up.sql deleted file mode 100644 index 664dfea..0000000 --- a/s-migrations/000002_s4_s7.up.sql +++ /dev/null @@ -1,64 +0,0 @@ -CREATE TABLE e_command_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), actor_user_id uuid NOT NULL REFERENCES users(id), - command text NOT NULL, target_id text NOT NULL, idempotency_key text NOT NULL UNIQUE, - state text NOT NULL CHECK (state IN ('accepted','completed','failed')), created_at timestamptz NOT NULL DEFAULT now(), completed_at timestamptz -); -CREATE INDEX e_command_requests_actor_idx ON e_command_requests(actor_user_id, created_at DESC); -CREATE TABLE e_event_inbox ( - event_id uuid PRIMARY KEY, event_type text NOT NULL, occurred_at timestamptz NOT NULL, - payload jsonb NOT NULL, received_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE invite_codes ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), creator_user_id uuid NOT NULL REFERENCES users(id), - code_hash bytea NOT NULL UNIQUE, max_uses integer NOT NULL DEFAULT 1 CHECK (max_uses > 0), used_count integer NOT NULL DEFAULT 0 CHECK (used_count >= 0), - expires_at timestamptz, revoked_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE invite_relations ( - parent_user_id uuid NOT NULL REFERENCES users(id), child_user_id uuid NOT NULL REFERENCES users(id), invite_code_id uuid NOT NULL REFERENCES invite_codes(id), - risk_state text NOT NULL DEFAULT 'normal' CHECK (risk_state IN ('normal','review','blocked')), detached_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY(parent_user_id, child_user_id), CHECK (parent_user_id <> child_user_id) -); -CREATE UNIQUE INDEX invite_relations_child_active_idx ON invite_relations(child_user_id) WHERE detached_at IS NULL; -CREATE TABLE media_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id), query_text text NOT NULL, - e_media_id uuid, availability text NOT NULL CHECK (availability IN ('unknown','available','unavailable')), status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','planned','fulfilled','rejected','cancelled')), - created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX media_requests_status_idx ON media_requests(status, created_at DESC); -CREATE TABLE ticket_types (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL UNIQUE, enabled boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now()); -CREATE TABLE tickets ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), owner_user_id uuid NOT NULL REFERENCES users(id), type_id uuid REFERENCES ticket_types(id), subject text NOT NULL, - status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','pending','resolved','closed')), priority text NOT NULL DEFAULT 'normal' CHECK (priority IN ('low','normal','high')), - created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), closed_at timestamptz -); -CREATE INDEX tickets_owner_idx ON tickets(owner_user_id, updated_at DESC); -CREATE INDEX tickets_status_idx ON tickets(status, updated_at DESC); -CREATE TABLE ticket_replies (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), ticket_id uuid NOT NULL REFERENCES tickets(id), author_user_id uuid NOT NULL REFERENCES users(id), body text NOT NULL, created_at timestamptz NOT NULL DEFAULT now()); -CREATE INDEX ticket_replies_ticket_idx ON ticket_replies(ticket_id, created_at); -CREATE TABLE ticket_assignments (ticket_id uuid PRIMARY KEY REFERENCES tickets(id), admin_user_id uuid NOT NULL REFERENCES users(id), assigned_at timestamptz NOT NULL DEFAULT now()); -CREATE TABLE telegram_accounts (user_id uuid PRIMARY KEY REFERENCES users(id), telegram_user_id bigint NOT NULL UNIQUE, username text, bound_at timestamptz NOT NULL DEFAULT now(), disabled_at timestamptz); -CREATE TABLE telegram_bind_codes (code_hash bytea PRIMARY KEY, user_id uuid NOT NULL REFERENCES users(id), expires_at timestamptz NOT NULL, consumed_at timestamptz, created_at timestamptz NOT NULL DEFAULT now()); - -CREATE TABLE license_products ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, edition text NOT NULL, max_s_instances integer NOT NULL CHECK (max_s_instances > 0), - max_e_nodes integer NOT NULL CHECK (max_e_nodes >= 0), feature_flags text[] NOT NULL DEFAULT '{}', valid_days integer NOT NULL CHECK (valid_days > 0), offline_days integer NOT NULL DEFAULT 60 CHECK (offline_days BETWEEN 0 AND 60), created_at timestamptz NOT NULL DEFAULT now() -); -CREATE TABLE license_activations ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), product_id uuid NOT NULL REFERENCES license_products(id), activation_hash bytea NOT NULL UNIQUE, - status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','revoked','exhausted')), max_instances integer NOT NULL DEFAULT 1 CHECK (max_instances > 0), created_at timestamptz NOT NULL DEFAULT now(), revoked_at timestamptz -); -CREATE TABLE license_instances ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), activation_id uuid NOT NULL REFERENCES license_activations(id), instance_id uuid NOT NULL, - fingerprint_hash bytea NOT NULL, signed_license text NOT NULL, expires_at timestamptz NOT NULL, offline_until timestamptz NOT NULL, last_seen_at timestamptz NOT NULL DEFAULT now(), unbound_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(activation_id, instance_id) -); -CREATE INDEX license_instances_active_idx ON license_instances(activation_id) WHERE unbound_at IS NULL; - -CREATE TABLE outbox_events ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), topic text NOT NULL, aggregate_type text NOT NULL, aggregate_id text NOT NULL, - payload jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), published_at timestamptz, attempts integer NOT NULL DEFAULT 0, last_error text - , locked_by text, locked_until timestamptz -); -CREATE INDEX outbox_events_pending_idx ON outbox_events(created_at) WHERE published_at IS NULL; -CREATE TABLE runtime_config (key text PRIMARY KEY, value jsonb NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), updated_by uuid REFERENCES users(id)); diff --git a/s-migrations/000003_user_management.up.sql b/s-migrations/000003_user_management.up.sql deleted file mode 100644 index f597795..0000000 --- a/s-migrations/000003_user_management.up.sql +++ /dev/null @@ -1,21 +0,0 @@ -ALTER TABLE users - ADD COLUMN ban_reason text, - ADD COLUMN banned_until timestamptz; - -CREATE TABLE user_permissions ( - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - permission text NOT NULL CHECK (permission IN ( - 'users.read', - 'users.write', - 'cards.manage', - 'operations.manage', - 'e.manage', - 'audit.read', - 'licenses.manage' - )), - granted_by uuid REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (user_id, permission) -); - -CREATE INDEX user_permissions_permission_idx ON user_permissions(permission, user_id); diff --git a/s-migrations/000004_access_revocation.up.sql b/s-migrations/000004_access_revocation.up.sql deleted file mode 100644 index 0bbe54e..0000000 --- a/s-migrations/000004_access_revocation.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE users - ADD COLUMN auth_version bigint NOT NULL DEFAULT 1 CHECK (auth_version > 0); diff --git a/s-migrations/000005_internal_auth_nonces.up.sql b/s-migrations/000005_internal_auth_nonces.up.sql deleted file mode 100644 index 5f20f0a..0000000 --- a/s-migrations/000005_internal_auth_nonces.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -CREATE TABLE internal_auth_nonces ( - nonce_hash bytea PRIMARY KEY, - expires_at timestamptz NOT NULL -); - -CREATE INDEX internal_auth_nonces_expiry_idx ON internal_auth_nonces(expires_at); diff --git a/s-migrations/000006_e_contract_playback_decisions.up.sql b/s-migrations/000006_e_contract_playback_decisions.up.sql deleted file mode 100644 index 1859a1b..0000000 --- a/s-migrations/000006_e_contract_playback_decisions.up.sql +++ /dev/null @@ -1,22 +0,0 @@ -CREATE TABLE playback_decisions ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id), - media_id uuid NOT NULL, - library_id uuid NOT NULL, - source_id uuid NOT NULL, - device_id text NOT NULL, - route_id text, - allowed boolean NOT NULL, - deny_code text, - max_bitrate bigint NOT NULL DEFAULT 0 CHECK (max_bitrate >= 0), - allow_download boolean NOT NULL DEFAULT false, - max_playbacks integer NOT NULL DEFAULT 0 CHECK (max_playbacks >= 0), - max_device_playbacks integer NOT NULL DEFAULT 0 CHECK (max_device_playbacks >= 0), - policy_revision bigint NOT NULL CHECK (policy_revision >= 0), - expires_at timestamptz NOT NULL, - consumed_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX playback_decisions_expiry_idx ON playback_decisions(expires_at); -CREATE INDEX playback_decisions_user_idx ON playback_decisions(user_id, created_at DESC); diff --git a/s-migrations/000007_external_auth_license_cache.up.sql b/s-migrations/000007_external_auth_license_cache.up.sql deleted file mode 100644 index 72d99de..0000000 --- a/s-migrations/000007_external_auth_license_cache.up.sql +++ /dev/null @@ -1,16 +0,0 @@ -CREATE TABLE commercial_license_cache ( - instance_id text PRIMARY KEY, - license_id text NOT NULL, - signed_license text NOT NULL, - edition text NOT NULL, - features text[] NOT NULL DEFAULT '{}', - max_s_instances integer NOT NULL CHECK (max_s_instances > 0), - max_e_nodes integer NOT NULL CHECK (max_e_nodes > 0), - expires_at timestamptz NOT NULL, - offline_until timestamptz NOT NULL, - status text NOT NULL CHECK (status IN ('active','revoked','expired')), - last_verified_at timestamptz NOT NULL DEFAULT now(), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX commercial_license_cache_status_expiry_idx ON commercial_license_cache(status,expires_at); diff --git a/s-migrations/000008_gateway_routing.up.sql b/s-migrations/000008_gateway_routing.up.sql deleted file mode 100644 index e105676..0000000 --- a/s-migrations/000008_gateway_routing.up.sql +++ /dev/null @@ -1,86 +0,0 @@ -CREATE TABLE gateway_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - public_origin text NOT NULL DEFAULT '', - trusted_proxy_cidrs text[] NOT NULL DEFAULT '{}', - active_revision bigint, - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO gateway_settings(singleton) VALUES (true); - -CREATE TABLE gateway_service_pools ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - code text NOT NULL UNIQUE CHECK (code ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$'), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - enabled boolean NOT NULL DEFAULT true, - is_default boolean NOT NULL DEFAULT false, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE UNIQUE INDEX gateway_service_pools_one_default_idx ON gateway_service_pools(is_default) WHERE is_default; -INSERT INTO gateway_service_pools(code,name,is_default) VALUES ('default','Default',true); - -CREATE TABLE gateway_nodes ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - node_id text NOT NULL UNIQUE CHECK (node_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$'), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - gateway_url text NOT NULL CHECK (length(gateway_url) BETWEEN 8 AND 2048), - pool_id uuid NOT NULL REFERENCES gateway_service_pools(id), - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - city text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0 CHECK (asn BETWEEN 0 AND 4294967295), - weight integer NOT NULL DEFAULT 100 CHECK (weight BETWEEN 1 AND 10000), - max_connections bigint NOT NULL DEFAULT 0 CHECK (max_connections >= 0), - max_playback_sessions bigint NOT NULL DEFAULT 0 CHECK (max_playback_sessions >= 0), - max_transcode_sessions bigint NOT NULL DEFAULT 0 CHECK (max_transcode_sessions >= 0), - bandwidth_capacity_mbps bigint NOT NULL DEFAULT 0 CHECK (bandwidth_capacity_mbps >= 0), - library_ids text[] NOT NULL DEFAULT '{}', - storage_ids text[] NOT NULL DEFAULT '{}', - enabled boolean NOT NULL DEFAULT true, - allow_login boolean NOT NULL DEFAULT true, - allow_new_playback boolean NOT NULL DEFAULT true, - maintenance boolean NOT NULL DEFAULT false, - draining boolean NOT NULL DEFAULT false, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX gateway_nodes_pool_idx ON gateway_nodes(pool_id,enabled,node_id); -CREATE INDEX gateway_nodes_route_idx ON gateway_nodes(enabled,maintenance,draining,country,region,carrier,asn); - -CREATE TABLE gateway_user_routes ( - user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, - forced_node_id text REFERENCES gateway_nodes(node_id) ON UPDATE CASCADE, - forced_pool_id uuid REFERENCES gateway_service_pools(id), - preferred_country text NOT NULL DEFAULT '', - preferred_region text NOT NULL DEFAULT '', - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE gateway_config_versions ( - revision bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - snapshot jsonb NOT NULL CHECK (jsonb_typeof(snapshot)='object'), - source_revision bigint REFERENCES gateway_config_versions(revision), - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX gateway_config_versions_created_idx ON gateway_config_versions(created_at DESC); -ALTER TABLE gateway_settings ADD CONSTRAINT gateway_settings_active_revision_fk FOREIGN KEY(active_revision) REFERENCES gateway_config_versions(revision); - -CREATE TABLE gateway_route_events ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - request_id text NOT NULL, - node_id text, - outcome text NOT NULL CHECK (outcome IN ('routed','rejected','upstream_failed')), - reason text NOT NULL DEFAULT '', - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0, - latency_ms integer NOT NULL DEFAULT 0 CHECK (latency_ms >= 0), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX gateway_route_events_recent_idx ON gateway_route_events(created_at DESC); -CREATE INDEX gateway_route_events_node_idx ON gateway_route_events(node_id,created_at DESC); diff --git a/s-migrations/000009_gateway_operations.up.sql b/s-migrations/000009_gateway_operations.up.sql deleted file mode 100644 index 946e637..0000000 --- a/s-migrations/000009_gateway_operations.up.sql +++ /dev/null @@ -1,19 +0,0 @@ -ALTER TABLE gateway_settings - ADD COLUMN routing_weights jsonb NOT NULL DEFAULT '{"country":1000,"region":100,"city":20,"carrier":10,"asn":10}'::jsonb, - ADD COLUMN recovery_ramp_seconds integer NOT NULL DEFAULT 300 CHECK (recovery_ramp_seconds BETWEEN 0 AND 3600), - ADD CONSTRAINT gateway_settings_routing_weights_object CHECK (jsonb_typeof(routing_weights) = 'object'); - -ALTER TABLE gateway_route_events - ADD COLUMN username text NOT NULL DEFAULT '', - ADD COLUMN device_hash text NOT NULL DEFAULT '', - ADD COLUMN resource_id text NOT NULL DEFAULT '', - ADD COLUMN path_class text NOT NULL DEFAULT 'other' - CHECK (path_class IN ('login','browse','image','playback','transcode','websocket','other')), - ADD COLUMN selection_source text NOT NULL DEFAULT ''; - -CREATE INDEX gateway_route_events_outcome_created_idx - ON gateway_route_events(outcome,created_at DESC); -CREATE INDEX gateway_route_events_location_created_idx - ON gateway_route_events(country,region,carrier,created_at DESC); -CREATE INDEX gateway_route_events_username_created_idx - ON gateway_route_events(username,created_at DESC) WHERE username <> ''; diff --git a/s-migrations/000010_license_entitlements.up.sql b/s-migrations/000010_license_entitlements.up.sql deleted file mode 100644 index 5d97a47..0000000 --- a/s-migrations/000010_license_entitlements.up.sql +++ /dev/null @@ -1,10 +0,0 @@ -ALTER TABLE commercial_license_cache - ADD COLUMN customer_id text NOT NULL DEFAULT '', - ADD COLUMN product text NOT NULL DEFAULT 'overcooked', - ADD COLUMN max_users integer NOT NULL DEFAULT 0 CHECK (max_users >= 0), - ADD COLUMN max_service_pools integer NOT NULL DEFAULT 0 CHECK (max_service_pools >= 0), - ADD COLUMN not_before timestamptz NOT NULL DEFAULT now(), - ADD COLUMN maintenance_until timestamptz; - -CREATE INDEX commercial_license_cache_product_status_idx - ON commercial_license_cache(product,status,offline_until); diff --git a/s-migrations/000011_e_address_directory.up.sql b/s-migrations/000011_e_address_directory.up.sql deleted file mode 100644 index e1dd3be..0000000 --- a/s-migrations/000011_e_address_directory.up.sql +++ /dev/null @@ -1,75 +0,0 @@ -CREATE TABLE e_address_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - trusted_proxy_cidrs text[] NOT NULL DEFAULT '{}', - default_address_id uuid, - rule_revision bigint NOT NULL DEFAULT 1, - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO e_address_settings(singleton) VALUES (true); - -CREATE TABLE e_addresses ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - public_url text NOT NULL CHECK (public_url ~ '^https?://[^/?#[:space:]]+'), - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - city text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0 CHECK (asn BETWEEN 0 AND 4294967295), - node_group text NOT NULL DEFAULT '', - priority integer NOT NULL DEFAULT 100 CHECK (priority BETWEEN -100000 AND 100000), - enabled boolean NOT NULL DEFAULT true, - healthy boolean NOT NULL DEFAULT false, - license_valid boolean NOT NULL DEFAULT false, - allow_user_publish boolean NOT NULL DEFAULT true, - maintenance boolean NOT NULL DEFAULT false, - description text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE UNIQUE INDEX e_addresses_public_url_idx ON e_addresses(public_url); -CREATE INDEX e_addresses_selection_idx ON e_addresses(enabled,healthy,license_valid,allow_user_publish,maintenance,priority); - -ALTER TABLE e_address_settings - ADD CONSTRAINT e_address_settings_default_fk FOREIGN KEY(default_address_id) REFERENCES e_addresses(id) ON DELETE SET NULL; - -CREATE TABLE e_address_rules ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - priority integer NOT NULL DEFAULT 100 CHECK (priority BETWEEN -100000 AND 100000), - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0 CHECK (asn BETWEEN 0 AND 4294967295), - node_group text NOT NULL DEFAULT '', - address_id uuid NOT NULL REFERENCES e_addresses(id) ON DELETE CASCADE, - enabled boolean NOT NULL DEFAULT true, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX e_address_rules_match_idx ON e_address_rules(enabled,priority,country,region,carrier,asn,node_group); - -CREATE TABLE e_user_assignments ( - user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, - address_id uuid NOT NULL REFERENCES e_addresses(id), - fixed boolean NOT NULL DEFAULT false, - rule_revision bigint NOT NULL DEFAULT 1, - reason text NOT NULL DEFAULT '', - source_ip_hash text NOT NULL DEFAULT '', - assigned_at timestamptz NOT NULL DEFAULT now(), - expires_at timestamptz, - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX e_user_assignments_expiry_idx ON e_user_assignments(expires_at); - -CREATE TABLE e_assignment_history ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - address_id uuid REFERENCES e_addresses(id) ON DELETE SET NULL, - source_ip_hash text NOT NULL DEFAULT '', - reason text NOT NULL DEFAULT '', - rule_revision bigint NOT NULL, - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX e_assignment_history_user_idx ON e_assignment_history(user_id,created_at DESC); diff --git a/s-migrations/000012_remove_gateway.up.sql b/s-migrations/000012_remove_gateway.up.sql deleted file mode 100644 index 53c7c6b..0000000 --- a/s-migrations/000012_remove_gateway.up.sql +++ /dev/null @@ -1,48 +0,0 @@ -INSERT INTO e_addresses ( - id, name, public_url, country, region, city, carrier, asn, node_group, - priority, enabled, healthy, license_valid, allow_user_publish, - maintenance, description, created_at, updated_at -) -SELECT - node.id, - node.name, - rtrim(node.gateway_url, '/'), - node.country, - node.region, - node.city, - node.carrier, - node.asn, - pool.code, - node.weight, - node.enabled, - false, - false, - false, - node.maintenance OR node.draining, - 'Migrated from retired proxy node ' || node.node_id, - node.created_at, - node.updated_at -FROM gateway_nodes AS node -JOIN gateway_service_pools AS pool ON pool.id = node.pool_id -WHERE node.gateway_url ~ '^https?://[^/?#[:space:]]+' -ON CONFLICT (public_url) DO UPDATE SET - name = EXCLUDED.name, - country = EXCLUDED.country, - region = EXCLUDED.region, - city = EXCLUDED.city, - carrier = EXCLUDED.carrier, - asn = EXCLUDED.asn, - node_group = EXCLUDED.node_group, - priority = EXCLUDED.priority, - description = CASE - WHEN e_addresses.description = '' THEN EXCLUDED.description - ELSE e_addresses.description - END, - updated_at = GREATEST(e_addresses.updated_at, EXCLUDED.updated_at); - -DROP TABLE gateway_route_events; -DROP TABLE gateway_settings; -DROP TABLE gateway_config_versions; -DROP TABLE gateway_user_routes; -DROP TABLE gateway_nodes; -DROP TABLE gateway_service_pools; diff --git a/s-migrations/000013_e_address_observability.up.sql b/s-migrations/000013_e_address_observability.up.sql deleted file mode 100644 index 2aa9254..0000000 --- a/s-migrations/000013_e_address_observability.up.sql +++ /dev/null @@ -1,22 +0,0 @@ -ALTER TABLE e_address_rules - ADD COLUMN match_count bigint NOT NULL DEFAULT 0 CHECK (match_count >= 0), - ADD COLUMN last_matched_at timestamptz; - -CREATE TABLE e_address_selection_events ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - user_id uuid REFERENCES users(id) ON DELETE SET NULL, - address_id uuid REFERENCES e_addresses(id) ON DELETE SET NULL, - rule_id uuid REFERENCES e_address_rules(id) ON DELETE SET NULL, - outcome text NOT NULL CHECK (outcome IN ('selected','unavailable','access_denied')), - reason text NOT NULL DEFAULT '', - source_ip_hash text NOT NULL DEFAULT '', - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint NOT NULL DEFAULT 0 CHECK (asn BETWEEN 0 AND 4294967295), - created_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX e_address_selection_events_created_idx ON e_address_selection_events(created_at DESC); -CREATE INDEX e_address_selection_events_outcome_idx ON e_address_selection_events(outcome,created_at DESC); -CREATE INDEX e_address_selection_events_user_idx ON e_address_selection_events(user_id,created_at DESC); diff --git a/s-migrations/000014_operations_closure.up.sql b/s-migrations/000014_operations_closure.up.sql deleted file mode 100644 index 05487c8..0000000 --- a/s-migrations/000014_operations_closure.up.sql +++ /dev/null @@ -1,218 +0,0 @@ -ALTER TABLE users DROP CONSTRAINT users_role_check; -ALTER TABLE users ADD CONSTRAINT users_role_check CHECK (role IN ('user','owner','admin','operator','support','auditor')); - -ALTER TABLE user_permissions DROP CONSTRAINT user_permissions_permission_check; -ALTER TABLE user_permissions ADD CONSTRAINT user_permissions_permission_check CHECK (permission IN ( - 'users.read','users.write','memberships.manage','cards.manage','invites.manage', - 'requests.manage','tickets.manage','notifications.manage','security.manage', - 'operations.manage','e.manage','audit.read','logs.read','backups.manage', - 'updates.manage','settings.manage','licenses.manage' -)); - -CREATE TABLE membership_plans ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - code text NOT NULL UNIQUE CHECK (code ~ '^[a-z0-9][a-z0-9._-]{0,63}$'), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - duration_days integer NOT NULL CHECK (duration_days > 0), - max_playbacks integer NOT NULL DEFAULT 0 CHECK (max_playbacks >= 0), - max_devices integer NOT NULL DEFAULT 0 CHECK (max_devices >= 0), - max_bitrate_kbps integer NOT NULL DEFAULT 0 CHECK (max_bitrate_kbps >= 0), - enabled boolean NOT NULL DEFAULT true, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -ALTER TABLE memberships DROP CONSTRAINT memberships_status_check; -ALTER TABLE memberships ADD CONSTRAINT memberships_status_check CHECK (status IN ('active','paused','expired','revoked')); -ALTER TABLE memberships - ADD COLUMN plan_id uuid REFERENCES membership_plans(id) ON DELETE SET NULL, - ADD COLUMN paused_at timestamptz, - ADD COLUMN pause_reason text NOT NULL DEFAULT '', - ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); - -ALTER TABLE media_requests DROP CONSTRAINT media_requests_status_check; -UPDATE media_requests SET status='submitted' WHERE status='open'; -UPDATE media_requests SET status='accepted' WHERE status='planned'; -ALTER TABLE media_requests ADD CONSTRAINT media_requests_status_check CHECK (status IN ( - 'submitted','reviewing','accepted','existing','downloading','fulfilled','failed','rejected','cancelled' -)); -ALTER TABLE media_requests - ADD COLUMN media_type text NOT NULL DEFAULT 'other' CHECK (media_type IN ('movie','series','season','episode','other')), - ADD COLUMN release_year integer CHECK (release_year BETWEEN 1800 AND 3000), - ADD COLUMN priority text NOT NULL DEFAULT 'normal' CHECK (priority IN ('low','normal','high','urgent')), - ADD COLUMN assignee_user_id uuid REFERENCES users(id) ON DELETE SET NULL, - ADD COLUMN public_reply text NOT NULL DEFAULT '', - ADD COLUMN internal_note text NOT NULL DEFAULT '', - ADD COLUMN merged_into uuid REFERENCES media_requests(id) ON DELETE SET NULL, - ADD COLUMN closed_at timestamptz; - -CREATE TABLE media_request_messages ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - request_id uuid NOT NULL REFERENCES media_requests(id) ON DELETE CASCADE, - author_user_id uuid REFERENCES users(id) ON DELETE SET NULL, - visibility text NOT NULL CHECK (visibility IN ('user','internal')), - body text NOT NULL CHECK (length(btrim(body)) BETWEEN 1 AND 5000), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX media_request_messages_request_idx ON media_request_messages(request_id,created_at); - -ALTER TABLE tickets DROP CONSTRAINT tickets_status_check; -UPDATE tickets SET status='in_progress' WHERE status='pending'; -ALTER TABLE tickets ADD CONSTRAINT tickets_status_check CHECK (status IN ('open','in_progress','waiting_user','resolved','closed')); -ALTER TABLE tickets DROP CONSTRAINT tickets_priority_check; -ALTER TABLE tickets ADD CONSTRAINT tickets_priority_check CHECK (priority IN ('low','normal','high','urgent')); -ALTER TABLE tickets - ADD COLUMN sla_due_at timestamptz, - ADD COLUMN last_user_reply_at timestamptz, - ADD COLUMN last_staff_reply_at timestamptz, - ADD COLUMN reopened_at timestamptz; -ALTER TABLE ticket_replies - ADD COLUMN visibility text NOT NULL DEFAULT 'user' CHECK (visibility IN ('user','internal')); - -CREATE TABLE announcements ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - title text NOT NULL CHECK (length(btrim(title)) BETWEEN 1 AND 200), - body text NOT NULL CHECK (length(btrim(body)) BETWEEN 1 AND 20000), - status text NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','scheduled','published','withdrawn')), - audience text NOT NULL DEFAULT 'all' CHECK (audience IN ('all','active_members','staff')), - pinned boolean NOT NULL DEFAULT false, - publish_at timestamptz, - expires_at timestamptz, - created_by uuid NOT NULL REFERENCES users(id), - updated_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - CHECK (expires_at IS NULL OR publish_at IS NULL OR expires_at > publish_at) -); -CREATE INDEX announcements_status_publish_idx ON announcements(status,publish_at DESC); - -CREATE TABLE announcement_reads ( - announcement_id uuid NOT NULL REFERENCES announcements(id) ON DELETE CASCADE, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - read_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (announcement_id,user_id) -); - -CREATE TABLE notification_events ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - event_type text NOT NULL, - target_user_id uuid REFERENCES users(id) ON DELETE CASCADE, - title text NOT NULL CHECK (length(btrim(title)) BETWEEN 1 AND 200), - body text NOT NULL CHECK (length(body) <= 10000), - link_path text NOT NULL DEFAULT '' CHECK (link_path='' OR link_path ~ '^/'), - related_type text NOT NULL DEFAULT '', - related_id text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX notification_events_target_idx ON notification_events(target_user_id,created_at DESC); - -CREATE TABLE notification_deliveries ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - event_id uuid NOT NULL REFERENCES notification_events(id) ON DELETE CASCADE, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - channel text NOT NULL CHECK (channel IN ('in_app','email','telegram')), - status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','sending','sent','failed','cancelled')), - attempts integer NOT NULL DEFAULT 0 CHECK (attempts >= 0), - next_attempt_at timestamptz, - last_error text NOT NULL DEFAULT '', - read_at timestamptz, - deleted_at timestamptz, - sent_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(event_id,user_id,channel) -); -CREATE INDEX notification_deliveries_user_idx ON notification_deliveries(user_id,channel,created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX notification_deliveries_pending_idx ON notification_deliveries(next_attempt_at,created_at) WHERE status IN ('pending','failed'); - -CREATE TABLE risk_rules ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - event_type text NOT NULL CHECK (event_type IN ('concurrency','multi_region','device_churn','auth_failures','manual')), - window_seconds integer NOT NULL CHECK (window_seconds BETWEEN 60 AND 2592000), - threshold integer NOT NULL CHECK (threshold > 0), - action text NOT NULL DEFAULT 'review' CHECK (action IN ('observe','review','revoke_sessions','disable_user')), - enabled boolean NOT NULL DEFAULT true, - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE risk_events ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - rule_id uuid REFERENCES risk_rules(id) ON DELETE SET NULL, - user_id uuid REFERENCES users(id) ON DELETE SET NULL, - event_type text NOT NULL, - severity text NOT NULL CHECK (severity IN ('info','low','medium','high','critical')), - status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','reviewing','resolved','dismissed','appealed')), - evidence jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(evidence)='object'), - resolution text NOT NULL DEFAULT '', - resolved_by uuid REFERENCES users(id) ON DELETE SET NULL, - resolved_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX risk_events_status_idx ON risk_events(status,severity,created_at DESC); - -ALTER TABLE refresh_token_families - ADD COLUMN device_name text NOT NULL DEFAULT '', - ADD COLUMN client_name text NOT NULL DEFAULT '', - ADD COLUMN platform text NOT NULL DEFAULT '', - ADD COLUMN ip_hash text NOT NULL DEFAULT '', - ADD COLUMN last_seen_at timestamptz NOT NULL DEFAULT now(); - -ALTER TABLE devices - ADD COLUMN device_name text NOT NULL DEFAULT '', - ADD COLUMN client_name text NOT NULL DEFAULT '', - ADD COLUMN platform text NOT NULL DEFAULT '', - ADD COLUMN client_version text NOT NULL DEFAULT '', - ADD COLUMN last_ip_hash text NOT NULL DEFAULT '', - ADD COLUMN trusted boolean NOT NULL DEFAULT false, - ADD COLUMN blocked_at timestamptz, - ADD COLUMN blocked_by uuid REFERENCES users(id) ON DELETE SET NULL; - -CREATE TABLE configuration_versions ( - revision bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - domain text NOT NULL CHECK (domain IN ('s','e')), - values jsonb NOT NULL CHECK (jsonb_typeof(values)='object'), - state text NOT NULL DEFAULT 'draft' CHECK (state IN ('draft','published','superseded','failed')), - source_revision bigint REFERENCES configuration_versions(revision), - created_by uuid NOT NULL REFERENCES users(id), - published_at timestamptz, - failure_reason text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE UNIQUE INDEX configuration_versions_published_domain_idx ON configuration_versions(domain) WHERE state='published'; - -CREATE TABLE operation_jobs ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - job_type text NOT NULL, - resource_type text NOT NULL DEFAULT '', - resource_id text NOT NULL DEFAULT '', - idempotency_key text NOT NULL UNIQUE, - state text NOT NULL DEFAULT 'queued' CHECK (state IN ('queued','running','pausing','paused','cancelling','cancelled','succeeded','partial_failed','failed')), - progress_total bigint NOT NULL DEFAULT 0 CHECK (progress_total >= 0), - progress_completed bigint NOT NULL DEFAULT 0 CHECK (progress_completed >= 0), - progress_failed bigint NOT NULL DEFAULT 0 CHECK (progress_failed >= 0), - error_code text NOT NULL DEFAULT '', - error_summary text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - started_at timestamptz, - updated_at timestamptz NOT NULL DEFAULT now(), - finished_at timestamptz, - CHECK (progress_completed + progress_failed <= progress_total OR progress_total=0) -); -CREATE INDEX operation_jobs_state_idx ON operation_jobs(state,created_at DESC); - -CREATE TABLE api_idempotency_keys ( - actor_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - operation text NOT NULL, - idempotency_key text NOT NULL, - resource_id text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now(), - expires_at timestamptz NOT NULL DEFAULT now() + interval '24 hours', - PRIMARY KEY (actor_user_id,operation,idempotency_key) -); -CREATE INDEX api_idempotency_keys_expiry_idx ON api_idempotency_keys(expires_at); diff --git a/s-migrations/000015_e_address_refresh_rate_limit.up.sql b/s-migrations/000015_e_address_refresh_rate_limit.up.sql deleted file mode 100644 index 2bbc67f..0000000 --- a/s-migrations/000015_e_address_refresh_rate_limit.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE e_address_selection_events DROP CONSTRAINT e_address_selection_events_outcome_check; -ALTER TABLE e_address_selection_events ADD CONSTRAINT e_address_selection_events_outcome_check CHECK (outcome IN ('selected','unavailable','access_denied','rate_limited')); diff --git a/s-migrations/000016_system_operations.up.sql b/s-migrations/000016_system_operations.up.sql deleted file mode 100644 index 19ae0d7..0000000 --- a/s-migrations/000016_system_operations.up.sql +++ /dev/null @@ -1,90 +0,0 @@ -ALTER TABLE audit_events - ADD COLUMN request_id text NOT NULL DEFAULT ''; -CREATE INDEX audit_events_request_idx ON audit_events(request_id, created_at DESC) WHERE request_id <> ''; - -CREATE TABLE runtime_log_events ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - level text NOT NULL CHECK (level IN ('debug','info','warn','error')), - message text NOT NULL CHECK (length(message) BETWEEN 1 AND 256), - request_id text NOT NULL DEFAULT '', - method text NOT NULL DEFAULT '', - path text NOT NULL DEFAULT '', - status_code integer NOT NULL DEFAULT 0 CHECK (status_code BETWEEN 0 AND 599), - duration_ms bigint NOT NULL DEFAULT 0 CHECK (duration_ms >= 0), - detail jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(detail)='object'), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX runtime_log_events_time_idx ON runtime_log_events(created_at DESC); -CREATE INDEX runtime_log_events_request_idx ON runtime_log_events(request_id,created_at DESC) WHERE request_id <> ''; - -ALTER TABLE operation_jobs - ADD COLUMN schema_version integer NOT NULL DEFAULT 1, - ADD COLUMN stage text NOT NULL DEFAULT 'queued', - ADD COLUMN priority integer NOT NULL DEFAULT 0 CHECK (priority BETWEEN -100 AND 100), - ADD COLUMN progress_skipped bigint NOT NULL DEFAULT 0 CHECK (progress_skipped >= 0), - ADD COLUMN retryable boolean NOT NULL DEFAULT true, - ADD COLUMN next_retry_at timestamptz, - ADD COLUMN claimed_by text NOT NULL DEFAULT '', - ADD COLUMN payload jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(payload)='object'); - -CREATE TABLE backup_policies ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - scope text NOT NULL CHECK (scope IN ('s','e','auth','all')), - enabled boolean NOT NULL DEFAULT true, - interval_hours integer NOT NULL DEFAULT 24 CHECK (interval_hours BETWEEN 1 AND 720), - retention_count integer NOT NULL DEFAULT 14 CHECK (retention_count BETWEEN 1 AND 365), - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(scope) -); - -CREATE TABLE backup_artifacts ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - scope text NOT NULL CHECK (scope IN ('s','e','auth','all')), - state text NOT NULL DEFAULT 'queued' CHECK (state IN ('queued','running','succeeded','failed','expired','deleted')), - storage_key text NOT NULL, - size_bytes bigint NOT NULL DEFAULT 0 CHECK (size_bytes >= 0), - sha256 text NOT NULL DEFAULT '' CHECK (sha256 = '' OR sha256 ~ '^[0-9a-f]{64}$'), - job_id uuid REFERENCES operation_jobs(id) ON DELETE SET NULL, - error_code text NOT NULL DEFAULT '', - error_summary text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - completed_at timestamptz, - deleted_at timestamptz -); -CREATE INDEX backup_artifacts_scope_idx ON backup_artifacts(scope,created_at DESC); - -CREATE TABLE system_runtime_state ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - maintenance boolean NOT NULL DEFAULT false, - maintenance_reason text NOT NULL DEFAULT '', - maintenance_job_id uuid REFERENCES operation_jobs(id) ON DELETE SET NULL, - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO system_runtime_state(singleton) VALUES (true) ON CONFLICT DO NOTHING; - -CREATE TABLE update_artifacts ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - product text NOT NULL CHECK (product IN ('s','e','auth')), - platform text NOT NULL, - version text NOT NULL, - channel text NOT NULL CHECK (channel IN ('stable','preview')), - download_url text NOT NULL DEFAULT '', - sha256 text NOT NULL CHECK (sha256 ~ '^[0-9a-f]{64}$'), - signature text NOT NULL DEFAULT '', - min_s_version text NOT NULL DEFAULT '', - min_e_version text NOT NULL DEFAULT '', - state text NOT NULL DEFAULT 'authorized' CHECK (state IN ('authorized','downloaded','verified','staged','applied','rolled_back','failed')), - storage_key text NOT NULL DEFAULT '', - job_id uuid REFERENCES operation_jobs(id) ON DELETE SET NULL, - error_code text NOT NULL DEFAULT '', - error_summary text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(product,platform,version,channel) -); -CREATE INDEX update_artifacts_state_idx ON update_artifacts(product,state,updated_at DESC); diff --git a/s-migrations/000017_notifications_security_lifecycle.up.sql b/s-migrations/000017_notifications_security_lifecycle.up.sql deleted file mode 100644 index 8688762..0000000 --- a/s-migrations/000017_notifications_security_lifecycle.up.sql +++ /dev/null @@ -1,124 +0,0 @@ -ALTER TABLE users - ADD COLUMN deleted_by uuid REFERENCES users(id) ON DELETE SET NULL, - ADD COLUMN deletion_reason text NOT NULL DEFAULT '', - ADD COLUMN restored_at timestamptz; - -ALTER TABLE card_batches - ADD COLUMN membership_plan_id uuid REFERENCES membership_plans(id) ON DELETE SET NULL, - ADD COLUMN edition text NOT NULL DEFAULT '' CHECK (char_length(edition) <= 64), - ADD COLUMN max_uses integer NOT NULL DEFAULT 1 CHECK (max_uses > 0); - -ALTER TABLE card_batches DROP CONSTRAINT card_batches_card_type_check; -ALTER TABLE card_batches ADD CONSTRAINT card_batches_card_type_check CHECK (card_type IN ('registration','renewal','membership')); - -ALTER TABLE cards - ADD COLUMN label text NOT NULL DEFAULT '' CHECK (char_length(label) <= 128); - -CREATE TABLE notification_channel_configs ( - channel text PRIMARY KEY CHECK (channel IN ('smtp','telegram')), - enabled boolean NOT NULL DEFAULT false, - settings jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(settings)='object'), - secret_cipher bytea, - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO notification_channel_configs(channel) VALUES ('smtp'),('telegram'); - -CREATE TABLE notification_templates ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - template_key text NOT NULL CHECK (template_key ~ '^[a-z0-9][a-z0-9._-]{0,127}$'), - channel text NOT NULL CHECK (channel IN ('email','telegram')), - subject text NOT NULL DEFAULT '' CHECK (char_length(subject) <= 300), - body text NOT NULL CHECK (char_length(body) BETWEEN 1 AND 20000), - enabled boolean NOT NULL DEFAULT true, - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(template_key,channel) -); - -CREATE TABLE notification_delivery_attempts ( - id bigserial PRIMARY KEY, - delivery_id uuid NOT NULL REFERENCES notification_deliveries(id) ON DELETE CASCADE, - attempt integer NOT NULL CHECK (attempt > 0), - status text NOT NULL CHECK (status IN ('sent','failed','skipped')), - error_code text NOT NULL DEFAULT '', - error_summary text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(delivery_id,attempt) -); - -CREATE TABLE telegram_runtime_state ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - update_offset bigint NOT NULL DEFAULT 0, - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO telegram_runtime_state(singleton) VALUES(true); - -CREATE TABLE user_ip_history ( - id bigserial PRIMARY KEY, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - ip_hash text NOT NULL CHECK (char_length(ip_hash)=64), - country text NOT NULL DEFAULT '', - region text NOT NULL DEFAULT '', - carrier text NOT NULL DEFAULT '', - asn bigint, - first_seen_at timestamptz NOT NULL DEFAULT now(), - last_seen_at timestamptz NOT NULL DEFAULT now(), - seen_count bigint NOT NULL DEFAULT 1 CHECK (seen_count > 0), - UNIQUE(user_id,ip_hash) -); -CREATE INDEX user_ip_history_user_time_idx ON user_ip_history(user_id,last_seen_at DESC); - -CREATE TABLE security_list_entries ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - subject_type text NOT NULL CHECK (subject_type IN ('ip','cidr','device','user')), - mode text NOT NULL CHECK (mode IN ('allow','deny')), - subject_value text NOT NULL CHECK (char_length(subject_value) BETWEEN 1 AND 512), - reason text NOT NULL CHECK (char_length(btrim(reason)) BETWEEN 1 AND 1000), - expires_at timestamptz, - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - revoked_at timestamptz, - revoked_by uuid REFERENCES users(id) ON DELETE SET NULL, - UNIQUE(subject_type,mode,subject_value) -); -CREATE INDEX security_list_active_idx ON security_list_entries(subject_type,mode,expires_at) WHERE revoked_at IS NULL; - -CREATE TABLE user_appeals ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - risk_event_id uuid REFERENCES risk_events(id) ON DELETE SET NULL, - body text NOT NULL CHECK (char_length(btrim(body)) BETWEEN 1 AND 5000), - status text NOT NULL DEFAULT 'submitted' CHECK (status IN ('submitted','reviewing','accepted','rejected')), - resolution text NOT NULL DEFAULT '', - resolved_by uuid REFERENCES users(id) ON DELETE SET NULL, - resolved_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE media_request_callbacks ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL CHECK (char_length(btrim(name)) BETWEEN 1 AND 128), - callback_url text NOT NULL CHECK (callback_url ~ '^https://'), - secret_cipher bytea NOT NULL, - enabled boolean NOT NULL DEFAULT true, - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE media_request_callback_events ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - callback_id uuid NOT NULL REFERENCES media_request_callbacks(id) ON DELETE CASCADE, - external_event_id text NOT NULL CHECK (char_length(external_event_id) BETWEEN 1 AND 256), - request_id uuid REFERENCES media_requests(id) ON DELETE SET NULL, - payload jsonb NOT NULL CHECK (jsonb_typeof(payload)='object'), - signature_valid boolean NOT NULL, - applied boolean NOT NULL DEFAULT false, - error_summary text NOT NULL DEFAULT '', - received_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(callback_id,external_event_id) -); diff --git a/s-migrations/000018_initial_setup.up.sql b/s-migrations/000018_initial_setup.up.sql deleted file mode 100644 index 78e1ffc..0000000 --- a/s-migrations/000018_initial_setup.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE system_installation ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - installation_id uuid NOT NULL DEFAULT gen_random_uuid(), - setup_completed_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO system_installation(singleton) VALUES(true); diff --git a/s-migrations/000019_e_address_group_rules.up.sql b/s-migrations/000019_e_address_group_rules.up.sql deleted file mode 100644 index 0133fb3..0000000 --- a/s-migrations/000019_e_address_group_rules.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE e_address_rules - ALTER COLUMN address_id DROP NOT NULL; - -ALTER TABLE e_address_rules - ADD CONSTRAINT e_address_rules_target_check - CHECK (address_id IS NOT NULL OR length(btrim(node_group)) > 0); diff --git a/s-migrations/000020_playback_session_context.up.sql b/s-migrations/000020_playback_session_context.up.sql deleted file mode 100644 index 3fed86b..0000000 --- a/s-migrations/000020_playback_session_context.up.sql +++ /dev/null @@ -1,17 +0,0 @@ -ALTER TABLE playback_decisions - ADD COLUMN device_name text NOT NULL DEFAULT '', - ADD COLUMN client_name text NOT NULL DEFAULT '', - ADD COLUMN client_version text NOT NULL DEFAULT '', - ADD COLUMN remote_ip_hash text NOT NULL DEFAULT ''; - -ALTER TABLE playback_sessions - ADD COLUMN media_id uuid, - ADD COLUMN source_id uuid, - ADD COLUMN node_id uuid, - ADD COLUMN position_ticks bigint NOT NULL DEFAULT 0 CHECK (position_ticks >= 0), - ADD COLUMN device_name text NOT NULL DEFAULT '', - ADD COLUMN client_name text NOT NULL DEFAULT '', - ADD COLUMN client_version text NOT NULL DEFAULT ''; - -CREATE INDEX playback_sessions_recent_idx - ON playback_sessions(updated_at DESC, id DESC); diff --git a/s-migrations/000021_subtitle_sources.up.sql b/s-migrations/000021_subtitle_sources.up.sql deleted file mode 100644 index 827dfbf..0000000 --- a/s-migrations/000021_subtitle_sources.up.sql +++ /dev/null @@ -1,55 +0,0 @@ -CREATE TABLE subtitle_sources ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL CHECK (char_length(btrim(name)) BETWEEN 1 AND 128), - source_type text NOT NULL DEFAULT 'json_rest_v1' CHECK (source_type IN ('json_rest_v1')), - base_url text NOT NULL CHECK (base_url ~ '^https://'), - api_key_cipher bytea, - enabled boolean NOT NULL DEFAULT true, - languages text[] NOT NULL DEFAULT '{}', - priority integer NOT NULL DEFAULT 100 CHECK (priority BETWEEN 1 AND 10000), - timeout_seconds integer NOT NULL DEFAULT 10 CHECK (timeout_seconds BETWEEN 2 AND 30), - created_by uuid NOT NULL REFERENCES users(id), - updated_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE (name) -); - -CREATE INDEX subtitle_sources_enabled_idx - ON subtitle_sources(priority,id) WHERE enabled; - -CREATE TABLE subtitle_search_candidates ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - source_id uuid NOT NULL REFERENCES subtitle_sources(id) ON DELETE CASCADE, - media_item_id uuid NOT NULL, - media_source_id uuid NOT NULL, - external_id text NOT NULL CHECK (char_length(external_id) BETWEEN 1 AND 512), - title text NOT NULL CHECK (char_length(title) BETWEEN 1 AND 512), - language text NOT NULL DEFAULT '' CHECK (char_length(language) <= 64), - codec text NOT NULL CHECK (codec IN ('srt','ass','ssa','vtt','sup','sub','idx')), - release_group text NOT NULL DEFAULT '' CHECK (char_length(release_group) <= 256), - score double precision NOT NULL DEFAULT 0 CHECK (score >= 0 AND score <= 1), - download_path text NOT NULL CHECK (char_length(download_path) BETWEEN 1 AND 2048), - hearing_impaired boolean NOT NULL DEFAULT false, - created_by uuid NOT NULL REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - expires_at timestamptz NOT NULL DEFAULT (now() + interval '1 hour') -); - -CREATE INDEX subtitle_search_candidates_actor_idx - ON subtitle_search_candidates(created_by,created_at DESC); -CREATE INDEX subtitle_search_candidates_expiry_idx - ON subtitle_search_candidates(expires_at); - -CREATE TABLE subtitle_imports ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - candidate_id uuid NOT NULL REFERENCES subtitle_search_candidates(id) ON DELETE CASCADE, - actor_user_id uuid NOT NULL REFERENCES users(id), - idempotency_key text NOT NULL CHECK (char_length(idempotency_key) BETWEEN 16 AND 128), - state text NOT NULL DEFAULT 'processing' CHECK (state IN ('processing','succeeded','failed')), - result jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(result)='object'), - error_code text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(actor_user_id,idempotency_key) -); diff --git a/s-migrations/000022_metadata_provider_credentials.up.sql b/s-migrations/000022_metadata_provider_credentials.up.sql deleted file mode 100644 index c547c2f..0000000 --- a/s-migrations/000022_metadata_provider_credentials.up.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE metadata_provider_credentials ( - provider text PRIMARY KEY CHECK (provider IN ('tmdb','bangumi')), - token_cipher bytea, - enabled boolean NOT NULL DEFAULT true, - updated_by uuid NOT NULL REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX metadata_provider_credentials_enabled_idx - ON metadata_provider_credentials(provider) WHERE enabled; diff --git a/s-migrations/000023_user_engagement_and_bot.up.sql b/s-migrations/000023_user_engagement_and_bot.up.sql deleted file mode 100644 index a083d93..0000000 --- a/s-migrations/000023_user_engagement_and_bot.up.sql +++ /dev/null @@ -1,80 +0,0 @@ -CREATE TABLE point_rules ( - rule_key text PRIMARY KEY CHECK (rule_key ~ '^[a-z0-9][a-z0-9._-]{0,63}$'), - name text NOT NULL CHECK (char_length(btrim(name)) BETWEEN 1 AND 128), - points integer NOT NULL CHECK (points BETWEEN -100000 AND 100000), - enabled boolean NOT NULL DEFAULT true, - description text NOT NULL DEFAULT '' CHECK (char_length(description) <= 1000), - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO point_rules(rule_key,name,points,description) VALUES - ('playback.completed','有效观看',5,'单次观看达到 10 分钟后奖励,每个播放会话仅奖励一次'), - ('feedback.accepted','有效反馈',10,'管理员确认媒体问题后奖励'); - -CREATE TABLE point_accounts ( - user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, - balance bigint NOT NULL DEFAULT 0 CHECK (balance >= 0), - lifetime_earned bigint NOT NULL DEFAULT 0 CHECK (lifetime_earned >= 0), - lifetime_spent bigint NOT NULL DEFAULT 0 CHECK (lifetime_spent >= 0), - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE TABLE point_ledger ( - id bigserial PRIMARY KEY, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - delta integer NOT NULL CHECK (delta <> 0 AND delta BETWEEN -100000 AND 100000), - balance_after bigint NOT NULL CHECK (balance_after >= 0), - reason text NOT NULL CHECK (char_length(btrim(reason)) BETWEEN 1 AND 200), - source_type text NOT NULL DEFAULT 'manual' CHECK (source_type IN ('manual','playback_session','media_feedback','redemption')), - source_id text NOT NULL DEFAULT '' CHECK (char_length(source_id) <= 128), - actor_user_id uuid REFERENCES users(id) ON DELETE SET NULL, - created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(user_id,source_type,source_id) -); -CREATE INDEX point_ledger_user_time_idx ON point_ledger(user_id,created_at DESC,id DESC); - -CREATE TABLE media_feedback ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - media_id uuid, - source_id uuid, - playback_session_id uuid REFERENCES playback_sessions(id) ON DELETE SET NULL, - category text NOT NULL CHECK (category IN ('playback','subtitle','audio','metadata','quality','other')), - title text NOT NULL CHECK (char_length(btrim(title)) BETWEEN 1 AND 200), - detail text NOT NULL CHECK (char_length(btrim(detail)) BETWEEN 1 AND 5000), - status text NOT NULL DEFAULT 'submitted' CHECK (status IN ('submitted','reviewing','resolved','rejected')), - priority text NOT NULL DEFAULT 'normal' CHECK (priority IN ('low','normal','high','urgent')), - resolution text NOT NULL DEFAULT '' CHECK (char_length(resolution) <= 5000), - assigned_to uuid REFERENCES users(id) ON DELETE SET NULL, - resolved_by uuid REFERENCES users(id) ON DELETE SET NULL, - resolved_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX media_feedback_user_time_idx ON media_feedback(user_id,created_at DESC); -CREATE INDEX media_feedback_queue_idx ON media_feedback(status,priority,updated_at DESC); - -CREATE TABLE telegram_bot_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - enabled boolean NOT NULL DEFAULT true, - commands_enabled boolean NOT NULL DEFAULT true, - rate_limit_per_minute integer NOT NULL DEFAULT 12 CHECK (rate_limit_per_minute BETWEEN 1 AND 120), - welcome_message text NOT NULL DEFAULT '欢迎使用站点助手,请先绑定账户。' CHECK (char_length(welcome_message) BETWEEN 1 AND 1000), - maintenance_message text NOT NULL DEFAULT '' CHECK (char_length(maintenance_message) <= 1000), - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); -INSERT INTO telegram_bot_settings(singleton) VALUES(true); - -CREATE TABLE telegram_command_logs ( - id bigserial PRIMARY KEY, - telegram_user_id bigint NOT NULL, - user_id uuid REFERENCES users(id) ON DELETE SET NULL, - command text NOT NULL CHECK (char_length(command) BETWEEN 1 AND 64), - result text NOT NULL CHECK (char_length(result) BETWEEN 1 AND 64), - duration_ms integer NOT NULL DEFAULT 0 CHECK (duration_ms >= 0), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX telegram_command_logs_rate_idx ON telegram_command_logs(telegram_user_id,created_at DESC); -CREATE INDEX telegram_command_logs_admin_idx ON telegram_command_logs(created_at DESC,id DESC); diff --git a/s-migrations/000024_daily_checkins.up.sql b/s-migrations/000024_daily_checkins.up.sql deleted file mode 100644 index 86b22f7..0000000 --- a/s-migrations/000024_daily_checkins.up.sql +++ /dev/null @@ -1,20 +0,0 @@ -ALTER TABLE point_ledger DROP CONSTRAINT point_ledger_source_type_check; -ALTER TABLE point_ledger ADD CONSTRAINT point_ledger_source_type_check - CHECK (source_type IN ('manual','playback_session','media_feedback','redemption','daily_checkin')); - -INSERT INTO point_rules(rule_key,name,points,description) VALUES - ('daily_checkin.base','每日签到',5,'每天首次签到获得的积分'), - ('daily_checkin.streak_7','连续签到 7 天',10,'连续签到第 7 天的额外积分'), - ('daily_checkin.streak_30','连续签到 30 天',30,'连续签到第 30 天的额外积分') -ON CONFLICT(rule_key) DO NOTHING; - -CREATE TABLE daily_checkins ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - checkin_date date NOT NULL, - streak integer NOT NULL CHECK(streak > 0), - reward_points integer NOT NULL CHECK(reward_points >= 0), - created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(user_id,checkin_date) -); -CREATE INDEX daily_checkins_user_time_idx ON daily_checkins(user_id,checkin_date DESC); diff --git a/s-migrations/000025_user_store_calendar_monitoring.up.sql b/s-migrations/000025_user_store_calendar_monitoring.up.sql deleted file mode 100644 index 3fe88d1..0000000 --- a/s-migrations/000025_user_store_calendar_monitoring.up.sql +++ /dev/null @@ -1,34 +0,0 @@ -ALTER TABLE membership_plans - ADD COLUMN price_points integer NOT NULL DEFAULT 0 CHECK(price_points >= 0), - ADD COLUMN self_service boolean NOT NULL DEFAULT false; - -INSERT INTO membership_plans(code,name,duration_days,max_playbacks,max_devices,max_bitrate_kbps,price_points,self_service,enabled) -VALUES('points-30d','30 天会员',30,1,3,0,100,true,true) -ON CONFLICT(code) DO NOTHING; - -ALTER TABLE point_ledger DROP CONSTRAINT point_ledger_source_type_check; -ALTER TABLE point_ledger ADD CONSTRAINT point_ledger_source_type_check - CHECK (source_type IN ('manual','playback_session','media_feedback','redemption','daily_checkin','membership_purchase')); - -CREATE TABLE membership_purchases ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - plan_id uuid NOT NULL REFERENCES membership_plans(id), - price_points integer NOT NULL CHECK(price_points >= 0), - membership_id uuid REFERENCES memberships(id), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX membership_purchases_user_time_idx ON membership_purchases(user_id,created_at DESC); - -CREATE TABLE watch_calendar_entries ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - title text NOT NULL CHECK(length(btrim(title)) BETWEEN 1 AND 200), - episode_label text NOT NULL DEFAULT '' CHECK(length(episode_label) <= 100), - next_episode_at timestamptz NOT NULL, - notes text NOT NULL DEFAULT '' CHECK(length(notes) <= 1000), - completed boolean NOT NULL DEFAULT false, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX watch_calendar_user_time_idx ON watch_calendar_entries(user_id,completed,next_episode_at); diff --git a/s-migrations/000026_telegram_brand_and_chat_state.up.sql b/s-migrations/000026_telegram_brand_and_chat_state.up.sql deleted file mode 100644 index 63c49a4..0000000 --- a/s-migrations/000026_telegram_brand_and_chat_state.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -ALTER TABLE telegram_bot_settings - ADD COLUMN server_name text NOT NULL DEFAULT 'OverCooked' CHECK(char_length(btrim(server_name)) BETWEEN 1 AND 80), - ADD COLUMN icon text NOT NULL DEFAULT '🎬' CHECK(char_length(btrim(icon)) BETWEEN 1 AND 16); - -CREATE TABLE telegram_chat_states ( - chat_id bigint PRIMARY KEY, - message_id bigint NOT NULL CHECK(message_id > 0), - updated_at timestamptz NOT NULL DEFAULT now() -); diff --git a/s-migrations/000027_site_brand_settings.up.sql b/s-migrations/000027_site_brand_settings.up.sql deleted file mode 100644 index c7cacb8..0000000 --- a/s-migrations/000027_site_brand_settings.up.sql +++ /dev/null @@ -1,17 +0,0 @@ -CREATE TABLE site_brand_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - server_name text NOT NULL DEFAULT 'OverCooked' CHECK (char_length(btrim(server_name)) BETWEEN 1 AND 80), - emby_display_name text NOT NULL DEFAULT 'OverCooked' CHECK (char_length(btrim(emby_display_name)) BETWEEN 1 AND 80), - icon_data_url text NOT NULL DEFAULT '' CHECK (char_length(icon_data_url) <= 700000), - updated_by uuid REFERENCES users(id) ON DELETE SET NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO site_brand_settings(singleton) VALUES(true); - -ALTER TABLE telegram_bot_settings - ADD COLUMN official_chat_id bigint NOT NULL DEFAULT 0, - ADD COLUMN official_chat_title text NOT NULL DEFAULT '' CHECK (char_length(official_chat_title) <= 120); - -ALTER TABLE telegram_chat_states - ADD COLUMN message_kind text NOT NULL DEFAULT 'text' CHECK (message_kind IN ('text','photo')); diff --git a/s-migrations/000028_media_request_metadata.up.sql b/s-migrations/000028_media_request_metadata.up.sql deleted file mode 100644 index 7e72cc9..0000000 --- a/s-migrations/000028_media_request_metadata.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -ALTER TABLE media_requests - ADD COLUMN provider text NOT NULL DEFAULT '', - ADD COLUMN external_id text NOT NULL DEFAULT '', - ADD COLUMN original_title text NOT NULL DEFAULT ''; - -ALTER TABLE media_requests - ADD CONSTRAINT media_requests_provider_check CHECK (provider IN ('', 'tmdb', 'bangumi')), - ADD CONSTRAINT media_requests_external_id_length CHECK (length(external_id) <= 512), - ADD CONSTRAINT media_requests_original_title_length CHECK (length(original_title) <= 300); - -CREATE INDEX media_requests_external_metadata_idx - ON media_requests(provider, external_id) - WHERE provider <> '' AND external_id <> ''; diff --git a/s-migrations/000029_support_attachments.up.sql b/s-migrations/000029_support_attachments.up.sql deleted file mode 100644 index 05e2585..0000000 --- a/s-migrations/000029_support_attachments.up.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE TABLE support_attachments ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - ticket_id uuid REFERENCES tickets(id) ON DELETE CASCADE, - media_feedback_id uuid REFERENCES media_feedback(id) ON DELETE CASCADE, - uploader_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - filename text NOT NULL CHECK (length(filename) BETWEEN 1 AND 255), - mime_type text NOT NULL CHECK (length(mime_type) BETWEEN 1 AND 128), - size_bytes bigint NOT NULL CHECK (size_bytes BETWEEN 1 AND 8388608), - content bytea NOT NULL, - created_at timestamptz NOT NULL DEFAULT now(), - CHECK ((ticket_id IS NOT NULL)::int + (media_feedback_id IS NOT NULL)::int = 1) -); - -CREATE INDEX support_attachments_ticket_idx ON support_attachments(ticket_id,created_at,id) WHERE ticket_id IS NOT NULL; -CREATE INDEX support_attachments_feedback_idx ON support_attachments(media_feedback_id,created_at,id) WHERE media_feedback_id IS NOT NULL; diff --git a/s-migrations/000030_user_password_and_banner.up.sql b/s-migrations/000030_user_password_and_banner.up.sql deleted file mode 100644 index a7e53c1..0000000 --- a/s-migrations/000030_user_password_and_banner.up.sql +++ /dev/null @@ -1,14 +0,0 @@ -ALTER TABLE site_brand_settings - ADD COLUMN banner_enabled boolean NOT NULL DEFAULT false, - ADD COLUMN banner_image_data_url text NOT NULL DEFAULT '' CHECK (char_length(banner_image_data_url) <= 2800000), - ADD COLUMN banner_link_url text NOT NULL DEFAULT '' CHECK (char_length(banner_link_url) <= 2000); - -CREATE TABLE telegram_password_flows ( - telegram_user_id bigint PRIMARY KEY, - stage text NOT NULL CHECK (stage IN ('new_password','confirm_password')), - password_hash text NOT NULL DEFAULT '', - expires_at timestamptz NOT NULL, - updated_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX telegram_password_flows_expiry_idx ON telegram_password_flows(expires_at); diff --git a/s-migrations/000031_login_background.up.sql b/s-migrations/000031_login_background.up.sql deleted file mode 100644 index 0ac3f59..0000000 --- a/s-migrations/000031_login_background.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE site_brand_settings - ADD COLUMN login_background_data_url text NOT NULL DEFAULT '' - CHECK (char_length(login_background_data_url) <= 5600000); diff --git a/s-migrations/000032_branding_image_limits.up.sql b/s-migrations/000032_branding_image_limits.up.sql deleted file mode 100644 index 1955a14..0000000 --- a/s-migrations/000032_branding_image_limits.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -ALTER TABLE site_brand_settings - DROP CONSTRAINT IF EXISTS site_brand_settings_banner_image_data_url_check, - DROP CONSTRAINT IF EXISTS site_brand_settings_login_background_data_url_check; - -ALTER TABLE site_brand_settings - ADD CONSTRAINT site_brand_settings_banner_image_data_url_check - CHECK (char_length(banner_image_data_url) <= 28000000), - ADD CONSTRAINT site_brand_settings_login_background_data_url_check - CHECK (char_length(login_background_data_url) <= 28000000); diff --git a/s-migrations/000033_negative_point_balances.up.sql b/s-migrations/000033_negative_point_balances.up.sql deleted file mode 100644 index b287298..0000000 --- a/s-migrations/000033_negative_point_balances.up.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE point_accounts - DROP CONSTRAINT IF EXISTS point_accounts_balance_check; - -ALTER TABLE point_ledger - DROP CONSTRAINT IF EXISTS point_ledger_balance_after_check; diff --git a/s-migrations/000034_membership_limits_points_red_packets.up.sql b/s-migrations/000034_membership_limits_points_red_packets.up.sql deleted file mode 100644 index 905a360..0000000 --- a/s-migrations/000034_membership_limits_points_red_packets.up.sql +++ /dev/null @@ -1,54 +0,0 @@ -ALTER TABLE site_brand_settings - ADD COLUMN IF NOT EXISTS points_name text NOT NULL DEFAULT '积分' - CHECK (char_length(btrim(points_name)) BETWEEN 1 AND 20); - -UPDATE memberships -SET ends_at = timestamptz '9999-12-31 23:59:59+00' -WHERE ends_at > timestamptz '9999-12-31 23:59:59+00'; - -UPDATE card_batches SET duration_days = 365000 WHERE duration_days > 365000; -UPDATE membership_plans SET duration_days = 365000 WHERE duration_days > 365000; - -ALTER TABLE card_batches - ADD CONSTRAINT card_batches_duration_days_limit CHECK (duration_days BETWEEN 1 AND 365000); -ALTER TABLE membership_plans - ADD CONSTRAINT membership_plans_duration_days_limit CHECK (duration_days BETWEEN 1 AND 365000); - -ALTER TABLE point_ledger DROP CONSTRAINT IF EXISTS point_ledger_source_type_check; -ALTER TABLE point_ledger - ADD CONSTRAINT point_ledger_source_type_check CHECK (source_type IN ('manual','playback_session','media_feedback','redemption','daily_checkin','membership_purchase','telegram_red_packet')); - -CREATE TABLE telegram_red_packets ( - id uuid PRIMARY KEY, - chat_id bigint NOT NULL, - creator_user_id uuid NOT NULL REFERENCES users(id) ON DELETE RESTRICT, - creator_telegram_id bigint NOT NULL, - allocation_mode text NOT NULL CHECK (allocation_mode IN ('normal','random')), - claim_mode text NOT NULL CHECK (claim_mode IN ('button','password')), - total_points integer NOT NULL CHECK (total_points BETWEEN 1 AND 100000), - packet_count integer NOT NULL CHECK (packet_count BETWEEN 1 AND 1000), - remaining_points integer NOT NULL CHECK (remaining_points >= 0), - remaining_count integer NOT NULL CHECK (remaining_count >= 0), - password_hash text NOT NULL DEFAULT '', - message text NOT NULL DEFAULT '' CHECK (char_length(message) <= 200), - status text NOT NULL DEFAULT 'open' CHECK (status IN ('open','expired','closed')), - message_id bigint NOT NULL DEFAULT 0, - expires_at timestamptz NOT NULL, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - CHECK (remaining_count <= packet_count), - CHECK (remaining_points <= total_points) -); - -CREATE INDEX telegram_red_packets_chat_open_idx ON telegram_red_packets(chat_id, expires_at DESC) WHERE status = 'open'; - -CREATE TABLE telegram_red_packet_claims ( - id bigserial PRIMARY KEY, - packet_id uuid NOT NULL REFERENCES telegram_red_packets(id) ON DELETE CASCADE, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE RESTRICT, - telegram_user_id bigint NOT NULL, - amount integer NOT NULL CHECK (amount > 0), - claimed_at timestamptz NOT NULL DEFAULT now(), - UNIQUE(packet_id, user_id), - UNIQUE(packet_id, telegram_user_id) -); diff --git a/s-migrations/000035_plugin_management_permission.up.sql b/s-migrations/000035_plugin_management_permission.up.sql deleted file mode 100644 index 0777f90..0000000 --- a/s-migrations/000035_plugin_management_permission.up.sql +++ /dev/null @@ -1,17 +0,0 @@ -ALTER TABLE user_permissions DROP CONSTRAINT user_permissions_permission_check; -ALTER TABLE user_permissions ADD CONSTRAINT user_permissions_permission_check CHECK (permission IN ( - 'users.read','users.write','memberships.manage','cards.manage','invites.manage', - 'requests.manage','tickets.manage','notifications.manage','security.manage', - 'operations.manage','e.manage','plugins.manage','audit.read','logs.read','backups.manage', - 'updates.manage','settings.manage','licenses.manage' -)); - -INSERT INTO user_permissions (user_id, permission, granted_by) -SELECT id, 'plugins.manage', id -FROM users -WHERE role = 'owner' -ON CONFLICT (user_id, permission) DO NOTHING; - -ALTER TABLE e_command_requests - ADD COLUMN response_status integer CHECK (response_status BETWEEN 100 AND 599), - ADD COLUMN response_body jsonb; diff --git a/s-migrations/000036_plugin_service_actor.up.sql b/s-migrations/000036_plugin_service_actor.up.sql deleted file mode 100644 index fe0f235..0000000 --- a/s-migrations/000036_plugin_service_actor.up.sql +++ /dev/null @@ -1,14 +0,0 @@ --- The plugin control plane is authenticated by E, but existing S transactions --- require a UUID actor for foreign keys and role checks. This principal has no --- credential row and is excluded from all user-management views. -INSERT INTO users (id, username, username_normalized, email, email_normalized, role, status) -VALUES ( - '00000000-0000-0000-0000-000000000036', - '__plugin_service__', - '__plugin_service__', - 'plugin-service@internal.invalid', - 'plugin-service@internal.invalid', - 'admin', - 'active' -) -ON CONFLICT (id) DO NOTHING; diff --git a/s-migrations/000037_user_groups.up.sql b/s-migrations/000037_user_groups.up.sql deleted file mode 100644 index 415d1b4..0000000 --- a/s-migrations/000037_user_groups.up.sql +++ /dev/null @@ -1,34 +0,0 @@ -CREATE TABLE user_groups ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL, - name_normalized text NOT NULL UNIQUE, - description text NOT NULL DEFAULT '', - created_by uuid REFERENCES users(id), - updated_by uuid REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - CHECK (char_length(btrim(name)) BETWEEN 1 AND 80), - CHECK (char_length(description) <= 1000) -); - -CREATE TABLE user_group_members ( - group_id uuid NOT NULL REFERENCES user_groups(id) ON DELETE CASCADE, - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - created_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (group_id, user_id) -); -CREATE INDEX user_group_members_user_idx ON user_group_members(user_id, group_id); - -CREATE TABLE user_group_permissions ( - group_id uuid NOT NULL REFERENCES user_groups(id) ON DELETE CASCADE, - permission text NOT NULL, - granted_by uuid REFERENCES users(id), - created_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (group_id, permission), - CHECK (permission IN ( - 'users.read','users.write','memberships.manage','cards.manage','invites.manage', - 'requests.manage','tickets.manage','notifications.manage','security.manage', - 'operations.manage','e.manage','plugins.manage','audit.read','logs.read', - 'backups.manage','updates.manage','settings.manage','licenses.manage' - )) -); diff --git a/s-migrations/000038_store_settings.up.sql b/s-migrations/000038_store_settings.up.sql deleted file mode 100644 index 47759b5..0000000 --- a/s-migrations/000038_store_settings.up.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE store_settings ( - singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton), - title text NOT NULL DEFAULT '会员商店', - introduction text NOT NULL DEFAULT '', - notice text NOT NULL DEFAULT '', - enabled boolean NOT NULL DEFAULT true, - allow_point_purchase boolean NOT NULL DEFAULT true, - updated_by text NOT NULL DEFAULT '', - updated_at timestamptz NOT NULL DEFAULT now() -); - -INSERT INTO store_settings(singleton) VALUES(true) ON CONFLICT (singleton) DO NOTHING; diff --git a/s-migrations/000039_telegram_command_auto_delete.up.sql b/s-migrations/000039_telegram_command_auto_delete.up.sql deleted file mode 100644 index 7854c4a..0000000 --- a/s-migrations/000039_telegram_command_auto_delete.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE telegram_bot_settings - ADD COLUMN command_auto_delete_seconds integer NOT NULL DEFAULT 0 - CHECK (command_auto_delete_seconds BETWEEN 0 AND 3600); diff --git a/s-migrations/000040_enable_telegram_command_auto_delete.up.sql b/s-migrations/000040_enable_telegram_command_auto_delete.up.sql deleted file mode 100644 index 5358114..0000000 --- a/s-migrations/000040_enable_telegram_command_auto_delete.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -ALTER TABLE telegram_bot_settings - ALTER COLUMN command_auto_delete_seconds SET DEFAULT 60; - --- The preceding migration introduced this setting with a disabled default. --- Enable the requested cleanup for existing installations while retaining 0 --- as the explicit opt-out value in the administration UI. -UPDATE telegram_bot_settings -SET command_auto_delete_seconds = 60 -WHERE singleton AND command_auto_delete_seconds = 0; diff --git a/s-migrations/000041_web_logins_not_playback_devices.up.sql b/s-migrations/000041_web_logins_not_playback_devices.up.sql deleted file mode 100644 index 6ad86db..0000000 --- a/s-migrations/000041_web_logins_not_playback_devices.up.sql +++ /dev/null @@ -1,10 +0,0 @@ --- WebUI refresh-token sessions are account sessions, not playback devices. --- Retire legacy rows created by the old login path only when they have never --- been used to reserve or play media, preserving real browser playback data. -UPDATE devices device -SET revoked_at = COALESCE(device.revoked_at, now()) -WHERE device.revoked_at IS NULL - AND lower(device.client_name) LIKE '%webui%' - AND NOT EXISTS ( - SELECT 1 FROM playback_sessions session WHERE session.device_id = device.id - ); diff --git a/s-migrations/000042_login_protection.up.sql b/s-migrations/000042_login_protection.up.sql deleted file mode 100644 index 0aeacc0..0000000 --- a/s-migrations/000042_login_protection.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE auth_login_failures ( - subject_type text NOT NULL CHECK (subject_type IN ('account','ip')), - subject_hash text NOT NULL, - failed_count integer NOT NULL DEFAULT 0 CHECK (failed_count >= 0), - window_started_at timestamptz NOT NULL DEFAULT now(), - locked_until timestamptz, - updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (subject_type, subject_hash) -); - -CREATE INDEX auth_login_failures_cleanup_idx ON auth_login_failures(updated_at); diff --git a/s-migrations/000043_plugin_v11.up.sql b/s-migrations/000043_plugin_v11.up.sql deleted file mode 100644 index a946153..0000000 --- a/s-migrations/000043_plugin_v11.up.sql +++ /dev/null @@ -1,104 +0,0 @@ -CREATE TABLE IF NOT EXISTS plugin_user_policies ( - user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, - is_disabled boolean NOT NULL DEFAULT false, - is_administrator boolean NOT NULL DEFAULT false, - allow_video_playback boolean NOT NULL DEFAULT true, - allow_audio_playback boolean NOT NULL DEFAULT true, - allow_content_downloading boolean NOT NULL DEFAULT false, - enable_remote_access boolean NOT NULL DEFAULT true, - enabled_library_ids text[] NOT NULL DEFAULT '{}', - blocked_library_ids text[] NOT NULL DEFAULT '{}', - max_concurrent_streams integer NOT NULL DEFAULT 0 CHECK (max_concurrent_streams >= 0), - updated_at timestamptz NOT NULL DEFAULT now() -); - -ALTER TABLE playback_decisions - ADD COLUMN IF NOT EXISTS media_type text NOT NULL DEFAULT '' CHECK (media_type IN ('','video','audio')); -ALTER TABLE playback_sessions - ADD COLUMN IF NOT EXISTS media_type text NOT NULL DEFAULT '' CHECK (media_type IN ('','video','audio')); -CREATE INDEX IF NOT EXISTS playback_sessions_plugin_report_idx ON playback_sessions(created_at DESC,user_id); - -CREATE TABLE IF NOT EXISTS user_group_media_policies ( - group_id uuid PRIMARY KEY REFERENCES user_groups(id) ON DELETE CASCADE, - enabled_library_ids text[] NOT NULL DEFAULT '{}', - blocked_library_ids text[] NOT NULL DEFAULT '{}', - updated_by uuid REFERENCES users(id), - updated_at timestamptz NOT NULL DEFAULT now(), - CHECK (cardinality(enabled_library_ids) <= 500), - CHECK (cardinality(blocked_library_ids) <= 500) -); - -ALTER TABLE invite_codes - ADD COLUMN IF NOT EXISTS owner_reward_points integer NOT NULL DEFAULT 0 CHECK (owner_reward_points >= 0), - ADD COLUMN IF NOT EXISTS owner_reward_membership_days integer NOT NULL DEFAULT 0 CHECK (owner_reward_membership_days >= 0), - ADD COLUMN IF NOT EXISTS redeemer_reward_points integer NOT NULL DEFAULT 0 CHECK (redeemer_reward_points >= 0), - ADD COLUMN IF NOT EXISTS redeemer_reward_membership_days integer NOT NULL DEFAULT 0 CHECK (redeemer_reward_membership_days >= 0); - -CREATE TABLE IF NOT EXISTS invite_redemptions ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - invite_id uuid NOT NULL REFERENCES invite_codes(id) ON DELETE CASCADE, - redeemed_by_user_id uuid NOT NULL REFERENCES users(id), - idempotency_key text NOT NULL CHECK (char_length(idempotency_key) BETWEEN 1 AND 256), - owner_reward_points integer NOT NULL DEFAULT 0, - owner_reward_membership_days integer NOT NULL DEFAULT 0, - redeemer_reward_points integer NOT NULL DEFAULT 0, - redeemer_reward_membership_days integer NOT NULL DEFAULT 0, - redeemed_at timestamptz NOT NULL DEFAULT now(), - UNIQUE (invite_id, redeemed_by_user_id), - UNIQUE (invite_id, idempotency_key) -); -CREATE INDEX IF NOT EXISTS invite_redemptions_user_time_idx ON invite_redemptions(redeemed_by_user_id,redeemed_at DESC); - -INSERT INTO membership_plans(code,name,duration_days,max_playbacks,max_devices,max_bitrate_kbps,price_points,self_service,enabled) -VALUES('points-7d','7 天会员',7,1,3,0,30,true,true) -ON CONFLICT(code) DO NOTHING; - -ALTER TABLE point_ledger DROP CONSTRAINT IF EXISTS point_ledger_source_type_check; -ALTER TABLE point_ledger ADD CONSTRAINT point_ledger_source_type_check CHECK (source_type IN ( - 'manual','playback_session','media_feedback','redemption','daily_checkin', - 'membership_purchase','telegram_red_packet','invite_reward' -)); - -CREATE TABLE IF NOT EXISTS plugin_telegram_contexts ( - token_hash bytea PRIMARY KEY, - plugin_id text NOT NULL, - tenant_id text NOT NULL, - telegram_user_id bigint NOT NULL, - chat_id bigint NOT NULL, - telegram_username text NOT NULL DEFAULT '', - expires_at timestamptz NOT NULL, - consumed_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - CHECK (expires_at > created_at) -); -CREATE INDEX IF NOT EXISTS plugin_telegram_contexts_expiry_idx ON plugin_telegram_contexts(expires_at) WHERE consumed_at IS NULL; - -CREATE TABLE IF NOT EXISTS plugin_telegram_sends ( - plugin_id text NOT NULL, - tenant_id text NOT NULL, - action text NOT NULL CHECK (action IN ('send_photo','send_document')), - idempotency_key text NOT NULL CHECK (char_length(idempotency_key) BETWEEN 1 AND 256), - status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','sent')), - message_id bigint NOT NULL DEFAULT 0, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (plugin_id,tenant_id,action,idempotency_key) -); -CREATE INDEX IF NOT EXISTS plugin_telegram_sends_pending_idx ON plugin_telegram_sends(updated_at) WHERE status='pending'; - -CREATE TABLE IF NOT EXISTS plugin_audit_records ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - plugin_id text NOT NULL, - plugin_version text NOT NULL, - tenant_id text NOT NULL, - actor_type text NOT NULL, - actor_id text NOT NULL, - action text NOT NULL, - target_type text NOT NULL, - target_id text NOT NULL DEFAULT '', - request_id text NOT NULL DEFAULT '', - trace_id text NOT NULL DEFAULT '', - result text NOT NULL, - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX IF NOT EXISTS plugin_audit_records_plugin_time_idx ON plugin_audit_records(plugin_id,tenant_id,created_at DESC,id DESC); diff --git a/s-migrations/000044_telegram_membership_signup.up.sql b/s-migrations/000044_telegram_membership_signup.up.sql deleted file mode 100644 index 385bce2..0000000 --- a/s-migrations/000044_telegram_membership_signup.up.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE TABLE telegram_membership_signup_flows ( - token_hash text PRIMARY KEY, - actor_user_id uuid NOT NULL REFERENCES users(id), - telegram_user_id bigint NOT NULL, - telegram_username text NOT NULL DEFAULT '', - started_at timestamptz, - consumed_at timestamptz, - expires_at timestamptz NOT NULL, - created_at timestamptz NOT NULL DEFAULT now() -); - -CREATE UNIQUE INDEX telegram_membership_signup_flows_active_target_idx - ON telegram_membership_signup_flows(telegram_user_id) - WHERE consumed_at IS NULL; diff --git a/s-migrations/000045_plugin_telegram_inbound_media.up.sql b/s-migrations/000045_plugin_telegram_inbound_media.up.sql deleted file mode 100644 index d79fc81..0000000 --- a/s-migrations/000045_plugin_telegram_inbound_media.up.sql +++ /dev/null @@ -1,8 +0,0 @@ -ALTER TABLE plugin_telegram_contexts - ADD COLUMN IF NOT EXISTS message_id bigint NOT NULL DEFAULT 0, - ADD COLUMN IF NOT EXISTS chat_type text NOT NULL DEFAULT 'private', - ADD COLUMN IF NOT EXISTS inbound_media jsonb NOT NULL DEFAULT '[]'::jsonb; - -ALTER TABLE plugin_telegram_sends DROP CONSTRAINT IF EXISTS plugin_telegram_sends_action_check; -ALTER TABLE plugin_telegram_sends ADD CONSTRAINT plugin_telegram_sends_action_check - CHECK (action IN ('send_message','send_photo','send_document','resend_inbound_media','bot_api','chat_api')); diff --git a/s-migrations/000046_plugin_telegram_chat_type.up.sql b/s-migrations/000046_plugin_telegram_chat_type.up.sql deleted file mode 100644 index e45c991..0000000 --- a/s-migrations/000046_plugin_telegram_chat_type.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE plugin_telegram_contexts - ADD COLUMN IF NOT EXISTS chat_type text NOT NULL DEFAULT 'private'; diff --git a/s-migrations/000047_plugin_telegram_send_message.up.sql b/s-migrations/000047_plugin_telegram_send_message.up.sql deleted file mode 100644 index 4fea51b..0000000 --- a/s-migrations/000047_plugin_telegram_send_message.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE plugin_telegram_sends DROP CONSTRAINT IF EXISTS plugin_telegram_sends_action_check; -ALTER TABLE plugin_telegram_sends ADD CONSTRAINT plugin_telegram_sends_action_check - CHECK (action IN ('send_message','send_photo','send_document','resend_inbound_media','bot_api','chat_api')); diff --git a/s-migrations/000048_telegram_pro_user_duration.up.sql b/s-migrations/000048_telegram_pro_user_duration.up.sql deleted file mode 100644 index f0f739f..0000000 --- a/s-migrations/000048_telegram_pro_user_duration.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE telegram_membership_signup_flows - ADD COLUMN membership_duration_days integer NOT NULL DEFAULT 30 - CHECK (membership_duration_days BETWEEN 1 AND 365000); diff --git a/s-migrations/000049_telegram_group_cleanup.up.sql b/s-migrations/000049_telegram_group_cleanup.up.sql deleted file mode 100644 index f90493d..0000000 --- a/s-migrations/000049_telegram_group_cleanup.up.sql +++ /dev/null @@ -1,21 +0,0 @@ -CREATE TABLE telegram_group_members ( - chat_id bigint NOT NULL, - telegram_user_id bigint NOT NULL, - username text NOT NULL DEFAULT '', - first_seen_at timestamptz NOT NULL DEFAULT now(), - last_seen_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (chat_id, telegram_user_id) -); - -CREATE TABLE telegram_group_cleanup_flows ( - token_hash text PRIMARY KEY, - actor_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - chat_id bigint NOT NULL, - expires_at timestamptz NOT NULL, - consumed_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX telegram_group_cleanup_flows_active_idx - ON telegram_group_cleanup_flows(actor_user_id, chat_id) - WHERE consumed_at IS NULL; diff --git a/s-migrations/000050_telegram_rebind_after_unbind.up.sql b/s-migrations/000050_telegram_rebind_after_unbind.up.sql deleted file mode 100644 index ead635a..0000000 --- a/s-migrations/000050_telegram_rebind_after_unbind.up.sql +++ /dev/null @@ -1,7 +0,0 @@ --- A disabled row records binding history only. It must not reserve the numeric --- Telegram identity forever, otherwise a user who unbinds cannot bind a newly --- registered panel account. -ALTER TABLE telegram_accounts DROP CONSTRAINT IF EXISTS telegram_accounts_telegram_user_id_key; -CREATE UNIQUE INDEX IF NOT EXISTS telegram_accounts_telegram_user_id_active_idx - ON telegram_accounts(telegram_user_id) - WHERE disabled_at IS NULL; diff --git a/s-migrations/000051_storage_webhook_events.up.sql b/s-migrations/000051_storage_webhook_events.up.sql deleted file mode 100644 index 171a107..0000000 --- a/s-migrations/000051_storage_webhook_events.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE storage_webhook_events ( - event_id text PRIMARY KEY, - library_id uuid NOT NULL, - event_type text NOT NULL, - paths jsonb NOT NULL, - task_ids jsonb NOT NULL DEFAULT '[]'::jsonb, - state text NOT NULL CHECK (state IN ('processing','accepted','failed')), - error_code text NOT NULL DEFAULT '', - received_at timestamptz NOT NULL DEFAULT now(), - completed_at timestamptz -); - -CREATE INDEX storage_webhook_events_received_idx ON storage_webhook_events(received_at DESC); diff --git a/s-migrations/000052_user_agent_security_rules.up.sql b/s-migrations/000052_user_agent_security_rules.up.sql deleted file mode 100644 index 4120f08..0000000 --- a/s-migrations/000052_user_agent_security_rules.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE security_list_entries DROP CONSTRAINT security_list_entries_subject_type_check; -ALTER TABLE security_list_entries ADD CONSTRAINT security_list_entries_subject_type_check - CHECK (subject_type IN ('ip','cidr','device','user','user_agent')); diff --git a/s-migrations/000053_precise_client_access_rules.up.sql b/s-migrations/000053_precise_client_access_rules.up.sql deleted file mode 100644 index c572ac6..0000000 --- a/s-migrations/000053_precise_client_access_rules.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE security_list_entries DROP CONSTRAINT security_list_entries_subject_type_check; -ALTER TABLE security_list_entries ADD CONSTRAINT security_list_entries_subject_type_check - CHECK (subject_type IN ('ip','cidr','device','user','user_agent','client_name','client_version')); diff --git a/s-migrations/000054_api_keys.up.sql b/s-migrations/000054_api_keys.up.sql deleted file mode 100644 index bd280aa..0000000 --- a/s-migrations/000054_api_keys.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE api_keys ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - name text NOT NULL CHECK (length(btrim(name)) BETWEEN 1 AND 128), - prefix text NOT NULL UNIQUE CHECK (prefix ~ '^oc_[a-z0-9]{8}$'), - key_hash bytea NOT NULL UNIQUE, - expires_at timestamptz, - enabled boolean NOT NULL DEFAULT true, - created_at timestamptz NOT NULL DEFAULT now(), - last_used_at timestamptz -); -CREATE INDEX api_keys_user_idx ON api_keys(user_id,created_at DESC); -CREATE INDEX api_keys_active_idx ON api_keys(prefix) WHERE enabled; diff --git a/s-migrations/000055_playback_traffic.up.sql b/s-migrations/000055_playback_traffic.up.sql deleted file mode 100644 index 97b640d..0000000 --- a/s-migrations/000055_playback_traffic.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE playback_sessions ADD COLUMN IF NOT EXISTS bytes_served bigint NOT NULL DEFAULT 0 CHECK (bytes_served >= 0); -ALTER TABLE playback_sessions ADD COLUMN IF NOT EXISTS estimated_bytes bigint NOT NULL DEFAULT 0 CHECK (estimated_bytes >= 0); -CREATE INDEX IF NOT EXISTS playback_sessions_traffic_user_idx ON playback_sessions(user_id, updated_at DESC); diff --git a/s-migrations/000056_playback_estimate.up.sql b/s-migrations/000056_playback_estimate.up.sql deleted file mode 100644 index dc0bca1..0000000 --- a/s-migrations/000056_playback_estimate.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE playback_sessions ADD COLUMN IF NOT EXISTS estimated_bytes bigint NOT NULL DEFAULT 0 CHECK (estimated_bytes >= 0); diff --git a/s-migrations/000057_admin_agent.up.sql b/s-migrations/000057_admin_agent.up.sql deleted file mode 100644 index ef635a7..0000000 --- a/s-migrations/000057_admin_agent.up.sql +++ /dev/null @@ -1,60 +0,0 @@ -CREATE TABLE agent_settings ( - id smallint PRIMARY KEY DEFAULT 1 CHECK (id = 1), - enabled boolean NOT NULL DEFAULT false, - provider_name text NOT NULL DEFAULT '', - provider_type text NOT NULL DEFAULT 'openai_compatible' CHECK (provider_type IN ('openai_compatible','openai','custom_http')), - base_url text NOT NULL DEFAULT '', - model text NOT NULL DEFAULT '', - encrypted_api_key bytea, - timeout_ms integer NOT NULL DEFAULT 30000 CHECK (timeout_ms BETWEEN 1000 AND 120000), - max_tokens integer NOT NULL DEFAULT 2048 CHECK (max_tokens BETWEEN 1 AND 32768), - temperature double precision NOT NULL DEFAULT 0.2 CHECK (temperature BETWEEN 0 AND 2), - system_prompt text NOT NULL DEFAULT '', - max_history integer NOT NULL DEFAULT 20 CHECK (max_history BETWEEN 1 AND 100), - rate_limit integer NOT NULL DEFAULT 30 CHECK (rate_limit BETWEEN 1 AND 600), - request_logging boolean NOT NULL DEFAULT false, - tool_enabled boolean NOT NULL DEFAULT true, - confirmation_required boolean NOT NULL DEFAULT true, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - updated_by uuid REFERENCES users(id) -); - -CREATE TABLE agent_sessions ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - admin_user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, - title text NOT NULL DEFAULT 'OverCooked Admin Agent', - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - expires_at timestamptz NOT NULL DEFAULT (now() + interval '24 hours') -); -CREATE INDEX agent_sessions_admin_idx ON agent_sessions(admin_user_id, updated_at DESC); - -CREATE TABLE agent_messages ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - session_id uuid NOT NULL REFERENCES agent_sessions(id) ON DELETE CASCADE, - role text NOT NULL CHECK (role IN ('system','user','assistant','tool')), - content text NOT NULL, - tool_name text, - tool_call_id text, - confirmation_state text NOT NULL DEFAULT 'none' CHECK (confirmation_state IN ('none','pending','confirmed','cancelled','expired')), - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX agent_messages_session_idx ON agent_messages(session_id, created_at); - -CREATE TABLE agent_audit ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - session_id uuid REFERENCES agent_sessions(id) ON DELETE SET NULL, - admin_user_id uuid NOT NULL REFERENCES users(id) ON DELETE RESTRICT, - capability text NOT NULL, - action text NOT NULL, - target text NOT NULL DEFAULT '', - request_summary text NOT NULL DEFAULT '', - result jsonb NOT NULL DEFAULT '{}'::jsonb, - status text NOT NULL CHECK (status IN ('planned','pending_confirmation','success','cancelled','failed')), - error_code text NOT NULL DEFAULT '', - trace_id text NOT NULL DEFAULT '', - latency_ms bigint NOT NULL DEFAULT 0, - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX agent_audit_admin_idx ON agent_audit(admin_user_id, created_at DESC);