#!/bin/bash # Database Setup Script # Provides convenient commands for database management set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" # Change to project root cd "$PROJECT_ROOT" # Logging functions log() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } print_header() { echo -e "${BLUE}=== $1 ===${NC}" } print_usage() { echo "Database Setup Script" echo echo "Usage: $0 [options]" echo echo "Commands:" echo " setup Full database setup (create + migrate + seed)" echo " create Create the database" echo " migrate Run migrations" echo " seed Seed database with test data" echo " reset Reset database (drop + create + migrate)" echo " status Show migration status" echo " drop Drop the database" echo " postgres Setup PostgreSQL database" echo " sqlite Setup SQLite database" echo echo "Options:" echo " --env ENV Environment (dev/prod) [default: dev]" echo " --force Skip confirmations" echo " --quiet Suppress verbose output" echo echo "Examples:" echo " $0 setup # Full setup with default settings" echo " $0 migrate # Run pending migrations" echo " $0 reset --force # Reset database without confirmation" echo " $0 postgres # Setup PostgreSQL specifically" echo " $0 sqlite # Setup SQLite specifically" } # Check if .env file exists check_env_file() { if [ ! -f ".env" ]; then log_warn ".env file not found" log "Creating .env file from template..." if [ -f ".env.example" ]; then cp ".env.example" ".env" log "Created .env from .env.example" else create_default_env fi fi } # Create default .env file create_default_env() { cat > ".env" << EOF # Environment Configuration ENVIRONMENT=dev # Database Configuration DATABASE_URL=postgresql://dev:dev@localhost:5432/rustelo_dev # Server Configuration SERVER_HOST=127.0.0.1 SERVER_PORT=3030 SERVER_PROTOCOL=http # Session Configuration SESSION_SECRET=dev-secret-not-for-production # Features ENABLE_AUTH=true ENABLE_CONTENT_DB=true ENABLE_TLS=false # Logging LOG_LEVEL=debug RUST_LOG=debug EOF log "Created default .env file" } # Check dependencies check_dependencies() { local missing=() if ! command -v cargo >/dev/null 2>&1; then missing+=("cargo (Rust)") fi if ! command -v psql >/dev/null 2>&1 && ! command -v sqlite3 >/dev/null 2>&1; then missing+=("psql (PostgreSQL) or sqlite3") fi if [ ${#missing[@]} -gt 0 ]; then log_error "Missing dependencies: ${missing[*]}" echo echo "Please install the missing dependencies:" echo "- Rust: https://rustup.rs/" echo "- PostgreSQL: https://postgresql.org/download/" echo "- SQLite: Usually pre-installed or via package manager" exit 1 fi } # Setup PostgreSQL database setup_postgresql() { print_header "Setting up PostgreSQL Database" # Check if PostgreSQL is running if ! pg_isready >/dev/null 2>&1; then log_warn "PostgreSQL is not running" echo "Please start PostgreSQL service:" echo " macOS (Homebrew): brew services start postgresql" echo " Linux (systemd): sudo systemctl start postgresql" echo " Windows: Start PostgreSQL service from Services panel" exit 1 fi # Create development user if it doesn't exist if ! psql -U postgres -tc "SELECT 1 FROM pg_user WHERE usename = 'dev'" | grep -q 1; then log "Creating development user..." psql -U postgres -c "CREATE USER dev WITH PASSWORD 'dev' CREATEDB;" fi # Update DATABASE_URL in .env if grep -q "sqlite://" .env; then log "Updating .env to use PostgreSQL..." sed -i.bak 's|DATABASE_URL=.*|DATABASE_URL=postgresql://dev:dev@localhost:5432/rustelo_dev|' .env rm -f .env.bak fi log "PostgreSQL setup complete" } # Setup SQLite database setup_sqlite() { print_header "Setting up SQLite Database" # Create data directory mkdir -p data # Update DATABASE_URL in .env if grep -q "postgresql://" .env; then log "Updating .env to use SQLite..." sed -i.bak 's|DATABASE_URL=.*|DATABASE_URL=sqlite://data/rustelo.db|' .env rm -f .env.bak fi log "SQLite setup complete" } # Run database tool command run_db_tool() { local command="$1" log "Running: cargo run --bin db_tool -- $command" if [ "$QUIET" = "true" ]; then cargo run --bin db_tool -- "$command" >/dev/null 2>&1 else cargo run --bin db_tool -- "$command" fi } # Create seed directory and files if they don't exist setup_seeds() { if [ ! -d "seeds" ]; then log "Creating seeds directory..." mkdir -p seeds # Create sample seed files cat > "seeds/001_sample_users.sql" << EOF -- Sample users for development -- This file works for both PostgreSQL and SQLite INSERT INTO users (username, email, password_hash, is_active, is_verified) VALUES ('admin', 'admin@example.com', '\$argon2id\$v=19\$m=65536,t=3,p=4\$Ym9vZm9v\$2RmTUplMXB3YUNGeFczL1NyTlJFWERsZVdrbUVuNHhDNlk5K1ZZWVorUT0', true, true), ('user', 'user@example.com', '\$argon2id\$v=19\$m=65536,t=3,p=4\$Ym9vZm9v\$2RmTUplMXB3YUNGeFczL1NyTlJFWERsZVdrbUVuNHhDNlk5K1ZZWVorUT0', true, true), ('editor', 'editor@example.com', '\$argon2id\$v=19\$m=65536,t=3,p=4\$Ym9vZm9v\$2RmTUplMXB3YUNGeFczL1NyTlJFWERsZVdrbUVuNHhDNlk5K1ZZWVorUT0', true, true) ON CONFLICT (email) DO NOTHING; EOF cat > "seeds/002_sample_content.sql" << EOF -- Sample content for development -- This file works for both PostgreSQL and SQLite INSERT INTO content (title, slug, content_type, body, is_published, published_at) VALUES ('Welcome to Rustelo', 'welcome', 'markdown', '# Welcome to Rustelo This is a sample content page created by the seed data. ## Features - Fast and secure - Built with Rust - Modern web framework - Easy to use Enjoy building with Rustelo!', true, CURRENT_TIMESTAMP), ('About Us', 'about', 'markdown', '# About Us This is the about page for your Rustelo application. You can edit this content through the admin interface or by modifying the seed files.', true, CURRENT_TIMESTAMP), ('Getting Started', 'getting-started', 'markdown', '# Getting Started Here are some tips to get you started with your new Rustelo application: 1. Check out the admin interface 2. Create your first content 3. Customize the design 4. Deploy to production Good luck!', false, NULL) ON CONFLICT (slug) DO NOTHING; EOF log "Created sample seed files" fi } # Main setup function full_setup() { print_header "Full Database Setup" check_env_file setup_seeds log "Creating database..." run_db_tool "create" log "Running migrations..." run_db_tool "migrate" log "Seeding database..." run_db_tool "seed" log "Checking status..." run_db_tool "status" print_header "Setup Complete!" log "Database is ready for development" echo log "Next steps:" echo " 1. Start the server: cargo leptos watch" echo " 2. Open http://localhost:3030 in your browser" echo " 3. Check the database status: $0 status" } # Parse command line arguments COMMAND="" ENVIRONMENT="dev" FORCE=false QUIET=false while [[ $# -gt 0 ]]; do case $1 in --env) ENVIRONMENT="$2" shift 2 ;; --force) FORCE=true shift ;; --quiet) QUIET=true shift ;; -h|--help) print_usage exit 0 ;; *) if [ -z "$COMMAND" ]; then COMMAND="$1" else log_error "Unknown option: $1" print_usage exit 1 fi shift ;; esac done # Set environment variable export ENVIRONMENT="$ENVIRONMENT" # Validate command if [ -z "$COMMAND" ]; then print_usage exit 1 fi # Check dependencies check_dependencies # Check if we're in the right directory if [ ! -f "Cargo.toml" ]; then log_error "Please run this script from the project root directory" exit 1 fi # Execute command case "$COMMAND" in "setup") full_setup ;; "create") print_header "Creating Database" check_env_file run_db_tool "create" ;; "migrate") print_header "Running Migrations" run_db_tool "migrate" ;; "seed") print_header "Seeding Database" setup_seeds run_db_tool "seed" ;; "reset") print_header "Resetting Database" if [ "$FORCE" != "true" ]; then echo -n "This will destroy all data. Are you sure? (y/N): " read -r confirm if [[ ! "$confirm" =~ ^[Yy]$ ]]; then log "Reset cancelled" exit 0 fi fi run_db_tool "reset" ;; "status") print_header "Database Status" run_db_tool "status" ;; "drop") print_header "Dropping Database" run_db_tool "drop" ;; "postgres") setup_postgresql full_setup ;; "sqlite") setup_sqlite full_setup ;; *) log_error "Unknown command: $COMMAND" print_usage exit 1 ;; esac