DO $$ BEGIN CREATE TYPE "public"."push_delivery_status" AS ENUM('pending', 'accepted_by_push_service', 'failed', 'invalid_subscription', 'expired'); EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN CREATE TYPE "public"."user_role" AS ENUM('admin', 'user'); EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint CREATE TABLE IF NOT EXISTS "api_token_topics" ( "token_id" uuid NOT NULL, "topic_id" uuid NOT NULL, CONSTRAINT "api_token_topics_token_id_topic_id_pk" PRIMARY KEY("token_id","topic_id") ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "api_tokens" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "name" text NOT NULL, "token_hash" text NOT NULL, "token_prefix" text NOT NULL, "allowed_source" text, "allowed_cidrs" text[], "expires_at" timestamp with time zone, "revoked_at" timestamp with time zone, "created_by" uuid, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "last_used_at" timestamp with time zone, "usage_count" bigint DEFAULT 0 NOT NULL ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "audit_log" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "actor_user_id" uuid, "actor_token_id" uuid, "action" text NOT NULL, "target_type" text, "target_id" text, "metadata" jsonb, "ip_address" "inet", "created_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "notifications" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "topic_id" uuid NOT NULL, "source" text NOT NULL, "title" text NOT NULL, "message" text NOT NULL, "external_url" text, "image_url" text, "attachment_filename" text, "attachment_content_type" text, "attachment_size_bytes" bigint, "deduplication_key" text, "occurrence_count" integer DEFAULT 1 NOT NULL, "raw_priority" text, "sender_token_id" uuid, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "push_delivery_attempts" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "notification_id" uuid NOT NULL, "push_subscription_id" uuid NOT NULL, "status" "push_delivery_status" NOT NULL, "http_status" integer, "error_message" text, "retry_count" integer DEFAULT 0 NOT NULL, "attempted_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "push_subscriptions" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "user_id" uuid NOT NULL, "endpoint" text NOT NULL, "p256dh" text NOT NULL, "auth" text NOT NULL, "user_agent" text, "device_label" text, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "last_seen_at" timestamp with time zone DEFAULT now() NOT NULL, "disabled_at" timestamp with time zone ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "system_settings" ( "key" text PRIMARY KEY NOT NULL, "value" jsonb NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "topic_members" ( "topic_id" uuid NOT NULL, "user_id" uuid NOT NULL, CONSTRAINT "topic_members_topic_id_user_id_pk" PRIMARY KEY("topic_id","user_id") ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "topics" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "slug" text NOT NULL, "display_name" text NOT NULL, "description" text, "icon" text, "accent_color" text, "retention_days" integer, "restricted" boolean DEFAULT false NOT NULL, "created_by" uuid, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, "archived_at" timestamp with time zone ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "user_notification_state" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "user_id" uuid NOT NULL, "notification_id" uuid NOT NULL, "read" boolean DEFAULT false NOT NULL, "read_at" timestamp with time zone, "starred" boolean DEFAULT false NOT NULL, "starred_at" timestamp with time zone ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "user_topic_subscriptions" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "user_id" uuid NOT NULL, "topic_id" uuid NOT NULL, "push_enabled" boolean DEFAULT true NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "users" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "email" text NOT NULL, "display_name" text NOT NULL, "avatar_url" text, "role" "user_role" DEFAULT 'user' NOT NULL, "auth_subject" text NOT NULL, "auth_issuer" text, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, "last_login_at" timestamp with time zone ); --> statement-breakpoint DO $$ BEGIN ALTER TABLE "api_token_topics" ADD CONSTRAINT "api_token_topics_token_id_api_tokens_id_fk" FOREIGN KEY ("token_id") REFERENCES "public"."api_tokens"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "api_token_topics" ADD CONSTRAINT "api_token_topics_topic_id_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."topics"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "api_tokens" ADD CONSTRAINT "api_tokens_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_actor_user_id_users_id_fk" FOREIGN KEY ("actor_user_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_actor_token_id_api_tokens_id_fk" FOREIGN KEY ("actor_token_id") REFERENCES "public"."api_tokens"("id") ON DELETE set null ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "notifications" ADD CONSTRAINT "notifications_topic_id_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."topics"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "notifications" ADD CONSTRAINT "notifications_sender_token_id_api_tokens_id_fk" FOREIGN KEY ("sender_token_id") REFERENCES "public"."api_tokens"("id") ON DELETE set null ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "push_delivery_attempts" ADD CONSTRAINT "push_delivery_attempts_notification_id_notifications_id_fk" FOREIGN KEY ("notification_id") REFERENCES "public"."notifications"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "push_delivery_attempts" ADD CONSTRAINT "push_delivery_attempts_push_subscription_id_push_subscriptions_id_fk" FOREIGN KEY ("push_subscription_id") REFERENCES "public"."push_subscriptions"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "push_subscriptions" ADD CONSTRAINT "push_subscriptions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "topic_members" ADD CONSTRAINT "topic_members_topic_id_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."topics"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "topic_members" ADD CONSTRAINT "topic_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "topics" ADD CONSTRAINT "topics_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "user_notification_state" ADD CONSTRAINT "user_notification_state_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "user_notification_state" ADD CONSTRAINT "user_notification_state_notification_id_notifications_id_fk" FOREIGN KEY ("notification_id") REFERENCES "public"."notifications"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "user_topic_subscriptions" ADD CONSTRAINT "user_topic_subscriptions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN ALTER TABLE "user_topic_subscriptions" ADD CONSTRAINT "user_topic_subscriptions_topic_id_topics_id_fk" FOREIGN KEY ("topic_id") REFERENCES "public"."topics"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint CREATE UNIQUE INDEX IF NOT EXISTS "api_tokens_token_hash_key" ON "api_tokens" USING btree ("token_hash");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "audit_log_created_idx" ON "audit_log" USING btree ("created_at");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "audit_log_action_idx" ON "audit_log" USING btree ("action");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "notifications_topic_created_idx" ON "notifications" USING btree ("topic_id","created_at");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "notifications_dedup_idx" ON "notifications" USING btree ("deduplication_key","topic_id","created_at");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "notifications_created_idx" ON "notifications" USING btree ("created_at");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "push_delivery_attempts_notification_idx" ON "push_delivery_attempts" USING btree ("notification_id");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "push_delivery_attempts_subscription_idx" ON "push_delivery_attempts" USING btree ("push_subscription_id","attempted_at");--> statement-breakpoint CREATE UNIQUE INDEX IF NOT EXISTS "push_subscriptions_endpoint_key" ON "push_subscriptions" USING btree ("endpoint");--> statement-breakpoint CREATE UNIQUE INDEX IF NOT EXISTS "topics_slug_key" ON "topics" USING btree ("slug");--> statement-breakpoint CREATE UNIQUE INDEX IF NOT EXISTS "user_notification_state_user_notification_key" ON "user_notification_state" USING btree ("user_id","notification_id");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "user_notification_state_user_read_idx" ON "user_notification_state" USING btree ("user_id","read");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "user_notification_state_user_starred_idx" ON "user_notification_state" USING btree ("user_id","starred");--> statement-breakpoint CREATE UNIQUE INDEX IF NOT EXISTS "user_topic_subscriptions_user_topic_key" ON "user_topic_subscriptions" USING btree ("user_id","topic_id");--> statement-breakpoint CREATE UNIQUE INDEX IF NOT EXISTS "users_email_key" ON "users" USING btree ("email");--> statement-breakpoint CREATE UNIQUE INDEX IF NOT EXISTS "users_issuer_subject_key" ON "users" USING btree ("auth_issuer","auth_subject");