#!/usr/bin/env bash set -euo pipefail # Simple TUI-based BeepZone setup helper using `dialog`. # Targets macOS + Debian-ish Linux (podman, rustup mysql-client already installed). SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WORK_DIR="${SCRIPT_DIR}" : "${DIALOG:=dialog}" if ! command -v "$DIALOG" >/dev/null 2>&1; then echo "\n[ERROR] 'dialog' is not installed or not in PATH." >&2 echo "On macOS: brew install dialog" >&2 echo "On Debian: sudo apt install dialog" >&2 exit 1 fi # Determine whether the dialog implementation supports custom OK label # 'dialog' uses --ok-label, 'whiptail' uses --ok-button declare -a DIALOG_OK_FLAG=() if "$DIALOG" --help 2>&1 | grep -q -- "--ok-label"; then DIALOG_OK_FLAG=(--ok-label "Continue") elif "$DIALOG" --help 2>&1 | grep -q -- "--ok-button"; then DIALOG_OK_FLAG=(--ok-button "Continue") fi HAS_PODMAN=true if ! command -v podman >/dev/null 2>&1; then HAS_PODMAN=false fi IS_DEBIAN_NATIVE=false if [[ -f /etc/debian_version ]]; then if grep -qE "^(12|13)" /etc/debian_version; then IS_DEBIAN_NATIVE=true fi fi # Check for MariaDB/MySQL client - try common brew locations on macOS MYSQL_CLIENT="" if command -v mariadb >/dev/null 2>&1; then MYSQL_CLIENT="mariadb" elif command -v mysql >/dev/null 2>&1; then MYSQL_CLIENT="mysql" elif [[ -x /opt/homebrew/opt/mysql-client/bin/mysql ]]; then MYSQL_CLIENT="/opt/homebrew/opt/mysql-client/bin/mysql" export PATH="/opt/homebrew/opt/mysql-client/bin:$PATH" elif [[ -x /opt/homebrew/opt/mariadb/bin/mariadb ]]; then MYSQL_CLIENT="/opt/homebrew/opt/mariadb/bin/mariadb" export PATH="/opt/homebrew/opt/mariadb/bin:$PATH" elif [[ -x /opt/homebrew/bin/mariadb ]]; then MYSQL_CLIENT="/opt/homebrew/bin/mariadb" export PATH="/opt/homebrew/bin:$PATH" elif [[ -x /opt/homebrew/bin/mysql ]]; then MYSQL_CLIENT="/opt/homebrew/bin/mysql" export PATH="/opt/homebrew/bin:$PATH" elif [[ -x /usr/local/opt/mysql-client/bin/mysql ]]; then MYSQL_CLIENT="/usr/local/opt/mysql-client/bin/mysql" export PATH="/usr/local/opt/mysql-client/bin:$PATH" elif [[ -x /usr/local/bin/mariadb ]]; then MYSQL_CLIENT="/usr/local/bin/mariadb" export PATH="/usr/local/bin:$PATH" elif [[ -x /usr/local/bin/mysql ]]; then MYSQL_CLIENT="/usr/local/bin/mysql" export PATH="/usr/local/bin:$PATH" fi # Cargo / Git detection (hide menu options if missing) HAS_CARGO=false if command -v cargo >/dev/null 2>&1; then HAS_CARGO=true fi HAS_GIT=false if command -v git >/dev/null 2>&1; then HAS_GIT=true fi TMP_FILE="$(mktemp)" CHOICE_FILE="$(mktemp)" trap 'rm -f "$TMP_FILE" "$CHOICE_FILE"' EXIT LOG_FILE="/tmp/beepzone-helper.log" > "$LOG_FILE" ENV_FILE="$SCRIPT_DIR/.env" # Load existing settings from .env if present if [[ -f "$ENV_FILE" ]]; then source "$ENV_FILE" fi # Set defaults if not loaded from .env : "${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}" save_env() { cat > "$ENV_FILE" << EOF # BeepZone Setup Configuration # Mostly autogenerated by beepzone-helper BEEPZONE_DB_CONTAINER_NAME="$BEEPZONE_DB_CONTAINER_NAME" BEEPZONE_DB_IMAGE="$BEEPZONE_DB_IMAGE" DB_HOST="$DB_HOST" DB_PORT="$DB_PORT" DB_NAME="$DB_NAME" DB_USER="$DB_USER" DB_PASS="$DB_PASS" DB_ROOT_PASSWORD="$DB_ROOT_PASSWORD" SECKELAPI_REPO="$SECKELAPI_REPO" CLIENT_REPO="$CLIENT_REPO" DEPLOYMENT_TYPE="$DEPLOYMENT_TYPE" EOF } show_capabilities_screen() { local cf cf="$(mktemp)" { echo "Dependency Overview" echo "" echo "What you can do :" if $HAS_PODMAN; then echo " [X] Configure and run MariaDB using podman" else echo " [ ] Configure and run MariaDB using podman - requires podman in PATH" fi if $IS_DEBIAN_NATIVE; then echo " [X] Configure and run MariaDB natively on your system" else echo " [ ] Configure and run MariaDB natively on your system - requires Debian 12/13" fi if [[ -n "$MYSQL_CLIENT" ]]; then echo " [X] Import DB schema or seeded demo DB" echo " [X] Manage users and roles" else echo " [ ] Import DB schema or seeded demo DB - requires MariaDB/MySQL client" echo " [ ] Manage users and roles - requires MariaDB/MySQL client" fi if $HAS_PODMAN; then echo " [X] Configure and setup SeckelAPI using podman" else echo " [ ] Configure and setup SeckelAPI using podman - requires podman in PATH" fi if $IS_DEBIAN_NATIVE && $HAS_CARGO; then echo " [X] Setup SeckelAPI natively on your system" else echo " [ ] Setup SeckelAPI natively on your system - requires Debian 12/13 and cargo" fi if $HAS_CARGO; then echo " [X] Build desktop client" else echo " [ ] Build desktop client - requires cargo (Rust toolchain)" fi if $HAS_GIT; then echo " [X] Update from git masters" else echo " [ ] Update from git masters - requires git" fi echo " [X] Clean sources" echo " [X] Quit" echo "Options that depend on missing tools are hidden in the main menu." } > "$cf" "$DIALOG" --title "BeepZone Helper" "${DIALOG_OK_FLAG[@]}" --textbox "$cf" 22 78 || true rm -f "$cf" } ask_main_menu() { while true; do local options=() if $HAS_PODMAN; then options+=(1 "Configure and run MariaDB (podman)") fi if $IS_DEBIAN_NATIVE; then options+=(9 "Configure and run MariaDB (native Debian)") if $HAS_CARGO; then options+=(10 "Setup SeckelAPI (Native Debian)") fi fi if [[ -n "$MYSQL_CLIENT" ]]; then options+=( 2 "Import DB schema and data" 3 "Manage users and roles" ) fi if $HAS_PODMAN; then options+=(4 "Configure and setup SeckelAPI (podman)") fi if $HAS_CARGO; then options+=(5 "Build desktop client") fi options+=(6 "Update from git masters") options+=(7 "Clean sources") options+=(8 "Quit") $DIALOG --clear \ --title "BeepZone Setup" \ --menu "Choose an action" 17 76 7 \ "${options[@]}" 2>"$CHOICE_FILE" choice=$(<"$CHOICE_FILE") case $choice in 1) configure_and_run_db ;; 9) configure_and_run_native_db ;; 10) setup_seckelapi_native ;; 2) import_schema_and_seed ;; 3) manage_users_and_roles ;; 4) setup_seckelapi ;; 5) build_desktop_client ;; 6) update_from_git_masters ;; 7) clean_sources ;; 8) exit 0 ;; esac done } configure_and_run_db() { $DIALOG --form "MariaDB container configuration" 18 70 8 \ "DB Host" 1 1 "$DB_HOST" 1 18 30 30 \ "DB Port" 2 1 "$DB_PORT" 2 18 30 30 \ "DB Name" 3 1 "$DB_NAME" 3 18 30 30 \ "DB User" 4 1 "$DB_USER" 4 18 30 30 \ "DB Password" 5 1 "$DB_PASS" 5 18 30 30 \ "Root Password" 6 1 "$DB_ROOT_PASSWORD" 6 18 30 30 \ "Container Name" 7 1 "$BEEPZONE_DB_CONTAINER_NAME" 7 18 30 30 \ "Image" 8 1 "$BEEPZONE_DB_IMAGE" 8 18 30 30 2>"$TMP_FILE" || return # Parse dialog output (8 lines, one per field) { read -r DB_HOST read -r DB_PORT read -r DB_NAME read -r DB_USER read -r DB_PASS read -r DB_ROOT_PASSWORD read -r BEEPZONE_DB_CONTAINER_NAME read -r BEEPZONE_DB_IMAGE } < "$TMP_FILE" save_env if podman ps -a --format '{{.Names}}' | grep -q "^${BEEPZONE_DB_CONTAINER_NAME}$"; then $DIALOG --yesno "Container '$BEEPZONE_DB_CONTAINER_NAME' already exists.\n\nStart (or restart) it now?" 10 60 if [[ $? -eq 0 ]]; then podman start "$BEEPZONE_DB_CONTAINER_NAME" >/dev/null 2>&1 || podman restart "$BEEPZONE_DB_CONTAINER_NAME" >/dev/null 2>&1 || true $DIALOG --msgbox "Container '$BEEPZONE_DB_CONTAINER_NAME' started (or already running)." 7 60 fi return fi run_cmd=( podman run -d --name "${BEEPZONE_DB_CONTAINER_NAME}" -e "MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}" -e "MARIADB_DATABASE=${DB_NAME}" -e "MARIADB_USER=${DB_USER}" -e "MARIADB_PASSWORD=${DB_PASS}" -p "${DB_PORT}:3306" "${BEEPZONE_DB_IMAGE}" ) pretty_cmd="podman run -d \\ --name ${BEEPZONE_DB_CONTAINER_NAME} \\ -e MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} \\ -e MARIADB_DATABASE=${DB_NAME} \\ -e MARIADB_USER=${DB_USER} \\ -e MARIADB_PASSWORD=${DB_PASS} \\ -p ${DB_PORT}:3306 ${BEEPZONE_DB_IMAGE}" if $DIALOG --yesno "About to run:\n\n${pretty_cmd}\n\nProceed?" 17 76; then if "${run_cmd[@]}" >>"$LOG_FILE" 2>&1; then $DIALOG --msgbox "MariaDB container '${BEEPZONE_DB_CONTAINER_NAME}' started successfully." 8 70 else error_log=$(tail -20 "$LOG_FILE") $DIALOG --title "Error" --msgbox "Failed to start MariaDB container.\n\nError:\n${error_log}" 20 76 fi fi } configure_and_run_native_db() { "$DIALOG" --msgbox "You'll need this for the Native MariaDB Setup:\n\n1. Debian 12/13\n2. Sudo privileges\n3. The following packages installed:\n - mariadb-server\n - mariadb-client\n - sudo\n\nIf you are unsure or know you're missing one hit CTRL-C now" 14 60 # Ask if root password is set if ! "$DIALOG" --yesno "Have you already set up a root password for MariaDB?" 10 60; then # User said No (exit code 1) clear echo "Running database secure installation..." if command -v mariadb-secure-installation >/dev/null 2>&1; then sudo mariadb-secure-installation || true elif command -v mysql_secure_installation >/dev/null 2>&1; then sudo mysql_secure_installation || true else "$DIALOG" --msgbox "Neither 'mariadb-secure-installation' nor 'mysql_secure_installation' was found in PATH.\n\nPlease install MariaDB/MySQL server packages and rerun this step." 12 70 fi fi $DIALOG --form "MariaDB configuration" 16 70 6 \ "DB Host" 1 1 "$DB_HOST" 1 18 30 30 \ "DB Port" 2 1 "$DB_PORT" 2 18 30 30 \ "DB Name" 3 1 "$DB_NAME" 3 18 30 30 \ "DB User" 4 1 "$DB_USER" 4 18 30 30 \ "DB Password" 5 1 "$DB_PASS" 5 18 30 30 \ "Root Password" 6 1 "$DB_ROOT_PASSWORD" 6 18 30 30 2>"$TMP_FILE" || return # Parse dialog output (6 lines) { read -r DB_HOST read -r DB_PORT read -r DB_NAME read -r DB_USER read -r DB_PASS read -r DB_ROOT_PASSWORD } < "$TMP_FILE" save_env # Check if mariadb is even running if ! systemctl is-active --quiet mariadb; then if $DIALOG --yesno "MariaDB service aint running. Try and start it?" 10 60; then sudo systemctl start mariadb || { $DIALOG --msgbox "Crap, Failed to start MariaDB service" 8 60 return } else return fi fi local create_sql=" CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\`; CREATE USER IF NOT EXISTS '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASS}'; GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'%'; FLUSH PRIVILEGES; " if "$DIALOG" --yesno "Script will now do the following:\n\nCreate DB: $DB_NAME\nCreate User: $DB_USER\n\nThis alright?" 12 70; then if echo "$create_sql" | mysql -u root -p"$DB_ROOT_PASSWORD" 2>>"$LOG_FILE"; then "$DIALOG" --msgbox "Database '$DB_NAME' and user '$DB_USER' created successfully." 8 70 else "$DIALOG" --msgbox "Fuck something went wrong Check $LOG_FILE for details." 8 70 fi fi } import_schema_and_seed() { local import_type schema_file full_dump_file schema_file="$WORK_DIR/backend/database/schema/beepzone-schema-dump.sql" full_dump_file="$WORK_DIR/backend/database/dev/beepzone-full-dump.sql" # Ask what type of import $DIALOG --clear \ --title "Database Import" \ --menu "Select import type" 12 70 2 \ 1 "Full dump (schema + data in one file, not recommended unless you know what you're doing)" \ 2 "Clean schema only (no data, recommended but make sure to read docs on how to get started)" 2>"$CHOICE_FILE" || return import_type=$(<"$CHOICE_FILE") if [[ "$import_type" == "1" ]]; then # Full dump import if [[ ! -f "$full_dump_file" ]]; then $DIALOG --msgbox "full dump file not found at:\n$full_dump_file" 10 70 return fi $DIALOG --yesno "import full database dump from:\n$full_dump_file\n\nThis will DROP and recreate the database.\n\nYou Sure?" 12 70 || return { echo "DROP DATABASE IF EXISTS \`$DB_NAME\`;" echo "CREATE DATABASE \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;" echo "USE \`$DB_NAME\`;" echo "SET FOREIGN_KEY_CHECKS=0;" cat "$full_dump_file" echo "SET FOREIGN_KEY_CHECKS=1;" } | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" >>"$LOG_FILE" 2>&1 || { error_log=$(tail -30 "$LOG_FILE") $DIALOG --title "Fuck" --msgbox "full dump import failed.\n\nError:\n${error_log}" 22 76 return } DEPLOYMENT_TYPE="dev" save_env $DIALOG --msgbox "full database dump imported successfully!" 8 70 else # Clean schema import if [[ ! -f "$schema_file" ]]; then $DIALOG --msgbox "schema file not found at:\n$schema_file" 10 60 return fi $DIALOG --yesno "import clean schema from:\n$schema_file\n\nThis will DROP and recreate the database.\n\nProceed?" 12 70 || return { echo "DROP DATABASE IF EXISTS \`$DB_NAME\`;" echo "CREATE DATABASE \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;" echo "USE \`$DB_NAME\`;" echo "SET FOREIGN_KEY_CHECKS=0;" cat "$schema_file" echo "SET FOREIGN_KEY_CHECKS=1;" } | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" >>"$LOG_FILE" 2>&1 || { error_log=$(tail -30 "$LOG_FILE") $DIALOG --title "Error" --msgbox "schema import failed.\n\nError:\n${error_log}" 22 76 return } DEPLOYMENT_TYPE="clean" save_env $DIALOG --msgbox "schema imported successfully!\n\nI recommend you make an Admin role with power 100 and and Admin user as the next step." 8 70 fi } manage_users_and_roles() { while true; do $DIALOG --clear \ --title "Manage Users & Roles" \ --menu "Choose an action:" 17 60 6 \ 1 "Create a role" \ 2 "Create a user" \ 3 "Delete a role" \ 4 "Delete a user" \ 5 "Back to main menu" 2>"$CHOICE_FILE" [[ ! -s "$CHOICE_FILE" ]] && return 0 choice=$(<"$CHOICE_FILE") case $choice in 1) create_role ;; 2) create_user ;; 3) delete_role ;; 4) delete_user ;; 5) return 0 ;; esac done } create_role() { $DIALOG --title "Create Role" \ --form "Enter role details:" 12 60 2 \ "Role name:" 1 1 "" 1 20 30 0 \ "Power (1-100):" 2 1 "" 2 20 10 0 \ 2>"$TMP_FILE" [[ ! -s "$TMP_FILE" ]] && return 1 local name power { read -r name read -r power } < "$TMP_FILE" [[ -z "$name" || -z "$power" ]] && { $DIALOG --msgbox "Role name and power are required." 8 50 return 1 } # Validate power is a number between 1-100 if ! [[ "$power" =~ ^[0-9]+$ ]] || [[ "$power" -lt 1 || "$power" -gt 100 ]]; then $DIALOG --msgbox "Power must be a number between 1 and 100." 8 50 return 1 fi local sql="INSERT INTO roles (name, power) VALUES ('$name', $power);" echo "$sql" | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" >>"$LOG_FILE" 2>&1 || { error_log=$(tail -20 "$LOG_FILE") $DIALOG --title "Error" --msgbox "Failed to create role.\n\nError:\n${error_log}" 20 76 return 1 } $DIALOG --msgbox "Role '$name' created successfully!" 8 50 } delete_role() { # Fetch roles from the database local roles_list roles_list=$(podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" \ -e "SELECT id, name, power FROM roles ORDER BY power DESC;" -s -N 2>>"$LOG_FILE") || { $DIALOG --msgbox "Failed to fetch roles from database." 10 60 return } # Build role selection menu local role_options=() while IFS=$'\t' read -r role_id role_name role_power; do role_options+=("$role_id" "$role_name (power: $role_power)") done <<< "$roles_list" if [[ ${#role_options[@]} -eq 0 ]]; then $DIALOG --msgbox "No roles found in database." 10 60 return fi # Select role to delete $DIALOG --menu "Select role to delete:" 20 70 10 "${role_options[@]}" 2>"$TMP_FILE" || return local selected_role_id selected_role_id=$(<"$TMP_FILE") # Get role name for confirmation local selected_role_name selected_role_name=$(echo "$roles_list" | awk -v id="$selected_role_id" -F'\t' '$1 == id {print $2}') # Check if any users are using this role local user_count user_count=$(podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" \ -e "SELECT COUNT(*) FROM users WHERE role_id = $selected_role_id;" -s -N 2>>"$LOG_FILE") if [[ "$user_count" -gt 0 ]]; then $DIALOG --msgbox "Cannot delete role '$selected_role_name'.\n$user_count user(s) are currently assigned this role." 10 60 return fi # Confirm deletion $DIALOG --yesno "Are you sure you want to delete role '$selected_role_name' (ID: $selected_role_id)?\n\nThis action cannot be undone." 10 60 || return # Delete role local sql="DELETE FROM roles WHERE id = $selected_role_id;" echo "$sql" | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" >>"$LOG_FILE" 2>&1 || { error_log=$(tail -20 "$LOG_FILE") $DIALOG --title "Error" --msgbox "Failed to delete role.\n\nError:\n${error_log}" 20 76 return } $DIALOG --msgbox "Role '$selected_role_name' deleted successfully!" 8 60 } create_user() { # Get available roles local roles_list roles_list=$(podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" \ -e "SELECT id, name, power FROM roles ORDER BY power DESC;" -s -N 2>>"$LOG_FILE") || { $DIALOG --msgbox "Failed to fetch roles from database.\nEnsure schema is imported and roles exist." 10 60 return } # Build role selection menu local role_options=() while IFS=$'\t' read -r role_id role_name role_power; do role_options+=("$role_id" "$role_name (power: $role_power)") done <<< "$roles_list" if [[ ${#role_options[@]} -eq 0 ]]; then $DIALOG --msgbox "No roles found in database.\nPlease create a role first." 10 60 return fi # Select role $DIALOG --menu "Select user role" 15 60 5 "${role_options[@]}" 2>"$TMP_FILE" || return local selected_role_id selected_role_id=$(<"$TMP_FILE") # Get user details $DIALOG --form "Create BeepZone user" 14 70 5 \ "Name (full name)" 1 1 "" 1 20 40 40 \ "Username" 2 1 "" 2 20 40 40 \ "Password" 3 1 "" 3 20 40 40 \ "Email" 4 1 "" 4 20 40 40 \ "Phone" 5 1 "" 5 20 40 40 2>"$TMP_FILE" || return local name username password email phone { read -r name read -r username read -r password read -r email read -r phone } < "$TMP_FILE" if [[ -z "$name" || -z "$username" || -z "$password" ]]; then $DIALOG --msgbox "Name, username, and password are required." 8 60 return fi # Hash password with bcrypt (cost 12) local password_hash # Try htpasswd first (native Unix tool) if command -v htpasswd >/dev/null 2>&1; then password_hash=$(htpasswd -nbB -C 12 "$username" "$password" 2>>"$LOG_FILE" | cut -d: -f2) # Fall back to Python bcrypt elif command -v python3 >/dev/null 2>&1; then password_hash=$(python3 -c "import bcrypt; print(bcrypt.hashpw('$password'.encode('utf-8'), bcrypt.gensalt(12)).decode('utf-8'))" 2>>"$LOG_FILE") fi if [[ -z "$password_hash" ]]; then $DIALOG --msgbox "Error: Failed to hash password. No bcrypt tool found.\n\nInstall options:\n- htpasswd: brew install httpd (macOS) or apt install apache2-utils (Linux)\n- Python bcrypt: pip3 install bcrypt" 12 70 return fi # Insert user with hashed password local sql sql="INSERT INTO users (name, username, password, role_id, email, phone, active) VALUES ('$name', '$username', '$password_hash', $selected_role_id, " [[ -n "$email" ]] && sql+="'$email'" || sql+="NULL" sql+=", " [[ -n "$phone" ]] && sql+="'$phone'" || sql+="NULL" sql+=", 1);" echo "$sql" | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" >>"$LOG_FILE" 2>&1 || { error_log=$(tail -20 "$LOG_FILE") $DIALOG --title "Error" --msgbox "Failed to create user.\n\nError:\n${error_log}" 20 76 return } $DIALOG --msgbox "User '$username' created successfully!" 8 60 } delete_user() { # Fetch users from the database local users_list users_list=$(podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" \ -e "SELECT id, username, name FROM users ORDER BY id;" -s -N 2>>"$LOG_FILE") || { $DIALOG --msgbox "Failed to fetch users from database." 10 60 return } # Build user selection menu local user_options=() while IFS=$'\t' read -r user_id username name; do user_options+=("$user_id" "$username - $name") done <<< "$users_list" if [[ ${#user_options[@]} -eq 0 ]]; then $DIALOG --msgbox "No users found in database." 10 60 return fi # Select user to delete $DIALOG --menu "Select user to delete:" 20 70 10 "${user_options[@]}" 2>"$TMP_FILE" || return local selected_user_id selected_user_id=$(<"$TMP_FILE") # Get username for confirmation local selected_username selected_username=$(echo "$users_list" | awk -v id="$selected_user_id" -F'\t' '$1 == id {print $2}') # Confirm deletion $DIALOG --yesno "Are you sure you want to delete user '$selected_username' (ID: $selected_user_id)?\n\nThis action cannot be undone." 10 60 || return # Delete user local sql="DELETE FROM users WHERE id = $selected_user_id;" echo "$sql" | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \ mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" >>"$LOG_FILE" 2>&1 || { error_log=$(tail -20 "$LOG_FILE") $DIALOG --title "Error" --msgbox "Failed to delete user.\n\nError:\n${error_log}" 20 76 return } $DIALOG --msgbox "User '$selected_username' deleted successfully!" 8 60 } clone_if_missing() { local repo_url="$1" dest_dir="$2" if [[ -d "$dest_dir/.git" ]]; then return fi git clone "$repo_url" "$dest_dir" >>"$LOG_FILE" 2>&1 || { error_log=$(tail -20 "$LOG_FILE") $DIALOG --title "Error" --msgbox "Failed to clone $repo_url.\n\nError:\n${error_log}" 20 76 return 1 } } setup_seckelapi() { local sources_dir="$WORK_DIR/backend/seckelapi/sources" local config_dir="$WORK_DIR/backend/seckelapi/config" local sources_basics_config="$sources_dir/config/basics.toml" # Check if sources are already cloned if [[ ! -d "$sources_dir/.git" ]]; then mkdir -p "$sources_dir" clone_if_missing "$SECKELAPI_REPO" "$sources_dir" || return fi # Ask about config update $DIALOG --clear \ --title "SeckelAPI Configuration" \ --menu "How do you want to handle the config?" 12 70 2 \ 1 "Auto-update from database settings" \ 2 "Manually edit config file" 2>"$CHOICE_FILE" [[ ! -s "$CHOICE_FILE" ]] && return 0 config_choice=$(<"$CHOICE_FILE") # Ensure sources config directory exists and copy all template configs mkdir -p "$sources_dir/config" # Always copy all template configs from config/ to sources/config/ for conf_file in "$config_dir"/*.toml; do conf_name=$(basename "$conf_file") cp "$conf_file" "$sources_dir/config/$conf_name" 2>>"$LOG_FILE" done if [[ "$config_choice" == "1" ]]; then # Auto-update basics.toml in sources with database settings (only [database] section) if [[ -f "$sources_basics_config" ]]; then # Determine database host - use host.containers.internal for container deployments local db_host_value="$DB_HOST" sed -i.bak \ -e '/^\[database\]/,/^\[/ { /^host = /s|= .*|= "'"$db_host_value"'"| /^port = /s|= .*|= '"$DB_PORT"'| /^database = /s|= .*|= "'"$DB_NAME"'"| /^username = /s|= .*|= "'"$DB_USER"'"| /^password = /s|= .*|= "'"$DB_PASS"'"| }' \ "$sources_basics_config" $DIALOG --msgbox "Config updated with database settings from .env" 8 60 else $DIALOG --msgbox "Error: basics.toml not found at $sources_basics_config" 8 60 return 1 fi else # Open config file for manual editing (in sources/config/) if [[ -f "$sources_basics_config" ]]; then ${EDITOR:-nano} "$sources_basics_config" else $DIALOG --msgbox "Error: basics.toml not found at $sources_basics_config" 8 60 return 1 fi fi # Ask about build and deployment $DIALOG --clear \ --title "Build & Deploy SeckelAPI" \ --menu "Choose an action:" 12 70 2 \ 1 "Build and deploy to podman container" \ $( $HAS_CARGO && echo "2 \"Build natively on host\"" ) 2>"$CHOICE_FILE" [[ ! -s "$CHOICE_FILE" ]] && return 0 build_choice=$(<"$CHOICE_FILE") if [[ "$build_choice" == "2" ]]; then # Native host build with live output clear echo "Building SeckelAPI..." echo "====================" echo "" if (cd "$sources_dir" && cargo build --release); then # Copy config files to the build output directory local target_dir="$sources_dir/target/release" mkdir -p "$target_dir/config" cp "$sources_dir/config"/*.toml "$target_dir/config/" 2>>"$LOG_FILE" echo "" echo "Build completed successfully!" echo "Binary location: $target_dir/seckelapi" echo "Config location: $target_dir/config/" read -p "Press Enter to continue..." else echo "" echo "Build failed! Check the output above for errors." read -p "Press Enter to continue..." return 1 fi elif [[ "$build_choice" == "1" ]]; then # Build and deploy to podman container clear echo "Building SeckelAPI container..." echo "================================" echo "" # Get the host gateway IP for container to access host services # For Podman, we'll use the gateway IP from the default bridge network local host_gateway="host.containers.internal" # Update database host for container networking if [[ -f "$sources_basics_config" ]]; then echo "Updating database host to: $host_gateway" sed -i.container-bak \ -e '/^\[database\]/,/^\[/ { /^host = /s|= .*|= "'"$host_gateway"'"| }' \ "$sources_basics_config" fi local container_name="beepzone-seckelapi" local image_name="beepzone-seckelapi:latest" local containerfile="$WORK_DIR/backend/seckelapi/Containerfile" # Stop and remove existing container if running if podman ps -a --format "{{.Names}}" | grep -q "^${container_name}$"; then echo "Stopping and removing existing container..." podman stop "$container_name" 2>/dev/null || true podman rm "$container_name" 2>/dev/null || true fi # Build container image echo "Building container image..." if podman build -t "$image_name" -f "$containerfile" "$WORK_DIR/backend/seckelapi"; then echo "" echo "Container image built successfully!" echo "" # Ask to run the container if $DIALOG --yesno "Start the SeckelAPI container now?" 8 50; then echo "Starting container..." # Run container with port mapping and host gateway if podman run -d \ --name "$container_name" \ --add-host host.containers.internal:host-gateway \ -p 5777:5777 \ "$image_name"; then echo "" echo "Container started successfully!" echo "Container name: $container_name" echo "API listening on: http://0.0.0.0:5777" echo "" echo "Useful commands:" echo " podman logs $container_name - View logs" echo " podman stop $container_name - Stop container" echo " podman start $container_name - Start container" echo " podman restart $container_name - Restart container" read -p "Press Enter to continue..." else echo "" echo "Failed to start container!" read -p "Press Enter to continue..." return 1 fi fi else echo "" echo "Container build failed! Check the output above for errors." read -p "Press Enter to continue..." return 1 fi fi } setup_seckelapi_native() { if ! $HAS_CARGO; then "$DIALOG" --msgbox "Cargo not found in PATH.\n\nInstall Rust toolchain (rustup) to use native SeckelAPI setup." 10 70 return fi # 1. Check Pre-requisites $DIALOG --msgbox "Pre-requisites for Native SeckelAPI Setup:\n\n1. Rust toolchain (rustup, cargo)\n2. build-essential (gcc, etc.)\n3. pkg-config\n4. libssl-dev\n5. Sudo privileges (for systemd service)\n\nEnsure these are met before proceeding." 14 60 local sources_dir="$WORK_DIR/backend/seckelapi/sources" local config_dir="$WORK_DIR/backend/seckelapi/config" local sources_basics_config="$sources_dir/config/basics.toml" # 2. Clone sources if missing if [[ ! -d "$sources_dir/.git" ]]; then mkdir -p "$sources_dir" clone_if_missing "$SECKELAPI_REPO" "$sources_dir" || return fi # 3. Configure basics.toml # Ensure sources config directory exists and copy all template configs mkdir -p "$sources_dir/config" for conf_file in "$config_dir"/*.toml; do conf_name=$(basename "$conf_file") cp "$conf_file" "$sources_dir/config/$conf_name" 2>>"$LOG_FILE" done if [[ -f "$sources_basics_config" ]]; then # Update with DB settings from .env (localhost since it's native) sed -i.bak \ -e '/^\[database\]/,/^\[/ { /^host = /s|= .*|= "'"$DB_HOST"'"| /^port = /s|= .*|= '"$DB_PORT"'| /^database = /s|= .*|= "'"$DB_NAME"'"| /^username = /s|= .*|= "'"$DB_USER"'"| /^password = /s|= .*|= "'"$DB_PASS"'"| }' \ "$sources_basics_config" else $DIALOG --msgbox "Error: basics.toml not found at $sources_basics_config" 8 60 return 1 fi # 4. Build Release clear echo "Building SeckelAPI (Native)..." echo "============================" echo "" if (cd "$sources_dir" && cargo build --release); then # Copy config files to the build output directory local target_dir="$sources_dir/target/release" mkdir -p "$target_dir/config" cp "$sources_dir/config"/*.toml "$target_dir/config/" 2>>"$LOG_FILE" echo "" echo "Build completed successfully!" else echo "" echo "Build failed! Check the output above for errors." read -p "Press Enter to continue..." return 1 fi # 5. Install Systemd Service if $DIALOG --yesno "Do you want to install SeckelAPI as a systemd service?" 8 60; then local service_name="beepzone-seckelapi" local service_file="/tmp/${service_name}.service" local current_user current_user=$(whoami) # Working directory for the binary local working_dir="$sources_dir/target/release" local exec_path="$working_dir/seckelapi" cat > "$service_file" <