// 2025-08-12-migrate-from-futureporn.ts // // we are migrating from v1 futureporn.net (a strapi site) to v2 future.porn (a fastify site with prisma) // // // the idea is to read a local backup of the strapi site from a .sql // and use the data contained within to populate the database on the v2 site using prisma. // // here's the schema from the v1 site ```sql CREATE TABLE "public"."b2_files" ( "id" SERIAL, "url" VARCHAR(255) NULL, "key" VARCHAR(255) NULL, "upload_id" VARCHAR(255) NULL, "created_at" TIMESTAMP NULL, "updated_at" TIMESTAMP NULL, "created_by_id" INTEGER NULL, "updated_by_id" INTEGER NULL, "cdn_url" VARCHAR(255) NULL, CONSTRAINT "b2_files_pkey" PRIMARY KEY ("id") ); CREATE TABLE "public"."mux_assets" ( "id" SERIAL, "playback_id" VARCHAR(255) NULL, "asset_id" VARCHAR(255) NULL, "created_at" TIMESTAMP NULL, "updated_at" TIMESTAMP NULL, "created_by_id" INTEGER NULL, "updated_by_id" INTEGER NULL, "deletion_queued_at" TIMESTAMP NULL, CONSTRAINT "mux_assets_pkey" PRIMARY KEY ("id") ); CREATE TABLE "public"."vods" ( "id" SERIAL, "video_src_hash" VARCHAR(255) NULL, "video_720_hash" VARCHAR(255) NULL, "video_480_hash" VARCHAR(255) NULL, "video_360_hash" VARCHAR(255) NULL, "video_240_hash" VARCHAR(255) NULL, "thin_hash" VARCHAR(255) NULL, "thicc_hash" VARCHAR(255) NULL, "announce_title" VARCHAR(255) NULL, "announce_url" VARCHAR(255) NULL, "note" TEXT NULL, "date" TIMESTAMP NULL, "spoilers" TEXT NULL, "created_at" TIMESTAMP NULL, "updated_at" TIMESTAMP NULL, "published_at" TIMESTAMP NULL, "created_by_id" INTEGER NULL, "updated_by_id" INTEGER NULL, "title" VARCHAR(255) NULL, "chat_log" TEXT NULL, "date_2" VARCHAR(255) NULL, "cuid" VARCHAR(255) NULL, "archive_status" VARCHAR(255) NULL, CONSTRAINT "vods_pkey" PRIMARY KEY ("id") ); CREATE TABLE "public"."vods_mux_asset_links" ( "id" SERIAL, "vod_id" INTEGER NULL, "mux_asset_id" INTEGER NULL, CONSTRAINT "vods_mux_asset_links_pkey" PRIMARY KEY ("id"), CONSTRAINT "vods_mux_asset_links_unique" UNIQUE ("vod_id", "mux_asset_id") ); CREATE TABLE "public"."vods_thumbnail_links" ( "id" SERIAL, "vod_id" INTEGER NULL, "b_2_file_id" INTEGER NULL, CONSTRAINT "vods_thumbnail_links_pkey" PRIMARY KEY ("id"), CONSTRAINT "vods_thumbnail_links_unique" UNIQUE ("vod_id", "b_2_file_id") ); CREATE TABLE "public"."vods_video_src_b_2_links" ( "id" SERIAL, "vod_id" INTEGER NULL, "b_2_file_id" INTEGER NULL, CONSTRAINT "vods_video_src_b_2_links_pkey" PRIMARY KEY ("id"), CONSTRAINT "vods_video_src_b_2_links_unique" UNIQUE ("vod_id", "b_2_file_id") ); CREATE TABLE "public"."vods_vtuber_links" ( "id" SERIAL, "vod_id" INTEGER NULL, "vtuber_id" INTEGER NULL, "vod_order" DOUBLE PRECISION NULL, CONSTRAINT "vods_vtuber_links_pkey" PRIMARY KEY ("id"), CONSTRAINT "vods_vtuber_links_unique" UNIQUE ("vod_id", "vtuber_id") ); CREATE TABLE "public"."vtubers" ( "id" SERIAL, "chaturbate" VARCHAR(255) NULL, "twitter" VARCHAR(255) NULL, "patreon" VARCHAR(255) NULL, "twitch" VARCHAR(255) NULL, "tiktok" VARCHAR(255) NULL, "onlyfans" VARCHAR(255) NULL, "youtube" VARCHAR(255) NULL, "linktree" VARCHAR(255) NULL, "carrd" VARCHAR(255) NULL, "fansly" VARCHAR(255) NULL, "pornhub" VARCHAR(255) NULL, "discord" VARCHAR(255) NULL, "reddit" VARCHAR(255) NULL, "throne" VARCHAR(255) NULL, "instagram" VARCHAR(255) NULL, "facebook" VARCHAR(255) NULL, "merch" VARCHAR(255) NULL, "slug" VARCHAR(255) NULL, "image" VARCHAR(255) NULL, "display_name" VARCHAR(255) NULL, "description_1" TEXT NULL, "description_2" TEXT NULL, "created_at" TIMESTAMP NULL, "updated_at" TIMESTAMP NULL, "published_at" TIMESTAMP NULL, "created_by_id" INTEGER NULL, "updated_by_id" INTEGER NULL, "theme_color" VARCHAR(255) NULL, "image_blur" VARCHAR(255) NULL, CONSTRAINT "vtubers_pkey" PRIMARY KEY ("id") ); ``` // here's the schema from the v2 site ```prisma generator client { provider = "prisma-client-js" output = "../generated/prisma" binaryTargets = ["native", "debian-openssl-3.0.x"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(uuid()) patreonId String @unique patreonFullName String? imageUrl String? roles Role[] vods Vod[] Vtuber Vtuber[] } enum RoleName { user supporterTier1 supporterTier2 supporterTier3 supporterTier4 supporterTier5 supporterTier6 moderator admin } model Role { id String @id @default(cuid(2)) name String @unique users User[] } model RateLimiterFlexible { key String @id points Int expire DateTime? } model Stream { id String @id @default(cuid(2)) date DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt announcementUrl String? vods Vod[] @@map("stream_entity") } enum VodStatus { ordering pending approved rejected processing processed } model Vod { id String @id @default(cuid(2)) streamId String? stream Stream? @relation(fields: [streamId], references: [id]) uploaderId String // previously in Upload uploader User @relation(fields: [uploaderId], references: [id]) streamDate DateTime notes String? segmentKeys Json? sourceVideo String? hlsPlaylist String? thumbnail String? asrVttKey String? slvttSheetKeys Json? slvttVTTKey String? magnetLink String? status VodStatus @default(pending) sha256sum String? cidv1 String? funscript String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt vtubers Vtuber[] } model Vtuber { id String @id @default(cuid(2)) image String? slug String? displayName String? chaturbate String? twitter String? patreon String? twitch String? tiktok String? onlyfans String? youtube String? linktree String? carrd String? fansly String? pornhub String? discord String? reddit String? throne String? instagram String? facebook String? merch String? description String? themeColor String? fanslyId String? chaturbateId String? twitterId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt vods Vod[] uploaderId String uploader User @relation(fields: [uploaderId], references: [id]) } ```