migrate from SQLite to PostgreSQL with Drizzle ORM
- Updated all packages to latest versions (React 19, Next.js 14.2.32) - Replaced sqlite3 with pg and drizzle-orm dependencies - Created complete PostgreSQL schema with relationships and indexes - Migrated all API endpoints from SQLite to Drizzle queries - Added database seeding with sample data - Updated authentication to use bcrypt instead of pbkdf2 - Configured connection pooling for PostgreSQL - Updated app version to 1.0.0 - All endpoints tested and working correctly
This commit is contained in:
@@ -1,19 +1,38 @@
|
||||
import { exec } from 'child_process'
|
||||
import path from 'path'
|
||||
import { promisify } from 'util'
|
||||
import fs from 'fs/promises'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export async function createDatabaseBackup(): Promise<string> {
|
||||
const dbPath = path.join(process.cwd(), 'database', 'antihoax.db')
|
||||
const backupPath = path.join(process.cwd(), 'backups', `backup_${Date.now()}.db`)
|
||||
if (!process.env.DATABASE_URL) {
|
||||
throw new Error('DATABASE_URL environment variable is required')
|
||||
}
|
||||
|
||||
const timestamp = Date.now()
|
||||
const backupPath = path.join(process.cwd(), 'backups', `backup_${timestamp}.sql`)
|
||||
|
||||
try {
|
||||
// Ensure backup directory exists
|
||||
await execAsync('mkdir -p backups')
|
||||
|
||||
// Create SQLite backup using .backup command
|
||||
await execAsync(`sqlite3 "${dbPath}" ".backup '${backupPath}'"`)
|
||||
// Create PostgreSQL backup using pg_dump
|
||||
// Extract database name from connection string
|
||||
const url = new URL(process.env.DATABASE_URL)
|
||||
const dbName = url.pathname.substring(1) // Remove leading slash
|
||||
const host = url.hostname
|
||||
const port = url.port || '5432'
|
||||
const username = url.username
|
||||
const password = url.password
|
||||
|
||||
// Set PGPASSWORD environment variable for pg_dump
|
||||
const env = { ...process.env, PGPASSWORD: password }
|
||||
|
||||
await execAsync(
|
||||
`pg_dump -h ${host} -p ${port} -U ${username} -d ${dbName} --no-password > "${backupPath}"`,
|
||||
{ env }
|
||||
)
|
||||
|
||||
return backupPath
|
||||
} catch (error) {
|
||||
@@ -23,14 +42,30 @@ export async function createDatabaseBackup(): Promise<string> {
|
||||
}
|
||||
|
||||
export async function restoreDatabase(backupPath: string): Promise<void> {
|
||||
const dbPath = path.join(process.cwd(), 'database', 'antihoax.db')
|
||||
if (!process.env.DATABASE_URL) {
|
||||
throw new Error('DATABASE_URL environment variable is required')
|
||||
}
|
||||
|
||||
try {
|
||||
// Create backup of current DB first
|
||||
await createDatabaseBackup()
|
||||
|
||||
// Copy backup to main location
|
||||
await execAsync(`cp "${backupPath}" "${dbPath}"`)
|
||||
// Extract database connection details
|
||||
const url = new URL(process.env.DATABASE_URL)
|
||||
const dbName = url.pathname.substring(1)
|
||||
const host = url.hostname
|
||||
const port = url.port || '5432'
|
||||
const username = url.username
|
||||
const password = url.password
|
||||
|
||||
// Set PGPASSWORD environment variable for psql
|
||||
const env = { ...process.env, PGPASSWORD: password }
|
||||
|
||||
// Restore from SQL backup
|
||||
await execAsync(
|
||||
`psql -h ${host} -p ${port} -U ${username} -d ${dbName} --no-password < "${backupPath}"`,
|
||||
{ env }
|
||||
)
|
||||
|
||||
console.log('Database restored successfully')
|
||||
} catch (error) {
|
||||
@@ -41,8 +76,25 @@ export async function restoreDatabase(backupPath: string): Promise<void> {
|
||||
|
||||
export async function listBackups(): Promise<string[]> {
|
||||
try {
|
||||
const { stdout } = await execAsync('ls -la backups/*.db 2>/dev/null || true')
|
||||
return stdout.trim() ? stdout.trim().split('\n') : []
|
||||
const backupDir = path.join(process.cwd(), 'backups')
|
||||
|
||||
try {
|
||||
const files = await fs.readdir(backupDir)
|
||||
const sqlFiles = files.filter(file => file.endsWith('.sql'))
|
||||
|
||||
// Get file stats for each backup
|
||||
const backupInfo = await Promise.all(
|
||||
sqlFiles.map(async (file) => {
|
||||
const filePath = path.join(backupDir, file)
|
||||
const stats = await fs.stat(filePath)
|
||||
return `${stats.mtime.toISOString()} ${file} (${Math.round(stats.size / 1024)}KB)`
|
||||
})
|
||||
)
|
||||
|
||||
return backupInfo.sort().reverse() // Most recent first
|
||||
} catch (dirError) {
|
||||
return []
|
||||
}
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user