beepzone-helper.sh 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. # Simple TUI-based BeepZone setup helper using `dialog`.
  4. # Targets macOS + Debian-ish Linux (podman, rustup mysql-client already installed).
  5. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  6. WORK_DIR="${SCRIPT_DIR}"
  7. : "${DIALOG:=dialog}"
  8. if ! command -v "$DIALOG" >/dev/null 2>&1; then
  9. echo "\n[ERROR] 'dialog' is not installed or not in PATH." >&2
  10. echo "On macOS: brew install dialog" >&2
  11. echo "On Debian: sudo apt install dialog" >&2
  12. exit 1
  13. fi
  14. # Determine whether the dialog implementation supports custom OK label
  15. # 'dialog' uses --ok-label, 'whiptail' uses --ok-button
  16. declare -a DIALOG_OK_FLAG=()
  17. if "$DIALOG" --help 2>&1 | grep -q -- "--ok-label"; then
  18. DIALOG_OK_FLAG=(--ok-label "Continue")
  19. elif "$DIALOG" --help 2>&1 | grep -q -- "--ok-button"; then
  20. DIALOG_OK_FLAG=(--ok-button "Continue")
  21. fi
  22. HAS_PODMAN=true
  23. if ! command -v podman >/dev/null 2>&1; then
  24. HAS_PODMAN=false
  25. fi
  26. IS_DEBIAN_NATIVE=false
  27. if [[ -f /etc/debian_version ]]; then
  28. if grep -qE "^(12|13)" /etc/debian_version; then
  29. IS_DEBIAN_NATIVE=true
  30. fi
  31. fi
  32. # Check for MariaDB/MySQL client - try common brew locations on macOS
  33. MYSQL_CLIENT=""
  34. if command -v mariadb >/dev/null 2>&1; then
  35. MYSQL_CLIENT="mariadb"
  36. elif command -v mysql >/dev/null 2>&1; then
  37. MYSQL_CLIENT="mysql"
  38. elif [[ -x /opt/homebrew/opt/mysql-client/bin/mysql ]]; then
  39. MYSQL_CLIENT="/opt/homebrew/opt/mysql-client/bin/mysql"
  40. export PATH="/opt/homebrew/opt/mysql-client/bin:$PATH"
  41. elif [[ -x /opt/homebrew/opt/mariadb/bin/mariadb ]]; then
  42. MYSQL_CLIENT="/opt/homebrew/opt/mariadb/bin/mariadb"
  43. export PATH="/opt/homebrew/opt/mariadb/bin:$PATH"
  44. elif [[ -x /opt/homebrew/bin/mariadb ]]; then
  45. MYSQL_CLIENT="/opt/homebrew/bin/mariadb"
  46. export PATH="/opt/homebrew/bin:$PATH"
  47. elif [[ -x /opt/homebrew/bin/mysql ]]; then
  48. MYSQL_CLIENT="/opt/homebrew/bin/mysql"
  49. export PATH="/opt/homebrew/bin:$PATH"
  50. elif [[ -x /usr/local/opt/mysql-client/bin/mysql ]]; then
  51. MYSQL_CLIENT="/usr/local/opt/mysql-client/bin/mysql"
  52. export PATH="/usr/local/opt/mysql-client/bin:$PATH"
  53. elif [[ -x /usr/local/bin/mariadb ]]; then
  54. MYSQL_CLIENT="/usr/local/bin/mariadb"
  55. export PATH="/usr/local/bin:$PATH"
  56. elif [[ -x /usr/local/bin/mysql ]]; then
  57. MYSQL_CLIENT="/usr/local/bin/mysql"
  58. export PATH="/usr/local/bin:$PATH"
  59. fi
  60. # Cargo / Git detection (hide menu options if missing)
  61. HAS_CARGO=false
  62. if command -v cargo >/dev/null 2>&1; then
  63. HAS_CARGO=true
  64. fi
  65. HAS_GIT=false
  66. if command -v git >/dev/null 2>&1; then
  67. HAS_GIT=true
  68. fi
  69. TMP_FILE="$(mktemp)"
  70. CHOICE_FILE="$(mktemp)"
  71. trap 'rm -f "$TMP_FILE" "$CHOICE_FILE"' EXIT
  72. LOG_FILE="/tmp/beepzone-helper.log"
  73. > "$LOG_FILE"
  74. ENV_FILE="$SCRIPT_DIR/.env"
  75. # Load existing settings from .env if present
  76. if [[ -f "$ENV_FILE" ]]; then
  77. source "$ENV_FILE"
  78. fi
  79. # Set defaults if not loaded from .env
  80. : "${BEEPZONE_DB_CONTAINER_NAME:=beepzone-mariadb}"
  81. : "${BEEPZONE_DB_IMAGE:=mariadb:12}"
  82. : "${DB_HOST:=127.0.0.1}"
  83. : "${DB_PORT:=3306}"
  84. : "${DB_NAME:=beepzone}"
  85. : "${DB_USER:=beepzone}"
  86. : "${DB_PASS:=beepzone}"
  87. : "${DB_ROOT_PASSWORD:=root}"
  88. : "${SECKELAPI_REPO:=https://git.teleco.ch/crt/seckelapi.git}"
  89. : "${CLIENT_REPO:=https://git.teleco.ch/crt/beepzone-client-egui-emo.git}"
  90. : "${DEPLOYMENT_TYPE:=clean}"
  91. save_env() {
  92. cat > "$ENV_FILE" << EOF
  93. # BeepZone Setup Configuration
  94. # Mostly autogenerated by beepzone-helper
  95. BEEPZONE_DB_CONTAINER_NAME="$BEEPZONE_DB_CONTAINER_NAME"
  96. BEEPZONE_DB_IMAGE="$BEEPZONE_DB_IMAGE"
  97. DB_HOST="$DB_HOST"
  98. DB_PORT="$DB_PORT"
  99. DB_NAME="$DB_NAME"
  100. DB_USER="$DB_USER"
  101. DB_PASS="$DB_PASS"
  102. DB_ROOT_PASSWORD="$DB_ROOT_PASSWORD"
  103. SECKELAPI_REPO="$SECKELAPI_REPO"
  104. CLIENT_REPO="$CLIENT_REPO"
  105. DEPLOYMENT_TYPE="$DEPLOYMENT_TYPE"
  106. EOF
  107. }
  108. show_capabilities_screen() {
  109. local cf
  110. cf="$(mktemp)"
  111. {
  112. echo "Dependency Overview"
  113. echo ""
  114. echo "What you can do :"
  115. if $HAS_PODMAN; then
  116. echo " [X] Configure and run MariaDB using podman"
  117. else
  118. echo " [ ] Configure and run MariaDB using podman - requires podman in PATH"
  119. fi
  120. if $IS_DEBIAN_NATIVE; then
  121. echo " [X] Configure and run MariaDB natively on your system"
  122. else
  123. echo " [ ] Configure and run MariaDB natively on your system - requires Debian 12/13"
  124. fi
  125. if [[ -n "$MYSQL_CLIENT" ]]; then
  126. echo " [X] Import DB schema or seeded demo DB"
  127. echo " [X] Manage users and roles"
  128. else
  129. echo " [ ] Import DB schema or seeded demo DB - requires MariaDB/MySQL client"
  130. echo " [ ] Manage users and roles - requires MariaDB/MySQL client"
  131. fi
  132. if $HAS_PODMAN; then
  133. echo " [X] Configure and setup SeckelAPI using podman"
  134. else
  135. echo " [ ] Configure and setup SeckelAPI using podman - requires podman in PATH"
  136. fi
  137. if $IS_DEBIAN_NATIVE && $HAS_CARGO; then
  138. echo " [X] Setup SeckelAPI natively on your system"
  139. else
  140. echo " [ ] Setup SeckelAPI natively on your system - requires Debian 12/13 and cargo"
  141. fi
  142. if $HAS_CARGO; then
  143. echo " [X] Build desktop client"
  144. else
  145. echo " [ ] Build desktop client - requires cargo (Rust toolchain)"
  146. fi
  147. if $HAS_GIT; then
  148. echo " [X] Update from git masters"
  149. else
  150. echo " [ ] Update from git masters - requires git"
  151. fi
  152. echo " [X] Clean sources"
  153. echo " [X] Quit"
  154. echo "Options that depend on missing tools are hidden in the main menu."
  155. } > "$cf"
  156. "$DIALOG" --title "BeepZone Helper" "${DIALOG_OK_FLAG[@]}" --textbox "$cf" 22 78 || true
  157. rm -f "$cf"
  158. }
  159. ask_main_menu() {
  160. while true; do
  161. local options=()
  162. if $HAS_PODMAN; then
  163. options+=(1 "Configure and run MariaDB (podman)")
  164. fi
  165. if $IS_DEBIAN_NATIVE; then
  166. options+=(9 "Configure and run MariaDB (native Debian)")
  167. if $HAS_CARGO; then
  168. options+=(10 "Setup SeckelAPI (Native Debian)")
  169. fi
  170. fi
  171. if [[ -n "$MYSQL_CLIENT" ]]; then
  172. options+=(
  173. 2 "Import DB schema and data"
  174. 3 "Manage users and roles"
  175. )
  176. fi
  177. if $HAS_PODMAN; then
  178. options+=(4 "Configure and setup SeckelAPI (podman)")
  179. fi
  180. if $HAS_CARGO; then
  181. options+=(5 "Build desktop client")
  182. fi
  183. options+=(6 "Update from git masters")
  184. options+=(7 "Clean sources")
  185. options+=(8 "Quit")
  186. $DIALOG --clear \
  187. --title "BeepZone Setup" \
  188. --menu "Choose an action" 17 76 7 \
  189. "${options[@]}" 2>"$CHOICE_FILE"
  190. choice=$(<"$CHOICE_FILE")
  191. case $choice in
  192. 1) configure_and_run_db ;;
  193. 9) configure_and_run_native_db ;;
  194. 10) setup_seckelapi_native ;;
  195. 2) import_schema_and_seed ;;
  196. 3) manage_users_and_roles ;;
  197. 4) setup_seckelapi ;;
  198. 5) build_desktop_client ;;
  199. 6) update_from_git_masters ;;
  200. 7) clean_sources ;;
  201. 8) exit 0 ;;
  202. esac
  203. done
  204. }
  205. configure_and_run_db() {
  206. $DIALOG --form "MariaDB container configuration" 18 70 8 \
  207. "DB Host" 1 1 "$DB_HOST" 1 18 30 30 \
  208. "DB Port" 2 1 "$DB_PORT" 2 18 30 30 \
  209. "DB Name" 3 1 "$DB_NAME" 3 18 30 30 \
  210. "DB User" 4 1 "$DB_USER" 4 18 30 30 \
  211. "DB Password" 5 1 "$DB_PASS" 5 18 30 30 \
  212. "Root Password" 6 1 "$DB_ROOT_PASSWORD" 6 18 30 30 \
  213. "Container Name" 7 1 "$BEEPZONE_DB_CONTAINER_NAME" 7 18 30 30 \
  214. "Image" 8 1 "$BEEPZONE_DB_IMAGE" 8 18 30 30 2>"$TMP_FILE" || return
  215. # Parse dialog output (8 lines, one per field)
  216. {
  217. read -r DB_HOST
  218. read -r DB_PORT
  219. read -r DB_NAME
  220. read -r DB_USER
  221. read -r DB_PASS
  222. read -r DB_ROOT_PASSWORD
  223. read -r BEEPZONE_DB_CONTAINER_NAME
  224. read -r BEEPZONE_DB_IMAGE
  225. } < "$TMP_FILE"
  226. save_env
  227. if podman ps -a --format '{{.Names}}' | grep -q "^${BEEPZONE_DB_CONTAINER_NAME}$"; then
  228. $DIALOG --yesno "Container '$BEEPZONE_DB_CONTAINER_NAME' already exists.\n\nStart (or restart) it now?" 10 60
  229. if [[ $? -eq 0 ]]; then
  230. podman start "$BEEPZONE_DB_CONTAINER_NAME" >/dev/null 2>&1 || podman restart "$BEEPZONE_DB_CONTAINER_NAME" >/dev/null 2>&1 || true
  231. $DIALOG --msgbox "Container '$BEEPZONE_DB_CONTAINER_NAME' started (or already running)." 7 60
  232. fi
  233. return
  234. fi
  235. run_cmd=(
  236. podman run -d
  237. --name "${BEEPZONE_DB_CONTAINER_NAME}"
  238. -e "MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}"
  239. -e "MARIADB_DATABASE=${DB_NAME}"
  240. -e "MARIADB_USER=${DB_USER}"
  241. -e "MARIADB_PASSWORD=${DB_PASS}"
  242. -p "${DB_PORT}:3306"
  243. "${BEEPZONE_DB_IMAGE}"
  244. )
  245. pretty_cmd="podman run -d \\
  246. --name ${BEEPZONE_DB_CONTAINER_NAME} \\
  247. -e MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} \\
  248. -e MARIADB_DATABASE=${DB_NAME} \\
  249. -e MARIADB_USER=${DB_USER} \\
  250. -e MARIADB_PASSWORD=${DB_PASS} \\
  251. -p ${DB_PORT}:3306 ${BEEPZONE_DB_IMAGE}"
  252. if $DIALOG --yesno "About to run:\n\n${pretty_cmd}\n\nProceed?" 17 76; then
  253. if "${run_cmd[@]}" >>"$LOG_FILE" 2>&1; then
  254. $DIALOG --msgbox "MariaDB container '${BEEPZONE_DB_CONTAINER_NAME}' started successfully." 8 70
  255. else
  256. error_log=$(tail -20 "$LOG_FILE")
  257. $DIALOG --title "Error" --msgbox "Failed to start MariaDB container.\n\nError:\n${error_log}" 20 76
  258. fi
  259. fi
  260. }
  261. configure_and_run_native_db() {
  262. "$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
  263. # Ask if root password is set
  264. if ! "$DIALOG" --yesno "Have you already set up a root password for MariaDB?" 10 60; then
  265. # User said No (exit code 1)
  266. clear
  267. echo "Running database secure installation..."
  268. if command -v mariadb-secure-installation >/dev/null 2>&1; then
  269. sudo mariadb-secure-installation || true
  270. elif command -v mysql_secure_installation >/dev/null 2>&1; then
  271. sudo mysql_secure_installation || true
  272. else
  273. "$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
  274. fi
  275. fi
  276. $DIALOG --form "MariaDB configuration" 16 70 6 \
  277. "DB Host" 1 1 "$DB_HOST" 1 18 30 30 \
  278. "DB Port" 2 1 "$DB_PORT" 2 18 30 30 \
  279. "DB Name" 3 1 "$DB_NAME" 3 18 30 30 \
  280. "DB User" 4 1 "$DB_USER" 4 18 30 30 \
  281. "DB Password" 5 1 "$DB_PASS" 5 18 30 30 \
  282. "Root Password" 6 1 "$DB_ROOT_PASSWORD" 6 18 30 30 2>"$TMP_FILE" || return
  283. # Parse dialog output (6 lines)
  284. {
  285. read -r DB_HOST
  286. read -r DB_PORT
  287. read -r DB_NAME
  288. read -r DB_USER
  289. read -r DB_PASS
  290. read -r DB_ROOT_PASSWORD
  291. } < "$TMP_FILE"
  292. save_env
  293. # Check if mariadb is even running
  294. if ! systemctl is-active --quiet mariadb; then
  295. if $DIALOG --yesno "MariaDB service aint running. Try and start it?" 10 60; then
  296. sudo systemctl start mariadb || {
  297. $DIALOG --msgbox "Crap, Failed to start MariaDB service" 8 60
  298. return
  299. }
  300. else
  301. return
  302. fi
  303. fi
  304. local create_sql="
  305. CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\`;
  306. CREATE USER IF NOT EXISTS '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASS}';
  307. GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'%';
  308. FLUSH PRIVILEGES;
  309. "
  310. if "$DIALOG" --yesno "Script will now do the following:\n\nCreate DB: $DB_NAME\nCreate User: $DB_USER\n\nThis alright?" 12 70; then
  311. if echo "$create_sql" | mysql -u root -p"$DB_ROOT_PASSWORD" 2>>"$LOG_FILE"; then
  312. "$DIALOG" --msgbox "Database '$DB_NAME' and user '$DB_USER' created successfully." 8 70
  313. else
  314. "$DIALOG" --msgbox "Fuck something went wrong Check $LOG_FILE for details." 8 70
  315. fi
  316. fi
  317. }
  318. import_schema_and_seed() {
  319. local import_type schema_file full_dump_file
  320. schema_file="$WORK_DIR/backend/database/schema/beepzone-schema-dump.sql"
  321. full_dump_file="$WORK_DIR/backend/database/dev/beepzone-full-dump.sql"
  322. # Ask what type of import
  323. $DIALOG --clear \
  324. --title "Database Import" \
  325. --menu "Select import type" 12 70 2 \
  326. 1 "Full dump (schema + data in one file, not recommended unless you know what you're doing)" \
  327. 2 "Clean schema only (no data, recommended but make sure to read docs on how to get started)" 2>"$CHOICE_FILE" || return
  328. import_type=$(<"$CHOICE_FILE")
  329. if [[ "$import_type" == "1" ]]; then
  330. # Full dump import
  331. if [[ ! -f "$full_dump_file" ]]; then
  332. $DIALOG --msgbox "full dump file not found at:\n$full_dump_file" 10 70
  333. return
  334. fi
  335. $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
  336. {
  337. echo "DROP DATABASE IF EXISTS \`$DB_NAME\`;"
  338. echo "CREATE DATABASE \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;"
  339. echo "USE \`$DB_NAME\`;"
  340. echo "SET FOREIGN_KEY_CHECKS=0;"
  341. cat "$full_dump_file"
  342. echo "SET FOREIGN_KEY_CHECKS=1;"
  343. } | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  344. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" >>"$LOG_FILE" 2>&1 || {
  345. error_log=$(tail -30 "$LOG_FILE")
  346. $DIALOG --title "Fuck" --msgbox "full dump import failed.\n\nError:\n${error_log}" 22 76
  347. return
  348. }
  349. DEPLOYMENT_TYPE="dev"
  350. save_env
  351. $DIALOG --msgbox "full database dump imported successfully!" 8 70
  352. else
  353. # Clean schema import
  354. if [[ ! -f "$schema_file" ]]; then
  355. $DIALOG --msgbox "schema file not found at:\n$schema_file" 10 60
  356. return
  357. fi
  358. $DIALOG --yesno "import clean schema from:\n$schema_file\n\nThis will DROP and recreate the database.\n\nProceed?" 12 70 || return
  359. {
  360. echo "DROP DATABASE IF EXISTS \`$DB_NAME\`;"
  361. echo "CREATE DATABASE \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;"
  362. echo "USE \`$DB_NAME\`;"
  363. echo "SET FOREIGN_KEY_CHECKS=0;"
  364. cat "$schema_file"
  365. echo "SET FOREIGN_KEY_CHECKS=1;"
  366. } | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  367. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" >>"$LOG_FILE" 2>&1 || {
  368. error_log=$(tail -30 "$LOG_FILE")
  369. $DIALOG --title "Error" --msgbox "schema import failed.\n\nError:\n${error_log}" 22 76
  370. return
  371. }
  372. DEPLOYMENT_TYPE="clean"
  373. save_env
  374. $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
  375. fi
  376. }
  377. manage_users_and_roles() {
  378. while true; do
  379. $DIALOG --clear \
  380. --title "Manage Users & Roles" \
  381. --menu "Choose an action:" 17 60 6 \
  382. 1 "Create a role" \
  383. 2 "Create a user" \
  384. 3 "Delete a role" \
  385. 4 "Delete a user" \
  386. 5 "Back to main menu" 2>"$CHOICE_FILE"
  387. [[ ! -s "$CHOICE_FILE" ]] && return 0
  388. choice=$(<"$CHOICE_FILE")
  389. case $choice in
  390. 1) create_role ;;
  391. 2) create_user ;;
  392. 3) delete_role ;;
  393. 4) delete_user ;;
  394. 5) return 0 ;;
  395. esac
  396. done
  397. }
  398. create_role() {
  399. $DIALOG --title "Create Role" \
  400. --form "Enter role details:" 12 60 2 \
  401. "Role name:" 1 1 "" 1 20 30 0 \
  402. "Power (1-100):" 2 1 "" 2 20 10 0 \
  403. 2>"$TMP_FILE"
  404. [[ ! -s "$TMP_FILE" ]] && return 1
  405. local name power
  406. {
  407. read -r name
  408. read -r power
  409. } < "$TMP_FILE"
  410. [[ -z "$name" || -z "$power" ]] && {
  411. $DIALOG --msgbox "Role name and power are required." 8 50
  412. return 1
  413. }
  414. # Validate power is a number between 1-100
  415. if ! [[ "$power" =~ ^[0-9]+$ ]] || [[ "$power" -lt 1 || "$power" -gt 100 ]]; then
  416. $DIALOG --msgbox "Power must be a number between 1 and 100." 8 50
  417. return 1
  418. fi
  419. local sql="INSERT INTO roles (name, power) VALUES ('$name', $power);"
  420. echo "$sql" | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  421. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" >>"$LOG_FILE" 2>&1 || {
  422. error_log=$(tail -20 "$LOG_FILE")
  423. $DIALOG --title "Error" --msgbox "Failed to create role.\n\nError:\n${error_log}" 20 76
  424. return 1
  425. }
  426. $DIALOG --msgbox "Role '$name' created successfully!" 8 50
  427. }
  428. delete_role() {
  429. # Fetch roles from the database
  430. local roles_list
  431. roles_list=$(podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  432. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" \
  433. -e "SELECT id, name, power FROM roles ORDER BY power DESC;" -s -N 2>>"$LOG_FILE") || {
  434. $DIALOG --msgbox "Failed to fetch roles from database." 10 60
  435. return
  436. }
  437. # Build role selection menu
  438. local role_options=()
  439. while IFS=$'\t' read -r role_id role_name role_power; do
  440. role_options+=("$role_id" "$role_name (power: $role_power)")
  441. done <<< "$roles_list"
  442. if [[ ${#role_options[@]} -eq 0 ]]; then
  443. $DIALOG --msgbox "No roles found in database." 10 60
  444. return
  445. fi
  446. # Select role to delete
  447. $DIALOG --menu "Select role to delete:" 20 70 10 "${role_options[@]}" 2>"$TMP_FILE" || return
  448. local selected_role_id
  449. selected_role_id=$(<"$TMP_FILE")
  450. # Get role name for confirmation
  451. local selected_role_name
  452. selected_role_name=$(echo "$roles_list" | awk -v id="$selected_role_id" -F'\t' '$1 == id {print $2}')
  453. # Check if any users are using this role
  454. local user_count
  455. user_count=$(podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  456. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" \
  457. -e "SELECT COUNT(*) FROM users WHERE role_id = $selected_role_id;" -s -N 2>>"$LOG_FILE")
  458. if [[ "$user_count" -gt 0 ]]; then
  459. $DIALOG --msgbox "Cannot delete role '$selected_role_name'.\n$user_count user(s) are currently assigned this role." 10 60
  460. return
  461. fi
  462. # Confirm deletion
  463. $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
  464. # Delete role
  465. local sql="DELETE FROM roles WHERE id = $selected_role_id;"
  466. echo "$sql" | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  467. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" >>"$LOG_FILE" 2>&1 || {
  468. error_log=$(tail -20 "$LOG_FILE")
  469. $DIALOG --title "Error" --msgbox "Failed to delete role.\n\nError:\n${error_log}" 20 76
  470. return
  471. }
  472. $DIALOG --msgbox "Role '$selected_role_name' deleted successfully!" 8 60
  473. }
  474. create_user() {
  475. # Get available roles
  476. local roles_list
  477. roles_list=$(podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  478. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" \
  479. -e "SELECT id, name, power FROM roles ORDER BY power DESC;" -s -N 2>>"$LOG_FILE") || {
  480. $DIALOG --msgbox "Failed to fetch roles from database.\nEnsure schema is imported and roles exist." 10 60
  481. return
  482. }
  483. # Build role selection menu
  484. local role_options=()
  485. while IFS=$'\t' read -r role_id role_name role_power; do
  486. role_options+=("$role_id" "$role_name (power: $role_power)")
  487. done <<< "$roles_list"
  488. if [[ ${#role_options[@]} -eq 0 ]]; then
  489. $DIALOG --msgbox "No roles found in database.\nPlease create a role first." 10 60
  490. return
  491. fi
  492. # Select role
  493. $DIALOG --menu "Select user role" 15 60 5 "${role_options[@]}" 2>"$TMP_FILE" || return
  494. local selected_role_id
  495. selected_role_id=$(<"$TMP_FILE")
  496. # Get user details
  497. $DIALOG --form "Create BeepZone user" 14 70 5 \
  498. "Name (full name)" 1 1 "" 1 20 40 40 \
  499. "Username" 2 1 "" 2 20 40 40 \
  500. "Password" 3 1 "" 3 20 40 40 \
  501. "Email" 4 1 "" 4 20 40 40 \
  502. "Phone" 5 1 "" 5 20 40 40 2>"$TMP_FILE" || return
  503. local name username password email phone
  504. {
  505. read -r name
  506. read -r username
  507. read -r password
  508. read -r email
  509. read -r phone
  510. } < "$TMP_FILE"
  511. if [[ -z "$name" || -z "$username" || -z "$password" ]]; then
  512. $DIALOG --msgbox "Name, username, and password are required." 8 60
  513. return
  514. fi
  515. # Hash password with bcrypt (cost 12)
  516. local password_hash
  517. # Try htpasswd first (native Unix tool)
  518. if command -v htpasswd >/dev/null 2>&1; then
  519. password_hash=$(htpasswd -nbB -C 12 "$username" "$password" 2>>"$LOG_FILE" | cut -d: -f2)
  520. # Fall back to Python bcrypt
  521. elif command -v python3 >/dev/null 2>&1; then
  522. password_hash=$(python3 -c "import bcrypt; print(bcrypt.hashpw('$password'.encode('utf-8'), bcrypt.gensalt(12)).decode('utf-8'))" 2>>"$LOG_FILE")
  523. fi
  524. if [[ -z "$password_hash" ]]; then
  525. $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
  526. return
  527. fi
  528. # Insert user with hashed password
  529. local sql
  530. sql="INSERT INTO users (name, username, password, role_id, email, phone, active) VALUES
  531. ('$name', '$username', '$password_hash', $selected_role_id, "
  532. [[ -n "$email" ]] && sql+="'$email'" || sql+="NULL"
  533. sql+=", "
  534. [[ -n "$phone" ]] && sql+="'$phone'" || sql+="NULL"
  535. sql+=", 1);"
  536. echo "$sql" | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  537. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" >>"$LOG_FILE" 2>&1 || {
  538. error_log=$(tail -20 "$LOG_FILE")
  539. $DIALOG --title "Error" --msgbox "Failed to create user.\n\nError:\n${error_log}" 20 76
  540. return
  541. }
  542. $DIALOG --msgbox "User '$username' created successfully!" 8 60
  543. }
  544. delete_user() {
  545. # Fetch users from the database
  546. local users_list
  547. users_list=$(podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  548. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" \
  549. -e "SELECT id, username, name FROM users ORDER BY id;" -s -N 2>>"$LOG_FILE") || {
  550. $DIALOG --msgbox "Failed to fetch users from database." 10 60
  551. return
  552. }
  553. # Build user selection menu
  554. local user_options=()
  555. while IFS=$'\t' read -r user_id username name; do
  556. user_options+=("$user_id" "$username - $name")
  557. done <<< "$users_list"
  558. if [[ ${#user_options[@]} -eq 0 ]]; then
  559. $DIALOG --msgbox "No users found in database." 10 60
  560. return
  561. fi
  562. # Select user to delete
  563. $DIALOG --menu "Select user to delete:" 20 70 10 "${user_options[@]}" 2>"$TMP_FILE" || return
  564. local selected_user_id
  565. selected_user_id=$(<"$TMP_FILE")
  566. # Get username for confirmation
  567. local selected_username
  568. selected_username=$(echo "$users_list" | awk -v id="$selected_user_id" -F'\t' '$1 == id {print $2}')
  569. # Confirm deletion
  570. $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
  571. # Delete user
  572. local sql="DELETE FROM users WHERE id = $selected_user_id;"
  573. echo "$sql" | podman exec -i "$BEEPZONE_DB_CONTAINER_NAME" \
  574. mariadb -h"$DB_HOST" -P"$DB_PORT" -uroot -p"$DB_ROOT_PASSWORD" "$DB_NAME" >>"$LOG_FILE" 2>&1 || {
  575. error_log=$(tail -20 "$LOG_FILE")
  576. $DIALOG --title "Error" --msgbox "Failed to delete user.\n\nError:\n${error_log}" 20 76
  577. return
  578. }
  579. $DIALOG --msgbox "User '$selected_username' deleted successfully!" 8 60
  580. }
  581. clone_if_missing() {
  582. local repo_url="$1" dest_dir="$2"
  583. if [[ -d "$dest_dir/.git" ]]; then
  584. return
  585. fi
  586. git clone "$repo_url" "$dest_dir" >>"$LOG_FILE" 2>&1 || {
  587. error_log=$(tail -20 "$LOG_FILE")
  588. $DIALOG --title "Error" --msgbox "Failed to clone $repo_url.\n\nError:\n${error_log}" 20 76
  589. return 1
  590. }
  591. }
  592. setup_seckelapi() {
  593. local sources_dir="$WORK_DIR/backend/seckelapi/sources"
  594. local config_dir="$WORK_DIR/backend/seckelapi/config"
  595. local sources_basics_config="$sources_dir/config/basics.toml"
  596. # Check if sources are already cloned
  597. if [[ ! -d "$sources_dir/.git" ]]; then
  598. mkdir -p "$sources_dir"
  599. clone_if_missing "$SECKELAPI_REPO" "$sources_dir" || return
  600. fi
  601. # Ask about config update
  602. $DIALOG --clear \
  603. --title "SeckelAPI Configuration" \
  604. --menu "How do you want to handle the config?" 12 70 2 \
  605. 1 "Auto-update from database settings" \
  606. 2 "Manually edit config file" 2>"$CHOICE_FILE"
  607. [[ ! -s "$CHOICE_FILE" ]] && return 0
  608. config_choice=$(<"$CHOICE_FILE")
  609. # Ensure sources config directory exists and copy all template configs
  610. mkdir -p "$sources_dir/config"
  611. # Always copy all template configs from config/ to sources/config/
  612. for conf_file in "$config_dir"/*.toml; do
  613. conf_name=$(basename "$conf_file")
  614. cp "$conf_file" "$sources_dir/config/$conf_name" 2>>"$LOG_FILE"
  615. done
  616. if [[ "$config_choice" == "1" ]]; then
  617. # Auto-update basics.toml in sources with database settings (only [database] section)
  618. if [[ -f "$sources_basics_config" ]]; then
  619. # Determine database host - use host.containers.internal for container deployments
  620. local db_host_value="$DB_HOST"
  621. sed -i.bak \
  622. -e '/^\[database\]/,/^\[/ {
  623. /^host = /s|= .*|= "'"$db_host_value"'"|
  624. /^port = /s|= .*|= '"$DB_PORT"'|
  625. /^database = /s|= .*|= "'"$DB_NAME"'"|
  626. /^username = /s|= .*|= "'"$DB_USER"'"|
  627. /^password = /s|= .*|= "'"$DB_PASS"'"|
  628. }' \
  629. "$sources_basics_config"
  630. $DIALOG --msgbox "Config updated with database settings from .env" 8 60
  631. else
  632. $DIALOG --msgbox "Error: basics.toml not found at $sources_basics_config" 8 60
  633. return 1
  634. fi
  635. else
  636. # Open config file for manual editing (in sources/config/)
  637. if [[ -f "$sources_basics_config" ]]; then
  638. ${EDITOR:-nano} "$sources_basics_config"
  639. else
  640. $DIALOG --msgbox "Error: basics.toml not found at $sources_basics_config" 8 60
  641. return 1
  642. fi
  643. fi
  644. # Ask about build and deployment
  645. $DIALOG --clear \
  646. --title "Build & Deploy SeckelAPI" \
  647. --menu "Choose an action:" 12 70 2 \
  648. 1 "Build and deploy to podman container" \
  649. $( $HAS_CARGO && echo "2 \"Build natively on host\"" ) 2>"$CHOICE_FILE"
  650. [[ ! -s "$CHOICE_FILE" ]] && return 0
  651. build_choice=$(<"$CHOICE_FILE")
  652. if [[ "$build_choice" == "2" ]]; then
  653. # Native host build with live output
  654. clear
  655. echo "Building SeckelAPI..."
  656. echo "===================="
  657. echo ""
  658. if (cd "$sources_dir" && cargo build --release); then
  659. # Copy config files to the build output directory
  660. local target_dir="$sources_dir/target/release"
  661. mkdir -p "$target_dir/config"
  662. cp "$sources_dir/config"/*.toml "$target_dir/config/" 2>>"$LOG_FILE"
  663. echo ""
  664. echo "Build completed successfully!"
  665. echo "Binary location: $target_dir/seckelapi"
  666. echo "Config location: $target_dir/config/"
  667. read -p "Press Enter to continue..."
  668. else
  669. echo ""
  670. echo "Build failed! Check the output above for errors."
  671. read -p "Press Enter to continue..."
  672. return 1
  673. fi
  674. elif [[ "$build_choice" == "1" ]]; then
  675. # Build and deploy to podman container
  676. clear
  677. echo "Building SeckelAPI container..."
  678. echo "================================"
  679. echo ""
  680. # Get the host gateway IP for container to access host services
  681. # For Podman, we'll use the gateway IP from the default bridge network
  682. local host_gateway="host.containers.internal"
  683. # Update database host for container networking
  684. if [[ -f "$sources_basics_config" ]]; then
  685. echo "Updating database host to: $host_gateway"
  686. sed -i.container-bak \
  687. -e '/^\[database\]/,/^\[/ {
  688. /^host = /s|= .*|= "'"$host_gateway"'"|
  689. }' \
  690. "$sources_basics_config"
  691. fi
  692. local container_name="beepzone-seckelapi"
  693. local image_name="beepzone-seckelapi:latest"
  694. local containerfile="$WORK_DIR/backend/seckelapi/Containerfile"
  695. # Stop and remove existing container if running
  696. if podman ps -a --format "{{.Names}}" | grep -q "^${container_name}$"; then
  697. echo "Stopping and removing existing container..."
  698. podman stop "$container_name" 2>/dev/null || true
  699. podman rm "$container_name" 2>/dev/null || true
  700. fi
  701. # Build container image
  702. echo "Building container image..."
  703. if podman build -t "$image_name" -f "$containerfile" "$WORK_DIR/backend/seckelapi"; then
  704. echo ""
  705. echo "Container image built successfully!"
  706. echo ""
  707. # Ask to run the container
  708. if $DIALOG --yesno "Start the SeckelAPI container now?" 8 50; then
  709. echo "Starting container..."
  710. # Run container with port mapping and host gateway
  711. if podman run -d \
  712. --name "$container_name" \
  713. --add-host host.containers.internal:host-gateway \
  714. -p 5777:5777 \
  715. "$image_name"; then
  716. echo ""
  717. echo "Container started successfully!"
  718. echo "Container name: $container_name"
  719. echo "API listening on: http://0.0.0.0:5777"
  720. echo ""
  721. echo "Useful commands:"
  722. echo " podman logs $container_name - View logs"
  723. echo " podman stop $container_name - Stop container"
  724. echo " podman start $container_name - Start container"
  725. echo " podman restart $container_name - Restart container"
  726. read -p "Press Enter to continue..."
  727. else
  728. echo ""
  729. echo "Failed to start container!"
  730. read -p "Press Enter to continue..."
  731. return 1
  732. fi
  733. fi
  734. else
  735. echo ""
  736. echo "Container build failed! Check the output above for errors."
  737. read -p "Press Enter to continue..."
  738. return 1
  739. fi
  740. fi
  741. }
  742. setup_seckelapi_native() {
  743. if ! $HAS_CARGO; then
  744. "$DIALOG" --msgbox "Cargo not found in PATH.\n\nInstall Rust toolchain (rustup) to use native SeckelAPI setup." 10 70
  745. return
  746. fi
  747. # 1. Check Pre-requisites
  748. $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
  749. local sources_dir="$WORK_DIR/backend/seckelapi/sources"
  750. local config_dir="$WORK_DIR/backend/seckelapi/config"
  751. local sources_basics_config="$sources_dir/config/basics.toml"
  752. # 2. Clone sources if missing
  753. if [[ ! -d "$sources_dir/.git" ]]; then
  754. mkdir -p "$sources_dir"
  755. clone_if_missing "$SECKELAPI_REPO" "$sources_dir" || return
  756. fi
  757. # 3. Configure basics.toml
  758. # Ensure sources config directory exists and copy all template configs
  759. mkdir -p "$sources_dir/config"
  760. for conf_file in "$config_dir"/*.toml; do
  761. conf_name=$(basename "$conf_file")
  762. cp "$conf_file" "$sources_dir/config/$conf_name" 2>>"$LOG_FILE"
  763. done
  764. if [[ -f "$sources_basics_config" ]]; then
  765. # Update with DB settings from .env (localhost since it's native)
  766. sed -i.bak \
  767. -e '/^\[database\]/,/^\[/ {
  768. /^host = /s|= .*|= "'"$DB_HOST"'"|
  769. /^port = /s|= .*|= '"$DB_PORT"'|
  770. /^database = /s|= .*|= "'"$DB_NAME"'"|
  771. /^username = /s|= .*|= "'"$DB_USER"'"|
  772. /^password = /s|= .*|= "'"$DB_PASS"'"|
  773. }' \
  774. "$sources_basics_config"
  775. else
  776. $DIALOG --msgbox "Error: basics.toml not found at $sources_basics_config" 8 60
  777. return 1
  778. fi
  779. # 4. Build Release
  780. clear
  781. echo "Building SeckelAPI (Native)..."
  782. echo "============================"
  783. echo ""
  784. if (cd "$sources_dir" && cargo build --release); then
  785. # Copy config files to the build output directory
  786. local target_dir="$sources_dir/target/release"
  787. mkdir -p "$target_dir/config"
  788. cp "$sources_dir/config"/*.toml "$target_dir/config/" 2>>"$LOG_FILE"
  789. echo ""
  790. echo "Build completed successfully!"
  791. else
  792. echo ""
  793. echo "Build failed! Check the output above for errors."
  794. read -p "Press Enter to continue..."
  795. return 1
  796. fi
  797. # 5. Install Systemd Service
  798. if $DIALOG --yesno "Do you want to install SeckelAPI as a systemd service?" 8 60; then
  799. local service_name="beepzone-seckelapi"
  800. local service_file="/tmp/${service_name}.service"
  801. local current_user
  802. current_user=$(whoami)
  803. # Working directory for the binary
  804. local working_dir="$sources_dir/target/release"
  805. local exec_path="$working_dir/seckelapi"
  806. cat > "$service_file" <<EOF
  807. [Unit]
  808. Description=BeepZone SeckelAPI Service
  809. After=network.target mariadb.service
  810. Requires=mariadb.service
  811. [Service]
  812. Type=simple
  813. User=${current_user}
  814. WorkingDirectory=${working_dir}
  815. ExecStart=${exec_path}
  816. Restart=always
  817. RestartSec=10
  818. Environment=RUST_LOG=info
  819. [Install]
  820. WantedBy=multi-user.target
  821. EOF
  822. echo "Installing systemd service..."
  823. sudo mv "$service_file" "/etc/systemd/system/${service_name}.service"
  824. sudo systemctl daemon-reload
  825. sudo systemctl enable "${service_name}"
  826. if sudo systemctl start "${service_name}"; then
  827. $DIALOG --msgbox "Service '${service_name}' installed and started successfully!" 8 60
  828. else
  829. $DIALOG --msgbox "Failed to start service '${service_name}'. Check 'sudo systemctl status ${service_name}'" 10 70
  830. fi
  831. fi
  832. }
  833. update_from_git_masters() {
  834. local seckelapi_sources="$WORK_DIR/backend/seckelapi/sources"
  835. local client_sources="$WORK_DIR/frontend/desktop-client/sources"
  836. if $DIALOG --yesno "This will run 'git pull' in both backend and frontend source directories.\n\nContinue?" 10 60; then
  837. clear
  838. echo "Updating sources from git masters..."
  839. echo "===================================="
  840. echo ""
  841. # Update SeckelAPI
  842. if [[ -d "$seckelapi_sources/.git" ]]; then
  843. echo "Updating SeckelAPI..."
  844. if (cd "$seckelapi_sources" && git pull); then
  845. echo "SeckelAPI updated successfully."
  846. else
  847. echo "Failed to update SeckelAPI."
  848. fi
  849. else
  850. echo "SeckelAPI sources not found or not a git repository. Skipping."
  851. fi
  852. echo ""
  853. # Update Desktop Client
  854. if [[ -d "$client_sources/.git" ]]; then
  855. echo "Updating Desktop Client..."
  856. if (cd "$client_sources" && git pull); then
  857. echo "Desktop Client updated successfully."
  858. else
  859. echo "Failed to update Desktop Client."
  860. fi
  861. else
  862. echo "Desktop Client sources not found or not a git repository. Skipping."
  863. fi
  864. echo ""
  865. read -p "Press Enter to continue..."
  866. fi
  867. }
  868. clean_sources() {
  869. $DIALOG --yesno "This will delete all sources directories including hidden files:\n\n- backend/seckelapi/sources\n- frontend/desktop-client/sources\n\nAre you sure?" 12 70 || return
  870. local seckelapi_sources="$WORK_DIR/backend/seckelapi/sources"
  871. local client_sources="$WORK_DIR/frontend/desktop-client/sources"
  872. # Clean SeckelAPI sources
  873. if [[ -d "$seckelapi_sources" ]]; then
  874. find "$seckelapi_sources" -mindepth 1 -delete 2>>"$LOG_FILE"
  875. if [[ $? -eq 0 ]]; then
  876. echo "SeckelAPI sources contents removed" >>"$LOG_FILE"
  877. fi
  878. fi
  879. # Clean desktop client sources
  880. if [[ -d "$client_sources" ]]; then
  881. find "$client_sources" -mindepth 1 -delete 2>>"$LOG_FILE"
  882. if [[ $? -eq 0 ]]; then
  883. echo "Desktop client sources contents removed" >>"$LOG_FILE"
  884. fi
  885. fi
  886. $DIALOG --msgbox "All sources cleaned!" 7 50
  887. # Ask to re-clone
  888. if $DIALOG --yesno "Do you want to pull fresh sources now?" 7 50; then
  889. # Clone SeckelAPI
  890. mkdir -p "$(dirname "$seckelapi_sources")"
  891. if clone_if_missing "$SECKELAPI_REPO" "$seckelapi_sources"; then
  892. $DIALOG --msgbox "SeckelAPI sources cloned successfully!" 7 50
  893. else
  894. $DIALOG --msgbox "Failed to clone SeckelAPI sources. Check log." 7 50
  895. return 1
  896. fi
  897. # Clone desktop client
  898. mkdir -p "$(dirname "$client_sources")"
  899. if clone_if_missing "$CLIENT_REPO" "$client_sources"; then
  900. $DIALOG --msgbox "Desktop client sources cloned successfully!" 7 50
  901. else
  902. $DIALOG --msgbox "Failed to clone desktop client sources. Check log." 7 50
  903. return 1
  904. fi
  905. $DIALOG --msgbox "All sources pulled fresh!" 7 50
  906. fi
  907. }
  908. build_desktop_client() {
  909. local sources_dir="$WORK_DIR/frontend/desktop-client/sources"
  910. if ! $HAS_CARGO; then
  911. "$DIALOG" --msgbox "Cargo not found in PATH.\n\nInstall Rust toolchain (rustup) to build the desktop client." 10 70
  912. return
  913. fi
  914. # Check if sources are already cloned
  915. if [[ ! -d "$sources_dir/.git" ]]; then
  916. mkdir -p "$sources_dir"
  917. clone_if_missing "$CLIENT_REPO" "$sources_dir" || return
  918. fi
  919. if $DIALOG --yesno "Build BeepZone desktop client in release mode now?" 8 70; then
  920. echo "Building BeepZone Desktop Client..."
  921. echo "===================================="
  922. echo ""
  923. if (cd "$sources_dir" && cargo build --release); then
  924. echo ""
  925. echo "Build completed successfully!"
  926. echo "Binary location: $sources_dir/target/release/"
  927. read -p "Press Enter to continue..."
  928. else
  929. echo ""
  930. echo "Build failed! Check the output above for errors."
  931. read -p "Press Enter to continue..."
  932. return 1
  933. fi
  934. fi
  935. }
  936. show_capabilities_screen
  937. ask_main_menu