diff options
| -rw-r--r-- | README-WIN.md | 85 | ||||
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | backend/seckelapi/.containerignore | 6 | ||||
| -rw-r--r-- | beepzone-helper.ps1 | 822 |
4 files changed, 915 insertions, 2 deletions
diff --git a/README-WIN.md b/README-WIN.md new file mode 100644 index 0000000..6ea2e3f --- /dev/null +++ b/README-WIN.md @@ -0,0 +1,85 @@ +# BeepZone Setup Guide for Windows + +Windows-specific setup instructions for BeepZone Inventory System using PowerShell. + +## what you need + +install these tools (download from official websites, winget is unreliable): + +### required dependencies: +1. **Podman Desktop** (for containers) + - download from: https://podman-desktop.io/downloads + - includes podman CLI and WSL2 machine + - after install, open Podman Desktop and initialize the machine + +2. **Rust & Cargo** (for building binaries) + - download rustup from: https://rustup.rs/ + - run the installer and follow prompts + - open Visual Studio Installer via Windows menu + - select modify + - make sure "Desktop Developement with C++" and "Linux, Mac, and Embedded Developement with C++" are checked + - press the modify button with windows shield of deceipt + - restart terminal after installation + +3. **Git** (for cloning repositories) + - download from: https://git-scm.com/download/win + - use default settings during installation other than default editor ... i recommend nano as vim is for more advanded linux nerds id say + +4. **MariaDB Client** (for database access) + - download from: https://mariadb.org/download/ + - during install make sure to not install database instance lol unless you want to run a database on your laptop + - script auto-detects at: `C:\Program Files\MariaDB *\bin\mysql.exe` + +## how to use + +1. open PowerShell in the BeepZone directory +2. allow script execution (one-time): + ```powershell + Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + ``` +3. run the helper script: + ```powershell + .\beepzone-helper.ps1 + ``` + +## what the script does + +provides an interactive menu for: +- running a podman container with mariadb and + - letting you configure access details + - importing the schema + - managing users and roles in the db (create/delete both with bcrypt password hashing) + - optionally import some seeding data +- compiling and setting up SeckelAPI as a podman container + - uses proper port mapping 5777:5777 and host gateway for db access + - auto-configures database connection settings +- compiling the desktop client natively on Windows + - produces native `.exe` binary for Windows + +## setup workflow + +1. **configure & run mariadb container** + - set database credentials + - creates and starts mariadb:12 container + - exposes port 3306 + +2. **import database schema** + - choose between: + - full dump (includes live dev sample data, admin:admin123 are the logins btw) + - clean schema (empty tables) + - automatically creates database and imports + +3. **create admin user & role** + - create a role with power level 100 (admin) + - create your first adminier user + - experience me compiling a fucking bcrypt tool that ive embedded into the powershell code because windows is stinky and wont let me encrypt the passwords for use in the db bruh + +4. **setup SeckelAPI backend** + - automatically clones SeckelAPI source from https://git.teleco.ch/crt/seckelapi.git + - auto updates config for container networking (once, after that it dont for some reason on my computer) + - builds container image and starts on port 5777 + +5. **build desktop client** + - clones client source from https://git.teleco.ch/crt/beepzone-client-egui-emo.git + - compiles native Windows binary with cargo + - executable location: `frontend\desktop-client\sources\target\release\` (ignore the debug terminal that opens along with the app, will be removed someday tm) @@ -3,6 +3,8 @@ Huh so you want to actually try beepzone? (Oh god) Well then since I have no setup guides tbh I'll provide a bash script here to build and get you started, do not use this in Prod like at all I dont recommend my own software for such things in general tbh. +**Windows Users**: See [README-WIN.md](README-WIN.md) for Windows specific setup instructions using PowerShell. + ## Whats in this repository? - Database Schema and Dumps from Dev Environement - Helper Script to get you started and do some basic user management as no client can do so yet lol @@ -25,8 +27,6 @@ someday once i consider em release worthy it will also obviously be able to comp a real installer that is not just a setup bash script is planned but thats far into the future idk when -Setup Scripts currently do not support windows - requirements for script: - mac - podman installed and configured (podman-desktop recommended for dev work) diff --git a/backend/seckelapi/.containerignore b/backend/seckelapi/.containerignore new file mode 100644 index 0000000..7d0306d --- /dev/null +++ b/backend/seckelapi/.containerignore @@ -0,0 +1,6 @@ +sources/target/ +sources/.git/ +sources/**/*.log +sources/.env +.git/ +*.log diff --git a/beepzone-helper.ps1 b/beepzone-helper.ps1 new file mode 100644 index 0000000..ffdd523 --- /dev/null +++ b/beepzone-helper.ps1 @@ -0,0 +1,822 @@ +# 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 + $script:MYSQL_CLIENT = "" + + # First check if already in PATH + foreach ($cmd in @("mysql", "mariadb")) { + if (Get-Command $cmd -ErrorAction SilentlyContinue) { + $script:MYSQL_CLIENT = $cmd + $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) { + $script:MYSQL_CLIENT = $found.FullName + $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 (winget install Oracle.MySQL or MariaDB.MariaDB)" + } + + if (-not (Get-Command git -ErrorAction SilentlyContinue)) { + $missing += "Git (winget install Git.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 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", + "Quit" + ) + + switch ($choice) { + 1 { Configure-Database } + 2 { Import-Database } + 3 { Manage-Users } + 4 { Setup-SeckelAPI } + 5 { Build-DesktopClient } + 6 { exit 0 } + } +} |
