diff --git a/src/lib/server/services/central-database-migration-service.ts b/src/lib/server/services/central-database-migration-service.ts index 4c53802..79ed131 100644 --- a/src/lib/server/services/central-database-migration-service.ts +++ b/src/lib/server/services/central-database-migration-service.ts @@ -1,7 +1,6 @@ import { drizzle } from "drizzle-orm/postgres-js"; import { migrate } from "drizzle-orm/postgres-js/migrator"; import postgres from "postgres"; -import { existsSync, readFileSync } from "fs"; import { join } from "path"; import { UniversalLogger } from "$lib/logger"; import { ValidationError } from "$lib/server/utils/errors"; @@ -104,93 +103,33 @@ export class CentralDatabaseMigrationService { } /** - * Get current schema version for the central database + * Check if migrations table exists in central database */ - private static async getCentralSchemaVersion( - config: CentralDatabaseConfig, - ): Promise { + private static async migrationTableExists(config: CentralDatabaseConfig): Promise { const connectionString = `postgres://${config.username}:${config.password}@${config.host}:${config.port}/${config.database}`; const client = postgres(connectionString); try { - // Check if migrations table exists const tableExists = await client` SELECT EXISTS ( - SELECT FROM information_schema.tables - WHERE table_schema = 'public' + SELECT FROM information_schema.tables + WHERE table_schema = 'drizzle' AND table_name = '__drizzle_migrations' ); `; - if (!tableExists[0].exists) { - return null; - } - - // Get the latest migration - const result = await client` - SELECT hash FROM __drizzle_migrations - ORDER BY created_at DESC - LIMIT 1 - `; - - return result.length > 0 ? result[0].hash : null; + return tableExists[0].exists; } catch (error) { - logger.error("Failed to get central schema version", { + logger.error("Failed to check migrations table existence", { database: config.database, error: String(error), }); - return null; + return false; } finally { await client.end(); } } - /** - * Get the latest available migration hash for central database - */ - private static getLatestCentralMigrationHash(): string | null { - try { - const migrationsPath = join(process.cwd(), "migrations"); - const metaPath = join(migrationsPath, "meta", "_journal.json"); - - if (!existsSync(metaPath)) { - return null; - } - - const journal = JSON.parse(readFileSync(metaPath, "utf-8")); - const entries = journal.entries || []; - - if (entries.length === 0) { - return null; - } - - // Get the latest entry - const latestEntry = entries[entries.length - 1]; - return latestEntry.hash || null; - } catch (error) { - logger.error("Failed to get latest central migration hash", { error: String(error) }); - return null; - } - } - - /** - * Check if central database needs migration - */ - private static async centralNeedsMigration(config: CentralDatabaseConfig): Promise { - const currentVersion = await this.getCentralSchemaVersion(config); - const latestVersion = this.getLatestCentralMigrationHash(); - - if (!latestVersion) { - return false; // No migrations available - } - - if (!currentVersion) { - return true; // Database has no schema yet - } - - return currentVersion !== latestVersion; - } - /** * Apply pending migrations to the central database */ @@ -280,13 +219,15 @@ export class CentralDatabaseMigrationService { database: config.database, }); - // Check if migration is needed - const needsMigration = await this.centralNeedsMigration(config); - if (needsMigration) { - logger.info("Central database needs migration, applying...", { database: config.database }); + // Check if migrations table exists to determine if we need to run migrations + const hasSchema = await this.migrationTableExists(config); + if (!hasSchema) { + logger.info("No migrations table found, applying migrations...", { database: config.database }); await this.migrateCentralDatabase(config); } else { - logger.info("Central database is up to date", { database: config.database }); + logger.info("Checking for pending migrations...", { database: config.database }); + // Always run migrate - it's idempotent and will apply only pending migrations + await this.migrateCentralDatabase(config); } } } diff --git a/src/lib/server/services/tenant-migration-service.ts b/src/lib/server/services/tenant-migration-service.ts index c58aae4..7b8c03b 100644 --- a/src/lib/server/services/tenant-migration-service.ts +++ b/src/lib/server/services/tenant-migration-service.ts @@ -1,7 +1,6 @@ import { drizzle } from "drizzle-orm/postgres-js"; import { migrate } from "drizzle-orm/postgres-js/migrator"; import postgres from "postgres"; -import { existsSync, readFileSync } from "fs"; import { join } from "path"; import { UniversalLogger } from "$lib/logger"; import { ValidationError } from "$lib/server/utils/errors"; @@ -101,91 +100,33 @@ export class TenantMigrationService { } /** - * Get current schema version for a tenant database + * Check if migrations table exists in tenant database */ - static async getTenantSchemaVersion(config: TenantDatabaseConfig): Promise { + static async migrationTableExists(config: TenantDatabaseConfig): Promise { const connectionString = `postgres://${config.username}:${config.password}@${config.host}:${config.port}/${config.database}`; const client = postgres(connectionString); try { - // Check if migrations table exists const tableExists = await client` SELECT EXISTS ( - SELECT FROM information_schema.tables - WHERE table_schema = 'public' + SELECT FROM information_schema.tables + WHERE table_schema = 'public' AND table_name = '__drizzle_migrations' ); `; - if (!tableExists[0].exists) { - return null; - } - - // Get the latest migration - const result = await client` - SELECT hash FROM __drizzle_migrations - ORDER BY created_at DESC - LIMIT 1 - `; - - return result.length > 0 ? result[0].hash : null; + return tableExists[0].exists; } catch (error) { - logger.error("Failed to get tenant schema version", { + logger.error("Failed to check migrations table existence", { database: config.database, error: String(error), }); - return null; + return false; } finally { await client.end(); } } - /** - * Get the latest available migration hash - */ - static getLatestMigrationHash(): string | null { - try { - const migrationsPath = join(process.cwd(), "tenant-migrations"); - const metaPath = join(migrationsPath, "meta", "_journal.json"); - - if (!existsSync(metaPath)) { - return null; - } - - const journal = JSON.parse(readFileSync(metaPath, "utf-8")); - const entries = journal.entries || []; - - if (entries.length === 0) { - return null; - } - - // Get the latest entry - const latestEntry = entries[entries.length - 1]; - return latestEntry.hash || null; - } catch (error) { - logger.error("Failed to get latest migration hash", { error: String(error) }); - return null; - } - } - - /** - * Check if tenant database needs migration - */ - static async tenantNeedsMigration(config: TenantDatabaseConfig): Promise { - const currentVersion = await this.getTenantSchemaVersion(config); - const latestVersion = this.getLatestMigrationHash(); - - if (!latestVersion) { - return false; // No migrations available - } - - if (!currentVersion) { - return true; // Database has no schema yet - } - - return currentVersion !== latestVersion; - } - /** * Apply pending migrations to a tenant database */ @@ -271,13 +212,15 @@ export class TenantMigrationService { database: config.database, }); - // Check if migration is needed - const needsMigration = await this.tenantNeedsMigration(config); - if (needsMigration) { - logger.info("Tenant database needs migration, applying...", { database: config.database }); + // Check if migrations table exists to determine if we need to run migrations + const hasSchema = await this.migrationTableExists(config); + if (!hasSchema) { + logger.info("No migrations table found, applying migrations...", { database: config.database }); await this.migrateTenantDatabase(config); } else { - logger.info("Tenant database is up to date", { database: config.database }); + logger.info("Checking for pending migrations...", { database: config.database }); + // Always run migrate - it's idempotent and will apply only pending migrations + await this.migrateTenantDatabase(config); } } }