| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882 |
- # 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<String> = env::args().collect();
- if args.len() != 2 {
- eprintln!("Usage: bcrypt_hasher <password>");
- 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 }
- }
- }
|