Some checks failed
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
- Add unified deployment script with Node.js 22 installation - Create comprehensive database migration script (28 migrations + admin tables) - Add production start/stop scripts for all services - Integrate admin dashboard (parentflow-admin) into PM2 ecosystem - Configure all services: Backend (3020), Frontend (3030), Admin (3335) - Update ecosystem.config.js with admin dashboard configuration - Add invite codes module for user registration management
278 lines
9.2 KiB
Bash
Executable File
278 lines
9.2 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ParentFlow Production Database Migration Script
|
|
# Runs all database migrations for both main app and admin dashboard
|
|
|
|
set -e
|
|
|
|
# Configuration
|
|
DB_HOST="10.0.0.207"
|
|
DB_PORT="5432"
|
|
DB_USER="postgres"
|
|
DB_PASSWORD="a3ppq"
|
|
DB_NAME="parentflow"
|
|
DB_NAME_ADMIN="parentflowadmin"
|
|
|
|
# Color codes
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
log() {
|
|
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
|
|
}
|
|
|
|
error() {
|
|
echo -e "${RED}[ERROR]${NC} $1" >&2
|
|
exit 1
|
|
}
|
|
|
|
success() {
|
|
echo -e "${GREEN}✓${NC} $1"
|
|
}
|
|
|
|
warning() {
|
|
echo -e "${YELLOW}⚠${NC} $1"
|
|
}
|
|
|
|
# Header
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " Database Migration for Production "
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# Check PostgreSQL client
|
|
if ! command -v psql &> /dev/null; then
|
|
error "PostgreSQL client not installed. Run: apt-get install postgresql-client"
|
|
fi
|
|
|
|
# Test database connection
|
|
log "Testing database connection..."
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d postgres -c "SELECT version();" > /dev/null 2>&1
|
|
if [ $? -ne 0 ]; then
|
|
error "Cannot connect to database at $DB_HOST:$DB_PORT"
|
|
fi
|
|
success "Database connection successful"
|
|
|
|
# Create databases if they don't exist
|
|
log "Ensuring databases exist..."
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d postgres << EOF
|
|
-- Create main database
|
|
SELECT 'CREATE DATABASE ${DB_NAME}'
|
|
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${DB_NAME}')\\gexec
|
|
|
|
-- Create admin database
|
|
SELECT 'CREATE DATABASE ${DB_NAME_ADMIN}'
|
|
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${DB_NAME_ADMIN}')\\gexec
|
|
EOF
|
|
success "Databases verified"
|
|
|
|
# Enable extensions
|
|
log "Enabling required extensions..."
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";"
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "CREATE EXTENSION IF NOT EXISTS vector;"
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME_ADMIN -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";"
|
|
success "Extensions enabled"
|
|
|
|
# Find migration directory
|
|
MIGRATION_DIR="$(dirname "$0")/maternal-app/maternal-app-backend/src/database/migrations"
|
|
if [ ! -d "$MIGRATION_DIR" ]; then
|
|
error "Migration directory not found: $MIGRATION_DIR"
|
|
fi
|
|
|
|
# Create migration tracking table
|
|
log "Creating migration tracking table..."
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME << 'EOF'
|
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
id SERIAL PRIMARY KEY,
|
|
version VARCHAR(255) NOT NULL UNIQUE,
|
|
executed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
execution_time_ms INTEGER,
|
|
success BOOLEAN DEFAULT true,
|
|
error_message TEXT,
|
|
checksum VARCHAR(64)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_schema_migrations_version ON schema_migrations(version);
|
|
CREATE INDEX IF NOT EXISTS idx_schema_migrations_executed ON schema_migrations(executed_at);
|
|
EOF
|
|
success "Migration tracking table ready"
|
|
|
|
# Define all migrations in order
|
|
MIGRATIONS=(
|
|
"V001_create_core_auth.sql"
|
|
"V002_create_family_structure.sql"
|
|
"V003_create_child_entities.sql"
|
|
"V004_create_activity_tables.sql"
|
|
"V005_create_notification_system.sql"
|
|
"V006_create_audit_log.sql"
|
|
"V007_create_analytics_tables.sql"
|
|
"V008_add_verification_system.sql"
|
|
"V008_add_eula_fields.sql"
|
|
"V009_add_family_preferences.sql"
|
|
"V009_add_multi_child_preferences.sql"
|
|
"V010_enhance_notifications.sql"
|
|
"V010_add_ai_conversations.sql"
|
|
"V011_add_tracking_enhancements.sql"
|
|
"V011_create_photos_module.sql"
|
|
"V012_add_device_registry.sql"
|
|
"V013_add_mfa_support.sql"
|
|
"V014_add_refresh_token_rotation.sql"
|
|
"V015_add_audit_fields.sql"
|
|
"V016_add_webauthn_support.sql"
|
|
"V017_add_gdpr_compliance.sql"
|
|
"V018_add_ai_embeddings.sql"
|
|
"V019_add_voice_feedback.sql"
|
|
"V020_add_indexes_optimization.sql"
|
|
"V021_add_timezone_support.sql"
|
|
"V022_add_data_archiving.sql"
|
|
"V028_create_invite_codes.sql"
|
|
)
|
|
|
|
# Function to calculate file checksum
|
|
calculate_checksum() {
|
|
local file=$1
|
|
if command -v sha256sum &> /dev/null; then
|
|
sha256sum "$file" | cut -d' ' -f1
|
|
else
|
|
echo "no-checksum"
|
|
fi
|
|
}
|
|
|
|
# Run migrations
|
|
log "Running migrations for main database..."
|
|
TOTAL=${#MIGRATIONS[@]}
|
|
CURRENT=0
|
|
SKIPPED=0
|
|
EXECUTED=0
|
|
|
|
for migration in "${MIGRATIONS[@]}"; do
|
|
CURRENT=$((CURRENT + 1))
|
|
MIGRATION_FILE="$MIGRATION_DIR/$migration"
|
|
|
|
if [ ! -f "$MIGRATION_FILE" ]; then
|
|
warning "[$CURRENT/$TOTAL] Migration file not found: $migration"
|
|
continue
|
|
fi
|
|
|
|
# Check if migration was already executed
|
|
VERSION=$(echo $migration | cut -d'_' -f1)
|
|
ALREADY_RUN=$(PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -tAc \
|
|
"SELECT COUNT(*) FROM schema_migrations WHERE version = '$VERSION';")
|
|
|
|
if [ "$ALREADY_RUN" = "1" ]; then
|
|
echo -e "${YELLOW}[$CURRENT/$TOTAL]${NC} Skipping $migration (already applied)"
|
|
SKIPPED=$((SKIPPED + 1))
|
|
continue
|
|
fi
|
|
|
|
echo -e "${BLUE}[$CURRENT/$TOTAL]${NC} Applying $migration..."
|
|
|
|
START_TIME=$(date +%s%3N)
|
|
CHECKSUM=$(calculate_checksum "$MIGRATION_FILE")
|
|
|
|
# Run migration
|
|
if PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -f "$MIGRATION_FILE" > /dev/null 2>&1; then
|
|
END_TIME=$(date +%s%3N)
|
|
EXEC_TIME=$((END_TIME - START_TIME))
|
|
|
|
# Record successful migration
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME << EOF
|
|
INSERT INTO schema_migrations (version, execution_time_ms, checksum)
|
|
VALUES ('$VERSION', $EXEC_TIME, '$CHECKSUM');
|
|
EOF
|
|
success "Applied $migration (${EXEC_TIME}ms)"
|
|
EXECUTED=$((EXECUTED + 1))
|
|
else
|
|
error "Failed to apply migration: $migration"
|
|
fi
|
|
done
|
|
|
|
log "Migration summary: $EXECUTED executed, $SKIPPED skipped, $TOTAL total"
|
|
|
|
# Run admin-specific migrations if needed
|
|
log "Setting up admin database..."
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME_ADMIN << 'EOF'
|
|
-- Admin users table (if not exists from main DB)
|
|
CREATE TABLE IF NOT EXISTS admin_users (
|
|
id VARCHAR(36) PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
email VARCHAR(255) NOT NULL UNIQUE,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
name VARCHAR(100),
|
|
role VARCHAR(50) DEFAULT 'admin',
|
|
is_active BOOLEAN DEFAULT true,
|
|
last_login_at TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
permissions JSONB DEFAULT '["users:read", "users:write", "invites:read", "invites:write", "analytics:read"]',
|
|
two_factor_secret VARCHAR(255),
|
|
two_factor_enabled BOOLEAN DEFAULT false
|
|
);
|
|
|
|
-- Admin sessions
|
|
CREATE TABLE IF NOT EXISTS admin_sessions (
|
|
id VARCHAR(36) PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
admin_user_id VARCHAR(36) REFERENCES admin_users(id) ON DELETE CASCADE,
|
|
token_hash VARCHAR(255) NOT NULL UNIQUE,
|
|
ip_address VARCHAR(45),
|
|
user_agent TEXT,
|
|
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
last_activity_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Admin audit logs
|
|
CREATE TABLE IF NOT EXISTS admin_audit_logs (
|
|
id VARCHAR(36) PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
admin_user_id VARCHAR(36) REFERENCES admin_users(id),
|
|
action VARCHAR(100) NOT NULL,
|
|
entity_type VARCHAR(50),
|
|
entity_id VARCHAR(36),
|
|
details JSONB DEFAULT '{}',
|
|
ip_address VARCHAR(45),
|
|
user_agent TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Create indexes
|
|
CREATE INDEX IF NOT EXISTS idx_admin_users_email ON admin_users(email);
|
|
CREATE INDEX IF NOT EXISTS idx_admin_sessions_token ON admin_sessions(token_hash);
|
|
CREATE INDEX IF NOT EXISTS idx_admin_sessions_admin ON admin_sessions(admin_user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_admin ON admin_audit_logs(admin_user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_created ON admin_audit_logs(created_at);
|
|
|
|
-- Insert default admin user if not exists (password: admin123)
|
|
INSERT INTO admin_users (email, password_hash, name, role)
|
|
VALUES (
|
|
'admin@parentflowapp.com',
|
|
'$2b$10$H5hw3/iwkCichU5dpVIMqe5Me7WV9jz.qWRm0V4JyGF9smgxgFBxm',
|
|
'System Administrator',
|
|
'super_admin'
|
|
) ON CONFLICT (email) DO NOTHING;
|
|
EOF
|
|
success "Admin database configured"
|
|
|
|
# Verify migration status
|
|
log "Verifying migration status..."
|
|
TOTAL_APPLIED=$(PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -tAc \
|
|
"SELECT COUNT(*) FROM schema_migrations WHERE success = true;")
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo -e "${GREEN} Database Migration Completed! ${NC}"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Summary:"
|
|
echo " Total migrations: $TOTAL"
|
|
echo " Applied in this run: $EXECUTED"
|
|
echo " Previously applied: $SKIPPED"
|
|
echo " Total in database: $TOTAL_APPLIED"
|
|
echo ""
|
|
echo "Databases ready:"
|
|
echo " Main: $DB_NAME at $DB_HOST:$DB_PORT"
|
|
echo " Admin: $DB_NAME_ADMIN at $DB_HOST:$DB_PORT"
|
|
echo ""
|
|
success "All migrations completed successfully" |