1
0

beepzone-helper.sh 38 KB

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