beepzone-helper.sh 43 KB

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