# Rustelo Unified Installer for Windows # PowerShell script for installing and setting up Rustelo projects # Supports development, production, and custom installations param( [string]$Mode = "dev", [string]$ProjectName = "my-rustelo-app", [string]$Environment = "dev", [string]$InstallDir = "", [switch]$EnableTLS, [switch]$EnableOAuth, [switch]$DisableAuth, [switch]$DisableContentDB, [switch]$SkipDeps, [switch]$Force, [switch]$Quiet, [switch]$Help ) # Set error action preference $ErrorActionPreference = "Stop" # Colors for output $Colors = @{ Red = "Red" Green = "Green" Yellow = "Yellow" Blue = "Blue" Purple = "Magenta" Cyan = "Cyan" White = "White" } # Configuration $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $ProjectRoot = $ScriptDir $TemplateDir = Join-Path $ProjectRoot "template" $InstallLog = Join-Path $ProjectRoot "install.log" # Installation options (can be overridden by environment variables) $INSTALL_MODE = if ($env:INSTALL_MODE) { $env:INSTALL_MODE } else { $Mode } $PROJECT_NAME = if ($env:PROJECT_NAME) { $env:PROJECT_NAME } else { $ProjectName } $ENVIRONMENT = if ($env:ENVIRONMENT) { $env:ENVIRONMENT } else { $Environment } $ENABLE_AUTH = if ($env:ENABLE_AUTH) { $env:ENABLE_AUTH -eq "true" } else { -not $DisableAuth } $ENABLE_CONTENT_DB = if ($env:ENABLE_CONTENT_DB) { $env:ENABLE_CONTENT_DB -eq "true" } else { -not $DisableContentDB } $ENABLE_TLS = if ($env:ENABLE_TLS) { $env:ENABLE_TLS -eq "true" } else { $EnableTLS } $ENABLE_OAUTH = if ($env:ENABLE_OAUTH) { $env:ENABLE_OAUTH -eq "true" } else { $EnableOAuth } $SKIP_DEPS = if ($env:SKIP_DEPS) { $env:SKIP_DEPS -eq "true" } else { $SkipDeps } $FORCE_REINSTALL = if ($env:FORCE_REINSTALL) { $env:FORCE_REINSTALL -eq "true" } else { $Force } $QUIET = if ($env:QUIET) { $env:QUIET -eq "true" } else { $Quiet } # Dependency versions $RUST_MIN_VERSION = "1.75.0" $NODE_MIN_VERSION = "18.0.0" # Logging functions function Write-Log { param([string]$Message, [string]$Level = "INFO") $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logEntry = "[$timestamp] [$Level] $Message" switch ($Level) { "INFO" { Write-Host "[INFO] $Message" -ForegroundColor $Colors.Green } "WARN" { Write-Host "[WARN] $Message" -ForegroundColor $Colors.Yellow } "ERROR" { Write-Host "[ERROR] $Message" -ForegroundColor $Colors.Red } "DEBUG" { if (-not $QUIET) { Write-Host "[DEBUG] $Message" -ForegroundColor $Colors.Cyan } } } Add-Content -Path $InstallLog -Value $logEntry } function Write-Header { param([string]$Message) Write-Host $Message -ForegroundColor $Colors.Blue } function Write-Step { param([string]$Message) Write-Host "➤ $Message" -ForegroundColor $Colors.Purple } function Write-Success { param([string]$Message) Write-Host "✓ $Message" -ForegroundColor $Colors.Green } function Write-Banner { Write-Host "" Write-Host "╭─────────────────────────────────────────────────────────────╮" -ForegroundColor $Colors.White Write-Host "│ RUSTELO INSTALLER │" -ForegroundColor $Colors.White Write-Host "│ │" -ForegroundColor $Colors.White Write-Host "│ A modern Rust web application framework built with Leptos │" -ForegroundColor $Colors.White Write-Host "│ │" -ForegroundColor $Colors.White Write-Host "╰─────────────────────────────────────────────────────────────╯" -ForegroundColor $Colors.White Write-Host "" } # Function to check if a command exists function Test-Command { param([string]$Command) return (Get-Command $Command -ErrorAction SilentlyContinue) -ne $null } # Function to compare versions function Compare-Version { param([string]$Version1, [string]$Version2) $v1 = [version]$Version1 $v2 = [version]$Version2 return $v1 -ge $v2 } # Function to check system requirements function Test-SystemRequirements { Write-Step "Checking system requirements..." $missingTools = @() if (-not (Test-Command "git")) { $missingTools += "git" } if ($missingTools.Count -gt 0) { Write-Log "Missing required system tools: $($missingTools -join ', ')" -Level "ERROR" Write-Host "Please install these tools before continuing." exit 1 } Write-Success "System requirements check passed" } # Function to install Rust function Install-Rust { Write-Step "Checking Rust installation..." if ((Test-Command "rustc") -and (Test-Command "cargo")) { $rustVersion = (rustc --version).Split()[1] Write-Log "Found Rust version: $rustVersion" -Level "DEBUG" if (Compare-Version $rustVersion $RUST_MIN_VERSION) { Write-Success "Rust $rustVersion is already installed" return } else { Write-Log "Rust version $rustVersion is too old (minimum: $RUST_MIN_VERSION)" -Level "WARN" } } if ($SKIP_DEPS) { Write-Log "Skipping Rust installation due to --skip-deps flag" -Level "WARN" return } Write-Log "Installing Rust..." # Download and install Rust $rustupUrl = "https://win.rustup.rs/x86_64" $rustupPath = Join-Path $env:TEMP "rustup-init.exe" try { Invoke-WebRequest -Uri $rustupUrl -OutFile $rustupPath & $rustupPath -y # Add Cargo to PATH for current session $env:PATH = "$env:USERPROFILE\.cargo\bin;$env:PATH" # Verify installation if ((Test-Command "rustc") -and (Test-Command "cargo")) { $rustVersion = (rustc --version).Split()[1] Write-Success "Rust $rustVersion installed successfully" } else { Write-Log "Rust installation failed" -Level "ERROR" exit 1 } } catch { Write-Log "Failed to install Rust: $_" -Level "ERROR" exit 1 } finally { if (Test-Path $rustupPath) { Remove-Item $rustupPath -Force } } } # Function to install Node.js function Install-NodeJS { Write-Step "Checking Node.js installation..." if ((Test-Command "node") -and (Test-Command "npm")) { $nodeVersion = (node --version).TrimStart('v') Write-Log "Found Node.js version: $nodeVersion" -Level "DEBUG" if (Compare-Version $nodeVersion $NODE_MIN_VERSION) { Write-Success "Node.js $nodeVersion is already installed" return } else { Write-Log "Node.js version $nodeVersion is too old (minimum: $NODE_MIN_VERSION)" -Level "WARN" } } if ($SKIP_DEPS) { Write-Log "Skipping Node.js installation due to --skip-deps flag" -Level "WARN" return } Write-Log "Node.js installation required" Write-Host "Please install Node.js manually from https://nodejs.org/" Write-Host "Then run this script again." exit 1 } # Function to install Rust tools function Install-RustTools { Write-Step "Installing Rust tools..." if (Test-Command "cargo-leptos") { Write-Success "cargo-leptos is already installed" } else { Write-Log "Installing cargo-leptos..." cargo install cargo-leptos Write-Success "cargo-leptos installed" } # Install other useful tools (only in dev mode) if ($INSTALL_MODE -eq "dev" -or $ENVIRONMENT -eq "dev") { $tools = @("cargo-watch", "cargo-audit", "cargo-outdated") foreach ($tool in $tools) { if (Test-Command $tool) { Write-Log "$tool is already installed" -Level "DEBUG" } else { Write-Log "Installing $tool..." try { cargo install $tool } catch { Write-Log "Failed to install $tool" -Level "WARN" } } } } } # Function to create project function New-Project { Write-Step "Setting up project: $PROJECT_NAME" # Determine installation directory if (-not $InstallDir) { $InstallDir = Join-Path (Get-Location) $PROJECT_NAME } # Create project directory if (Test-Path $InstallDir) { if ($FORCE_REINSTALL) { Write-Log "Removing existing project directory: $InstallDir" -Level "WARN" Remove-Item $InstallDir -Recurse -Force } else { Write-Log "Project directory already exists: $InstallDir" -Level "ERROR" Write-Host "Use --force to overwrite or choose a different name/location" exit 1 } } Write-Log "Creating project directory: $InstallDir" New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null # Copy template files Write-Log "Copying template files..." try { Copy-Item -Path "$TemplateDir\*" -Destination $InstallDir -Recurse -Force } catch { Write-Log "Failed to copy template files: $_" -Level "ERROR" exit 1 } # Copy additional files $readmePath = Join-Path $ProjectRoot "README.md" if (Test-Path $readmePath) { Copy-Item -Path $readmePath -Destination $InstallDir -Force } Write-Success "Project files copied to $InstallDir" } # Function to configure project function Set-ProjectConfiguration { Write-Step "Configuring project..." Set-Location $InstallDir # Create .env file $envPath = ".env" if (-not (Test-Path $envPath)) { Write-Log "Creating .env file..." $serverHost = if ($ENVIRONMENT -eq "dev") { "127.0.0.1" } else { "0.0.0.0" } $serverPort = if ($ENVIRONMENT -eq "dev") { "3030" } else { "443" } $serverProtocol = if ($ENABLE_TLS) { "https" } else { "http" } $dbUrl = if ($ENVIRONMENT -eq "dev") { "postgresql://dev:dev@localhost:5432/${PROJECT_NAME}_dev" } else { "postgresql://prod:`${DATABASE_PASSWORD}@db.example.com:5432/${PROJECT_NAME}_prod" } $sessionSecret = if ($ENVIRONMENT -eq "dev") { "dev-secret-not-for-production" } else { -join ((1..32) | ForEach-Object { [char]((65..90) + (97..122) | Get-Random) }) } $logLevel = if ($ENVIRONMENT -eq "dev") { "debug" } else { "info" } $envContent = @" # Environment Configuration ENVIRONMENT=$ENVIRONMENT # Server Configuration SERVER_HOST=$serverHost SERVER_PORT=$serverPort SERVER_PROTOCOL=$serverProtocol # Database Configuration DATABASE_URL=$dbUrl # Session Configuration SESSION_SECRET=$sessionSecret # Features ENABLE_AUTH=$ENABLE_AUTH ENABLE_CONTENT_DB=$ENABLE_CONTENT_DB ENABLE_TLS=$ENABLE_TLS ENABLE_OAUTH=$ENABLE_OAUTH # OAuth Configuration (if enabled) $(if ($ENABLE_OAUTH) { "GOOGLE_CLIENT_ID=" } else { "# GOOGLE_CLIENT_ID=" }) $(if ($ENABLE_OAUTH) { "GOOGLE_CLIENT_SECRET=" } else { "# GOOGLE_CLIENT_SECRET=" }) $(if ($ENABLE_OAUTH) { "GITHUB_CLIENT_ID=" } else { "# GITHUB_CLIENT_ID=" }) $(if ($ENABLE_OAUTH) { "GITHUB_CLIENT_SECRET=" } else { "# GITHUB_CLIENT_SECRET=" }) # Email Configuration # SMTP_HOST= # SMTP_PORT=587 # SMTP_USERNAME= # SMTP_PASSWORD= # FROM_EMAIL= # FROM_NAME= # Logging LOG_LEVEL=$logLevel RUST_LOG=$logLevel "@ Set-Content -Path $envPath -Value $envContent Write-Success ".env file created" } else { Write-Log ".env file already exists, skipping creation" -Level "WARN" } # Update Cargo.toml with project name $cargoPath = "Cargo.toml" if (Test-Path $cargoPath) { $content = Get-Content $cargoPath $content = $content -replace 'name = "rustelo"', "name = `"$PROJECT_NAME`"" Set-Content -Path $cargoPath -Value $content Write-Log "Updated project name in Cargo.toml" -Level "DEBUG" } # Create necessary directories $dirs = @("public", "uploads", "logs", "cache", "config", "data") if ($ENVIRONMENT -eq "prod") { $dirs += "backups" } foreach ($dir in $dirs) { if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } } if ($ENABLE_TLS) { if (-not (Test-Path "certs")) { New-Item -ItemType Directory -Path "certs" -Force | Out-Null } Write-Log "Created certs directory for TLS" -Level "DEBUG" } Write-Success "Project configured" } # Function to install dependencies function Install-Dependencies { Write-Step "Installing project dependencies..." Set-Location $InstallDir # Install Rust dependencies Write-Log "Installing Rust dependencies..." try { cargo fetch } catch { Write-Log "Failed to fetch Rust dependencies: $_" -Level "ERROR" exit 1 } # Install Node.js dependencies if (Test-Path "package.json") { Write-Log "Installing Node.js dependencies..." try { if (Test-Command "pnpm") { pnpm install } elseif (Test-Command "npm") { npm install } else { Write-Log "Neither pnpm nor npm found" -Level "ERROR" exit 1 } } catch { Write-Log "Failed to install Node.js dependencies: $_" -Level "ERROR" exit 1 } } Write-Success "Dependencies installed" } # Function to build project function Build-Project { Write-Step "Building project..." Set-Location $InstallDir # Build CSS Write-Log "Building CSS..." try { if (Test-Command "pnpm") { pnpm run build:css } elseif (Test-Command "npm") { npm run build:css } } catch { Write-Log "Failed to build CSS" -Level "WARN" } # Build Rust project Write-Log "Building Rust project..." try { if ($ENVIRONMENT -eq "prod") { cargo build --release } else { cargo build } } catch { Write-Log "Failed to build Rust project: $_" -Level "ERROR" exit 1 } Write-Success "Project built successfully" } # Function to create startup scripts function New-StartupScripts { Write-Step "Creating startup scripts..." Set-Location $InstallDir # Create development start script $startScript = @" @echo off cd /d "%~dp0" cargo leptos watch pause "@ Set-Content -Path "start.bat" -Value $startScript # Create production start script $startProdScript = @" @echo off cd /d "%~dp0" cargo leptos build --release .\target\release\server.exe pause "@ Set-Content -Path "start-prod.bat" -Value $startProdScript # Create build script $buildScript = @" @echo off cd /d "%~dp0" cargo leptos build --release pause "@ Set-Content -Path "build.bat" -Value $buildScript # Create PowerShell start script $startPsScript = @" # Start development server Set-Location (Split-Path -Parent `$MyInvocation.MyCommand.Path) cargo leptos watch "@ Set-Content -Path "start.ps1" -Value $startPsScript Write-Success "Startup scripts created" } # Function to display final instructions function Show-Instructions { Write-Host "" Write-Header "╭─────────────────────────────────────────────────────────────╮" Write-Header "│ INSTALLATION COMPLETE │" Write-Header "╰─────────────────────────────────────────────────────────────╯" Write-Host "" Write-Success "Project '$PROJECT_NAME' has been successfully installed!" Write-Host "" Write-Host "Installation Details:" -ForegroundColor $Colors.White Write-Host " Mode: $INSTALL_MODE" Write-Host " Environment: $ENVIRONMENT" Write-Host " Location: $InstallDir" Write-Host " Features:" Write-Host " - Authentication: $ENABLE_AUTH" Write-Host " - Content Database: $ENABLE_CONTENT_DB" Write-Host " - TLS/HTTPS: $ENABLE_TLS" Write-Host " - OAuth: $ENABLE_OAUTH" Write-Host "" Write-Host "Quick Start:" -ForegroundColor $Colors.White Write-Host "1. cd $InstallDir" Write-Host "2. .\start.bat (or .\start.ps1)" Write-Host "3. Open $(if ($ENABLE_TLS) { "https" } else { "http" })://127.0.0.1:3030" Write-Host "" Write-Host "Available Commands:" -ForegroundColor $Colors.White Write-Host " .\start.bat - Start development server" Write-Host " .\start-prod.bat - Start production server" Write-Host " .\build.bat - Build for production" Write-Host " cargo leptos watch - Development with hot reload" Write-Host " cargo leptos build - Build project" Write-Host " cargo build - Build Rust code only" Write-Host " npm run dev - Watch CSS changes" Write-Host "" Write-Host "Configuration Files:" -ForegroundColor $Colors.White Write-Host " .env - Environment variables" Write-Host " Cargo.toml - Rust dependencies" Write-Host " package.json - Node.js dependencies" Write-Host "" if ($ENABLE_TLS) { Write-Host "Note: " -ForegroundColor $Colors.Yellow -NoNewline Write-Host "Self-signed certificates were generated for HTTPS." Write-Host "Your browser will show a security warning for development." Write-Host "" } if ($ENVIRONMENT -eq "prod") { Write-Host "Production Checklist:" -ForegroundColor $Colors.Yellow Write-Host "□ Update SESSION_SECRET in .env" Write-Host "□ Configure database connection" Write-Host "□ Set up proper TLS certificates" Write-Host "□ Review security settings" Write-Host "□ Configure OAuth providers (if enabled)" Write-Host "" } Write-Success "Happy coding with Rustelo! 🚀" } # Function to show usage function Show-Usage { Write-Host "Rustelo Unified Installer for Windows" Write-Host "" Write-Host "Usage: .\install.ps1 [OPTIONS]" Write-Host "" Write-Host "Options:" Write-Host " -Mode Installation mode (dev, prod, custom) [default: dev]" Write-Host " -ProjectName Project name [default: my-rustelo-app]" Write-Host " -Environment Environment (dev, prod) [default: dev]" Write-Host " -InstallDir Installation directory [default: .\]" Write-Host " -EnableTLS Enable TLS/HTTPS support" Write-Host " -EnableOAuth Enable OAuth authentication" Write-Host " -DisableAuth Disable authentication features" Write-Host " -DisableContentDB Disable content database features" Write-Host " -SkipDeps Skip dependency installation" Write-Host " -Force Force reinstallation (overwrite existing)" Write-Host " -Quiet Suppress debug output" Write-Host " -Help Show this help message" Write-Host "" Write-Host "Installation Modes:" Write-Host " dev - Development setup with debugging enabled" Write-Host " prod - Production setup with optimizations" Write-Host " custom - Interactive configuration selection" Write-Host "" Write-Host "Environment Variables:" Write-Host " INSTALL_MODE Installation mode (dev/prod/custom)" Write-Host " PROJECT_NAME Project name" Write-Host " ENVIRONMENT Environment (dev/prod)" Write-Host " ENABLE_TLS Enable TLS (true/false)" Write-Host " ENABLE_AUTH Enable authentication (true/false)" Write-Host " ENABLE_CONTENT_DB Enable content database (true/false)" Write-Host " ENABLE_OAUTH Enable OAuth (true/false)" Write-Host " SKIP_DEPS Skip dependencies (true/false)" Write-Host " FORCE_REINSTALL Force reinstall (true/false)" Write-Host " QUIET Quiet mode (true/false)" Write-Host "" Write-Host "Examples:" Write-Host " .\install.ps1 # Quick dev setup" Write-Host " .\install.ps1 -Mode prod -EnableTLS # Production with HTTPS" Write-Host " .\install.ps1 -Mode custom # Interactive setup" Write-Host " `$env:INSTALL_MODE='prod'; .\install.ps1 # Using environment variable" } # Function for custom installation function Invoke-CustomInstall { Write-Header "Custom Installation Configuration" Write-Host "" # Project name $input = Read-Host "Project name [$PROJECT_NAME]" if ($input) { $PROJECT_NAME = $input } # Environment $input = Read-Host "Environment (dev/prod) [$ENVIRONMENT]" if ($input) { $ENVIRONMENT = $input } # Features $input = Read-Host "Enable authentication? (Y/n)" $ENABLE_AUTH = -not ($input -match "^[Nn]$") $input = Read-Host "Enable content database? (Y/n)" $ENABLE_CONTENT_DB = -not ($input -match "^[Nn]$") $input = Read-Host "Enable TLS/HTTPS? (y/N)" $ENABLE_TLS = $input -match "^[Yy]$" if ($ENABLE_AUTH) { $input = Read-Host "Enable OAuth authentication? (y/N)" $ENABLE_OAUTH = $input -match "^[Yy]$" } $input = Read-Host "Skip dependency installation? (y/N)" $SKIP_DEPS = $input -match "^[Yy]$" Write-Host "" Write-Host "Configuration Summary:" Write-Host " Project Name: $PROJECT_NAME" Write-Host " Environment: $ENVIRONMENT" Write-Host " Authentication: $ENABLE_AUTH" Write-Host " Content Database: $ENABLE_CONTENT_DB" Write-Host " TLS/HTTPS: $ENABLE_TLS" Write-Host " OAuth: $ENABLE_OAUTH" Write-Host " Skip Dependencies: $SKIP_DEPS" Write-Host "" $input = Read-Host "Proceed with installation? (Y/n)" if ($input -match "^[Nn]$") { Write-Host "Installation cancelled." exit 0 } } # Main installation function function Start-Installation { Write-Banner # Initialize log "Installation started at $(Get-Date)" | Out-File -FilePath $InstallLog -Encoding UTF8 "Mode: $INSTALL_MODE, Environment: $ENVIRONMENT" | Add-Content -Path $InstallLog # Check if we're in the right directory if (-not (Test-Path $TemplateDir)) { Write-Log "Template directory not found: $TemplateDir" -Level "ERROR" Write-Log "Please run this script from the Rustelo project root" -Level "ERROR" exit 1 } # Configure based on mode switch ($INSTALL_MODE) { "dev" { $script:ENVIRONMENT = "dev" $script:ENABLE_TLS = $ENABLE_TLS $script:ENABLE_OAUTH = $ENABLE_OAUTH } "prod" { $script:ENVIRONMENT = "prod" $script:ENABLE_TLS = if ($ENABLE_TLS) { $true } else { $true } } "custom" { Invoke-CustomInstall } } # Run installation steps Test-SystemRequirements if (-not $SKIP_DEPS) { Install-Rust Install-NodeJS Install-RustTools } New-Project Set-ProjectConfiguration Install-Dependencies Build-Project New-StartupScripts # Display final instructions Show-Instructions Write-Log "Installation completed successfully at $(Get-Date)" } # Main execution if ($Help) { Show-Usage exit 0 } # Validate parameters if ($INSTALL_MODE -notin @("dev", "prod", "custom")) { Write-Log "Invalid installation mode: $INSTALL_MODE" -Level "ERROR" Write-Host "Valid modes: dev, prod, custom" exit 1 } if ($ENVIRONMENT -notin @("dev", "prod")) { Write-Log "Invalid environment: $ENVIRONMENT" -Level "ERROR" Write-Host "Valid environments: dev, prod" exit 1 } # Run main installation try { Start-Installation } catch { Write-Log "Installation failed: $_" -Level "ERROR" exit 1 }