cms.c2sgmbh/src/migrations/20260112_150000_add_youtube_operations_hub.ts
Martin Porwoll 3294fbb506 feat(YouTube): add YouTube Operations Hub with YtSeries collection
Complete YouTube content management system:
- YouTubeChannels: Channel management with branding and metrics
- YouTubeContent: Video pipeline with workflow, approvals, scheduling
- YtSeries: Dedicated series management per channel (NEW)
- YtBatches: Production batch tracking with targets and progress
- YtTasks: Task management with notifications
- YtNotifications: User notification system
- YtMonthlyGoals: Monthly production goals per channel
- YtScriptTemplates: Reusable script templates
- YtChecklistTemplates: Checklist templates for workflows

Features:
- Role-based access (YouTubeManager, YouTubeCreator, YouTubeViewer)
- Auto-task generation on status changes
- Series relationship with channel-based filtering
- API endpoints for dashboard, tasks, and task completion
- German/English localization support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 14:54:40 +00:00

580 lines
27 KiB
TypeScript

import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
/**
* Migration: Add YouTube Operations Hub Collections
*
* This migration creates:
* - youtube_channels table with _locales and content_series array table
* - youtube_content table with _locales and multiple array tables
* - yt_tasks table with _locales and array tables for attachments/comments
* - yt_notifications table with _locales
* - Extends users table with youtubeRole enum
* - Adds columns to payload_locked_documents_rels for all 4 collections
*/
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
// Create enums
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_users_youtube_role" AS ENUM (
'none', 'viewer', 'editor', 'producer', 'creator', 'manager'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_youtube_channels_language" AS ENUM ('de', 'en');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_youtube_channels_category" AS ENUM ('lifestyle', 'corporate', 'b2b');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_youtube_channels_status" AS ENUM ('active', 'planned', 'paused', 'archived');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_youtube_content_format" AS ENUM ('short', 'longform', 'premiere');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_youtube_content_status" AS ENUM (
'idea', 'script_draft', 'script_review', 'script_approved',
'shoot_scheduled', 'shot', 'rough_cut', 'fine_cut',
'final_review', 'approved', 'upload_scheduled', 'published',
'tracked', 'discarded'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_youtube_content_priority" AS ENUM ('urgent', 'high', 'normal', 'low');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_youtube_content_visibility" AS ENUM ('public', 'unlisted', 'private');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_yt_tasks_task_type" AS ENUM (
'script_write', 'script_review', 'shoot_prep', 'shoot',
'edit', 'graphics', 'thumbnail', 'review', 'upload',
'track', 'comments', 'other'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_yt_tasks_status" AS ENUM (
'todo', 'in_progress', 'blocked', 'waiting_review', 'done', 'cancelled'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_yt_tasks_priority" AS ENUM ('urgent', 'high', 'normal', 'low');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
await db.execute(sql`
DO $$ BEGIN
CREATE TYPE "enum_yt_notifications_type" AS ENUM (
'task_assigned', 'task_due', 'task_overdue', 'approval_required',
'approved', 'rejected', 'video_published', 'comment', 'mention', 'system'
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
// Extend users table
await db.execute(sql`
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "youtube_role" "enum_users_youtube_role" DEFAULT 'none';
`)
// Create YouTube Channels table
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_channels" (
"id" serial PRIMARY KEY NOT NULL,
"slug" varchar NOT NULL,
"youtube_channel_id" varchar NOT NULL,
"youtube_handle" varchar,
"language" "enum_youtube_channels_language" NOT NULL,
"category" "enum_youtube_channels_category" NOT NULL,
"status" "enum_youtube_channels_status" DEFAULT 'active' NOT NULL,
"branding_primary_color" varchar,
"branding_secondary_color" varchar,
"branding_logo_id" integer REFERENCES media(id) ON DELETE SET NULL,
"branding_thumbnail_template_id" integer REFERENCES media(id) ON DELETE SET NULL,
"publishing_schedule_default_days" jsonb,
"publishing_schedule_default_time" varchar,
"publishing_schedule_shorts_per_week" numeric DEFAULT 4,
"publishing_schedule_longform_per_week" numeric DEFAULT 1,
"current_metrics_subscriber_count" numeric,
"current_metrics_total_views" numeric,
"current_metrics_video_count" numeric,
"current_metrics_last_synced_at" timestamp(3) with time zone,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
CONSTRAINT "youtube_channels_slug_unique" UNIQUE("slug")
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_channels_locales" (
"name" varchar NOT NULL,
"id" serial PRIMARY KEY NOT NULL,
"_locale" varchar NOT NULL,
"_parent_id" integer NOT NULL REFERENCES youtube_channels(id) ON DELETE CASCADE,
CONSTRAINT "youtube_channels_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id")
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_channels_content_series" (
"id" serial PRIMARY KEY NOT NULL,
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL REFERENCES youtube_channels(id) ON DELETE CASCADE,
"slug" varchar NOT NULL,
"color" varchar,
"is_active" boolean DEFAULT true
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_channels_content_series_locales" (
"name" varchar NOT NULL,
"description" varchar,
"id" serial PRIMARY KEY NOT NULL,
"_locale" varchar NOT NULL,
"_parent_id" integer NOT NULL REFERENCES youtube_channels_content_series(id) ON DELETE CASCADE,
CONSTRAINT "youtube_channels_content_series_locales_unique" UNIQUE("_locale", "_parent_id")
);
`)
// Table for hasMany select field: publishingSchedule.defaultDays
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_channels_publishing_schedule_default_days" (
"order" integer NOT NULL,
"parent_id" integer NOT NULL REFERENCES youtube_channels(id) ON DELETE CASCADE,
"value" varchar NOT NULL,
"id" serial PRIMARY KEY NOT NULL
);
`)
await db.execute(sql`
CREATE INDEX IF NOT EXISTS "youtube_channels_slug_idx" ON "youtube_channels" USING btree ("slug");
CREATE INDEX IF NOT EXISTS "youtube_channels_branding_logo_idx" ON "youtube_channels" USING btree ("branding_logo_id");
CREATE INDEX IF NOT EXISTS "youtube_channels_branding_thumbnail_template_idx" ON "youtube_channels" USING btree ("branding_thumbnail_template_id");
CREATE INDEX IF NOT EXISTS "youtube_channels_updated_at_idx" ON "youtube_channels" USING btree ("updated_at");
CREATE INDEX IF NOT EXISTS "youtube_channels_created_at_idx" ON "youtube_channels" USING btree ("created_at");
CREATE INDEX IF NOT EXISTS "youtube_channels_locales_locale_idx" ON "youtube_channels_locales" USING btree ("_locale");
CREATE INDEX IF NOT EXISTS "youtube_channels_locales_parent_idx" ON "youtube_channels_locales" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "youtube_channels_content_series_order_idx" ON "youtube_channels_content_series" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "youtube_channels_content_series_parent_idx" ON "youtube_channels_content_series" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "youtube_channels_psd_order_idx" ON "youtube_channels_publishing_schedule_default_days" USING btree ("order");
CREATE INDEX IF NOT EXISTS "youtube_channels_psd_parent_idx" ON "youtube_channels_publishing_schedule_default_days" USING btree ("parent_id");
`)
// Create YouTube Content table
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_content" (
"id" serial PRIMARY KEY NOT NULL,
"slug" varchar NOT NULL,
"channel_id" integer NOT NULL REFERENCES youtube_channels(id) ON DELETE SET NULL,
"content_series" varchar,
"format" "enum_youtube_content_format" NOT NULL,
"status" "enum_youtube_content_status" DEFAULT 'idea' NOT NULL,
"priority" "enum_youtube_content_priority" DEFAULT 'normal',
"assigned_to_id" integer REFERENCES users(id) ON DELETE SET NULL,
"created_by_id" integer REFERENCES users(id) ON DELETE SET NULL,
"script_url" varchar,
"shoot_date" timestamp(3) with time zone,
"edit_deadline" timestamp(3) with time zone,
"review_deadline" timestamp(3) with time zone,
"scheduled_publish_date" timestamp(3) with time zone,
"actual_publish_date" timestamp(3) with time zone,
"thumbnail_id" integer REFERENCES media(id) ON DELETE SET NULL,
"thumbnail_alt_id" integer REFERENCES media(id) ON DELETE SET NULL,
"video_file_id" integer REFERENCES media(id) ON DELETE SET NULL,
"approvals_script_approval_approved" boolean DEFAULT false,
"approvals_script_approval_approved_by_id" integer REFERENCES users(id) ON DELETE SET NULL,
"approvals_script_approval_approved_at" timestamp(3) with time zone,
"approvals_medical_approval_required" boolean DEFAULT false,
"approvals_medical_approval_approved" boolean DEFAULT false,
"approvals_medical_approval_approved_by_id" integer REFERENCES users(id) ON DELETE SET NULL,
"approvals_medical_approval_approved_at" timestamp(3) with time zone,
"approvals_legal_approval_approved" boolean DEFAULT false,
"approvals_legal_approval_approved_by_id" integer REFERENCES users(id) ON DELETE SET NULL,
"approvals_legal_approval_approved_at" timestamp(3) with time zone,
"approvals_legal_approval_disclaimer_included" boolean DEFAULT false,
"approvals_final_approval_approved" boolean DEFAULT false,
"approvals_final_approval_approved_by_id" integer REFERENCES users(id) ON DELETE SET NULL,
"approvals_final_approval_approved_at" timestamp(3) with time zone,
"youtube_video_id" varchar,
"youtube_url" varchar,
"youtube_metadata_visibility" "enum_youtube_content_visibility" DEFAULT 'private',
"youtube_metadata_chapters" varchar,
"performance_views" numeric,
"performance_likes" numeric,
"performance_comments" numeric,
"performance_shares" numeric,
"performance_watch_time_minutes" numeric,
"performance_avg_view_duration" numeric,
"performance_avg_view_percentage" numeric,
"performance_ctr" numeric,
"performance_impressions" numeric,
"performance_subscribers_gained" numeric,
"performance_last_synced_at" timestamp(3) with time zone,
"internal_notes" jsonb,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_content_locales" (
"title" varchar NOT NULL,
"description" varchar,
"hook" varchar,
"call_to_action" varchar,
"script_content" jsonb,
"approvals_script_approval_notes" varchar,
"approvals_medical_approval_notes" varchar,
"approvals_legal_approval_notes" varchar,
"approvals_final_approval_notes" varchar,
"youtube_metadata_youtube_title" varchar,
"youtube_metadata_youtube_description" varchar,
"youtube_metadata_pinned_comment" varchar,
"id" serial PRIMARY KEY NOT NULL,
"_locale" varchar NOT NULL,
"_parent_id" integer NOT NULL REFERENCES youtube_content(id) ON DELETE CASCADE,
CONSTRAINT "youtube_content_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id")
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_content_key_points" (
"id" serial PRIMARY KEY NOT NULL,
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL REFERENCES youtube_content(id) ON DELETE CASCADE
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_content_key_points_locales" (
"point" varchar,
"id" serial PRIMARY KEY NOT NULL,
"_locale" varchar NOT NULL,
"_parent_id" integer NOT NULL REFERENCES youtube_content_key_points(id) ON DELETE CASCADE,
CONSTRAINT "youtube_content_key_points_locales_unique" UNIQUE("_locale", "_parent_id")
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_content_raw_footage" (
"id" serial PRIMARY KEY NOT NULL,
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL REFERENCES youtube_content(id) ON DELETE CASCADE,
"file_id" integer REFERENCES media(id) ON DELETE SET NULL,
"description" varchar
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "youtube_content_youtube_metadata_tags" (
"id" serial PRIMARY KEY NOT NULL,
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL REFERENCES youtube_content(id) ON DELETE CASCADE,
"tag" varchar
);
`)
await db.execute(sql`
CREATE INDEX IF NOT EXISTS "youtube_content_slug_idx" ON "youtube_content" USING btree ("slug");
CREATE INDEX IF NOT EXISTS "youtube_content_channel_idx" ON "youtube_content" USING btree ("channel_id");
CREATE INDEX IF NOT EXISTS "youtube_content_assigned_to_idx" ON "youtube_content" USING btree ("assigned_to_id");
CREATE INDEX IF NOT EXISTS "youtube_content_created_by_idx" ON "youtube_content" USING btree ("created_by_id");
CREATE INDEX IF NOT EXISTS "youtube_content_status_idx" ON "youtube_content" USING btree ("status");
CREATE INDEX IF NOT EXISTS "youtube_content_thumbnail_idx" ON "youtube_content" USING btree ("thumbnail_id");
CREATE INDEX IF NOT EXISTS "youtube_content_thumbnail_alt_idx" ON "youtube_content" USING btree ("thumbnail_alt_id");
CREATE INDEX IF NOT EXISTS "youtube_content_video_file_idx" ON "youtube_content" USING btree ("video_file_id");
CREATE INDEX IF NOT EXISTS "youtube_content_scheduled_publish_idx" ON "youtube_content" USING btree ("scheduled_publish_date");
CREATE INDEX IF NOT EXISTS "youtube_content_updated_at_idx" ON "youtube_content" USING btree ("updated_at");
CREATE INDEX IF NOT EXISTS "youtube_content_created_at_idx" ON "youtube_content" USING btree ("created_at");
CREATE INDEX IF NOT EXISTS "youtube_content_locales_locale_idx" ON "youtube_content_locales" USING btree ("_locale");
CREATE INDEX IF NOT EXISTS "youtube_content_locales_parent_idx" ON "youtube_content_locales" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "youtube_content_key_points_order_idx" ON "youtube_content_key_points" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "youtube_content_key_points_parent_idx" ON "youtube_content_key_points" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "youtube_content_raw_footage_order_idx" ON "youtube_content_raw_footage" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "youtube_content_raw_footage_parent_idx" ON "youtube_content_raw_footage" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "youtube_content_youtube_metadata_tags_order_idx" ON "youtube_content_youtube_metadata_tags" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "youtube_content_youtube_metadata_tags_parent_idx" ON "youtube_content_youtube_metadata_tags" USING btree ("_parent_id");
`)
// Create YT Tasks table
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "yt_tasks" (
"id" serial PRIMARY KEY NOT NULL,
"video_id" integer REFERENCES youtube_content(id) ON DELETE SET NULL,
"channel_id" integer REFERENCES youtube_channels(id) ON DELETE SET NULL,
"task_type" "enum_yt_tasks_task_type" NOT NULL,
"status" "enum_yt_tasks_status" DEFAULT 'todo' NOT NULL,
"priority" "enum_yt_tasks_priority" DEFAULT 'normal',
"assigned_to_id" integer NOT NULL REFERENCES users(id) ON DELETE SET NULL,
"due_date" timestamp(3) with time zone,
"completed_at" timestamp(3) with time zone,
"completed_by_id" integer REFERENCES users(id) ON DELETE SET NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "yt_tasks_locales" (
"title" varchar NOT NULL,
"description" varchar,
"blocked_reason" varchar,
"id" serial PRIMARY KEY NOT NULL,
"_locale" varchar NOT NULL,
"_parent_id" integer NOT NULL REFERENCES yt_tasks(id) ON DELETE CASCADE,
CONSTRAINT "yt_tasks_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id")
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "yt_tasks_attachments" (
"id" serial PRIMARY KEY NOT NULL,
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL REFERENCES yt_tasks(id) ON DELETE CASCADE,
"file_id" integer REFERENCES media(id) ON DELETE SET NULL,
"note" varchar
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "yt_tasks_comments" (
"id" serial PRIMARY KEY NOT NULL,
"_order" integer NOT NULL,
"_parent_id" integer NOT NULL REFERENCES yt_tasks(id) ON DELETE CASCADE,
"author_id" integer REFERENCES users(id) ON DELETE SET NULL,
"content" varchar,
"created_at" timestamp(3) with time zone
);
`)
await db.execute(sql`
CREATE INDEX IF NOT EXISTS "yt_tasks_video_idx" ON "yt_tasks" USING btree ("video_id");
CREATE INDEX IF NOT EXISTS "yt_tasks_channel_idx" ON "yt_tasks" USING btree ("channel_id");
CREATE INDEX IF NOT EXISTS "yt_tasks_assigned_to_idx" ON "yt_tasks" USING btree ("assigned_to_id");
CREATE INDEX IF NOT EXISTS "yt_tasks_status_idx" ON "yt_tasks" USING btree ("status");
CREATE INDEX IF NOT EXISTS "yt_tasks_due_date_idx" ON "yt_tasks" USING btree ("due_date");
CREATE INDEX IF NOT EXISTS "yt_tasks_completed_by_idx" ON "yt_tasks" USING btree ("completed_by_id");
CREATE INDEX IF NOT EXISTS "yt_tasks_updated_at_idx" ON "yt_tasks" USING btree ("updated_at");
CREATE INDEX IF NOT EXISTS "yt_tasks_created_at_idx" ON "yt_tasks" USING btree ("created_at");
CREATE INDEX IF NOT EXISTS "yt_tasks_locales_locale_idx" ON "yt_tasks_locales" USING btree ("_locale");
CREATE INDEX IF NOT EXISTS "yt_tasks_locales_parent_idx" ON "yt_tasks_locales" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "yt_tasks_attachments_order_idx" ON "yt_tasks_attachments" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "yt_tasks_attachments_parent_idx" ON "yt_tasks_attachments" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "yt_tasks_attachments_file_idx" ON "yt_tasks_attachments" USING btree ("file_id");
CREATE INDEX IF NOT EXISTS "yt_tasks_comments_order_idx" ON "yt_tasks_comments" USING btree ("_order");
CREATE INDEX IF NOT EXISTS "yt_tasks_comments_parent_idx" ON "yt_tasks_comments" USING btree ("_parent_id");
CREATE INDEX IF NOT EXISTS "yt_tasks_comments_author_idx" ON "yt_tasks_comments" USING btree ("author_id");
`)
// Create YT Notifications table
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "yt_notifications" (
"id" serial PRIMARY KEY NOT NULL,
"recipient_id" integer NOT NULL REFERENCES users(id) ON DELETE CASCADE,
"type" "enum_yt_notifications_type" NOT NULL,
"link" varchar,
"related_video_id" integer REFERENCES youtube_content(id) ON DELETE SET NULL,
"related_task_id" integer REFERENCES yt_tasks(id) ON DELETE SET NULL,
"read" boolean DEFAULT false,
"read_at" timestamp(3) with time zone,
"email_sent" boolean DEFAULT false,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
`)
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "yt_notifications_locales" (
"title" varchar NOT NULL,
"message" varchar,
"id" serial PRIMARY KEY NOT NULL,
"_locale" varchar NOT NULL,
"_parent_id" integer NOT NULL REFERENCES yt_notifications(id) ON DELETE CASCADE,
CONSTRAINT "yt_notifications_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id")
);
`)
await db.execute(sql`
CREATE INDEX IF NOT EXISTS "yt_notifications_recipient_idx" ON "yt_notifications" USING btree ("recipient_id");
CREATE INDEX IF NOT EXISTS "yt_notifications_type_idx" ON "yt_notifications" USING btree ("type");
CREATE INDEX IF NOT EXISTS "yt_notifications_related_video_idx" ON "yt_notifications" USING btree ("related_video_id");
CREATE INDEX IF NOT EXISTS "yt_notifications_related_task_idx" ON "yt_notifications" USING btree ("related_task_id");
CREATE INDEX IF NOT EXISTS "yt_notifications_read_idx" ON "yt_notifications" USING btree ("read");
CREATE INDEX IF NOT EXISTS "yt_notifications_updated_at_idx" ON "yt_notifications" USING btree ("updated_at");
CREATE INDEX IF NOT EXISTS "yt_notifications_created_at_idx" ON "yt_notifications" USING btree ("created_at");
CREATE INDEX IF NOT EXISTS "yt_notifications_locales_locale_idx" ON "yt_notifications_locales" USING btree ("_locale");
CREATE INDEX IF NOT EXISTS "yt_notifications_locales_parent_idx" ON "yt_notifications_locales" USING btree ("_parent_id");
`)
// Create users_rels table for youtubeChannels relationship
await db.execute(sql`
CREATE TABLE IF NOT EXISTS "users_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL REFERENCES users(id) ON DELETE CASCADE,
"path" varchar NOT NULL,
"youtube_channels_id" integer REFERENCES youtube_channels(id) ON DELETE CASCADE
);
`)
await db.execute(sql`
CREATE INDEX IF NOT EXISTS "users_rels_order_idx" ON "users_rels" USING btree ("order");
CREATE INDEX IF NOT EXISTS "users_rels_parent_idx" ON "users_rels" USING btree ("parent_id");
CREATE INDEX IF NOT EXISTS "users_rels_path_idx" ON "users_rels" USING btree ("path");
CREATE INDEX IF NOT EXISTS "users_rels_youtube_channels_idx" ON "users_rels" USING btree ("youtube_channels_id");
`)
// Add columns to payload_locked_documents_rels
await db.execute(sql`
ALTER TABLE "payload_locked_documents_rels"
ADD COLUMN IF NOT EXISTS "youtube_channels_id" integer REFERENCES youtube_channels(id) ON DELETE CASCADE;
`)
await db.execute(sql`
ALTER TABLE "payload_locked_documents_rels"
ADD COLUMN IF NOT EXISTS "youtube_content_id" integer REFERENCES youtube_content(id) ON DELETE CASCADE;
`)
await db.execute(sql`
ALTER TABLE "payload_locked_documents_rels"
ADD COLUMN IF NOT EXISTS "yt_tasks_id" integer REFERENCES yt_tasks(id) ON DELETE CASCADE;
`)
await db.execute(sql`
ALTER TABLE "payload_locked_documents_rels"
ADD COLUMN IF NOT EXISTS "yt_notifications_id" integer REFERENCES yt_notifications(id) ON DELETE CASCADE;
`)
await db.execute(sql`
CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_youtube_channels_idx"
ON "payload_locked_documents_rels" USING btree ("youtube_channels_id");
`)
await db.execute(sql`
CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_youtube_content_idx"
ON "payload_locked_documents_rels" USING btree ("youtube_content_id");
`)
await db.execute(sql`
CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_yt_tasks_idx"
ON "payload_locked_documents_rels" USING btree ("yt_tasks_id");
`)
await db.execute(sql`
CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_yt_notifications_idx"
ON "payload_locked_documents_rels" USING btree ("yt_notifications_id");
`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
// Remove indexes from system tables first
await db.execute(sql`DROP INDEX IF EXISTS "payload_locked_documents_rels_youtube_channels_idx";`)
await db.execute(sql`DROP INDEX IF EXISTS "payload_locked_documents_rels_youtube_content_idx";`)
await db.execute(sql`DROP INDEX IF EXISTS "payload_locked_documents_rels_yt_tasks_idx";`)
await db.execute(sql`DROP INDEX IF EXISTS "payload_locked_documents_rels_yt_notifications_idx";`)
// Remove columns from system tables
await db.execute(sql`ALTER TABLE "payload_locked_documents_rels" DROP COLUMN IF EXISTS "youtube_channels_id";`)
await db.execute(sql`ALTER TABLE "payload_locked_documents_rels" DROP COLUMN IF EXISTS "youtube_content_id";`)
await db.execute(sql`ALTER TABLE "payload_locked_documents_rels" DROP COLUMN IF EXISTS "yt_tasks_id";`)
await db.execute(sql`ALTER TABLE "payload_locked_documents_rels" DROP COLUMN IF EXISTS "yt_notifications_id";`)
// Drop users_rels table (created for youtubeChannels relationship)
await db.execute(sql`DROP TABLE IF EXISTS "users_rels";`)
// Drop YT Notifications tables
await db.execute(sql`DROP TABLE IF EXISTS "yt_notifications_locales";`)
await db.execute(sql`DROP TABLE IF EXISTS "yt_notifications";`)
// Drop YT Tasks tables
await db.execute(sql`DROP TABLE IF EXISTS "yt_tasks_comments";`)
await db.execute(sql`DROP TABLE IF EXISTS "yt_tasks_attachments";`)
await db.execute(sql`DROP TABLE IF EXISTS "yt_tasks_locales";`)
await db.execute(sql`DROP TABLE IF EXISTS "yt_tasks";`)
// Drop YouTube Content tables
await db.execute(sql`DROP TABLE IF EXISTS "youtube_content_youtube_metadata_tags";`)
await db.execute(sql`DROP TABLE IF EXISTS "youtube_content_raw_footage";`)
await db.execute(sql`DROP TABLE IF EXISTS "youtube_content_key_points_locales";`)
await db.execute(sql`DROP TABLE IF EXISTS "youtube_content_key_points";`)
await db.execute(sql`DROP TABLE IF EXISTS "youtube_content_locales";`)
await db.execute(sql`DROP TABLE IF EXISTS "youtube_content";`)
// Drop YouTube Channels tables
await db.execute(sql`DROP TABLE IF EXISTS "youtube_channels_publishing_schedule_default_days";`)
await db.execute(sql`DROP TABLE IF EXISTS "youtube_channels_content_series_locales";`)
await db.execute(sql`DROP TABLE IF EXISTS "youtube_channels_content_series";`)
await db.execute(sql`DROP TABLE IF EXISTS "youtube_channels_locales";`)
await db.execute(sql`DROP TABLE IF EXISTS "youtube_channels";`)
// Remove users column
await db.execute(sql`ALTER TABLE "users" DROP COLUMN IF EXISTS "youtube_role";`)
// Drop enums
await db.execute(sql`DROP TYPE IF EXISTS "enum_yt_notifications_type";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_yt_tasks_priority";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_yt_tasks_status";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_yt_tasks_task_type";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_youtube_content_visibility";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_youtube_content_priority";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_youtube_content_status";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_youtube_content_format";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_youtube_channels_status";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_youtube_channels_category";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_youtube_channels_language";`)
await db.execute(sql`DROP TYPE IF EXISTS "enum_users_youtube_role";`)
}