mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-18 02:44:12 +00:00
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>
580 lines
27 KiB
TypeScript
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";`)
|
|
}
|