# BeepZone Setup Helper for Windows # PowerShell version - no WSL needed $ErrorActionPreference = "Stop" $SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path $WORK_DIR = $SCRIPT_DIR $LOG_FILE = "$env:TEMP\beepzone-helper.log" $ENV_FILE = Join-Path $SCRIPT_DIR ".env" # Initialize log "" | Out-File $LOG_FILE -Force # Default configuration $config = @{ BEEPZONE_DB_CONTAINER_NAME = "beepzone-mariadb" BEEPZONE_DB_IMAGE = "mariadb:12" DB_HOST = "127.0.0.1" DB_PORT = "3306" DB_NAME = "beepzone" DB_USER = "beepzone" DB_PASS = "beepzone" DB_ROOT_PASSWORD = "root" SECKELAPI_REPO = "https://git.teleco.ch/crt/seckelapi.git" CLIENT_REPO = "https://git.teleco.ch/crt/beepzone-client-egui-emo.git" DEPLOYMENT_TYPE = "clean" } # Load existing .env if present if (Test-Path $ENV_FILE) { Get-Content $ENV_FILE | ForEach-Object { if ($_ -match '^([^=]+)="?([^"]*)"?$') { $config[$matches[1]] = $matches[2] } } } function Save-Config { @" # BeepZone Setup Configuration # Auto-generated by beepzone-helper.ps1 BEEPZONE_DB_CONTAINER_NAME="$($config.BEEPZONE_DB_CONTAINER_NAME)" BEEPZONE_DB_IMAGE="$($config.BEEPZONE_DB_IMAGE)" DB_HOST="$($config.DB_HOST)" DB_PORT="$($config.DB_PORT)" DB_NAME="$($config.DB_NAME)" DB_USER="$($config.DB_USER)" DB_PASS="$($config.DB_PASS)" DB_ROOT_PASSWORD="$($config.DB_ROOT_PASSWORD)" SECKELAPI_REPO="$($config.SECKELAPI_REPO)" CLIENT_REPO="$($config.CLIENT_REPO)" DEPLOYMENT_TYPE="$($config.DEPLOYMENT_TYPE)" "@ | Out-File $ENV_FILE -Encoding UTF8 } function Show-Menu { param([string]$Title, [array]$Options) Write-Host "`n=== $Title ===" -ForegroundColor Cyan for ($i = 0; $i -lt $Options.Count; $i++) { Write-Host " [$($i+1)] $($Options[$i])" } Write-Host "" $choice = Read-Host "Choose an option (1-$($Options.Count))" return [int]$choice } function Run-Command { param( [string]$Command, [array]$Arguments, [string]$SuccessMessage, [string]$ErrorMessage, [switch]$ShowOutput, [switch]$LiveOutput ) Write-Host "Running: $Command $($Arguments -join ' ')" -ForegroundColor DarkGray Write-Host "" if ($LiveOutput) { # Show output in real-time for long-running commands $process = Start-Process -FilePath $Command -ArgumentList $Arguments -NoNewWindow -Wait -PassThru $exitCode = $process.ExitCode # Log the command execution "$Command $($Arguments -join ' ')" | Out-File $LOG_FILE -Append "Exit Code: $exitCode" | Out-File $LOG_FILE -Append Write-Host "" if ($exitCode -ne 0) { Write-Host "$ErrorMessage (Exit Code: $exitCode)" -ForegroundColor Red Write-Host "Full log: $LOG_FILE" -ForegroundColor DarkGray return $false } else { if ($SuccessMessage) { Write-Host $SuccessMessage -ForegroundColor Green } return $true } } else { # Capture output for commands where we want to parse it $tempOutput = New-TemporaryFile $tempError = New-TemporaryFile try { $process = Start-Process -FilePath $Command -ArgumentList $Arguments -NoNewWindow -Wait -PassThru ` -RedirectStandardOutput $tempOutput.FullName -RedirectStandardError $tempError.FullName $exitCode = $process.ExitCode $stdOut = Get-Content $tempOutput.FullName -Raw -ErrorAction SilentlyContinue $stdErr = Get-Content $tempError.FullName -Raw -ErrorAction SilentlyContinue # Log everything if ($stdOut) { $stdOut | Out-File $LOG_FILE -Append } if ($stdErr) { $stdErr | Out-File $LOG_FILE -Append } # Show informational stderr messages if requested if ($ShowOutput -and $stdErr) { $stdErr.Split("`n") | ForEach-Object { if ($_.Trim()) { Write-Host " $_" -ForegroundColor DarkGray } } } if ($exitCode -ne 0) { Write-Host "`n$ErrorMessage" -ForegroundColor Red Write-Host "Exit Code: $exitCode" -ForegroundColor Yellow if ($stdOut) { Write-Host "Output:" -ForegroundColor Yellow $stdOut.Split("`n") | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow } } if ($stdErr) { Write-Host "Error:" -ForegroundColor Yellow $stdErr.Split("`n") | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow } } Write-Host "`nFull log: $LOG_FILE" -ForegroundColor DarkGray return $false } else { if ($SuccessMessage) { Write-Host $SuccessMessage -ForegroundColor Green } # Show stdout if it contains useful info (like container ID) if ($stdOut -and $stdOut.Trim().Length -lt 100) { Write-Host " → $($stdOut.Trim())" -ForegroundColor DarkGray } return $true } } finally { Remove-Item $tempOutput -ErrorAction SilentlyContinue Remove-Item $tempError -ErrorAction SilentlyContinue } } } function Test-Dependencies { $missing = @() if (-not (Get-Command podman -ErrorAction SilentlyContinue)) { $missing += "Podman (install Podman Desktop from podman.io)" } # Check for MySQL/MariaDB client in PATH and common installation locations $mysqlFound = $false # First check if already in PATH foreach ($cmd in @("mysql", "mariadb")) { if (Get-Command $cmd -ErrorAction SilentlyContinue) { $mysqlFound = $true break } } # If not in PATH, check common Windows installation locations if (-not $mysqlFound) { $searchPaths = @( "C:\Program Files\MariaDB*\bin\mysql.exe", "C:\Program Files\MariaDB*\bin\mariadb.exe", "C:\Program Files\MySQL\MySQL Server*\bin\mysql.exe", "C:\Program Files (x86)\MariaDB*\bin\mysql.exe", "C:\Program Files (x86)\MySQL\MySQL Server*\bin\mysql.exe" ) foreach ($pattern in $searchPaths) { $found = Get-ChildItem $pattern -ErrorAction SilentlyContinue | Select-Object -First 1 if ($found) { $mysqlFound = $true # Add to PATH for this session $binDir = Split-Path $found.FullName $env:Path = "$binDir;$env:Path" Write-Host "Found MySQL/MariaDB at: $($found.FullName)" -ForegroundColor Green break } } } if (-not $mysqlFound) { $missing += "MySQL/MariaDB client" } if (-not (Get-Command git -ErrorAction SilentlyContinue)) { $missing += "Git" } if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) { $missing += "Rust/Cargo (https://rustup.rs)" } if ($missing.Count -gt 0) { Write-Host "`nMissing dependencies:" -ForegroundColor Red $missing | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow } Write-Host "`nPlease install the missing tools and try again.`n" exit 1 } } function Configure-Database { Write-Host "`n=== MariaDB Container Configuration ===" -ForegroundColor Cyan $config.DB_HOST = Read-Host "DB Host [$($config.DB_HOST)]" if ([string]::IsNullOrWhiteSpace($config.DB_HOST)) { $config.DB_HOST = "127.0.0.1" } $port = Read-Host "DB Port [$($config.DB_PORT)]" if ($port) { $config.DB_PORT = $port } $name = Read-Host "DB Name [$($config.DB_NAME)]" if ($name) { $config.DB_NAME = $name } $user = Read-Host "DB User [$($config.DB_USER)]" if ($user) { $config.DB_USER = $user } $pass = Read-Host "DB Password [$($config.DB_PASS)]" if ($pass) { $config.DB_PASS = $pass } $rootPass = Read-Host "Root Password [$($config.DB_ROOT_PASSWORD)]" if ($rootPass) { $config.DB_ROOT_PASSWORD = $rootPass } Save-Config # Check if container exists $exists = podman ps -a --format "{{.Names}}" | Select-String -Pattern "^$($config.BEEPZONE_DB_CONTAINER_NAME)$" -Quiet if ($exists) { $restart = Read-Host "`nContainer '$($config.BEEPZONE_DB_CONTAINER_NAME)' exists. Start/restart it? (y/n)" if ($restart -eq 'y') { $null = Run-Command -Command "podman" -Arguments @("start", $config.BEEPZONE_DB_CONTAINER_NAME) ` -SuccessMessage "Container started successfully!" ` -ErrorMessage "Failed to start container" } return } # Create new container Write-Host "`nCreating new MariaDB container..." -ForegroundColor Yellow $cmd = @( "run", "-d", "--name", $config.BEEPZONE_DB_CONTAINER_NAME, "-e", "MARIADB_ROOT_PASSWORD=$($config.DB_ROOT_PASSWORD)", "-e", "MARIADB_DATABASE=$($config.DB_NAME)", "-e", "MARIADB_USER=$($config.DB_USER)", "-e", "MARIADB_PASSWORD=$($config.DB_PASS)", "-p", "$($config.DB_PORT):3306", $config.BEEPZONE_DB_IMAGE ) $success = Run-Command -Command "podman" -Arguments $cmd ` -SuccessMessage "MariaDB container started successfully!" ` -ErrorMessage "Failed to start MariaDB container" if (-not $success) { Read-Host "`nPress Enter to continue" } } function Import-Database { $schemaFile = Join-Path $WORK_DIR "backend\database\schema\beepzone-schema-dump.sql" $fullDumpFile = Join-Path $WORK_DIR "backend\database\dev\beepzone-full-dump.sql" $choice = Show-Menu "Database Import" @( "Full dump (schema + data)", "Clean schema only (no data)" ) $file = if ($choice -eq 1) { $fullDumpFile } else { $schemaFile } if (-not (Test-Path $file)) { Write-Host "File not found: $file" -ForegroundColor Red Read-Host "Press Enter to continue" return } $confirm = Read-Host "`nThis will DROP and recreate the database. Continue? (y/n)" if ($confirm -ne 'y') { return } Write-Host "Importing database..." -ForegroundColor Yellow $sql = @" DROP DATABASE IF EXISTS ``$($config.DB_NAME)``; CREATE DATABASE ``$($config.DB_NAME)`` CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci; USE ``$($config.DB_NAME)``; SET FOREIGN_KEY_CHECKS=0; $(Get-Content $file -Raw) SET FOREIGN_KEY_CHECKS=1; "@ # Import via podman exec Write-Host "Importing SQL..." -ForegroundColor Yellow $tempFile = New-TemporaryFile $sql | Out-File $tempFile.FullName -Encoding UTF8 $importCmd = "podman exec -i $($config.BEEPZONE_DB_CONTAINER_NAME) mariadb -h $($config.DB_HOST) -P $($config.DB_PORT) -uroot -p`"$($config.DB_ROOT_PASSWORD)`" < `"$($tempFile.FullName)`"" $output = cmd /c $importCmd 2`>`&1 $exitCode = $LASTEXITCODE Remove-Item $tempFile -ErrorAction SilentlyContinue $output | Out-File $LOG_FILE -Append if ($exitCode -ne 0) { Write-Host "`nDatabase import failed!" -ForegroundColor Red Write-Host "Error output:" -ForegroundColor Yellow $output | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow } Write-Host "`nFull log: $LOG_FILE" -ForegroundColor DarkGray } else { Write-Host "Database imported successfully!" -ForegroundColor Green if ($choice -eq 2) { Write-Host "`nRecommendation: Create an Admin role (power 100) and Admin user next." -ForegroundColor Cyan } $config.DEPLOYMENT_TYPE = if ($choice -eq 1) { "dev" } else { "clean" } Save-Config } Read-Host "`nPress Enter to continue" } function Manage-Users { while ($true) { $choice = Show-Menu "Manage Users & Roles" @( "Create a role", "Create a user", "Delete a role", "Delete a user", "Back to main menu" ) switch ($choice) { 1 { Create-Role } 2 { Create-User } 3 { Delete-Role } 4 { Delete-User } 5 { return } } } } function Create-Role { Write-Host "`n=== Create Role ===" -ForegroundColor Cyan $name = Read-Host "Role name" $power = Read-Host "Power (1-100)" if ([string]::IsNullOrWhiteSpace($name) -or [string]::IsNullOrWhiteSpace($power)) { Write-Host "Name and power are required!" -ForegroundColor Red Read-Host "Press Enter to continue" return } if (-not ($power -match '^\d+$') -or [int]$power -lt 1 -or [int]$power -gt 100) { Write-Host "Power must be 1-100!" -ForegroundColor Red Read-Host "Press Enter to continue" return } $sql = "INSERT INTO roles (name, power) VALUES ('$name', $power);" $ErrorActionPreference = 'Continue' $output = $sql | podman exec -i $config.BEEPZONE_DB_CONTAINER_NAME mariadb -h $config.DB_HOST -P $config.DB_PORT -uroot -p"$($config.DB_ROOT_PASSWORD)" $config.DB_NAME 2>&1 | Where-Object { $_ -notmatch '^(mysql:|mariadb:)' } $ErrorActionPreference = 'Stop' $output | Out-File $LOG_FILE -Append if ($LASTEXITCODE -ne 0) { Write-Host "`nFailed to create role!" -ForegroundColor Red Write-Host "Error output:" -ForegroundColor Yellow $output | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow } } else { Write-Host "Role '$name' created successfully!" -ForegroundColor Green } Read-Host "Press Enter to continue" } function Delete-Role { Write-Host "`nFetching roles..." -ForegroundColor Yellow $ErrorActionPreference = 'Continue' $roles = podman exec -i $config.BEEPZONE_DB_CONTAINER_NAME mariadb -h $config.DB_HOST -P $config.DB_PORT -uroot -p"$($config.DB_ROOT_PASSWORD)" $config.DB_NAME -e "SELECT id, name, power FROM roles ORDER BY power DESC;" -s -N 2>&1 | Where-Object { $_ -notmatch '^(mysql:|mariadb:)' } $ErrorActionPreference = 'Stop' if (-not $roles) { Write-Host "No roles found!" -ForegroundColor Red Read-Host "Press Enter to continue" return } Write-Host "`n=== Select Role to Delete ===" -ForegroundColor Cyan $roleList = @() $i = 1 $roles | ForEach-Object { $parts = $_ -split "`t" Write-Host " [$i] $($parts[1]) (power: $($parts[2]))" $roleList += @{ Id = $parts[0]; Name = $parts[1]; Power = $parts[2] } $i++ } $choice = Read-Host "`nSelect role (1-$($roleList.Count))" $selected = $roleList[[int]$choice - 1] $confirm = Read-Host "Delete role '$($selected.Name)'? (y/n)" if ($confirm -ne 'y') { return } $ErrorActionPreference = 'Continue' $output = "DELETE FROM roles WHERE id = $($selected.Id);" | podman exec -i $config.BEEPZONE_DB_CONTAINER_NAME mariadb -h $config.DB_HOST -P $config.DB_PORT -uroot -p"$($config.DB_ROOT_PASSWORD)" $config.DB_NAME 2>&1 | Where-Object { $_ -notmatch '^(mysql:|mariadb:)' } $ErrorActionPreference = 'Stop' $output | Out-File $LOG_FILE -Append if ($LASTEXITCODE -ne 0) { Write-Host "`nFailed to delete role!" -ForegroundColor Red Write-Host "Error output:" -ForegroundColor Yellow $output | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow } } else { Write-Host "Role deleted!" -ForegroundColor Green } Read-Host "Press Enter to continue" } function Create-User { Write-Host "`nFetching roles..." -ForegroundColor Yellow $ErrorActionPreference = 'Continue' $roles = podman exec -i $config.BEEPZONE_DB_CONTAINER_NAME mariadb -h $config.DB_HOST -P $config.DB_PORT -uroot -p"$($config.DB_ROOT_PASSWORD)" $config.DB_NAME -e "SELECT id, name, power FROM roles ORDER BY power DESC;" -s -N 2>&1 | Where-Object { $_ -notmatch '^(mysql:|mariadb:)' } $ErrorActionPreference = 'Stop' if (-not $roles) { Write-Host "No roles found! Create a role first." -ForegroundColor Red Read-Host "Press Enter to continue" return } Write-Host "`n=== Select Role ===" -ForegroundColor Cyan $roleList = @() $i = 1 $roles | ForEach-Object { $parts = $_ -split "`t" Write-Host " [$i] $($parts[1]) (power: $($parts[2]))" $roleList += @{ Id = $parts[0]; Name = $parts[1] } $i++ } $roleChoice = Read-Host "`nSelect role (1-$($roleList.Count))" $selectedRole = $roleList[[int]$roleChoice - 1] Write-Host "`n=== Create User ===" -ForegroundColor Cyan $name = Read-Host "Full name" $username = Read-Host "Username" $password = Read-Host "Password" -AsSecureString $passwordPlain = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)) $email = Read-Host "Email (optional)" $phone = Read-Host "Phone (optional)" if ([string]::IsNullOrWhiteSpace($name) -or [string]::IsNullOrWhiteSpace($username) -or [string]::IsNullOrWhiteSpace($passwordPlain)) { Write-Host "Name, username, and password are required!" -ForegroundColor Red Read-Host "Press Enter to continue" return } # Hash password with bcrypt cost 12 using Rust (since cargo is already available) Write-Host "Hashing password..." -ForegroundColor Yellow $bcryptToolPath = Join-Path $env:TEMP "beepzone_bcrypt_tool.exe" # Build tiny bcrypt tool if not already built if (-not (Test-Path $bcryptToolPath)) { Write-Host "Building bcrypt tool (one-time setup)..." -ForegroundColor Yellow $tempDir = Join-Path $env:TEMP "bcrypt_hasher" New-Item -ItemType Directory -Force -Path $tempDir | Out-Null # Create minimal Cargo.toml @" [package] name = "bcrypt_hasher" version = "0.1.0" edition = "2021" [dependencies] bcrypt = "0.15" "@ | Out-File (Join-Path $tempDir "Cargo.toml") -Encoding UTF8 # Create main.rs @" use std::env; fn main() { let args: Vec = env::args().collect(); if args.len() != 2 { eprintln!("Usage: bcrypt_hasher "); std::process::exit(1); } match bcrypt::hash(&args[1], 12) { Ok(hash) => println!("{}", hash), Err(e) => { eprintln!("Error: {}", e); std::process::exit(1); } } } "@ | Out-File (Join-Path $tempDir "main.rs") -Encoding UTF8 # Create src directory and move main.rs $srcDir = Join-Path $tempDir "src" New-Item -ItemType Directory -Force -Path $srcDir | Out-Null Move-Item (Join-Path $tempDir "main.rs") (Join-Path $srcDir "main.rs") -Force # Build it Push-Location $tempDir Write-Host " Compiling bcrypt tool..." -ForegroundColor DarkGray cargo build --release --quiet 2>&1 | Out-Null Pop-Location # Copy to temp location $builtExe = Join-Path $tempDir "target\release\bcrypt_hasher.exe" if (Test-Path $builtExe) { Copy-Item $builtExe $bcryptToolPath -Force Write-Host " Bcrypt tool ready!" -ForegroundColor Green } else { Write-Host " Failed to build bcrypt tool" -ForegroundColor Red } # Cleanup build directory Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue } # Hash the password if (Test-Path $bcryptToolPath) { try { $passwordHash = & $bcryptToolPath $passwordPlain if ($LASTEXITCODE -eq 0 -and $passwordHash -match '^\$2[ab]\$12\$') { Write-Host "Password hashed successfully!" -ForegroundColor Green } else { Write-Host "`nError: Failed to hash password" -ForegroundColor Red Read-Host "Press Enter to continue" return } } catch { Write-Host "`nError: Failed to hash password: $_" -ForegroundColor Red Read-Host "Press Enter to continue" return } } else { Write-Host "`nError: Could not build bcrypt tool. Make sure cargo is working." -ForegroundColor Red Read-Host "Press Enter to continue" return } $emailVal = if ($email) { "'$email'" } else { "NULL" } $phoneVal = if ($phone) { "'$phone'" } else { "NULL" } $sql = "INSERT INTO users (name, username, password, role_id, email, phone, active) VALUES ('$name', '$username', '$passwordHash', $($selectedRole.Id), $emailVal, $phoneVal, 1);" $ErrorActionPreference = 'Continue' $output = $sql | podman exec -i $config.BEEPZONE_DB_CONTAINER_NAME mariadb -h $config.DB_HOST -P $config.DB_PORT -uroot -p"$($config.DB_ROOT_PASSWORD)" $config.DB_NAME 2>&1 | Where-Object { $_ -notmatch '^(mysql:|mariadb:)' } $ErrorActionPreference = 'Stop' $output | Out-File $LOG_FILE -Append if ($LASTEXITCODE -ne 0) { Write-Host "`nFailed to create user!" -ForegroundColor Red Write-Host "Error output:" -ForegroundColor Yellow $output | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow } } else { Write-Host "User '$username' created successfully!" -ForegroundColor Green } Read-Host "Press Enter to continue" } function Delete-User { Write-Host "`nFetching users..." -ForegroundColor Yellow $ErrorActionPreference = 'Continue' $users = podman exec -i $config.BEEPZONE_DB_CONTAINER_NAME mariadb -h $config.DB_HOST -P $config.DB_PORT -uroot -p"$($config.DB_ROOT_PASSWORD)" $config.DB_NAME -e "SELECT id, username, name FROM users ORDER BY id;" -s -N 2>&1 | Where-Object { $_ -notmatch '^(mysql:|mariadb:)' } $ErrorActionPreference = 'Stop' if (-not $users) { Write-Host "No users found!" -ForegroundColor Red Read-Host "Press Enter to continue" return } Write-Host "`n=== Select User to Delete ===" -ForegroundColor Cyan $userList = @() $i = 1 $users | ForEach-Object { $parts = $_ -split "`t" Write-Host " [$i] $($parts[1]) - $($parts[2])" $userList += @{ Id = $parts[0]; Username = $parts[1] } $i++ } $choice = Read-Host "`nSelect user (1-$($userList.Count))" $selected = $userList[[int]$choice - 1] $confirm = Read-Host "Delete user '$($selected.Username)'? (y/n)" if ($confirm -ne 'y') { return } $ErrorActionPreference = 'Continue' $output = "DELETE FROM users WHERE id = $($selected.Id);" | podman exec -i $config.BEEPZONE_DB_CONTAINER_NAME mariadb -h $config.DB_HOST -P $config.DB_PORT -uroot -p"$($config.DB_ROOT_PASSWORD)" $config.DB_NAME 2>&1 | Where-Object { $_ -notmatch '^(mysql:|mariadb:)' } $ErrorActionPreference = 'Stop' $output | Out-File $LOG_FILE -Append if ($LASTEXITCODE -ne 0) { Write-Host "`nFailed to delete user!" -ForegroundColor Red Write-Host "Error output:" -ForegroundColor Yellow $output | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow } } else { Write-Host "User deleted!" -ForegroundColor Green } Read-Host "Press Enter to continue" } function Setup-SeckelAPI { $sourcesDir = Join-Path $WORK_DIR "backend\seckelapi\sources" $configDir = Join-Path $WORK_DIR "backend\seckelapi\config" if (-not (Test-Path (Join-Path $sourcesDir ".git"))) { Write-Host "Cloning SeckelAPI repository..." -ForegroundColor Yellow $parentDir = Split-Path $sourcesDir -Parent New-Item -ItemType Directory -Force -Path $parentDir | Out-Null $success = Run-Command -Command "git" -Arguments @("clone", $config.SECKELAPI_REPO, $sourcesDir) ` -SuccessMessage "Repository cloned successfully!" ` -ErrorMessage "Failed to clone SeckelAPI repository" ` -ShowOutput if (-not $success) { Read-Host "`nPress Enter to continue" return } } # Copy config files New-Item -ItemType Directory -Force -Path (Join-Path $sourcesDir "config") | Out-Null Get-ChildItem "$configDir\*.toml" | ForEach-Object { Copy-Item $_.FullName (Join-Path $sourcesDir "config\$($_.Name)") -Force } # Update config with database settings for container $basicsConfig = Join-Path $sourcesDir "config\basics.toml" if (Test-Path $basicsConfig) { Write-Host "Updating config for container deployment..." -ForegroundColor Yellow # Read line by line and only update values in [database] section $lines = Get-Content $basicsConfig $inDatabaseSection = $false $newLines = @() foreach ($line in $lines) { if ($line -match '^\[database\]') { $inDatabaseSection = $true $newLines += $line } elseif ($line -match '^\[.*\]') { # Entering a different section $inDatabaseSection = $false $newLines += $line } elseif ($inDatabaseSection) { # We're in [database] section, update relevant values if ($line -match '^host\s*=') { $newLines += 'host = "host.containers.internal"' } elseif ($line -match '^port\s*=') { $newLines += "port = $($config.DB_PORT)" } elseif ($line -match '^database\s*=') { $newLines += "database = `"$($config.DB_NAME)`"" } elseif ($line -match '^username\s*=') { $newLines += "username = `"$($config.DB_USER)`"" } elseif ($line -match '^password\s*=') { $newLines += "password = `"$($config.DB_PASS)`"" } else { $newLines += $line } } else { $newLines += $line } } $newLines | Out-File $basicsConfig -Encoding UTF8 Write-Host "Config updated!" -ForegroundColor Green } # Build and deploy container $choice = Show-Menu "SeckelAPI Container Build" @( "Build and run in podman container", "Skip for now" ) if ($choice -eq 1) { $containerName = "beepzone-seckelapi" $imageName = "beepzone-seckelapi:latest" $containerfile = Join-Path $WORK_DIR "backend\seckelapi\Containerfile" # Stop and remove existing container $existing = podman ps -a --format "{{.Names}}" | Select-String -Pattern "^${containerName}$" -Quiet if ($existing) { Write-Host "Stopping existing container..." -ForegroundColor Yellow podman stop $containerName 2>&1 | Out-Null podman rm $containerName 2>&1 | Out-Null } # Build container Write-Host "`nBuilding SeckelAPI container..." -ForegroundColor Yellow $success = Run-Command -Command "podman" -Arguments @("build", "-t", $imageName, "-f", $containerfile, (Join-Path $WORK_DIR "backend\seckelapi")) ` -SuccessMessage "Container image built successfully!" ` -ErrorMessage "Container build failed!" ` -LiveOutput if ($success) { $run = Read-Host "`nStart the container now? (y/n)" if ($run -eq 'y') { Write-Host "Starting container..." -ForegroundColor Yellow $success = Run-Command -Command "podman" -Arguments @("run", "-d", "--name", $containerName, "--add-host", "host.containers.internal:host-gateway", "-p", "5777:5777", $imageName) ` -SuccessMessage "Container started successfully!" ` -ErrorMessage "Failed to start container!" if ($success) { Write-Host "`nSeckelAPI is running at: http://localhost:5777" -ForegroundColor Cyan Write-Host "`nUseful commands:" -ForegroundColor DarkGray Write-Host " podman logs $containerName - View logs" -ForegroundColor DarkGray Write-Host " podman stop $containerName - Stop container" -ForegroundColor DarkGray Write-Host " podman start $containerName - Start container" -ForegroundColor DarkGray Write-Host " podman restart $containerName - Restart container" -ForegroundColor DarkGray } } } } Read-Host "`nPress Enter to continue" } function Clean-Sources { Write-Host "`n=== Clean Sources ==="-ForegroundColor Cyan Write-Host "This will delete all sources directories including hidden files." -ForegroundColor Yellow Write-Host " - backend\seckelapi\sources" -ForegroundColor Yellow Write-Host " - frontend\desktop-client\sources" -ForegroundColor Yellow $confirm = Read-Host "`nAre you sure you want to clean all sources? (y/n)" if ($confirm -ne 'y') { Write-Host "Cancelled." -ForegroundColor Gray Read-Host "Press Enter to continue" return } $seckelapiSources = Join-Path $WORK_DIR "backend\seckelapi\sources" $clientSources = Join-Path $WORK_DIR "frontend\desktop-client\sources" # Clean SeckelAPI sources if (Test-Path $seckelapiSources) { Write-Host "`nRemoving SeckelAPI sources contents..." -ForegroundColor Yellow Get-ChildItem -Path $seckelapiSources -Force | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue Write-Host " SeckelAPI sources cleaned" -ForegroundColor Green } else { Write-Host " SeckelAPI sources folder not found" -ForegroundColor Gray } # Clean desktop client sources if (Test-Path $clientSources) { Write-Host "Removing desktop client sources contents..." -ForegroundColor Yellow Get-ChildItem -Path $clientSources -Force | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue Write-Host " Desktop client sources cleaned" -ForegroundColor Green } else { Write-Host " Desktop client sources folder not found" -ForegroundColor Gray } Write-Host "`nAll sources cleaned!" -ForegroundColor Green # Ask to re-clone $reclone = Read-Host "`nDo you want to pull fresh sources now? (y/n)" if ($reclone -eq 'y') { Write-Host "`nCloning SeckelAPI..." -ForegroundColor Yellow $parentDir = Split-Path $seckelapiSources -Parent New-Item -ItemType Directory -Force -Path $parentDir | Out-Null $success = Run-Command -Command "git" -Arguments @("clone", $config.SECKELAPI_REPO, $seckelapiSources) ` -SuccessMessage "SeckelAPI cloned successfully!" ` -ErrorMessage "Failed to clone SeckelAPI" ` -ShowOutput Write-Host "`nCloning desktop client..." -ForegroundColor Yellow $parentDir = Split-Path $clientSources -Parent New-Item -ItemType Directory -Force -Path $parentDir | Out-Null $success = Run-Command -Command "git" -Arguments @("clone", $config.CLIENT_REPO, $clientSources) ` -SuccessMessage "Desktop client cloned successfully!" ` -ErrorMessage "Failed to clone desktop client" ` -ShowOutput Write-Host "`nAll sources pulled fresh!" -ForegroundColor Green } Read-Host "`nPress Enter to continue" } function Build-DesktopClient { $sourcesDir = Join-Path $WORK_DIR "frontend\desktop-client\sources" if (-not (Test-Path (Join-Path $sourcesDir ".git"))) { Write-Host "Cloning client repository..." -ForegroundColor Yellow $parentDir = Split-Path $sourcesDir -Parent New-Item -ItemType Directory -Force -Path $parentDir | Out-Null $success = Run-Command -Command "git" -Arguments @("clone", $config.CLIENT_REPO, $sourcesDir) ` -SuccessMessage "Repository cloned successfully!" ` -ErrorMessage "Failed to clone client repository" ` -ShowOutput if (-not $success) { Read-Host "`nPress Enter to continue" return } } $confirm = Read-Host "`nBuild BeepZone desktop client for Windows? (y/n)" if ($confirm -ne 'y') { return } Write-Host "Building desktop client..." -ForegroundColor Yellow Push-Location $sourcesDir $success = Run-Command -Command "cargo" -Arguments @("build", "--release") ` -SuccessMessage "" ` -ErrorMessage "Build failed!" ` -LiveOutput Pop-Location if ($success) { Write-Host "`nBuild complete!" -ForegroundColor Green Write-Host "Binary: $sourcesDir\target\release\" -ForegroundColor Cyan } Read-Host "Press Enter to continue" } # Main Clear-Host Write-Host @" ######################################### # BeepZone Setup Helper (Windows) # ######################################### "@ -ForegroundColor Cyan Test-Dependencies while ($true) { $choice = Show-Menu "BeepZone Setup" @( "Configure & run MariaDB (podman)", "Import DB schema & data", "Manage users & roles", "Configure & setup SeckelAPI", "Build desktop client", "Clean sources", "Quit" ) switch ($choice) { 1 { Configure-Database } 2 { Import-Database } 3 { Manage-Users } 4 { Setup-SeckelAPI } 5 { Build-DesktopClient } 6 { Clean-Sources } 7 { exit 0 } } }