From 87b27a1ec036e8ec59bf552c18f1448be75d5d4d Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Nov 2025 02:22:12 +0000 Subject: [PATCH 1/3] fix: Improve database migration error logging for Railway deployment Enhanced the migration script with better error visibility for production deployments: - Added diagnostic logging to verify migrations folder and files exist - Added console.error() fallback to ensure errors are visible in Railway logs - Added detailed error reporting including code, message, stack, and cause - Added success confirmation logs for better deployment monitoring This will help diagnose the silent migration failures happening on Railway by providing comprehensive error details in the deployment logs. --- apps/core/server/db/migrate.ts | 61 +++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/apps/core/server/db/migrate.ts b/apps/core/server/db/migrate.ts index c7cc7d8..86c10ca 100644 --- a/apps/core/server/db/migrate.ts +++ b/apps/core/server/db/migrate.ts @@ -55,11 +55,41 @@ const db = drizzle(migrationClient); // Run migrations async function main() { + // Verify migrations folder exists and has files + try { + const fs = await import("node:fs/promises"); + const migrationFiles = await fs.readdir(migrationsFolder); + const sqlFiles = migrationFiles.filter((f) => f.endsWith(".sql")); + + logger.info( + { + migrationsFolder, + totalFiles: migrationFiles.length, + sqlFiles: sqlFiles.length, + }, + "[Migrations] Found migration files", + ); + + if (sqlFiles.length === 0) { + const errorMsg = `No SQL migration files found in ${migrationsFolder}`; + console.error(`[Migrations] ERROR: ${errorMsg}`); + logger.error({}, errorMsg); + process.exit(1); + } + } catch (error: any) { + const errorMsg = `Failed to read migrations folder: ${error.message}`; + console.error(`[Migrations] ERROR: ${errorMsg}`); + console.error(`[Migrations] Stack: ${error.stack}`); + logger.error({ err: error }, errorMsg); + process.exit(1); + } + logger.info({}, "[Migrations] Running migrations..."); try { await migrate(db, { migrationsFolder }); logger.info({}, "[Migrations] ✓ Migrations completed successfully"); + console.log("[Migrations] ✓ Migrations completed successfully"); // Ensure visibility in Railway } catch (error: any) { // Check if it's a "relation already exists" error (PostgreSQL code 42P07) // Drizzle wraps PostgreSQL errors, so check the cause as well @@ -72,16 +102,29 @@ async function main() { if (isAlreadyExistsError) { logger.warn({}, "[Migrations] ⚠️ Some tables already exist - skipping"); logger.info({}, "[Migrations] ✓ Database schema is up to date"); - } else { - logger.error( - { - err: error, - code: errorCode, - message: errorMessage, - migrationsFolder, - }, - "[Migrations] ✗ Migration failed", + console.log( + "[Migrations] ✓ Database schema is up to date (some tables already exist)", ); + } else { + // Log error details to both logger and console for maximum visibility + const errorDetails = { + code: errorCode, + message: errorMessage, + stack: error?.stack, + cause: error?.cause, + migrationsFolder, + }; + + console.error("[Migrations] ✗ MIGRATION FAILED - ERROR DETAILS:"); + console.error("Error Code:", errorCode); + console.error("Error Message:", errorMessage); + console.error("Full Error:", JSON.stringify(error, null, 2)); + console.error("Error Stack:", error?.stack); + if (error?.cause) { + console.error("Error Cause:", JSON.stringify(error.cause, null, 2)); + } + + logger.error(errorDetails, "[Migrations] ✗ Migration failed"); process.exit(1); } } From 5ad07264a3f6763f7f9d4a02dbe49eb59c2d1d52 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Nov 2025 02:28:12 +0000 Subject: [PATCH 2/3] fix: Add comprehensive database migration diagnostics for Railway This addresses silent migration failures by adding detailed diagnostic steps: **Pre-Migration Checks:** - Verify migrations folder exists and contains SQL files - Test database connection before attempting migrations - Check for previously applied migrations to understand database state - Display all diagnostic info via console.log for Railway visibility **Enhanced Error Reporting:** - Database connection failures now show full error details - Migration errors include code, message, stack, and cause - All errors logged to both console and structured logger - Sanitized DATABASE_URL shown in logs (password redacted) **New Diagnostic Output:** - Migrations folder path verification - SQL file count - Database connection test result - Previous migration count (if any) - Detailed error context for all failure scenarios This will help identify the root cause of silent migration failures in Railway by providing comprehensive visibility into every step of the migration process. --- apps/core/server/db/migrate.ts | 85 ++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/apps/core/server/db/migrate.ts b/apps/core/server/db/migrate.ts index 86c10ca..87e00f3 100644 --- a/apps/core/server/db/migrate.ts +++ b/apps/core/server/db/migrate.ts @@ -30,18 +30,24 @@ if (!process.env.DATABASE_URL) { } // Log migration info for debugging +const sanitizedDbUrl = (() => { + try { + const url = new URL(process.env.DATABASE_URL!); + url.password = "****"; + return url.toString(); + } catch { + return process.env.DATABASE_URL!.replace(/:[^:@]+@/, ":****@"); + } +})(); // Hide password + +console.log("[Migrations] Starting migration process"); +console.log("[Migrations] Migrations folder:", migrationsFolder); +console.log("[Migrations] Database URL:", sanitizedDbUrl); + logger.info( { migrationsFolder, - databaseUrl: (() => { - try { - const url = new URL(process.env.DATABASE_URL!); - url.password = "****"; - return url.toString(); - } catch { - return process.env.DATABASE_URL!.replace(/:[^:@]+@/, ":****@"); - } - })(), // Hide password + databaseUrl: sanitizedDbUrl, }, "[Migrations] Starting migration process", ); @@ -84,6 +90,67 @@ async function main() { process.exit(1); } + // Test database connection before running migrations + console.log("[Migrations] Testing database connection..."); + try { + const result = await migrationClient`SELECT 1 as test`; + console.log("[Migrations] ✓ Database connection successful"); + logger.info({}, "[Migrations] Database connection test passed"); + } catch (error: any) { + const errorMsg = `Database connection failed: ${error.message}`; + console.error("[Migrations] ✗ DATABASE CONNECTION FAILED"); + console.error("Error Code:", error?.code); + console.error("Error Message:", error?.message); + console.error("Database URL:", sanitizedDbUrl); + console.error("Full Error:", JSON.stringify(error, null, 2)); + + logger.error( + { + err: error, + code: error?.code, + databaseUrl: sanitizedDbUrl, + }, + errorMsg, + ); + await migrationClient.end(); + process.exit(1); + } + + // Check for existing migrations (helps diagnose state) + try { + const existingMigrations = await migrationClient` + SELECT table_name + FROM information_schema.tables + WHERE table_schema = 'drizzle' + AND table_name = '__drizzle_migrations' + `; + + if (existingMigrations.length > 0) { + const appliedMigrations = await migrationClient` + SELECT id, hash, created_at + FROM drizzle.__drizzle_migrations + ORDER BY created_at DESC + LIMIT 5 + `; + console.log( + `[Migrations] Found ${appliedMigrations.length} previously applied migrations`, + ); + logger.info( + { count: appliedMigrations.length }, + "[Migrations] Previous migrations detected", + ); + } else { + console.log("[Migrations] No previous migrations found (fresh database)"); + logger.info({}, "[Migrations] Fresh database detected"); + } + } catch (error: any) { + // Ignore errors here - schema might not exist yet + console.log( + "[Migrations] Could not check migration history (expected on first run)", + ); + } + + console.log("[Migrations] Running migrations..."); logger.info({}, "[Migrations] Running migrations..."); try { From 4a133dfa2881984e20d1f7bcfca440557a18a628 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Nov 2025 03:00:47 +0000 Subject: [PATCH 3/3] fix: Make migration 0031 idempotent to prevent rerun failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Root Cause Found Migration 0031_stormy_black_panther.sql was failing on Railway because it's NOT IDEMPOTENT. The migration has 111 DROP statements without IF EXISTS: - 27 DROP INDEX statements - 84 DROP COLUMN statements If this migration runs twice (e.g., if the drizzle migrations table wasn't properly tracking state), PostgreSQL throws errors: - Error 42704: "index does not exist" - Error 42703: "column does not exist" This causes the migration to fail and prevents the app from starting. ## The Fix Changed all DROP statements to be idempotent: - `DROP INDEX "name"` → `DROP INDEX IF EXISTS "name"` - `DROP COLUMN "name"` → `DROP COLUMN IF EXISTS "name"` Now the migration can safely run multiple times without errors. ## Why This Happened 1. Commit 26bd43a created migration 0031 (massive schema simplification) 2. Migration was applied to Railway database 3. Something caused migrations to try to rerun (possibly db state issue) 4. Non-idempotent DROP statements failed on already-dropped columns 5. App failed to start due to migration errors ## Impact Migration 0031 will now succeed even if: - Columns/indexes have already been dropped - Migration runs multiple times - Database is in an intermediate state This is a critical fix for production stability. --- .../migrations/0031_stormy_black_panther.sql | 222 +++++++++--------- 1 file changed, 111 insertions(+), 111 deletions(-) diff --git a/apps/core/server/db/migrations/0031_stormy_black_panther.sql b/apps/core/server/db/migrations/0031_stormy_black_panther.sql index 163bd67..1de2c25 100644 --- a/apps/core/server/db/migrations/0031_stormy_black_panther.sql +++ b/apps/core/server/db/migrations/0031_stormy_black_panther.sql @@ -1,116 +1,116 @@ -DROP INDEX "idx_dialogues_deleted_at";--> statement-breakpoint -DROP INDEX "idx_dialogues_is_public";--> statement-breakpoint -DROP INDEX "idx_dialogues_view_count";--> statement-breakpoint -DROP INDEX "idx_dialogues_created_by_date";--> statement-breakpoint -DROP INDEX "idx_locations_deleted_at";--> statement-breakpoint -DROP INDEX "idx_locations_is_public";--> statement-breakpoint -DROP INDEX "idx_locations_view_count";--> statement-breakpoint -DROP INDEX "idx_lores_deleted_at";--> statement-breakpoint -DROP INDEX "idx_lores_is_public";--> statement-breakpoint -DROP INDEX "idx_lores_view_count";--> statement-breakpoint -DROP INDEX "idx_lores_created_by_date";--> statement-breakpoint -DROP INDEX "idx_music_deleted_at";--> statement-breakpoint -DROP INDEX "idx_music_is_public";--> statement-breakpoint -DROP INDEX "idx_music_view_count";--> statement-breakpoint -DROP INDEX "idx_music_mood_public";--> statement-breakpoint -DROP INDEX "idx_npcs_deleted_at";--> statement-breakpoint -DROP INDEX "idx_npcs_is_public";--> statement-breakpoint -DROP INDEX "idx_npcs_view_count";--> statement-breakpoint -DROP INDEX "idx_npcs_created_by_date";--> statement-breakpoint -DROP INDEX "idx_quests_deleted_at";--> statement-breakpoint -DROP INDEX "idx_quests_is_public";--> statement-breakpoint -DROP INDEX "idx_quests_view_count";--> statement-breakpoint -DROP INDEX "idx_quests_created_by_date";--> statement-breakpoint -DROP INDEX "idx_worlds_deleted_at";--> statement-breakpoint -DROP INDEX "idx_worlds_is_public";--> statement-breakpoint -DROP INDEX "idx_worlds_view_count";--> statement-breakpoint -DROP INDEX "idx_worlds_created_by_date";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_dialogues_deleted_at";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_dialogues_is_public";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_dialogues_view_count";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_dialogues_created_by_date";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_locations_deleted_at";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_locations_is_public";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_locations_view_count";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_lores_deleted_at";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_lores_is_public";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_lores_view_count";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_lores_created_by_date";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_music_deleted_at";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_music_is_public";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_music_view_count";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_music_mood_public";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_npcs_deleted_at";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_npcs_is_public";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_npcs_view_count";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_npcs_created_by_date";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_quests_deleted_at";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_quests_is_public";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_quests_view_count";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_quests_created_by_date";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_worlds_deleted_at";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_worlds_is_public";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_worlds_view_count";--> statement-breakpoint +DROP INDEX IF EXISTS "idx_worlds_created_by_date";--> statement-breakpoint ALTER TABLE "projects" ALTER COLUMN "owner_id" DROP NOT NULL;--> statement-breakpoint ALTER TABLE "projects" ALTER COLUMN "is_public" SET DEFAULT true;--> statement-breakpoint ALTER TABLE "users" ALTER COLUMN "privy_user_id" DROP NOT NULL;--> statement-breakpoint ALTER TABLE "users" ALTER COLUMN "role" SET DEFAULT 'admin';--> statement-breakpoint ALTER TABLE "assets" ALTER COLUMN "visibility" SET DEFAULT 'public';--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "deleted_at";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "deleted_by";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "view_count";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "favorite_count";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "last_viewed_at";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "is_public";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "is_featured";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "version";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "parent_id";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "is_template";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "quality_score";--> statement-breakpoint -ALTER TABLE "dialogues" DROP COLUMN "is_verified";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "deleted_at";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "deleted_by";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "view_count";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "favorite_count";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "last_viewed_at";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "is_public";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "is_featured";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "version";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "parent_id";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "is_template";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "quality_score";--> statement-breakpoint -ALTER TABLE "locations" DROP COLUMN "is_verified";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "deleted_at";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "deleted_by";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "view_count";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "favorite_count";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "last_viewed_at";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "is_public";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "is_featured";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "version";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "parent_id";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "is_template";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "quality_score";--> statement-breakpoint -ALTER TABLE "lores" DROP COLUMN "is_verified";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "deleted_at";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "deleted_by";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "view_count";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "favorite_count";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "last_viewed_at";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "is_public";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "is_featured";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "version";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "parent_id";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "is_template";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "quality_score";--> statement-breakpoint -ALTER TABLE "music_tracks" DROP COLUMN "is_verified";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "deleted_at";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "deleted_by";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "view_count";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "favorite_count";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "last_viewed_at";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "is_public";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "is_featured";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "version";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "parent_id";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "is_template";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "quality_score";--> statement-breakpoint -ALTER TABLE "npcs" DROP COLUMN "is_verified";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "deleted_at";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "deleted_by";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "view_count";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "favorite_count";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "last_viewed_at";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "is_public";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "is_featured";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "version";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "parent_id";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "is_template";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "quality_score";--> statement-breakpoint -ALTER TABLE "quests" DROP COLUMN "is_verified";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "deleted_at";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "deleted_by";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "view_count";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "favorite_count";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "last_viewed_at";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "is_public";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "is_featured";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "version";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "parent_id";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "is_template";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "quality_score";--> statement-breakpoint -ALTER TABLE "worlds" DROP COLUMN "is_verified"; \ No newline at end of file +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "deleted_at";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "deleted_by";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "view_count";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "favorite_count";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "last_viewed_at";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "is_public";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "is_featured";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "version";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "parent_id";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "is_template";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "quality_score";--> statement-breakpoint +ALTER TABLE "dialogues" DROP COLUMN IF EXISTS "is_verified";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "deleted_at";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "deleted_by";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "view_count";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "favorite_count";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "last_viewed_at";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "is_public";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "is_featured";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "version";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "parent_id";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "is_template";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "quality_score";--> statement-breakpoint +ALTER TABLE "locations" DROP COLUMN IF EXISTS "is_verified";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "deleted_at";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "deleted_by";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "view_count";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "favorite_count";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "last_viewed_at";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "is_public";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "is_featured";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "version";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "parent_id";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "is_template";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "quality_score";--> statement-breakpoint +ALTER TABLE "lores" DROP COLUMN IF EXISTS "is_verified";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "deleted_at";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "deleted_by";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "view_count";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "favorite_count";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "last_viewed_at";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "is_public";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "is_featured";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "version";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "parent_id";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "is_template";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "quality_score";--> statement-breakpoint +ALTER TABLE "music_tracks" DROP COLUMN IF EXISTS "is_verified";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "deleted_at";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "deleted_by";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "view_count";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "favorite_count";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "last_viewed_at";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "is_public";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "is_featured";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "version";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "parent_id";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "is_template";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "quality_score";--> statement-breakpoint +ALTER TABLE "npcs" DROP COLUMN IF EXISTS "is_verified";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "deleted_at";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "deleted_by";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "view_count";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "favorite_count";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "last_viewed_at";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "is_public";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "is_featured";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "version";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "parent_id";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "is_template";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "quality_score";--> statement-breakpoint +ALTER TABLE "quests" DROP COLUMN IF EXISTS "is_verified";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "deleted_at";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "deleted_by";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "view_count";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "favorite_count";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "last_viewed_at";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "is_public";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "is_featured";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "version";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "parent_id";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "is_template";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "quality_score";--> statement-breakpoint +ALTER TABLE "worlds" DROP COLUMN IF EXISTS "is_verified"; \ No newline at end of file