UNPKG

@damourlabs/euro-clinical-trials

Version:

A Nuxt 3 application for visualizing and managing clinical trials data in Europe, built with modern web technologies.

115 lines (93 loc) 4.25 kB
import { PGlite } from '@electric-sql/pglite' import { readdir, readFile } from 'node:fs/promises' import { join } from 'node:path' export default defineNitroPlugin(async () => { const config = useRuntimeConfig().pgliteMigrations // Skip migrations if auto-migrate is disabled if (!config.autoMigrate) { return } try { console.log('🔄 PGLite Migrations: Starting database migration process...') // Initialize PGLite database const db = new PGlite({ dataDir: config.dataDir, }) // Create migrations tracking table if it doesn't exist await db.exec(` CREATE TABLE IF NOT EXISTS ${config.migrationTable} ( id SERIAL PRIMARY KEY, filename VARCHAR(255) NOT NULL UNIQUE, applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, checksum VARCHAR(64) ); `) // Get list of applied migrations const appliedMigrationsResult = await db.query<{ filename: string }>( `SELECT filename FROM ${config.migrationTable} ORDER BY filename` ) const appliedMigrations = new Set( appliedMigrationsResult.rows.map(row => row.filename as string) ) // Read migration files const migrationsDir = join(process.cwd(), config.migrationsPath) const migrationFiles = await readdir(migrationsDir) // Filter and sort SQL migration files const sqlFiles = migrationFiles .filter(file => file.endsWith('.sql')) .sort() let migrationsApplied = 0 for (const filename of sqlFiles) { if (appliedMigrations.has(filename)) { console.log(`⏭️ PGLite Migrations: Skipping already applied migration: ${filename}`) continue } try { console.log(`🔧 PGLite Migrations: Applying migration: ${filename}`) const migrationPath = join(migrationsDir, filename) const migrationSQL = await readFile(migrationPath, 'utf-8') // Calculate checksum for integrity verification const checksum = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(migrationSQL)) const checksumHex = Array.from(new Uint8Array(checksum)) .map(b => b.toString(16).padStart(2, '0')) .join('') // Split migration by statement breakpoint and execute each statement const statements = migrationSQL .split('--> statement-breakpoint') .map(stmt => stmt.trim()) .filter(stmt => stmt.length > 0) // Execute migration in a transaction await db.transaction(async (tx) => { for (const statement of statements) { if (statement.trim()) { await tx.exec(statement) } } // Record this migration as applied with checksum await tx.exec( `INSERT INTO ${config.migrationTable} (filename, checksum) VALUES ('${filename}', '${checksumHex}')` ) }) migrationsApplied++ console.log(`✅ PGLite Migrations: Successfully applied migration: ${filename}`) } catch (error) { console.error(`❌ PGLite Migrations: Failed to apply migration ${filename}:`, error) throw error } } if (migrationsApplied === 0) { console.log('✨ PGLite Migrations: Database is up to date - no migrations needed') } else { console.log(`🎉 PGLite Migrations: Successfully applied ${migrationsApplied} migration(s)`) } await db.close() } catch (error) { console.error('💥 PGLite Migrations: Database migration failed:', error) // In development, we might want to fail fast if (process.env.NODE_ENV === 'development') { throw error } } // nitroApp.hooks.hook('', async () => { // }) })