#!/bin/bash # Database Migration Script # Handles database schema migrations and data updates set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" DB_PATH="${PROJECT_ROOT}/data/metadata.db" echo "๐Ÿ”„ Database Migration Script" echo "===========================" cd "$PROJECT_ROOT" # Check if database exists if [ ! -f "$DB_PATH" ]; then echo "โŒ Database not found: $DB_PATH" echo "Please ensure the gateway has been initialized first" exit 1 fi # Create backup before migration echo "๐Ÿ’พ Creating pre-migration backup..." TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="./backups/pre_migration_${TIMESTAMP}.sql" mkdir -p backups sqlite3 "$DB_PATH" .dump > "$BACKUP_FILE" echo "โœ… Backup created: $BACKUP_FILE" # Check current schema version echo "๐Ÿ“Š Checking current schema..." CURRENT_TABLES=$(sqlite3 "$DB_PATH" ".tables") echo "Current tables: $CURRENT_TABLES" # Migration functions run_migration() { local version="$1" local description="$2" local sql="$3" echo "๐Ÿ”„ Migration $version: $description" if sqlite3 "$DB_PATH" "$sql"; then echo "โœ… Migration $version completed" # Log migration sqlite3 "$DB_PATH" "INSERT OR IGNORE INTO schema_migrations (version, description, applied_at) VALUES ('$version', '$description', datetime('now'));" else echo "โŒ Migration $version failed" exit 1 fi } # Create migrations table if it doesn't exist echo "๐Ÿ—„๏ธ Creating migrations table..." sqlite3 "$DB_PATH" " CREATE TABLE IF NOT EXISTS schema_migrations ( version TEXT PRIMARY KEY, description TEXT NOT NULL, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP );" # Check which migrations have been applied APPLIED_MIGRATIONS=$(sqlite3 "$DB_PATH" "SELECT version FROM schema_migrations;" 2>/dev/null || echo "") echo "Applied migrations: $APPLIED_MIGRATIONS" # Migration 1: Add performance indexes if ! echo "$APPLIED_MIGRATIONS" | grep -q "001_performance_indexes"; then run_migration "001_performance_indexes" "Add performance indexes" " CREATE INDEX IF NOT EXISTS idx_files_owner_pubkey ON files(owner_pubkey); CREATE INDEX IF NOT EXISTS idx_files_storage_type ON files(storage_type); CREATE INDEX IF NOT EXISTS idx_files_access_level ON files(access_level); CREATE INDEX IF NOT EXISTS idx_files_size ON files(size); CREATE INDEX IF NOT EXISTS idx_files_last_access ON files(last_access); CREATE INDEX IF NOT EXISTS idx_chunks_chunk_hash ON chunks(chunk_hash); CREATE INDEX IF NOT EXISTS idx_users_storage_used ON users(storage_used); " else echo "โญ๏ธ Skipping migration 001_performance_indexes (already applied)" fi # Migration 2: Add monitoring columns if ! echo "$APPLIED_MIGRATIONS" | grep -q "002_monitoring_columns"; then run_migration "002_monitoring_columns" "Add monitoring and metrics columns" " ALTER TABLE files ADD COLUMN download_count INTEGER DEFAULT 0; ALTER TABLE files ADD COLUMN stream_count INTEGER DEFAULT 0; ALTER TABLE users ADD COLUMN bandwidth_used INTEGER DEFAULT 0; ALTER TABLE users ADD COLUMN api_requests INTEGER DEFAULT 0; CREATE INDEX IF NOT EXISTS idx_files_download_count ON files(download_count); CREATE INDEX IF NOT EXISTS idx_files_stream_count ON files(stream_count); " else echo "โญ๏ธ Skipping migration 002_monitoring_columns (already applied)" fi # Migration 3: Add cache tables if ! echo "$APPLIED_MIGRATIONS" | grep -q "003_cache_tables"; then run_migration "003_cache_tables" "Add cache management tables" " CREATE TABLE IF NOT EXISTS cache_entries ( cache_key TEXT PRIMARY KEY, cache_value BLOB, cache_type TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, expires_at DATETIME, hit_count INTEGER DEFAULT 0 ); CREATE INDEX IF NOT EXISTS idx_cache_entries_type ON cache_entries(cache_type); CREATE INDEX IF NOT EXISTS idx_cache_entries_expires ON cache_entries(expires_at); " else echo "โญ๏ธ Skipping migration 003_cache_tables (already applied)" fi # Migration 4: Add rate limiting tables if ! echo "$APPLIED_MIGRATIONS" | grep -q "004_rate_limiting"; then run_migration "004_rate_limiting" "Add rate limiting tracking" " CREATE TABLE IF NOT EXISTS rate_limit_events ( id INTEGER PRIMARY KEY, client_ip TEXT NOT NULL, limit_type TEXT NOT NULL, blocked BOOLEAN DEFAULT FALSE, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_rate_limit_ip ON rate_limit_events(client_ip); CREATE INDEX IF NOT EXISTS idx_rate_limit_timestamp ON rate_limit_events(timestamp); " else echo "โญ๏ธ Skipping migration 004_rate_limiting (already applied)" fi # Data consistency checks echo "๐Ÿ” Running data consistency checks..." # Check for orphaned chunks ORPHANED_CHUNKS=$(sqlite3 "$DB_PATH" " SELECT COUNT(*) FROM chunks c LEFT JOIN files f ON c.file_hash = f.hash WHERE f.hash IS NULL; ") if [ "$ORPHANED_CHUNKS" -gt 0 ]; then echo "โš ๏ธ Found $ORPHANED_CHUNKS orphaned chunks" read -p "Remove orphaned chunks? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then sqlite3 "$DB_PATH" " DELETE FROM chunks WHERE file_hash NOT IN (SELECT hash FROM files); " echo "โœ… Orphaned chunks removed" fi else echo "โœ… No orphaned chunks found" fi # Check for expired sessions EXPIRED_SESSIONS=$(sqlite3 "$DB_PATH" " SELECT COUNT(*) FROM sessions WHERE expires_at < datetime('now'); ") if [ "$EXPIRED_SESSIONS" -gt 0 ]; then echo "๐Ÿงน Cleaning up $EXPIRED_SESSIONS expired sessions..." sqlite3 "$DB_PATH" "DELETE FROM sessions WHERE expires_at < datetime('now');" echo "โœ… Expired sessions cleaned" else echo "โœ… No expired sessions found" fi # Update storage statistics echo "๐Ÿ“Š Updating storage statistics..." sqlite3 "$DB_PATH" " UPDATE users SET storage_used = ( SELECT COALESCE(SUM(size), 0) FROM files WHERE owner_pubkey = users.pubkey ), file_count = ( SELECT COUNT(*) FROM files WHERE owner_pubkey = users.pubkey ); " echo "โœ… Storage statistics updated" # Vacuum database for performance echo "๐Ÿงน Optimizing database..." sqlite3 "$DB_PATH" "VACUUM;" sqlite3 "$DB_PATH" "ANALYZE;" echo "โœ… Database optimized" # Final validation echo "๐Ÿ” Final validation..." # Check table integrity INTEGRITY_CHECK=$(sqlite3 "$DB_PATH" "PRAGMA integrity_check;") if [ "$INTEGRITY_CHECK" = "ok" ]; then echo "โœ… Database integrity check passed" else echo "โŒ Database integrity check failed: $INTEGRITY_CHECK" FAILED_CHECKS=$((FAILED_CHECKS + 1)) fi # Check foreign key constraints FK_CHECK=$(sqlite3 "$DB_PATH" "PRAGMA foreign_key_check;") if [ -z "$FK_CHECK" ]; then echo "โœ… Foreign key constraints valid" else echo "โš ๏ธ Foreign key constraint violations found: $FK_CHECK" fi echo "" echo "๐Ÿ“Š Migration Summary" echo "===================" echo "Total checks: $TOTAL_CHECKS" echo "Passed: $PASSED_CHECKS" echo "Failed: $FAILED_CHECKS" if [ $FAILED_CHECKS -eq 0 ]; then echo "" echo "๐ŸŽ‰ All migrations and checks completed successfully!" echo "โœ… Database is healthy and up-to-date" exit 0 else echo "" echo "โš ๏ธ Some checks failed" echo "๐Ÿ’พ Backup available at: $BACKUP_FILE" echo "๐Ÿ”ง Please investigate and fix issues" exit 1 fi