154 lines
6.6 KiB
PL/PgSQL
154 lines
6.6 KiB
PL/PgSQL
-- Fix: missing messages and attachments tables on VPS
|
|||
|
|
-- Run: PGPASSWORD=hunterz psql -h 100.108.1.124 -U asephs -d hub -f scripts/fix-missing-tables.sql
|
||
|
|
|
||
|
|
BEGIN;
|
||
|
|
|
||
|
|
-- 1. Create messages table if not exists
|
||
|
|
CREATE TABLE IF NOT EXISTS "messages" (
|
||
|
|
"id" text PRIMARY KEY NOT NULL,
|
||
|
|
"guild_id" text NOT NULL,
|
||
|
|
"channel_id" text NOT NULL,
|
||
|
|
"thread_id" text,
|
||
|
|
"user_id" text NOT NULL,
|
||
|
|
"username" text NOT NULL,
|
||
|
|
"avatar_url" text,
|
||
|
|
"content" text NOT NULL,
|
||
|
|
"edited_content" text,
|
||
|
|
"created_at" bigint NOT NULL,
|
||
|
|
"edited_at" bigint,
|
||
|
|
"deleted_at" bigint,
|
||
|
|
"type" text DEFAULT 'text' NOT NULL,
|
||
|
|
"metadata" text,
|
||
|
|
"ai_status" text DEFAULT 'pending' NOT NULL,
|
||
|
|
"ai_moderation_flags" text,
|
||
|
|
"ai_moderation_score" real,
|
||
|
|
"ai_moderation_raw" text,
|
||
|
|
"ai_analysis" text,
|
||
|
|
"ai_analyzed_at" bigint,
|
||
|
|
"ai_error" text
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 2. Create attachments table if not exists
|
||
|
|
CREATE TABLE IF NOT EXISTS "attachments" (
|
||
|
|
"id" text PRIMARY KEY NOT NULL,
|
||
|
|
"message_id" text NOT NULL,
|
||
|
|
"guild_id" text NOT NULL,
|
||
|
|
"channel_id" text NOT NULL,
|
||
|
|
"thread_id" text,
|
||
|
|
"user_id" text NOT NULL,
|
||
|
|
"filename" text NOT NULL,
|
||
|
|
"size" integer NOT NULL,
|
||
|
|
"type" text NOT NULL,
|
||
|
|
"discord_url" text NOT NULL,
|
||
|
|
"uploaded_url" text,
|
||
|
|
"upload_status" text DEFAULT 'pending' NOT NULL,
|
||
|
|
"upload_error" text,
|
||
|
|
"created_at" bigint NOT NULL,
|
||
|
|
"uploaded_at" bigint
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 3. Foreign key
|
||
|
|
ALTER TABLE "attachments" DROP CONSTRAINT IF EXISTS "fk_attachments_message_id";
|
||
|
|
ALTER TABLE "attachments" ADD CONSTRAINT "fk_attachments_message_id"
|
||
|
|
FOREIGN KEY ("message_id") REFERENCES "public"."messages"("id")
|
||
|
|
ON DELETE cascade ON UPDATE no action;
|
||
|
|
|
||
|
|
-- 4. Indexes from migration 0000
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_attachments_channel" ON "attachments" USING btree ("channel_id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_attachments_message" ON "attachments" USING btree ("message_id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_attachments_status" ON "attachments" USING btree ("upload_status");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_channel" ON "messages" USING btree ("channel_id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_user" ON "messages" USING btree ("user_id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_created" ON "messages" USING btree ("created_at");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_thread" ON "messages" USING btree ("thread_id");
|
||
|
|
|
||
|
|
-- 5. Indexes from migration 0001
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_attachments_channel_created" ON "attachments" ("channel_id","created_at","id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_attachments_thread_created" ON "attachments" ("thread_id","created_at","id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_channel_created" ON "messages" ("channel_id","created_at","id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_thread_created" ON "messages" ("thread_id","created_at","id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_ai_status_created" ON "messages" ("ai_status","created_at","id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_guild_ai_status_created" ON "messages" ("guild_id","ai_status","created_at","id");
|
||
|
|
|
||
|
|
-- 6. Columns from migration 0003
|
||
|
|
ALTER TABLE "messages" ADD COLUMN IF NOT EXISTS "ai_categories" text;
|
||
|
|
ALTER TABLE "messages" ADD COLUMN IF NOT EXISTS "ai_severity" text;
|
||
|
|
ALTER TABLE "messages" ADD COLUMN IF NOT EXISTS "ai_confidence" real;
|
||
|
|
ALTER TABLE "messages" ADD COLUMN IF NOT EXISTS "ai_recommended_action" text;
|
||
|
|
ALTER TABLE "messages" ADD COLUMN IF NOT EXISTS "ai_policy_version" text;
|
||
|
|
ALTER TABLE "messages" ADD COLUMN IF NOT EXISTS "ai_evidence" text;
|
||
|
|
|
||
|
|
-- 7. message_reviews, moderation_actions, retention_policies (from 0003 — should already exist, but IF NOT EXISTS safe)
|
||
|
|
CREATE TABLE IF NOT EXISTS "message_reviews" (
|
||
|
|
"id" text PRIMARY KEY NOT NULL,
|
||
|
|
"message_id" text NOT NULL,
|
||
|
|
"guild_id" text NOT NULL,
|
||
|
|
"channel_id" text NOT NULL,
|
||
|
|
"reviewer_id" text,
|
||
|
|
"status" text DEFAULT 'pending' NOT NULL,
|
||
|
|
"notes" text,
|
||
|
|
"created_at" bigint NOT NULL,
|
||
|
|
"reviewed_at" bigint
|
||
|
|
);
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_message_reviews_message_id" ON "message_reviews" USING btree ("message_id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_message_reviews_guild_status" ON "message_reviews" USING btree ("guild_id", "status", "created_at");
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS "moderation_actions" (
|
||
|
|
"id" text PRIMARY KEY NOT NULL,
|
||
|
|
"message_id" text,
|
||
|
|
"user_id" text,
|
||
|
|
"guild_id" text NOT NULL,
|
||
|
|
"action_type" text NOT NULL,
|
||
|
|
"reason" text,
|
||
|
|
"executed_by" text,
|
||
|
|
"status" text DEFAULT 'pending' NOT NULL,
|
||
|
|
"error" text,
|
||
|
|
"created_at" bigint NOT NULL,
|
||
|
|
"executed_at" bigint
|
||
|
|
);
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_moderation_actions_message_id" ON "moderation_actions" USING btree ("message_id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_moderation_actions_user_id" ON "moderation_actions" USING btree ("user_id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_moderation_actions_status" ON "moderation_actions" USING btree ("status");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_moderation_actions_guild_status" ON "moderation_actions" USING btree ("guild_id", "status", "created_at");
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS "retention_policies" (
|
||
|
|
"id" text PRIMARY KEY NOT NULL,
|
||
|
|
"guild_id" text NOT NULL,
|
||
|
|
"channel_id" text,
|
||
|
|
"retention_days" integer DEFAULT 90 NOT NULL,
|
||
|
|
"apply_to_media" boolean DEFAULT true NOT NULL,
|
||
|
|
"apply_to_voice" boolean DEFAULT true NOT NULL,
|
||
|
|
"enabled" boolean DEFAULT true NOT NULL,
|
||
|
|
"created_at" bigint NOT NULL,
|
||
|
|
"updated_at" bigint NOT NULL
|
||
|
|
);
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_retention_policies_guild_id" ON "retention_policies" USING btree ("guild_id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_retention_policies_enabled" ON "retention_policies" USING btree ("enabled");
|
||
|
|
|
||
|
|
-- 8. Indexes from migration 0005
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_guild_created_deleted" ON "messages" USING btree ("guild_id","created_at","deleted_at","id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_channel_ai_status_created" ON "messages" USING btree ("channel_id","ai_status","created_at","id");
|
||
|
|
CREATE INDEX IF NOT EXISTS "idx_messages_thread_ai_status_created" ON "messages" USING btree ("thread_id","ai_status","created_at","id");
|
||
|
|
|
||
|
|
-- 9. Drizzle migration tracking table (so future drizzle-kit migrate works)
|
||
|
|
CREATE TABLE IF NOT EXISTS "__drizzle_migrations" (
|
||
|
|
"id" serial PRIMARY KEY,
|
||
|
|
"hash" text NOT NULL,
|
||
|
|
"created_at" bigint NOT NULL
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Seed migration tracking so drizzle-kit doesn't try to re-apply
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (SELECT 1 FROM "__drizzle_migrations" WHERE hash = '0000_rare_kitty_pryde') THEN
|
||
|
|
INSERT INTO "__drizzle_migrations" (hash, created_at) VALUES
|
||
|
|
('0000_rare_kitty_pryde', 1778750697764),
|
||
|
|
('0001_curious_zodiak', 1778764447718),
|
||
|
|
('0002_dark_omega_flight', 1779109619461),
|
||
|
|
('0003_ai_moderation_review_guardrails', 1780079000000),
|
||
|
|
('0005_optimize-message-index', 1780218363790);
|
||
|
|
END IF;
|
||
|
|
END $$;
|
||
|
|
|
||
|
|
COMMIT;
|