diff --git a/install.sh b/install.sh index b34d91c..bd20101 100755 --- a/install.sh +++ b/install.sh @@ -5,7 +5,7 @@ # ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.# # Nightscout Mikr.us setup script # # ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.# -# (c)2025 by Dominik Dzienia # +# (c)2023-2026 by Dominik Dzienia # # # # Licensed under MIT license # # ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.# @@ -13,6 +13,12 @@ # https://github.com/Homebrew/brew # # ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.# +# This file is automatically generated. Do not modify it directly! +# Instead, modify the source files in the src directory and run the build script! +# +# Build time: 2026.01.05 22:10 + + # shellcheck disable=SC2148 # shellcheck disable=SC2155 @@ -54,7 +60,7 @@ DISK_LOW_MAIL=5184000 # == 60 days in seconds DISK_CRITICAL_WARNING=104857600 # == 100 MiB DISK_CRITICAL_MAIL=604800 # == 7 days in seconds DOCKER_DOWN_MAIL=604800 # == 7 days in seconds -SCRIPT_VERSION="1.10.2" #auto-update +SCRIPT_VERSION="1.10.2" #auto-update SCRIPT_BUILD_TIME="2026.01.05" #auto-update #======================================= @@ -255,6 +261,15 @@ major_minor() { )" } +extract_version() { + regex='version:\s+([0-9]+\.[0-9]+\.[0-9]+)' + if [[ "$1" =~ $regex ]]; then + echo "${BASH_REMATCH[1]}" + else + echo "0.0.0" + fi +} + version_gt() { [[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -gt "${2#*.}" ]] } @@ -275,6 +290,28 @@ exit_on_no_cancel() { fi } +check_interactive() { + shopt -q login_shell && echo 'Login shell' || echo 'Not login shell' + + # if [[ $- == *i* ]]; then + # msgok "Interactive setup" + # else + # msgok "Non-interactive setup" + # fi +} + +read_or_default() { + if [ -f "$1" ]; then + cat "$1" + else + if [ $# -eq 2 ]; then + echo "$2" + else + echo "" + fi + fi +} + #======================================= @@ -704,38 +741,24 @@ clear_last_time() { event_mark "last_${actionName}_clear" } - +get_events_status() { + local count="$(event_count)" + if ((count == 0)); then + printf "\U2728 brak zdarzeń" + elif ((count == 1)); then + printf "\U1F4C5 jedno zdarzenie" + elif (((count % 10) > 1)) && (((count % 10) < 5)); then + printf "\U1F4C5 %s zdarzenia" "$count" + else + printf "\U1F4C5 %s zdarzeń" "$count" + fi +} #======================================= -# ACTIONS AND STEPS +# SETUP CHECKS #======================================= -check_interactive() { - - shopt -q login_shell && echo 'Login shell' || echo 'Not login shell' - - # if [[ $- == *i* ]]; then - # msgok "Interactive setup" - # else - # msgok "Non-interactive setup" - # fi -} - -setup_update_repo() { - if [ "$aptGetWasUpdated" -eq "0" ]; then - aptGetWasUpdated=1 - ohai "Updating package repository" - apt-get -yq update >>"$LOGTO" 2>&1 - fi -} - -test_node() { - local node_version_output - node_version_output="$(node -v 2>/dev/null)" - version_ge "$(major_minor "${node_version_output/v/}")" "$(major_minor "${REQUIRED_NODE_VERSION}")" -} - # $1 lib name # $2 package name add_if_not_ok() { @@ -770,6 +793,12 @@ add_if_not_ok_compose() { fi } +test_node() { + local node_version_output + node_version_output="$(node -v 2>/dev/null)" + version_ge "$(major_minor "${node_version_output/v/}")" "$(major_minor "${REQUIRED_NODE_VERSION}")" +} + check_git() { git --version >/dev/null 2>&1 add_if_not_ok "GIT" "git" @@ -785,47 +814,6 @@ check_docker_compose() { add_if_not_ok_compose "Docker compose" } -patch_docker_compose() { - if [[ -f $DOCKER_COMPOSE_FILE ]]; then - local patched=0 - local containers_running=0 - - # Check if containers are already running before patching - local ns_status=$(get_docker_status "ns-server") - local db_status=$(get_docker_status "ns-database") - if [[ "$ns_status" == "running" ]] || [[ "$db_status" == "running" ]]; then - containers_running=1 - fi - - # Check if mongodb image needs patching (bitnami/mongodb) - if grep -q "bitnami/mongodb" "$DOCKER_COMPOSE_FILE"; then - ohai "Patching docker-compose.yml MongoDB image..." - # Replace bitnami/mongodb with official mongo image - sed -i -E 's|image:\s*"*(bitnami/)?mongodb:.*"|image: "mongo:${NS_MONGODB_TAG}"|g' "$DOCKER_COMPOSE_FILE" - patched=1 - fi - # Check if volume path needs patching (bitnami/mongodb -> data/db) - if grep -q "/bitnami/mongodb" "$DOCKER_COMPOSE_FILE"; then - ohai "Patching docker-compose.yml MongoDB volume path..." - # Replace both host path and container path for mongodb volume - sed -i -E 's|(\$\{NS_DATA_DIR\}/mongodb):/bitnami/mongodb"|\1/data/db:/data/db"|g' "$DOCKER_COMPOSE_FILE" - patched=1 - fi - - if [ "$patched" -eq 1 ]; then - msgcheck "Docker compose file patched" - # Restart containers only if they were already running - if [ "$containers_running" -eq 1 ]; then - do_cleanup_sys - ohai "Restarting containers to apply patched configuration..." - update_containers - do_cleanup_docker - msgcheck "Containers restarted" - fi - fi - fi -} - check_jq() { jq --help >/dev/null 2>&1 add_if_not_ok "JSON parser" "jq" @@ -871,209 +859,56 @@ check_diceware() { add_if_not_ok "Secure Password Generator" "diceware" } -setup_provisional_key() { - ohai "Generating provisional log encryption key" - local randPass=$(openssl rand -base64 30) - local fixedPass=$(echo "$randPass" | sed -e 's/[+\/]/-/g') - echo "tymczasowe-${fixedPass}" >"$LOG_ENCRYPTION_KEY_FILE" - msgcheck "Provisional key generated" -} - -setup_security() { - if [[ -f $LOG_ENCRYPTION_KEY_FILE ]]; then - # -------------------- - # JAKIŚ klucz istnieje - # -------------------- - local logKey=$(<"$LOG_ENCRYPTION_KEY_FILE") - local regexTemp='tymczasowe-' - - # ----------------------- - # ...ale jest tymczasowy - # ----------------------- - if [[ "$logKey" =~ $regexTemp ]]; then - msgerr "Using provisional key" - test_diceware - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - ohai "Generating proper log encryption file..." - diceware -n 5 -d - >"$LOG_ENCRYPTION_KEY_FILE" - msgcheck "Key generated" - else - msgerr "Required tool (diceware) still cannot be installed - apt is locked!" - msgnote "Zrestartuj serwer mikr.us i sprawdź czy ten błąd nadal występuje - wtedy odbokuj apt-get i zainstaluj diceware (apt-get install diceware)" - fi - else - local keySize=${#logKey} - - # ---------------------- - # ...ale jest za krótki - # ---------------------- - if ((keySize < 12)); then - msgerr "Encryption key empty or too short, generating better one" - test_diceware - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - ohai "Generating proper log encryption file..." - diceware -n 5 -d - >"$LOG_ENCRYPTION_KEY_FILE" - msgcheck "Key generated" - else - msgerr "Generating provisional key while diceware tool is not installed" - setup_provisional_key - fi - else - msgok "Found log encryption key" - fi - fi - else - - # --------------------- - # jescze nie ma klucza - # --------------------- - - test_diceware - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - ohai "Generating log encryption key..." - diceware -n 5 -d - >"$LOG_ENCRYPTION_KEY_FILE" - msgcheck "Key generated" - else - msgerr "Generating provisional key while diceware tool is not installed" - setup_provisional_key - fi - - fi -} - -setup_packages() { - # shellcheck disable=SC2145 - # shellcheck disable=SC2068 - (if_is_set packages && setup_update_repo && - ohai "Installing packages: ${packages[@]}" && - apt-get -yq install ${packages[@]} >>"$LOGTO" 2>&1 && - msgcheck "Install successfull") || msgok "All required packages already installed" -} - -setup_node() { - test_node - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - msgcheck "Node installed in correct version" - else - ohai "Cleaning old Node.js" - { - rm -f /etc/apt/sources.list.d/nodesource.list - apt-get -yq --fix-broken install - apt-get -yq update - apt-get -yq remove nodejs nodejs-doc libnode* - } >>"$LOGTO" 2>&1 - - ohai "Preparing Node.js setup" - curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - >/dev/null 2>&1 - - ohai "Installing Node.js" - apt-get install -y nodejs >>"$LOGTO" 2>&1 - - test_node - local RECHECK=$? - if [ "$RECHECK" -ne 0 ]; then - msgerr "Nie udało się zainstalować Node.js" +#======================================= +# PATCH OLDER CONFIGS +#======================================= - msgerr "Instalacja Node.js jest skomplikowanym procesem i zależy od wersji systemu Linux i konfiguracji Mikr.us-a" - msgerr "Spróbuj ręcznie uruchomić instalację poniższą komendą i sprawdź czy pojawiają się błędy (i jakie):" - msgerr " apt-get install -y nodejs " +patch_docker_compose() { + if [[ -f $DOCKER_COMPOSE_FILE ]]; then + local patched=0 + local containers_running=0 - exit 1 + # Check if containers are already running before patching + local ns_status=$(get_docker_status "ns-server") + local db_status=$(get_docker_status "ns-database") + if [[ "$ns_status" == "running" ]] || [[ "$db_status" == "running" ]]; then + containers_running=1 fi - fi -} - -setup_users() { - id -u mongodb &>/dev/null - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - msgcheck "Mongo DB user detected" - else - ohai "Configuring Mongo DB user" - useradd -u 1001 -g 0 mongodb - fi -} - -setup_dir_structure() { - ohai "Configuring folder structure" - mkdir -p "$MONGO_DB_DIR" - mkdir -p /srv/nightscout/config - mkdir -p /srv/nightscout/tools - mkdir -p /srv/nightscout/data - mkdir -p "$UPDATES_DIR" - chown -R mongodb:root "$MONGO_DB_DIR" -} - -setup_firewall() { - ohai "Configuring firewall" - - { - ufw default deny incoming - ufw default allow outgoing - - ufw allow OpenSSH - ufw allow ssh - } >>"$LOGTO" 2>&1 - - host=$(hostname) - - # Extract the last 3 digits from the hostname - port_number=$(echo "$host" | grep -oE '[0-9]{3}$') - - port1=$((10000 + port_number)) - port2=$((20000 + port_number)) - port3=$((30000 + port_number)) - - if ufw allow "$port1" >>"$LOGTO" 2>&1; then - msgcheck "Do regul firewalla poprawnie dodano port $port1" - else - msgerr "Blad dodawania $port1 do regul firewalla" - fi - - if ufw allow "$port2" >>"$LOGTO" 2>&1; then - msgcheck "Do regul firewalla poprawnie dodano port $port2" - else - msgerr "Blad dodawania $port2 do regul firewalla" - fi - - if ufw allow "$port3" >>"$LOGTO" 2>&1; then - msgcheck "Do regul firewalla poprawnie dodano port $port3" - else - msgerr "Blad dodawania $port3 do regul firewalla" - fi - - ufw --force enable >>"$LOGTO" 2>&1 -} + # Check if mongodb image needs patching (bitnami/mongodb) + if grep -q "bitnami/mongodb" "$DOCKER_COMPOSE_FILE"; then + ohai "Patching docker-compose.yml MongoDB image..." + # Replace bitnami/mongodb with official mongo image + sed -i -E 's|image:\s*"*(bitnami/)?mongodb:.*"|image: "mongo:${NS_MONGODB_TAG}"|g' "$DOCKER_COMPOSE_FILE" + patched=1 + fi + # Check if volume path needs patching (bitnami/mongodb -> data/db) + if grep -q "/bitnami/mongodb" "$DOCKER_COMPOSE_FILE"; then + ohai "Patching docker-compose.yml MongoDB volume path..." + # Replace both host path and container path for mongodb volume + sed -i -E 's|(\$\{NS_DATA_DIR\}/mongodb):/bitnami/mongodb"|\1/data/db:/data/db"|g' "$DOCKER_COMPOSE_FILE" + patched=1 + fi -setup_firewall_for_ns() { - ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") - if ufw allow "$ns_external_port" >>"$LOGTO" 2>&1; then - msgcheck "Do regul firewalla poprawnie dodano port Nightscout: $ns_external_port" - else - msgerr "Blad dodawania portu Nightscout: $ns_external_port do reguł firewalla" + if [ "$patched" -eq 1 ]; then + msgcheck "Docker compose file patched" + # Restart containers only if they were already running + if [ "$containers_running" -eq 1 ]; then + do_cleanup_sys + ohai "Restarting containers to apply patched configuration..." + update_containers + do_cleanup_docker + msgcheck "Containers restarted" + fi + fi fi } -install_cron() { - local croncmd="$TOOL_LINK -w > $WATCHDOG_CRON_LOG 2>&1" - local cronjob="*/5 * * * * $croncmd" - msgok "Configuring watchdog..." - ( - crontab -l | grep -v -F "$croncmd" || : - echo "$cronjob" - ) | crontab - -} -uninstall_cron() { - local croncmd="nightscout-tool" - (crontab -l | grep -v -F "$croncmd") | crontab - -} +#======================================= +# DOCKER +#======================================= get_docker_status() { local ID=$(docker ps -a --no-trunc --filter name="^$1" --format '{{ .ID }}') @@ -1084,17 +919,13 @@ get_docker_status() { fi } -get_space_info() { - df -B1 --output=target,size,avail,pcent | tail -n +2 | awk '$1 ~ /^\/$/' -} - install_containers() { - docker compose --env-file /srv/nightscout/config/deployment.env -f /srv/nightscout/config/docker-compose.yml up --no-recreate -d >>"$LOGTO" 2>&1 + docker compose --env-file $ENV_FILE_DEP -f $DOCKER_COMPOSE_FILE up --no-recreate -d >>"$LOGTO" 2>&1 } update_containers() { - docker compose --env-file /srv/nightscout/config/deployment.env -f /srv/nightscout/config/docker-compose.yml pull >>"$LOGTO" 2>&1 - docker compose --env-file /srv/nightscout/config/deployment.env -f /srv/nightscout/config/docker-compose.yml up -d >>"$LOGTO" 2>&1 + docker compose --env-file $ENV_FILE_DEP -f $DOCKER_COMPOSE_FILE pull >>"$LOGTO" 2>&1 + docker compose --env-file $ENV_FILE_DEP -f $DOCKER_COMPOSE_FILE up -d >>"$LOGTO" 2>&1 } install_containers_progress() { @@ -1105,7 +936,7 @@ install_containers_progress() { } uninstall_containers() { - docker compose --env-file /srv/nightscout/config/deployment.env -f /srv/nightscout/config/docker-compose.yml down >>"$LOGTO" 2>&1 + docker compose --env-file $ENV_FILE_DEP -f $DOCKER_COMPOSE_FILE down >>"$LOGTO" 2>&1 } uninstall_containers_progress() { @@ -1119,1017 +950,1320 @@ uninstall_containers_progress() { fi } -source_admin() { - if [[ -f $ENV_FILE_ADMIN ]]; then - # shellcheck disable=SC1090 - source "$ENV_FILE_ADMIN" - msgok "Imported admin config" - fi -} +get_container_status() { + local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') + if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then + local status=$(docker inspect "$ID" | jq -r ".[0].State.Status") + case "$status" in + "running") + printf "\U1F7E2 działa" + ;; + "restarting") + printf "\U1F7E3 restart" + ;; + "created") + printf "\U26AA utworzono" + ;; + "exited") + printf "\U1F534 wyłączono" + ;; + "paused") + printf "\U1F7E1 zapauzowano" + ;; + "dead") + printf "\U1F480 zablokowany" + ;; + esac -download_if_not_exists() { - if [[ -f $2 ]]; then - msgok "Found $1" else - ohai "Downloading $1..." - curl -fsSL -o "$2" "$3" - msgcheck "Downloaded $1" + printf '\U2753 nie odnaleziono' fi } -download_conf() { - download_if_not_exists "deployment config" "$ENV_FILE_DEP" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/deployment.env" - download_if_not_exists "nightscout config" "$ENV_FILE_NS" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/nightscout.env" - download_if_not_exists "docker compose file" "$DOCKER_COMPOSE_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/docker-compose.yml" - download_if_not_exists "profanity database" "$PROFANITY_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/profanity.db" - download_if_not_exists "reservation database" "$RESERVED_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/reserved.db" +get_container_status_code() { + local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') + if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then + local status=$(docker inspect "$ID" | jq -r ".[0].State.Status") + echo "$status" + else + echo "unknown" + fi } -download_tools() { - download_if_not_exists "update stamp" "$UPDATES_DIR/updated" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated" - if ! [[ -f $TOOL_FILE ]]; then - download_if_not_exists "nightscout-tool file" "$TOOL_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/install.sh" - local timestamp=$(date +%s) - echo "$timestamp" >"$UPDATES_DIR/timestamp" - else - msgok "Found nightscout-tool" - fi - if ! [[ -f $TOOL_LINK ]]; then - ohai "Linking nightscout-tool" - ln -s "$TOOL_FILE" "$TOOL_LINK" - fi - chmod +x "$TOOL_FILE" - chmod +x "$TOOL_LINK" -} +#======================================= +# APP +#======================================= -extract_version() { - regex='version:\s+([0-9]+\.[0-9]+\.[0-9]+)' - if [[ "$1" =~ $regex ]]; then - echo "${BASH_REMATCH[1]}" - else - echo "0.0.0" - fi +get_space_info() { + df -B1 --output=target,size,avail,pcent | tail -n +2 | awk '$1 ~ /^\/$/' } -read_or_default() { - if [ -f "$1" ]; then - cat "$1" - else - if [ $# -eq 2 ]; then - echo "$2" - else - echo "" - fi - fi -} -download_updates() { - ohai "Downloading updated scripts and config files" - local onlineUpdated="$(curl -fsSL "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated")" - if [ ! "$onlineUpdated" == "" ]; then - curl -fsSL -o "$UPDATES_DIR/install.sh" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/install.sh" - curl -fsSL -o "$UPDATES_DIR/deployment.env" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/deployment.env" - curl -fsSL -o "$UPDATES_DIR/nightscout.env" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/nightscout.env" - curl -fsSL -o "$UPDATES_DIR/docker-compose.yml" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/docker-compose.yml" - curl -fsSL -o "$PROFANITY_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/profanity.db" - curl -fsSL -o "$RESERVED_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/reserved.db" - else - onlineUpdated="error" - fi - echo "$onlineUpdated" >"$UPDATES_DIR/downloaded" -} -download_if_needed() { - local lastCheck=$(read_or_default "$UPDATES_DIR/timestamp") - local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "") - local timestampNow=$(date +%s) - local updateCheck=$UPDATE_CHECK - if (((timestampNow - lastCheck) > updateCheck)) || [ "$lastDownload" == "" ] || [ "$lastDownload" == "error" ] || ((forceUpdateCheck == 1)) || [ $# -eq 1 ]; then - echo "$timestampNow" >"$UPDATES_DIR/timestamp" - ohai "Checking if new version is available..." - local onlineUpdated="$(curl -fsSL "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated")" - local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded") - if [ "$onlineUpdated" == "$lastDownload" ] && ((forceUpdateCheck == 0)); then - msgok "Latest update already downloaded" - else - download_updates - fi - else - msgok "Too soon to download update, skipping..." - fi -} -update_background_check() { - download_if_needed +#======================================= +# WATCHDOG LOGIC +#======================================= - local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "") - local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "") +get_watchdog_age_string() { + local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - if [ ! "$lastDownload" == "$updateInstalled" ] && [ ! "$lastDownload" == "" ] && [ ! "$lastDownload" == "error" ]; then - echo "Update needed" - local lastCalled=$(get_since_last_time "update_needed") - if ((lastCalled == -1)) || ((lastCalled > UPDATE_MAIL)); then - set_last_time "update_needed" - echo "Sending mail to user - tool update needed" - { - echo "✨ Na Twoim serwerze mikr.us z Nightscoutem można zaktualizować narzędzie nightscout-tool!" - echo " " - echo "🐕 Watchdog wykrył że dostępna jest nowa aktualizacja nightscout-tool." - echo "Na Twoim serwerze zainstalowana jest starsza wersja narzędzia - zaktualizuj go by poprawić stabilność systemu i uzyskać dostęp do nowych funkcji." - echo " " - echo "Aby zaktualizować narzędzie:" - echo " " - echo "1. Zaloguj się do panelu administracyjnego mikrusa i zaloguj się do WebSSH:" - echo " https://mikr.us/panel/?a=webssh" - echo " " - echo "2. Uruchom narzędzie komendą:" - echo " nightscout-tool" - echo " " - echo "3. Potwierdź naciskając przycisk:" - echo " 【 Aktualizacja 】" - echo " " - } | pusher "✨_Na_Twoim_serwerze_Nightscout_dostępna_jest_aktualizacja" - fi + if [[ -f $WATCHDOG_TIME_FILE ]]; then + last_time=$(cat "$WATCHDOG_TIME_FILE") + local status_ago=$(dateutils.ddiff "$last_time" "$curr_time" -f '%Mmin. %Ssek.') + echo "$last_time ($status_ago temu)" + else + echo "jescze nie uruchomiony" fi } -update_if_needed() { +get_watchdog_status_code() { + local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local status="unknown" - download_if_needed "$@" + if [[ -f $WATCHDOG_TIME_FILE ]]; then + last_time=$(cat "$WATCHDOG_TIME_FILE") + fi - local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "???") - local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") + if [[ -f $WATCHDOG_STATUS_FILE ]]; then + status=$(cat "$WATCHDOG_STATUS_FILE") + fi - if [ "$lastDownload" == "$updateInstalled" ] && ((forceUpdateCheck == 0)) && [ $# -eq 0 ]; then - msgok "Scripts and config files are up to date" - else + local status_ago=$(dateutils.ddiff "$curr_time" "$last_time" -f '%S') - if [ "$lastDownload" == "error" ]; then - msgerr "Download update failed" - if [ $# -eq 1 ]; then - okdlg "Aktualizacja niemożliwa" "Nie można w tej chwili aktualizować narzędzia.${TL}Spróbuj ponownie później.${NL}Jeśli problem nie ustąpi - sprawdź konfigurację kanału aktualizacji" - fi - else + if [ "$status_ago" -gt 900 ]; then + status="unknown" + fi - if [ $# -eq 0 ] && [ "$UPDATE_CHANNEL" == "master" ] && [[ "$lastDownload" < "$updateInstalled" ]]; then - msgnote "Downgrade not possible on master channel" - forceUpdateCheck=1 - download_if_needed - else + echo "$status" +} - local changed=0 - local redeploy=0 +get_watchdog_status_code_live() { + local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local status="unknown" - local instOnlineVer=$(extract_version "$(<"$UPDATES_DIR/install.sh")") - local depEnvOnlineVer=$(extract_version "$(<"$UPDATES_DIR/deployment.env")") - local nsEnvOnlineVer=$(extract_version "$(<"$UPDATES_DIR/nightscout.env")") - local compOnlineVer=$(extract_version "$(<"$UPDATES_DIR/docker-compose.yml")") + if [[ -f $WATCHDOG_TIME_FILE ]]; then + last_time=$(cat "$WATCHDOG_TIME_FILE") + fi - local instLocalVer=$(extract_version "$(<"$TOOL_FILE")") - local depEnvLocalVer=$(extract_version "$(<"$ENV_FILE_DEP")") - local nsEnvLocalVer=$(extract_version "$(<"$ENV_FILE_NS")") - local compLocalVer=$(extract_version "$(<"$DOCKER_COMPOSE_FILE")") + if [[ -f $WATCHDOG_STATUS_FILE ]]; then + status=$(cat "$WATCHDOG_STATUS_FILE") + fi - local msgInst="$(printf "\U1F7E2") $instLocalVer" - local msgDep="$(printf "\U1F7E2") $depEnvLocalVer" - local msgNs="$(printf "\U1F7E2") $nsEnvLocalVer" - local msgComp="$(printf "\U1F7E2") $compLocalVer" + local status_ago=$(dateutils.ddiff "$curr_time" "$last_time" -f '%S') - if ! [ "$instOnlineVer" == "$instLocalVer" ] || ! [ "$lastDownload" == "$updateInstalled" ]; then - changed=$((changed + 1)) - msgInst="$(printf "\U1F534") $instLocalVer $(printf "\U27A1") $instOnlineVer" - fi + if [ "$status_ago" -gt 900 ]; then + status="unknown" + fi - if ! [ "$depEnvLocalVer" == "$depEnvOnlineVer" ]; then - changed=$((changed + 1)) - redeploy=$((redeploy + 1)) - msgDep="$(printf "\U1F534") $depEnvLocalVer $(printf "\U27A1") $depEnvOnlineVer" - fi + local NS_STATUS=$(get_container_status_code 'ns-server') + local DB_STATUS=$(get_container_status_code 'ns-database') + local COMBINED_STATUS="$NS_STATUS $DB_STATUS" - if ! [ "$nsEnvLocalVer" == "$nsEnvOnlineVer" ]; then - changed=$((changed + 1)) - redeploy=$((redeploy + 1)) - msgNs="$(printf "\U1F534") $nsEnvLocalVer $(printf "\U27A1") $nsEnvOnlineVer" - fi + if [ "$COMBINED_STATUS" = "running running" ]; then - if ! [ "$compLocalVer" == "$compOnlineVer" ]; then - changed=$((changed + 1)) - redeploy=$((redeploy + 1)) - msgComp="$(printf "\U1F534") $compLocalVer $(printf "\U27A1") $compOnlineVer" - fi + status="detection_failed" - if [ "$changed" -eq 0 ]; then - if [ $# -eq 1 ]; then - msgok "Scripts and config files are up to date" - okdlg "Aktualizacja skryptów" "$1" - fi - else - local okTxt="" - if [ "$redeploy" -gt 0 ]; then - okTxt="${TL}${uni_warn} Aktualizacja zrestartuje i zaktualizuje kontenery ${uni_warn}" - fi + local domain=$cachedMenuDomain + local cachedDomainLen=${#cachedMenuDomain} + if ((cachedDomainLen < 16)); then + domain=$(get_td_domain) + fi - local versionMsg="${TL}Build: ${updateInstalled}" - if [ ! "$lastDownload" == "$updateInstalled" ]; then - versionMsg="$(pad_multiline "${TL}Masz build: ${updateInstalled}${NL} Dostępny: ${lastDownload}")" - fi + local domainLen=${#domain} + if ((domainLen > 15)); then + cachedMenuDomain=$domain + local html=$(curl -Lks "$domain") - yesnodlg "Aktualizacja skryptów" "$uni_confirm_upd" "$uni_resign" \ - "Zalecana jest aktualizacja plików:${versionMsg}" \ - "$( - pad_multiline \ - "${TL}${uni_bullet}Skrypt instalacyjny: $msgInst" \ - "${NL}${uni_bullet}Konfiguracja deploymentu: $msgDep" \ - "${NL}${uni_bullet}Konfiguracja Nightscout: $msgNs" \ - "${NL}${uni_bullet}Kompozycja usług: $msgComp${NL}" - )" \ - "$okTxt" + if [[ "$html" =~ github.com/nightscout/cgm-remote-monitor ]]; then + status="ok" + fi - if ! [ $? -eq 1 ]; then + if [[ "$html" =~ 'MongoDB connection failed' ]]; then + status="crashed" + fi - clear_last_time "update_needed" + regex3='poszło nie tak' + if [[ "$html" =~ $regex3 ]]; then + status="awaiting" + fi - if [ "$redeploy" -gt 0 ]; then - docker_compose_down - fi - - if ! [ "$compOnlineVer" == "$compLocalVer" ]; then - ohai "Updating $DOCKER_COMPOSE_FILE" - cp -fr "$UPDATES_DIR/docker-compose.yml" "$DOCKER_COMPOSE_FILE" - fi - - if ! [ "$depEnvLocalVer" == "$depEnvOnlineVer" ]; then - ohai "Updating $ENV_FILE_DEP" - dotenv-tool -pr -o "$ENV_FILE_DEP" -i "$UPDATES_DIR/deployment.env" "$ENV_FILE_DEP" - fi - - if ! [ "$nsEnvLocalVer" == "$nsEnvOnlineVer" ]; then - ohai "Updating $ENV_FILE_NS" - dotenv-tool -pr -o "$ENV_FILE_NS" -i "$UPDATES_DIR/deployment.env" "$ENV_FILE_NS" - fi - - echo "$lastDownload" >"$UPDATES_DIR/updated" + else + status="domain_failed" + fi - if ! [ "$instOnlineVer" == "$instLocalVer" ] || ! [ "$lastDownload" == "$updateInstalled" ]; then - ohai "Updating $TOOL_FILE" - cp -fr "$UPDATES_DIR/install.sh" "$TOOL_FILE" - okdlg "Aktualizacja zakończona" "Narzędzie zostanie uruchomione ponownie" - ohai "Restarting tool" - exec "$TOOL_FILE" - fi - fi - fi + else + if [ "$NS_STATUS" = "restarting" ] || [ "$DB_STATUS" = "restarting" ]; then + status="awaiting" + else + local logSample=$(timeout -k 15 10 docker logs ns-server --tail "10" 2>&1) + local regexSample='Cannot connect to the Docker daemon' + if [[ "$logSample" =~ $regexSample ]]; then + status="docker_down" + else + status="not_running" fi fi fi + echo "$status" } -about_dialog() { - LOG_KEY=$(<"$LOG_ENCRYPTION_KEY_FILE") - okdlg "O tym narzędziu..." \ - "$(printf '\U1F9D1') (c) 2023 Dominik Dzienia" \ - "${NL}$(printf '\U1F4E7') dominik.dzienia@gmail.com" \ - "${TL}$(printf '\U1F3DB') To narzędzie jest dystrybuowane na licencji CC BY-NC-ND 4.0" \ - "${NL}htps://creativecommons.org/licenses/by-nc-nd/4.0/deed.pl" \ - "${TL}wersja: $SCRIPT_VERSION ($SCRIPT_BUILD_TIME) $UPDATE_CHANNEL" \ - "${TL}hasło do logów: $LOG_KEY" -} +get_watchdog_status() { + local status="$1" + case "$status" in + "ok") + echo "$2" + ;; + "restart") + printf "\U1F680 wymuszono restart NS" + ;; + "awaiting") + printf "\U23F3 uruchamia się" + ;; + "restart_failed") + printf "\U1F680 restart NS to za mało" + ;; + "full_restart") + printf "\U1F680 restart NS i DB" + ;; + "unknown") + printf "\U1F4A4 brak statusu" + ;; + "not_running") + printf "\U1F534 serwer nie działa" + ;; + "detection_failed") + printf "\U2753 nieznany stan" + ;; + "domain_failed") + printf "\U2753 problem z domeną" + ;; + "crashed") + printf "\U1F4A5 awaria NS" + ;; + "docker_down") + printf "\U1F4A5 awaria Dockera" + ;; + esac -prompt_welcome() { - yesnodlg "Witamy" "$uni_start" "$uni_exit" \ - "Ten skrypt zainstaluje Nightscout na bieżącym serwerze mikr.us" \ - "${TL}Jeśli na tym serwerze jest już Nightscout " \ - "${NL}- ten skrypt umożliwia jego aktualizację oraz diagnostykę.${TL}" - exit_on_no_cancel -} -prompt_disclaimer() { - confirmdlg "Ostrzeżenie!" \ - "Zrozumiano!" \ - "Te narzędzie pozwala TOBIE zainstalować WŁASNĄ instancję Nightscout." \ - "${NL}Ty odpowiadasz za ten serwer i ewentualne skutki jego używania." \ - "${NL}Ty nim zarządzasz, to nie jest usługa czy produkt." \ - "${NL}To rozwiązanie \"Zrób to sam\" - SAM za nie odpowiadasz!" \ - "${TL}Autorzy skryptu nie ponoszą odpowiedzialności za skutki jego użycia!" \ - "${NL}Nie dajemy żadnych gwarancji co do jego poprawności czy dostępności!" \ - "${NL}Używasz go na własną odpowiedzialność!" \ - "${NL}Nie opieraj decyzji terapeutycznych na podstawie wskazań tego narzędzia!" \ - "${TL}Twórcy tego narzędzia NIE SĄ administratorami Mikr.us-ów ani Hetznera!" \ - "${NL}W razie problemów z dostępnością serwera najpierw sprawdź status Mikr.us-a!" } -instal_now_prompt() { - yesnodlg "Instalować Nightscout?" "$uni_install" "$uni_noenter" \ - "Wykryto konfigurację ale brak uruchomionych usług" \ - "${NL}Czy chcesz zainstalować teraz kontenery Nightscout?" -} +watchdog_check() { + echo "Nightscout Watchdog mode" -prompt_mikrus_host() { - if ! [[ "$MIKRUS_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - MIKRUS_HOST=$(hostname) - while :; do - if [[ "$MIKRUS_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - break - else - MIKRUS_NEW_HOST=$(whiptail --title "Podaj identyfikator serwera" --inputbox "\nNie udało się wykryć identyfikatora serwera,\npodaj go poniżej ręcznie.\n\nIdentyfikator składa się z jednej litery i trzech cyfr\n" --cancel-button "Anuluj" 13 65 3>&1 1>&2 2>&3) - exit_on_no_cancel - if [[ "$MIKRUS_NEW_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - MIKRUS_HOST=$MIKRUS_NEW_HOST - break - else - whiptail --title "$uni_excl Nieprawidłowy identyfikator serwera $uni_excl" --yesno "Podany identyfikator serwera ma nieprawidłowy format.\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 - exit_on_no_cancel - fi - fi - done + WATCHDOG_LAST_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + WATCHDOG_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + WATCHDOG_LAST_STATUS="unknown" + WATCHDOG_STATUS="unknown" - ohai "Updating admin config (host)" - dotenv-tool -pmr -i "$ENV_FILE_ADMIN" -- "MIKRUS_HOST=$MIKRUS_HOST" + if [[ -f $WATCHDOG_TIME_FILE ]]; then + echo "Found $WATCHDOG_TIME_FILE" + WATCHDOG_LAST_TIME=$(cat "$WATCHDOG_TIME_FILE") + else + echo "First watchdog run" fi -} - -prompt_mikrus_apikey() { - if ! [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then - freshInstall=$((freshInstall + 1)) - if [ -f "/klucz_api" ]; then - MIKRUS_APIKEY=$(cat "/klucz_api") - MIKRUS_INFO_HOST=$(curl -s -d "srv=$MIKRUS_HOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .server_id) + if [[ -f $WATCHDOG_STATUS_FILE ]]; then + echo "Found $WATCHDOG_STATUS_FILE" + WATCHDOG_LAST_STATUS=$(cat "$WATCHDOG_STATUS_FILE") + fi - if [[ "$MIKRUS_INFO_HOST" == "$MIKRUS_HOST" ]] || [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - msgcheck "Mikrus OK" - else - MIKRUS_APIKEY="" - fi - fi + local STATUS_AGO=$(dateutils.ddiff "$WATCHDOG_TIME" "$WATCHDOG_LAST_TIME" -f '%S') - if ! [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then + if [ "$STATUS_AGO" -gt 900 ]; then + echo "Watchdog last status is $STATUS_AGO seconds old, ignoring" + WATCHDOG_LAST_STATUS="unknown" + fi - whiptail --title "Przygotuj klucz API" --msgbox "Do zarządzania mikrusem [$MIKRUS_HOST] potrzebujemy klucz API.\n\n${uni_bullet}otwórz nową zakładkę w przeglądarce,\n${uni_bullet}wejdź do panelu administracyjnego swojego Mikr.us-a,\n${uni_bullet}otwórz sekcję API, pod adresem:\n\n${uni_bullet_pad}https://mikr.us/panel/?a=api\n\n${uni_bullet}skopiuj do schowka wartość klucza API" 16 70 - exit_on_no_cancel + free_space_check + update_background_check - while :; do - MIKRUS_APIKEY=$(whiptail --title "Podaj klucz API" --passwordbox "\nWpisz klucz API. Jeśli masz go skopiowanego w schowku,\nkliknij prawym przyciskiem i wybierz z menu:" --cancel-button "Anuluj" 11 65 3>&1 1>&2 2>&3) - exit_on_no_cancel - if [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then - MIKRUS_INFO_HOST=$(curl -s -d "srv=$MIKRUS_HOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .server_id) + local NS_STATUS=$(get_container_status_code 'ns-server') + local DB_STATUS=$(get_container_status_code 'ns-database') + local COMBINED_STATUS="$NS_STATUS $DB_STATUS" - if [[ "$MIKRUS_INFO_HOST" == "$MIKRUS_HOST" ]] || [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - msgcheck "Mikrus OK" - break - else - whiptail --title "$uni_excl Nieprawidłowy API key $uni_excl" --yesno "Podany API key wydaje się mieć dobry format, ale NIE DZIAŁA!\nMoże to literówka lub podano API KEY z innego Mikr.us-a?.\n\nPotrzebujesz API KEY serwera [$MIKRUS_HOST]\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 - exit_on_no_cancel - fi - else - whiptail --title "$uni_excl Nieprawidłowy API key $uni_excl" --yesno "Podany API key ma nieprawidłowy format.\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 - exit_on_no_cancel - fi - done + echo "Server container: $NS_STATUS" + echo "Database container: $DB_STATUS" - fi + if [ "$COMBINED_STATUS" = "running running" ]; then - ohai "Updating admin config (api key)" - dotenv-tool -pmr -i "$ENV_FILE_ADMIN" -- "MIKRUS_APIKEY=$MIKRUS_APIKEY" - fi -} + clear_last_time "docker_down" + clear_last_time "server_restart_needed" -prompt_api_secret() { - API_SECRET=$(dotenv-tool -r get -f "$ENV_FILE_NS" "API_SECRET") + echo "Will check page contents" + local domain=$(get_td_domain) - if ! [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; then - freshInstall=$((freshInstall + 1)) - while :; do - CHOICE=$(whiptail --title "Ustal API SECRET" --menu "\nUstal bezpieczny API_SECRET, tajne główne hasło zabezpieczające dostęp do Twojego Nightscouta\n" 13 70 2 \ - "1)" "Wygeneruj losowo." \ - "2)" "Podaj własny." \ - --ok-button="$uni_select" --cancel-button="$uni_exit" \ - 3>&2 2>&1 1>&3) - exit_on_no_cancel + local domainLen=${#domain} + if ((domainLen > 15)); then + local html=$(curl -iLsk "$domain") - case $CHOICE in - "1)") - API_SECRET=$(openssl rand -base64 100 | tr -dc '23456789@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | fold -w 16 | head -n 1) - whiptail --title "Zapisz API SECRET" --msgbox "Zapisz poniższy wygenerowany API SECRET w bezpiecznym miejscu, np.: managerze haseł:\n\n\n $API_SECRET" 12 50 - ;; - "2)") - while :; do - API_SECRET=$(whiptail --title "Podaj API SECRET" --passwordbox "\nWpisz API SECRET do serwera Nightscout:\n${uni_bullet}Upewnij się że masz go zapisanego np.: w managerze haseł\n${uni_bullet}Użyj conajmniej 12 znaków: małych i dużych liter i cyfr\n\n" --cancel-button "Anuluj" 12 75 3>&1 1>&2 2>&3) + WATCHDOG_STATUS="detection_failed" - if [ $? -eq 1 ]; then - break - fi + if [[ "$html" =~ github.com/nightscout/cgm-remote-monitor ]]; then + echo "Nightscout is running" + WATCHDOG_STATUS="ok" + fi - if [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; then - break + if [[ "$html" =~ 'MongoDB connection failed' ]]; then + echo "Nightscout crash detected" + WATCHDOG_STATUS="restart" + if [ "$WATCHDOG_LAST_STATUS" == "restart_failed" ]; then + event_mark "restart_both" + echo "Restarting DB first..." + docker restart 'ns-database' + echo "Then, restarting Nightscout..." + docker restart 'ns-server' + echo "...done" + WATCHDOG_STATUS="full_restart" + else + if [ "$WATCHDOG_LAST_STATUS" != "restart" ]; then + event_mark "restart_ns" + echo "Restarting only Nightscout..." + docker restart 'ns-server' + echo "...done" else - whiptail --title "$uni_excl Nieprawidłowy API SECRET $uni_excl" --yesno "Podany API SECRET ma nieprawidłowy format.\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_noenter" 10 73 - if [ $? -eq 1 ]; then - API_SECRET='' - break - fi - fi - done + echo "Restart was tried but NS still crashed, will retry restart next time" + WATCHDOG_STATUS="restart_failed" + fi + fi + else + regex3='poszło nie tak' + if [[ "$html" =~ $regex3 ]]; then + echo "Nightscout is still restarting..." + WATCHDOG_STATUS="awaiting" + fi + fi - ;; - esac + if [ "$WATCHDOG_STATUS" = "detection_failed" ]; then + { + echo "----------------------------------------------------------------" + echo "[$WATCHDOG_TIME] Unknown server failure:" + echo "CONTAINERS:" + docker stats --no-stream + echo "HTTP DUMP:" + echo "$html" + } >>"$WATCHDOG_FAILURES_FILE" + fi - while [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; do - API_SECRET_CHECK=$(whiptail --title "Podaj ponownie API SECRET" --passwordbox "\nDla sprawdzenia, wpisz ustalony przed chwilą API SECRET\n\n" --cancel-button "Anuluj" 11 65 3>&1 1>&2 2>&3) - if [ $? -eq 1 ]; then - API_SECRET='' - break - fi - if [[ "$API_SECRET" == "$API_SECRET_CHECK" ]]; then - ohai "Updating nightscout config (api secret)" - dotenv-tool -pmr -i "$ENV_FILE_NS" -- "API_SECRET=$API_SECRET" - break 2 + else + WATCHDOG_STATUS="domain_failed" + fi + + else + if [ "$NS_STATUS" = "restarting" ] || [ "$DB_STATUS" = "restarting" ]; then + WATCHDOG_STATUS="awaiting" + else + WATCHDOG_STATUS="not_running" + + local logSample=$(timeout -k 15 10 docker logs ns-server --tail "10" 2>&1) + local regexSample='Cannot connect to the Docker daemon' + if [[ "$logSample" =~ $regexSample ]]; then + WATCHDOG_STATUS="docker_down" + if [ "$WATCHDOG_LAST_STATUS" != "docker_down" ]; then + echo "Cannot connect to Docker, will restart service..." + set_last_time "docker_down" + sudo systemctl restart docker else - whiptail --title "$uni_excl Nieprawidłowe API SECRET $uni_excl" --yesno "Podana wartości API SECRET różni się od poprzedniej!\nChcesz podać ponownie?\n" --yes-button "$uni_reenter" --no-button "$uni_noenter" 9 60 - if [ $? -eq 1 ]; then - API_SECRET='' - break + echo "Cannot connect to Docker, and service cannot be restarted" + local lastCalled=$(get_since_last_time "server_restart_needed") + if ((lastCalled == -1)) || ((lastCalled > DOCKER_DOWN_MAIL)); then + set_last_time "server_restart_needed" + echo "Sending mail to user - manual server restart needed" + mail_restart_needed "Usługa Docker uległa awarii i nie można automatycznie jej uruchomić" + else + echo "Mail for manual restart already recently sent" fi fi + fi + fi + fi - done + echo "Watchdog observation: $WATCHDOG_STATUS" - done + # if [ "$WATCHDOG_LAST_STATUS" != "$WATCHDOG_STATUS" ]; then + echo "$WATCHDOG_TIME [$WATCHDOG_STATUS]" >>"$WATCHDOG_LOG_FILE" + LOGSIZE=$(wc -l <"$WATCHDOG_LOG_FILE") + if [ "$LOGSIZE" -gt 1000 ]; then + tail -1000 "$WATCHDOG_LOG_FILE" >"$WATCHDOG_LOG_FILE.tmp" + mv -f "$WATCHDOG_LOG_FILE.tmp" "$WATCHDOG_LOG_FILE" fi -} + # fi -docker_compose_up() { - process_gauge install_containers install_containers_progress "Uruchamianie Nightscouta" "Proszę czekać, trwa uruchamianie kontenerów..." + if [[ -f $WATCHDOG_FAILURES_FILE ]]; then + FAILSIZE=$(wc -l <"$WATCHDOG_FAILURES_FILE") + if [ "$FAILSIZE" -gt 10000 ]; then + tail -10000 "$WATCHDOG_FAILURES_FILE" >"$WATCHDOG_FAILURES_FILE.tmp" + mv -f "$WATCHDOG_FAILURES_FILE.tmp" "$WATCHDOG_FAILURES_FILE" + fi + fi + + echo "$WATCHDOG_TIME" >"$WATCHDOG_TIME_FILE" + echo "$WATCHDOG_STATUS" >"$WATCHDOG_STATUS_FILE" + + exit 0 } -docker_compose_update() { - process_gauge update_containers install_containers_progress "Uruchamianie Nightscouta" "Proszę czekać, trwa aktualizacja kontenerów..." + +#======================================= +# CLEANUP LOGIC +#======================================= + +do_cleanup_sys() { + ohai "Sprzątanie dziennik systemowego..." + event_mark "cleanup" + journalctl --vacuum-size=50M >>"$LOGTO" 2>&1 + ohai "Czyszczenie systemu apt..." + msgnote "Ta operacja może TROCHĘ potrwać (od kilku do kilkudziesięciu minut...)" + apt-get -y autoremove >>"$LOGTO" 2>&1 && apt-get -y clean >>"$LOGTO" 2>&1 + msgcheck "Czyszczenie dziennika i apt zakończono" } -docker_compose_down() { - process_gauge uninstall_containers uninstall_containers_progress "Zatrzymywanie Nightscouta" "Proszę czekać, trwa zatrzymywanie i usuwanie kontenerów..." +do_cleanup_docker() { + ohai "Usuwanie nieużywanych obrazów Dockera..." + event_mark "cleanup" + msgnote "Ta operacja może TROCHĘ potrwać (do kilku minut...)" + docker image prune -af >>"$LOGTO" 2>&1 + msgcheck "Czyszczenie Dockera zakończono" } -domain_setup_manual() { - ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") - whiptail --title "Ustaw domenę" --msgbox "Aby Nightscout był widoczny z internetu ustaw subdomenę:\n\n${uni_bullet}otwórz nową zakładkę w przeglądarce,\n${uni_bullet}wejdź do panelu administracyjnego swojego Mikr.us-a,\n${uni_bullet}otwórz sekcję [Subdomeny], pod adresem:\n\n${uni_bullet_pad} https://mikr.us/panel/?a=domain\n\n${uni_bullet}w pole nazwy wpisz dowolną własną nazwę\n${uni_bullet_pad}(tylko małe litery i cyfry, max. 12 znaków)\n${uni_bullet}w pole numer portu wpisz:\n${uni_bullet_pad}\n $ns_external_port\n\n${uni_bullet}kliknij [Dodaj subdomenę] i poczekaj do kilku minut" 22 75 +do_cleanup_db() { + ohai "Usuwanie kopii zapasowych bazy danych..." + event_mark "cleanup" + find /srv/nightscout/data/dbbackup ! -type d -delete + msgcheck "Czyszczenie kopii zapasowych zakończono" } -domain_setup() { +do_cleanup_container_logs() { + ohai "Zatrzymywanie kontenerów..." + event_mark "cleanup" + docker stop 'ns-server' + docker stop 'ns-database' + docker stop 'ns-backup' + ohai "Usuwanie logów kontenerów..." + truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-server')" + truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-database')" + truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-backup')" + ohai "Ponowne uruchamianie kontenerów..." + docker start 'ns-server' + docker start 'ns-database' + docker start 'ns-backup' + msgok "Logi usunięte" +} - local domain=$(get_td_domain) - local domainLen=${#domain} - if ((domainLen > 15)); then - msgcheck "Subdomena jest już skonfigurowana ($domain)" - okdlg "Subdomena już ustawiona" \ - "Wykryto poprzednio skonfigurowaną subdomenę:" \ - "${TL}$domain" \ - "${TL}Strona Nightscout powinna być widoczna z internetu." - return - fi +do_cleanup_diagnostics() { + ohai "Sprzątanie diagnostyki" + rm -f "$SUPPORT_LOG" + rm -f "$SUPPORT_LOG.gz" + rm -f "$SUPPORT_LOG.gz.asc" +} - ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") - whiptail --title "Ustaw subdomenę" --msgbox "Aby Nightscout był widoczny z internetu ustaw adres - subdomenę:\n\n [wybierz].ns.techdiab.pl\n\nWybrany początek subdomeny powinien:\n${uni_bullet}mieć długość od 4 do 12 znaków\n${uni_bullet}zaczynać się z małej litery,\n${uni_bullet}może składać się z małych liter i cyfr\n${uni_bullet}być unikalny, charakterystyczny i łatwa do zapamiętania" 16 75 +do_cleanup_app_state() { + ohai "Sprzątanie stanu aplikacji" + rm -f "$UPDATE_CHANNEL_FILE" + rm -f "$EVENTS_DB" +} - while :; do - SUBDOMAIN='' - while :; do - SUBDOMAIN=$(whiptail --title "Podaj początek subdomeny" --inputbox "\n(4-12 znaków, tylko: małe litery i cyfry)\n\n" --cancel-button "Anuluj" 12 60 3>&1 1>&2 2>&3) +do_cleanup_app_logs() { + ohai "Sprzątanie logów aplikacji" + rm -f "$WATCHDOG_STATUS_FILE" + rm -f "$WATCHDOG_TIME_FILE" + rm -f "$WATCHDOG_LOG_FILE" + rm -f "$WATCHDOG_FAILURES_FILE" + rm -f "$WATCHDOG_CRON_LOG" +} - if [ $? -eq 1 ]; then - break - fi - if [[ "$SUBDOMAIN" =~ ^[a-z][a-z0-9]{3,11}$ ]]; then - if printf "%s" "$SUBDOMAIN" | grep -f "$PROFANITY_DB_FILE" >>"$LOGTO" 2>&1; then - okdlg "$uni_excl Nieprawidłowa subdomena $uni_excl" \ - "Podana wartość:" \ - "${NL}$SUBDOMAIN" \ - "${TL}jest zajęta, zarezerwowana lub niedopuszczalna." \ - "${TL}Wymyśl coś innego" - SUBDOMAIN='' - continue - fi +#======================================= +# CONFIGURATION +#======================================= - if printf "%s" "$SUBDOMAIN" | grep -xf "$RESERVED_DB_FILE" >>"$LOGTO" 2>&1; then - okdlg "$uni_excl Nieprawidłowa subdomena $uni_excl" \ - "Podana wartość:" \ - "${NL}$SUBDOMAIN" \ - "${TL}jest zajęta lub zarezerwowana." \ - "${TL}Wymyśl coś innego" - SUBDOMAIN='' - continue - fi +source_admin() { + if [[ -f $ENV_FILE_ADMIN ]]; then + # shellcheck disable=SC1090 + source "$ENV_FILE_ADMIN" + msgok "Imported admin config" + fi +} - break - else - okdlg "$uni_excl Nieprawidłowy początek subdomeny $uni_excl" \ - "Podany początek subdomeny:" \ - "${NL}$SUBDOMAIN" \ - "${TL}ma nieprawidłowy format. Wymyśl coś innego" - if [ $? -eq 1 ]; then - SUBDOMAIN='' - continue - fi - fi +#======================================= +# UPGRADE +#======================================= - done +download_if_not_exists() { + if [[ -f $2 ]]; then + msgok "Found $1" + else + ohai "Downloading $1..." + curl -fsSL -o "$2" "$3" + msgcheck "Downloaded $1" + fi +} - if [ "$SUBDOMAIN" == "" ]; then - domain_setup_manual - break - fi +download_conf() { + download_if_not_exists "deployment config" "$ENV_FILE_DEP" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/deployment.env" + download_if_not_exists "nightscout config" "$ENV_FILE_NS" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/nightscout.env" + download_if_not_exists "docker compose file" "$DOCKER_COMPOSE_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/docker-compose.yml" + download_if_not_exists "profanity database" "$PROFANITY_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/profanity.db" + download_if_not_exists "reservation database" "$RESERVED_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/reserved.db" +} - local MHOST=$(hostname) - local APISEC=$(dotenv-tool -r get -f "$ENV_FILE_ADMIN" "MIKRUS_APIKEY") +download_tools() { + download_if_not_exists "update stamp" "$UPDATES_DIR/updated" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated" - ohai "Rejestrowanie subdomeny $SUBDOMAIN.ns.techdiab.pl" - local REGSTATUS=$(curl -sd "srv=$MHOST&key=$APISEC&domain=$SUBDOMAIN.ns.techdiab.pl" https://api.mikr.us/domain) - local STATOK=$(echo "$REGSTATUS" | jq -r ".status") - local STATERR=$(echo "$REGSTATUS" | jq -r ".error") + if ! [[ -f $TOOL_FILE ]]; then + download_if_not_exists "nightscout-tool file" "$TOOL_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/install.sh" + local timestamp=$(date +%s) + echo "$timestamp" >"$UPDATES_DIR/timestamp" + else + msgok "Found nightscout-tool" + fi - if ! [ "$STATOK" == "null" ]; then - msgcheck "Subdomena ustawiona poprawnie ($STATOK)" - okdlg "Subdomena ustawiona" \ - "Ustawiono subdomenę:\n\n$SUBDOMAIN.ns.techdiab.pl\n($STATOK)\n\nZa kilka minut strona będzie widoczna z internetu." - break - else - msgerr "Nie udało się ustawić subdomeny ($STATERR)" - whiptail --title "$uni_excl Błąd rezerwacji domeny $uni_excl" --yesno "Nie udało się zarezerwować subdomeny:\n $STATERR\n\nChcesz podać inną subdomenę?" --yes-button "$uni_reenter" --no-button "$uni_noenter" 10 73 - if [ $? -eq 1 ]; then - SUBDOMAIN='' - domain_setup_manual - break - fi - fi - done + if ! [[ -f $TOOL_LINK ]]; then + ohai "Linking nightscout-tool" + ln -s "$TOOL_FILE" "$TOOL_LINK" + fi + chmod +x "$TOOL_FILE" + chmod +x "$TOOL_LINK" } -admin_panel_promo() { - whiptail --title "Panel zarządzania Mikr.us-em" --msgbox "$(center_multiline 70 \ - "Ta instalacja Nightscout dodaje dodatkowy panel administracyjny" \ - "${NL}do zarządzania serwerem i konfiguracją - online." \ - "${TL}Znajdziesz go klikając na ikonkę serwera w menu strony Nightscout" \ - "${NL}lub dodając /mikrus na końcu swojego adresu Nightscout")" \ - 12 75 +download_updates() { + ohai "Downloading updated scripts and config files" + local onlineUpdated="$(curl -fsSL "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated")" + if [ ! "$onlineUpdated" == "" ]; then + curl -fsSL -o "$UPDATES_DIR/install.sh" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/install.sh" + curl -fsSL -o "$UPDATES_DIR/deployment.env" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/deployment.env" + curl -fsSL -o "$UPDATES_DIR/nightscout.env" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/nightscout.env" + curl -fsSL -o "$UPDATES_DIR/docker-compose.yml" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/docker-compose.yml" + curl -fsSL -o "$PROFANITY_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/profanity.db" + curl -fsSL -o "$RESERVED_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/reserved.db" + else + onlineUpdated="error" + fi + echo "$onlineUpdated" >"$UPDATES_DIR/downloaded" } -get_watchdog_age_string() { - local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - - if [[ -f $WATCHDOG_TIME_FILE ]]; then - last_time=$(cat "$WATCHDOG_TIME_FILE") - local status_ago=$(dateutils.ddiff "$last_time" "$curr_time" -f '%Mmin. %Ssek.') - echo "$last_time ($status_ago temu)" +download_if_needed() { + local lastCheck=$(read_or_default "$UPDATES_DIR/timestamp") + local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "") + local timestampNow=$(date +%s) + local updateCheck=$UPDATE_CHECK + if (((timestampNow - lastCheck) > updateCheck)) || [ "$lastDownload" == "" ] || [ "$lastDownload" == "error" ] || ((forceUpdateCheck == 1)) || [ $# -eq 1 ]; then + echo "$timestampNow" >"$UPDATES_DIR/timestamp" + ohai "Checking if new version is available..." + local onlineUpdated="$(curl -fsSL "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated")" + local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded") + if [ "$onlineUpdated" == "$lastDownload" ] && ((forceUpdateCheck == 0)); then + msgok "Latest update already downloaded" + else + download_updates + fi else - echo "jescze nie uruchomiony" + msgok "Too soon to download update, skipping..." fi } -get_watchdog_status_code() { - local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local status="unknown" +update_background_check() { + download_if_needed - if [[ -f $WATCHDOG_TIME_FILE ]]; then - last_time=$(cat "$WATCHDOG_TIME_FILE") - fi + local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "") + local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "") - if [[ -f $WATCHDOG_STATUS_FILE ]]; then - status=$(cat "$WATCHDOG_STATUS_FILE") + if [ ! "$lastDownload" == "$updateInstalled" ] && [ ! "$lastDownload" == "" ] && [ ! "$lastDownload" == "error" ]; then + echo "Update needed" + local lastCalled=$(get_since_last_time "update_needed") + if ((lastCalled == -1)) || ((lastCalled > UPDATE_MAIL)); then + set_last_time "update_needed" + echo "Sending mail to user - tool update needed" + { + echo "✨ Na Twoim serwerze mikr.us z Nightscoutem można zaktualizować narzędzie nightscout-tool!" + echo " " + echo "🐕 Watchdog wykrył że dostępna jest nowa aktualizacja nightscout-tool." + echo "Na Twoim serwerze zainstalowana jest starsza wersja narzędzia - zaktualizuj go by poprawić stabilność systemu i uzyskać dostęp do nowych funkcji." + echo " " + echo "Aby zaktualizować narzędzie:" + echo " " + echo "1. Zaloguj się do panelu administracyjnego mikrusa i zaloguj się do WebSSH:" + echo " https://mikr.us/panel/?a=webssh" + echo " " + echo "2. Uruchom narzędzie komendą:" + echo " nightscout-tool" + echo " " + echo "3. Potwierdź naciskając przycisk:" + echo " 【 Aktualizacja 】" + echo " " + } | pusher "✨_Na_Twoim_serwerze_Nightscout_dostępna_jest_aktualizacja" + fi fi +} - local status_ago=$(dateutils.ddiff "$curr_time" "$last_time" -f '%S') - if [ "$status_ago" -gt 900 ]; then - status="unknown" - fi - echo "$status" -} +#======================================= +# DIAGNOSTICS +#======================================= -get_watchdog_status_code_live() { - local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local status="unknown" +gather_diagnostics() { - if [[ -f $WATCHDOG_TIME_FILE ]]; then - last_time=$(cat "$WATCHDOG_TIME_FILE") - fi + local maxNsLogs=$1 + local maxDbLogs=$2 + local curr_time=$3 - if [[ -f $WATCHDOG_STATUS_FILE ]]; then - status=$(cat "$WATCHDOG_STATUS_FILE") - fi + diagnosticsSizeOk=0 - local status_ago=$(dateutils.ddiff "$curr_time" "$last_time" -f '%S') + do_cleanup_diagnostics - if [ "$status_ago" -gt 900 ]; then - status="unknown" - fi + ohai "Zbieranie diagnostyki" - local NS_STATUS=$(get_container_status_code 'ns-server') - local DB_STATUS=$(get_container_status_code 'ns-database') - local COMBINED_STATUS="$NS_STATUS $DB_STATUS" + local domain=$(get_td_domain) + local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") + local mikrus_h=$(hostname) + local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") - if [ "$COMBINED_STATUS" = "running running" ]; then + local LOG_DIVIDER="=======================================================" - status="detection_failed" + { + echo "Dane diagnostyczne zebrane $curr_time" + echo " serwer : $mikrus_h" + echo " domena : $domain" + echo " wersja nightscout : $ns_tag" + echo " wersja nightscout-tool : $SCRIPT_VERSION ($SCRIPT_BUILD_TIME) $UPDATE_CHANNEL" + echo " build : ${updateInstalled}" + } >"$SUPPORT_LOG" - local domain=$cachedMenuDomain - local cachedDomainLen=${#cachedMenuDomain} - if ((cachedDomainLen < 16)); then - domain=$(get_td_domain) - fi + ohai "Zbieranie statusu usług" - local domainLen=${#domain} - if ((domainLen > 15)); then - cachedMenuDomain=$domain - local html=$(curl -Lks "$domain") + { + echo "$LOG_DIVIDER" + echo " Statusy usług" + echo "$LOG_DIVIDER" + echo " Nightscout: $(get_container_status 'ns-server')" + echo " Baza danych: $(get_container_status 'ns-database')" + echo " Backup: $(get_container_status 'ns-backup')" + echo " Watchdog: $(get_watchdog_status "$(get_watchdog_status_code)" "$uni_watchdog_ok")" + } >>"$SUPPORT_LOG" - if [[ "$html" =~ github.com/nightscout/cgm-remote-monitor ]]; then - status="ok" - fi + local spaceInfo=$(get_space_info) + local remainingTxt=$(echo "$spaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) + local totalTxt=$(echo "$spaceInfo" | awk '{print $2}' | numfmt --to iec-i --suffix=B) + local percTxt=$(echo "$spaceInfo" | awk '{print $4}') - if [[ "$html" =~ 'MongoDB connection failed' ]]; then - status="crashed" - fi + { + echo "$LOG_DIVIDER" + echo " Miejsce na dysku" + echo "$LOG_DIVIDER" + echo " Dostępne: ${remainingTxt}" + echo " Zajęte: ${percTxt} (z ${totalTxt})" + } >>"$SUPPORT_LOG" - regex3='poszło nie tak' - if [[ "$html" =~ $regex3 ]]; then - status="awaiting" - fi + ohai "Zbieranie zdarzeń" + { + echo "$LOG_DIVIDER" + echo " Zdarzenia" + echo "$LOG_DIVIDER" + event_list + } >>"$SUPPORT_LOG" - else - status="domain_failed" - fi + ohai "Zbieranie logów watchdoga" - else - if [ "$NS_STATUS" = "restarting" ] || [ "$DB_STATUS" = "restarting" ]; then - status="awaiting" - else - local logSample=$(timeout -k 15 10 docker logs ns-server --tail "10" 2>&1) - local regexSample='Cannot connect to the Docker daemon' - if [[ "$logSample" =~ $regexSample ]]; then - status="docker_down" - else - status="not_running" - fi - fi + if [[ -f $WATCHDOG_LOG_FILE ]]; then + { + echo "$LOG_DIVIDER" + echo " Watchdog log" + echo "$LOG_DIVIDER" + timeout -k 15 10 tail -n 200 "$WATCHDOG_LOG_FILE" + } >>"$SUPPORT_LOG" fi - echo "$status" + if [[ -f $WATCHDOG_FAILURES_FILE ]]; then + { + echo "$LOG_DIVIDER" + echo " Watchdog failures log" + echo "$LOG_DIVIDER" + timeout -k 15 10 tail -n 200 "$WATCHDOG_FAILURES_FILE" + } >>"$SUPPORT_LOG" + fi + + ohai "Zbieranie logów usług" + + { + echo "$LOG_DIVIDER" + echo " Nightscout log" + echo "$LOG_DIVIDER" + timeout -k 15 10 docker logs ns-server --tail "$maxNsLogs" >>"$SUPPORT_LOG" 2>&1 + echo "$LOG_DIVIDER" + echo " MongoDB database log" + echo "$LOG_DIVIDER" + timeout -k 15 10 docker logs ns-database --tail "$maxDbLogs" >>"$SUPPORT_LOG" 2>&1 + } >>"$SUPPORT_LOG" + + ohai "Kompresowanie i szyfrowanie raportu" + + gzip -9 "$SUPPORT_LOG" + + local logkey=$(<"$LOG_ENCRYPTION_KEY_FILE") + + gpg --passphrase "$logkey" --batch --quiet --yes -a -c "$SUPPORT_LOG.gz" } -get_watchdog_status() { - local status="$1" - case "$status" in - "ok") - echo "$2" - ;; - "restart") - printf "\U1F680 wymuszono restart NS" - ;; - "awaiting") - printf "\U23F3 uruchamia się" - ;; - "restart_failed") - printf "\U1F680 restart NS to za mało" - ;; - "full_restart") - printf "\U1F680 restart NS i DB" - ;; - "unknown") - printf "\U1F4A4 brak statusu" - ;; - "not_running") - printf "\U1F534 serwer nie działa" - ;; - "detection_failed") - printf "\U2753 nieznany stan" - ;; - "domain_failed") - printf "\U2753 problem z domeną" - ;; - "crashed") - printf "\U1F4A5 awaria NS" - ;; - "docker_down") - printf "\U1F4A5 awaria Dockera" - ;; - esac +retry_diagnostics() { + local maxNsLogs=$1 + local maxDbLogs=$2 + local curr_time=$3 -} + if ((diagnosticsSizeOk == 0)); then -show_watchdog_logs() { - local col=$((COLUMNS - 10)) - local rws=$((LINES - 3)) - if [ "$col" -gt 120 ]; then - col=160 - fi - if [ "$col" -lt 60 ]; then - col=60 - fi - if [ "$rws" -lt 12 ]; then - rws=12 - fi + ohai "Sprawdzanie rozmiaru raportu" - local tmpfile=$(mktemp) - { - echo "Ostatnie uruchomienie watchdoga:" - get_watchdog_age_string - echo "-------------------------------------------------------" + local logSize=$(stat --printf="%s" "$SUPPORT_LOG.gz.asc") + local allowedTxt=$(echo "18000" | numfmt --to si --suffix=B) + local currentTxt=$(echo "$logSize" | numfmt --to si --suffix=B) - if [[ -f $WATCHDOG_LOG_FILE ]]; then - echo "Statusy ostatnich przebiegów watchdoga:" - tail -5 "$WATCHDOG_LOG_FILE" + if ((logSize > 18000)); then + msgerr "Zebrana diagnostyka jest zbyt duża do wysłania (${currentTxt})" + ohai "Spróbuję zebrać mniej danych aby zmieścić się w limicie (${allowedTxt})" + gather_diagnostics "$maxNsLogs" "$maxDbLogs" "$curr_time" else - echo "Brak logów z ostatnich przebiegów watchdoga" - fi - echo "-------------------------------------------------------" - - if [[ -f $WATCHDOG_CRON_LOG ]]; then - echo "Log ostatniego przebiegu watchdoga:" - cat "$WATCHDOG_CRON_LOG" + diagnosticsSizeOk=1 + msgok "Raport ma rozmiar ${currentTxt} i mieści się w limicie ${allowedTxt} dla usługi pusher-a" fi - } >"$tmpfile" - - whiptail --title "Logi Watchdoga" --scrolltext --textbox "$tmpfile" "$rws" "$col" - rm "$tmpfile" -} - -get_events_status() { - local count="$(event_count)" - if ((count == 0)); then - printf "\U2728 brak zdarzeń" - elif ((count == 1)); then - printf "\U1F4C5 jedno zdarzenie" - elif (((count % 10) > 1)) && (((count % 10) < 5)); then - printf "\U1F4C5 %s zdarzenia" "$count" - else - printf "\U1F4C5 %s zdarzeń" "$count" fi } -get_container_status() { - local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') - if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then - local status=$(docker inspect "$ID" | jq -r ".[0].State.Status") - case "$status" in - "running") - printf "\U1F7E2 działa" - ;; - "restarting") - printf "\U1F7E3 restart" - ;; - "created") - printf "\U26AA utworzono" - ;; - "exited") - printf "\U1F534 wyłączono" - ;; - "paused") - printf "\U1F7E1 zapauzowano" - ;; - "dead") - printf "\U1F480 zablokowany" - ;; - esac - else - printf '\U2753 nie odnaleziono' +#======================================= +# APP LOGIC +#======================================= + +get_td_domain() { + local MHOST=$(hostname) + if ! [[ "$MHOST" =~ [a-zA-Z]{2,16}[0-9]{3} ]]; then + MIKRUS_APIKEY=$(cat "/klucz_api") + MIKRUS_INFO_HOST=$(curl -s -d "srv=$MHOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .imie_id) + if [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{2,16}[0-9]{3} ]]; then + MHOST="$MIKRUS_INFO_HOST" + fi fi + local APIKEY=$(dotenv-tool -r get -f "$ENV_FILE_ADMIN" "MIKRUS_APIKEY") + curl -sd "srv=$MHOST&key=$APIKEY" https://api.mikr.us/domain | jq -r ".[].name" | grep ".ns.techdiab.pl" | head -n 1 } -get_container_status_code() { - local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') - if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then - local status=$(docker inspect "$ID" | jq -r ".[0].State.Status") - echo "$status" +get_domain_status() { + local domain=$(get_td_domain) + local domainLen=${#domain} + if ((domainLen > 15)); then + printf "\U1F7E2 %s" "$domain" else - echo "unknown" + printf "\U26AA nie zarejestrowano" fi } -show_logs() { - local col=$((COLUMNS - 10)) - local rws=$((LINES - 4)) - if [ "$col" -gt 120 ]; then - col=160 - fi - if [ "$col" -lt 60 ]; then - col=60 - fi - if [ "$rws" -lt 12 ]; then - rws=12 - fi - - local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') - if [ -n "$ID" ]; then - local tmpfile=$(mktemp) - docker logs "$ID" 2>&1 | tail $((rws * -6)) | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g' >"$tmpfile" - whiptail --title "Logi $2" --scrolltext --textbox "$tmpfile" "$rws" "$col" - rm "$tmpfile" +load_update_channel() { + if [[ -f $UPDATE_CHANNEL_FILE ]]; then + UPDATE_CHANNEL=$(cat "$UPDATE_CHANNEL_FILE") + msgok "Loaded update channel: $UPDATE_CHANNEL" fi } -status_menu() { - while :; do - local CHOICE=$(whiptail --title "Status kontenerów" --menu "\n Aktualizacja: kontenery na żywo, watchdog co 5 minut\n\n Wybierz pozycję aby zobaczyć logi:\n" 18 60 6 \ - "1)" " Nightscout: $(get_container_status 'ns-server')" \ - "2)" " Baza danych: $(get_container_status 'ns-database')" \ - "3)" " Backup: $(get_container_status 'ns-backup')" \ - "4)" " Watchdog: $(get_watchdog_status "$(get_watchdog_status_code)" "$uni_watchdog_ok")" \ - "5)" " Zdarzenia: $(get_events_status)" \ - "M)" "Powrót do menu" \ - --ok-button="Zobacz logi" --cancel-button="$uni_back" \ - 3>&2 2>&1 1>&3) - - case $CHOICE in - "1)") - show_logs 'ns-server' 'Nightscouta' - ;; - "2)") - show_logs 'ns-database' 'bazy danych' - ;; - "3)") - show_logs 'ns-backup' 'usługi kopii zapasowych' - ;; - "4)") - show_watchdog_logs - ;; - "5)") - okdlg "Zdarzenia" \ - "$(pad_multiline "$(event_list)")" - ;; - "M)") - break - ;; - "") - break - ;; - esac - done +startup_version() { + local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") + msgnote "nightscout-tool version $SCRIPT_VERSION ($SCRIPT_BUILD_TIME)" + msgnote "build ${updateInstalled}" + msgnote "$uni_copyright 2023-2026 Dominik Dzienia" + msgnote "Licensed under CC BY-NC-ND 4.0" } -version_menu() { - - local tags=$(wget -q -O - "https://hub.docker.com/v2/namespaces/nightscout/repositories/cgm-remote-monitor/tags?page_size=100" | jq -r ".results[].name" | sed "/dev_[a-f0-9]*/d" | sort --version-sort -u -r | head -n 8) - while :; do - local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") - local versions=() - while read -r line; do - if [ "$line" == "$ns_tag" ]; then - continue - fi +#======================================= +# OTHER UI +#======================================= - label=" - na sztywno $line " +about_dialog() { + LOG_KEY=$(<"$LOG_ENCRYPTION_KEY_FILE") + okdlg "O tym narzędziu..." \ + "$(printf '\U1F9D1') (c) 2023-2026 Dominik Dzienia" \ + "${NL}$(printf '\U1F4E7') dominik.dzienia@gmail.com" \ + "${TL}$(printf '\U1F3DB') To narzędzie jest dystrybuowane na licencji CC BY-NC-ND 4.0" \ + "${NL}htps://creativecommons.org/licenses/by-nc-nd/4.0/deed.pl" \ + "${TL}wersja: $SCRIPT_VERSION ($SCRIPT_BUILD_TIME) $UPDATE_CHANNEL" \ + "${TL}hasło do logów: $LOG_KEY" +} - if [ "$line" == "latest_dev" ]; then - label=" - najnowsza wersja rozwojowa " - fi +prompt_welcome() { + yesnodlg "Witamy" "$uni_start" "$uni_exit" \ + "Ten skrypt zainstaluje Nightscout na bieżącym serwerze mikr.us" \ + "${TL}Jeśli na tym serwerze jest już Nightscout " \ + "${NL}- ten skrypt umożliwia jego aktualizację oraz diagnostykę.${TL}" + exit_on_no_cancel +} - if [ "$line" == "latest" ]; then - label=" - aktualna wersja stabilna " - fi +prompt_disclaimer() { + confirmdlg "Ostrzeżenie!" \ + "Zrozumiano!" \ + "Te narzędzie pozwala TOBIE zainstalować WŁASNĄ instancję Nightscout." \ + "${NL}Ty odpowiadasz za ten serwer i ewentualne skutki jego używania." \ + "${NL}Ty nim zarządzasz, to nie jest usługa czy produkt." \ + "${NL}To rozwiązanie \"Zrób to sam\" - SAM za nie odpowiadasz!" \ + "${TL}Autorzy skryptu nie ponoszą odpowiedzialności za skutki jego użycia!" \ + "${NL}Nie dajemy żadnych gwarancji co do jego poprawności czy dostępności!" \ + "${NL}Używasz go na własną odpowiedzialność!" \ + "${NL}Nie opieraj decyzji terapeutycznych na podstawie wskazań tego narzędzia!" \ + "${TL}Twórcy tego narzędzia NIE SĄ administratorami Mikr.us-ów ani Hetznera!" \ + "${NL}W razie problemów z dostępnością serwera najpierw sprawdź status Mikr.us-a!" +} - versions+=("$line") - versions+=("$label") - done <<<"$tags" +install_now_prompt() { + yesnodlg "Instalować Nightscout?" "$uni_install" "$uni_noenter" \ + "Wykryto konfigurację ale brak uruchomionych usług" \ + "${NL}Czy chcesz zainstalować teraz kontenery Nightscout?" +} - versions+=("M)") - versions+=(" Powrót do poprzedniego menu") +# Promocja panelu administracyjnego - nie jest używany +admin_panel_promo() { + whiptail --title "Panel zarządzania Mikr.us-em" --msgbox "$(center_multiline 70 \ + "Ta instalacja Nightscout dodaje dodatkowy panel administracyjny" \ + "${NL}do zarządzania serwerem i konfiguracją - online." \ + "${TL}Znajdziesz go klikając na ikonkę serwera w menu strony Nightscout" \ + "${NL}lub dodając /mikrus na końcu swojego adresu Nightscout")" \ + 12 75 +} - local CHOICE=$(whiptail --title "Wersja Nightscout" --menu "\nZmień wersję kontenera Nightscout z: $ns_tag na:\n\n" 20 60 10 \ - "${versions[@]}" \ - --ok-button="Zmień" --cancel-button="$uni_back" \ - 3>&2 2>&1 1>&3) - if [ "$CHOICE" == "M)" ]; then - break - fi +#======================================= +# SETUP PROMPT DIALOGS +#======================================= - if [ "$CHOICE" == "" ]; then - break - fi +prompt_mikrus_host() { + if ! [[ "$MIKRUS_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + MIKRUS_HOST=$(hostname) + while :; do + if [[ "$MIKRUS_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + break + else + MIKRUS_NEW_HOST=$(whiptail --title "Podaj identyfikator serwera" --inputbox "\nNie udało się wykryć identyfikatora serwera,\npodaj go poniżej ręcznie.\n\nIdentyfikator składa się z jednej litery i trzech cyfr\n" --cancel-button "Anuluj" 13 65 3>&1 1>&2 2>&3) + exit_on_no_cancel + if [[ "$MIKRUS_NEW_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + MIKRUS_HOST=$MIKRUS_NEW_HOST + break + else + whiptail --title "$uni_excl Nieprawidłowy identyfikator serwera $uni_excl" --yesno "Podany identyfikator serwera ma nieprawidłowy format.\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 + exit_on_no_cancel + fi + fi + done - if [ "$CHOICE" == "$ns_tag" ]; then - whiptail --title "Ta sama wersja!" --msgbox "Wybrano bieżącą wersję - brak zmiany" 7 50 - else + ohai "Updating admin config (host)" + dotenv-tool -pmr -i "$ENV_FILE_ADMIN" -- "MIKRUS_HOST=$MIKRUS_HOST" + fi +} - whiptail --title "Zmienić wersję Nightscout?" --yesno --defaultno "Czy na pewno chcesz zmienić wersję z: $ns_tag na: $CHOICE?\n\n${uni_bullet}dane i konfiguracja NIE SĄ usuwane\n${uni_bullet}wersję można łatwo zmienić ponownie\n${uni_bullet}dane w bazie danych mogą ulec zmianie i NIE BYĆ kompatybilne" --yes-button "$uni_confirm_ch" --no-button "$uni_resign" 13 73 - if ! [ $? -eq 1 ]; then - event_mark "change_ns_version" - docker_compose_down - ohai "Changing Nightscout container tag from: $ns_tag to: $CHOICE" - dotenv-tool -pmr -i "$ENV_FILE_DEP" -- "NS_NIGHTSCOUT_TAG=$CHOICE" - docker_compose_update - whiptail --title "Zmieniono wersję Nightscout" --msgbox "$(center_multiline 65 \ - "Zmieniono wersję Nightscout na: $CHOICE" \ - "${TL}Sprawdź czy Nightscout działa poprawnie, w razie problemów:" \ - "${NL}${uni_bullet}aktualizuj kontenery" \ - "${NL}${uni_bullet}spróbuj wyczyścić bazę danych" \ - "${NL}${uni_bullet}wróć do poprzedniej wersji ($ns_tag)")" \ - 13 70 - break +prompt_mikrus_apikey() { + if ! [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then + freshInstall=$((freshInstall + 1)) + + if [ -f "/klucz_api" ]; then + MIKRUS_APIKEY=$(cat "/klucz_api") + MIKRUS_INFO_HOST=$(curl -s -d "srv=$MIKRUS_HOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .server_id) + + if [[ "$MIKRUS_INFO_HOST" == "$MIKRUS_HOST" ]] || [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + msgcheck "Mikrus OK" + else + MIKRUS_APIKEY="" fi + fi + + if ! [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then + + whiptail --title "Przygotuj klucz API" --msgbox "Do zarządzania mikrusem [$MIKRUS_HOST] potrzebujemy klucz API.\n\n${uni_bullet}otwórz nową zakładkę w przeglądarce,\n${uni_bullet}wejdź do panelu administracyjnego swojego Mikr.us-a,\n${uni_bullet}otwórz sekcję API, pod adresem:\n\n${uni_bullet_pad}https://mikr.us/panel/?a=api\n\n${uni_bullet}skopiuj do schowka wartość klucza API" 16 70 + exit_on_no_cancel + + while :; do + MIKRUS_APIKEY=$(whiptail --title "Podaj klucz API" --passwordbox "\nWpisz klucz API. Jeśli masz go skopiowanego w schowku,\nkliknij prawym przyciskiem i wybierz z menu:" --cancel-button "Anuluj" 11 65 3>&1 1>&2 2>&3) + exit_on_no_cancel + if [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then + MIKRUS_INFO_HOST=$(curl -s -d "srv=$MIKRUS_HOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .server_id) + + if [[ "$MIKRUS_INFO_HOST" == "$MIKRUS_HOST" ]] || [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + msgcheck "Mikrus OK" + break + else + whiptail --title "$uni_excl Nieprawidłowy API key $uni_excl" --yesno "Podany API key wydaje się mieć dobry format, ale NIE DZIAŁA!\nMoże to literówka lub podano API KEY z innego Mikr.us-a?.\n\nPotrzebujesz API KEY serwera [$MIKRUS_HOST]\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 + exit_on_no_cancel + fi + else + whiptail --title "$uni_excl Nieprawidłowy API key $uni_excl" --yesno "Podany API key ma nieprawidłowy format.\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 + exit_on_no_cancel + fi + done fi - done + ohai "Updating admin config (api key)" + dotenv-tool -pmr -i "$ENV_FILE_ADMIN" -- "MIKRUS_APIKEY=$MIKRUS_APIKEY" + fi } -do_cleanup_sys() { - ohai "Sprzątanie dziennik systemowego..." - event_mark "cleanup" - journalctl --vacuum-size=50M >>"$LOGTO" 2>&1 - ohai "Czyszczenie systemu apt..." - msgnote "Ta operacja może TROCHĘ potrwać (od kilku do kilkudziesięciu minut...)" - apt-get -y autoremove >>"$LOGTO" 2>&1 && apt-get -y clean >>"$LOGTO" 2>&1 - msgcheck "Czyszczenie dziennika i apt zakończono" -} +prompt_api_secret() { + API_SECRET=$(dotenv-tool -r get -f "$ENV_FILE_NS" "API_SECRET") -do_cleanup_docker() { - ohai "Usuwanie nieużywanych obrazów Dockera..." - event_mark "cleanup" - msgnote "Ta operacja może TROCHĘ potrwać (do kilku minut...)" - docker image prune -af >>"$LOGTO" 2>&1 - msgcheck "Czyszczenie Dockera zakończono" -} + if ! [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; then + freshInstall=$((freshInstall + 1)) + while :; do + CHOICE=$(whiptail --title "Ustal API SECRET" --menu "\nUstal bezpieczny API_SECRET, tajne główne hasło zabezpieczające dostęp do Twojego Nightscouta\n" 13 70 2 \ + "1)" "Wygeneruj losowo." \ + "2)" "Podaj własny." \ + --ok-button="$uni_select" --cancel-button="$uni_exit" \ + 3>&2 2>&1 1>&3) + exit_on_no_cancel -do_cleanup_db() { - ohai "Usuwanie kopii zapasowych bazy danych..." - event_mark "cleanup" - find /srv/nightscout/data/dbbackup ! -type d -delete - msgcheck "Czyszczenie kopii zapasowych zakończono" -} + case $CHOICE in + "1)") + API_SECRET=$(openssl rand -base64 100 | tr -dc '23456789@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | fold -w 16 | head -n 1) + whiptail --title "Zapisz API SECRET" --msgbox "Zapisz poniższy wygenerowany API SECRET w bezpiecznym miejscu, np.: managerze haseł:\n\n\n $API_SECRET" 12 50 + ;; + "2)") + while :; do + API_SECRET=$(whiptail --title "Podaj API SECRET" --passwordbox "\nWpisz API SECRET do serwera Nightscout:\n${uni_bullet}Upewnij się że masz go zapisanego np.: w managerze haseł\n${uni_bullet}Użyj conajmniej 12 znaków: małych i dużych liter i cyfr\n\n" --cancel-button "Anuluj" 12 75 3>&1 1>&2 2>&3) -do_cleanup_container_logs() { - ohai "Zatrzymywanie kontenerów..." - event_mark "cleanup" - docker stop 'ns-server' - docker stop 'ns-database' - docker stop 'ns-backup' - ohai "Usuwanie logów kontenerów..." - truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-server')" - truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-database')" - truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-backup')" - ohai "Ponowne uruchamianie kontenerów..." - docker start 'ns-server' - docker start 'ns-database' - docker start 'ns-backup' - msgok "Logi usunięte" + if [ $? -eq 1 ]; then + break + fi + + if [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; then + break + else + whiptail --title "$uni_excl Nieprawidłowy API SECRET $uni_excl" --yesno "Podany API SECRET ma nieprawidłowy format.\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_noenter" 10 73 + if [ $? -eq 1 ]; then + API_SECRET='' + break + fi + fi + done + + ;; + esac + + while [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; do + API_SECRET_CHECK=$(whiptail --title "Podaj ponownie API SECRET" --passwordbox "\nDla sprawdzenia, wpisz ustalony przed chwilą API SECRET\n\n" --cancel-button "Anuluj" 11 65 3>&1 1>&2 2>&3) + if [ $? -eq 1 ]; then + API_SECRET='' + break + fi + if [[ "$API_SECRET" == "$API_SECRET_CHECK" ]]; then + ohai "Updating nightscout config (api secret)" + dotenv-tool -pmr -i "$ENV_FILE_NS" -- "API_SECRET=$API_SECRET" + break 2 + else + whiptail --title "$uni_excl Nieprawidłowe API SECRET $uni_excl" --yesno "Podana wartości API SECRET różni się od poprzedniej!\nChcesz podać ponownie?\n" --yes-button "$uni_reenter" --no-button "$uni_noenter" 9 60 + if [ $? -eq 1 ]; then + API_SECRET='' + break + fi + fi + + done + + done + fi } -do_cleanup_diagnostics() { - ohai "Sprzątanie diagnostyki" - rm -f "$SUPPORT_LOG" - rm -f "$SUPPORT_LOG.gz" - rm -f "$SUPPORT_LOG.gz.asc" + + +#======================================= +# SETUP UI +#======================================= + +docker_compose_up() { + process_gauge install_containers install_containers_progress "Uruchamianie Nightscouta" "Proszę czekać, trwa uruchamianie kontenerów..." } -do_cleanup_app_state() { - ohai "Sprzątanie stanu aplikacji" - rm -f "$UPDATE_CHANNEL_FILE" - rm -f "$EVENTS_DB" +docker_compose_update() { + process_gauge update_containers install_containers_progress "Uruchamianie Nightscouta" "Proszę czekać, trwa aktualizacja kontenerów..." } -do_cleanup_app_logs() { - ohai "Sprzątanie logów aplikacji" - rm -f "$WATCHDOG_STATUS_FILE" - rm -f "$WATCHDOG_TIME_FILE" - rm -f "$WATCHDOG_LOG_FILE" - rm -f "$WATCHDOG_FAILURES_FILE" - rm -f "$WATCHDOG_CRON_LOG" +docker_compose_down() { + process_gauge uninstall_containers uninstall_containers_progress "Zatrzymywanie Nightscouta" "Proszę czekać, trwa zatrzymywanie i usuwanie kontenerów..." } -prompt_cleanup_container_logs() { - yesnodlg "Usunąć logi kontenerów?" "$uni_delete" "$uni_leave_logs" \ - "Czy chcesz usunąć logi kontenerów nightscout i bazy?" \ - "${TL}Jeśli Twój serwer działa poprawnie," \ - "${NL}- możesz spokojnie usunąć logi." \ - "${TL}Jeśli masz problem z serwerem - zostaw logi!" \ - "${NL}- logi mogą być niezbędne do diagnostyki" \ - "${TL}(ta operacja uruchomi ponownie kontenery)" +setup_done() { + whiptail --title "Gotowe!" --yesno --defaultno " Możesz teraz zamknąć to narzędzie lub wrócić do menu.\n Narzędzie dostępne jest też jako komenda konsoli:\n\n nightscout-tool" --yes-button "$uni_menu" --no-button "$uni_finish" 12 70 + exit_on_no_cancel + main_menu } -cleanup_menu() { +domain_setup_manual() { + ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") + whiptail --title "Ustaw domenę" --msgbox "Aby Nightscout był widoczny z internetu ustaw subdomenę:\n\n${uni_bullet}otwórz nową zakładkę w przeglądarce,\n${uni_bullet}wejdź do panelu administracyjnego swojego Mikr.us-a,\n${uni_bullet}otwórz sekcję [Subdomeny], pod adresem:\n\n${uni_bullet_pad} https://mikr.us/panel/?a=domain\n\n${uni_bullet}w pole nazwy wpisz dowolną własną nazwę\n${uni_bullet_pad}(tylko małe litery i cyfry, max. 12 znaków)\n${uni_bullet}w pole numer portu wpisz:\n${uni_bullet_pad}\n $ns_external_port\n\n${uni_bullet}kliknij [Dodaj subdomenę] i poczekaj do kilku minut" 22 75 +} - while :; do +domain_setup() { - local spaceInfo=$(get_space_info) - local remainingTxt=$(echo "$spaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) - local totalTxt=$(echo "$spaceInfo" | awk '{print $2}' | numfmt --to iec-i --suffix=B) - local percTxt=$(echo "$spaceInfo" | awk '{print $4}') + local domain=$(get_td_domain) + local domainLen=${#domain} + if ((domainLen > 15)); then + msgcheck "Subdomena jest już skonfigurowana ($domain)" + okdlg "Subdomena już ustawiona" \ + "Wykryto poprzednio skonfigurowaną subdomenę:" \ + "${TL}$domain" \ + "${TL}Strona Nightscout powinna być widoczna z internetu." + return + fi + + ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") + whiptail --title "Ustaw subdomenę" --msgbox "Aby Nightscout był widoczny z internetu ustaw adres - subdomenę:\n\n [wybierz].ns.techdiab.pl\n\nWybrany początek subdomeny powinien:\n${uni_bullet}mieć długość od 4 do 12 znaków\n${uni_bullet}zaczynać się z małej litery,\n${uni_bullet}może składać się z małych liter i cyfr\n${uni_bullet}być unikalny, charakterystyczny i łatwa do zapamiętania" 16 75 + + while :; do + SUBDOMAIN='' + while :; do + SUBDOMAIN=$(whiptail --title "Podaj początek subdomeny" --inputbox "\n(4-12 znaków, tylko: małe litery i cyfry)\n\n" --cancel-button "Anuluj" 12 60 3>&1 1>&2 2>&3) + + if [ $? -eq 1 ]; then + break + fi + + if [[ "$SUBDOMAIN" =~ ^[a-z][a-z0-9]{3,11}$ ]]; then + + if printf "%s" "$SUBDOMAIN" | grep -f "$PROFANITY_DB_FILE" >>"$LOGTO" 2>&1; then + okdlg "$uni_excl Nieprawidłowa subdomena $uni_excl" \ + "Podana wartość:" \ + "${NL}$SUBDOMAIN" \ + "${TL}jest zajęta, zarezerwowana lub niedopuszczalna." \ + "${TL}Wymyśl coś innego" + SUBDOMAIN='' + continue + fi + + if printf "%s" "$SUBDOMAIN" | grep -xf "$RESERVED_DB_FILE" >>"$LOGTO" 2>&1; then + okdlg "$uni_excl Nieprawidłowa subdomena $uni_excl" \ + "Podana wartość:" \ + "${NL}$SUBDOMAIN" \ + "${TL}jest zajęta lub zarezerwowana." \ + "${TL}Wymyśl coś innego" + SUBDOMAIN='' + continue + fi + + break + + else + okdlg "$uni_excl Nieprawidłowy początek subdomeny $uni_excl" \ + "Podany początek subdomeny:" \ + "${NL}$SUBDOMAIN" \ + "${TL}ma nieprawidłowy format. Wymyśl coś innego" + if [ $? -eq 1 ]; then + SUBDOMAIN='' + continue + fi + fi + + done + + if [ "$SUBDOMAIN" == "" ]; then + domain_setup_manual + break + fi + + local MHOST=$(hostname) + local APISEC=$(dotenv-tool -r get -f "$ENV_FILE_ADMIN" "MIKRUS_APIKEY") + + ohai "Rejestrowanie subdomeny $SUBDOMAIN.ns.techdiab.pl" + local REGSTATUS=$(curl -sd "srv=$MHOST&key=$APISEC&domain=$SUBDOMAIN.ns.techdiab.pl" https://api.mikr.us/domain) + local STATOK=$(echo "$REGSTATUS" | jq -r ".status") + local STATERR=$(echo "$REGSTATUS" | jq -r ".error") + + if ! [ "$STATOK" == "null" ]; then + msgcheck "Subdomena ustawiona poprawnie ($STATOK)" + okdlg "Subdomena ustawiona" \ + "Ustawiono subdomenę:\n\n$SUBDOMAIN.ns.techdiab.pl\n($STATOK)\n\nZa kilka minut strona będzie widoczna z internetu." + break + else + msgerr "Nie udało się ustawić subdomeny ($STATERR)" + whiptail --title "$uni_excl Błąd rezerwacji domeny $uni_excl" --yesno "Nie udało się zarezerwować subdomeny:\n $STATERR\n\nChcesz podać inną subdomenę?" --yes-button "$uni_reenter" --no-button "$uni_noenter" 10 73 + if [ $? -eq 1 ]; then + SUBDOMAIN='' + domain_setup_manual + break + fi + fi + done + +} + + + +#======================================= +# REMINDERS +#======================================= + +free_space_check() { + lastTimeSpaceInfo=$(get_space_info) + + local remainingB=$(echo "$lastTimeSpaceInfo" | awk '{print $3}') + local remainingTxt=$(echo "$lastTimeSpaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) + + if ((remainingB < DISK_LOW_WARNING)); then + if ((remainingB < DISK_CRITICAL_WARNING)); then + local lastCalled=$(get_since_last_time "disk_critical") + local domain=$(get_td_domain) + if ((lastCalled == -1)) || ((lastCalled > DISK_CRITICAL_MAIL)); then + set_last_time "disk_critical" + { + echo "Na twoim serwerze mikr.us z Nightscoutem (https://$domain) zostało krytycznie mało miejsca (${remainingTxt})!" + echo " " + echo "Tak mała ilość miejsca nie pozwala serwerowi na stabilne działanie!" + echo "🚨PILNIE🚨 posprzątaj na serwerze, aby to zrobić możesz:" + echo " " + echo "1. Usunąć stare statusy i wpisy z poziomu strony Nightscout:" + echo " - wejdź do hamburger menu strony Nightscout i wybierz: 【 Narzędzia administratora 】- wymaga zalogowania" + echo " to powinno otwórzyć adres: https://${domain}/admin" + echo " - w polach tekstowych poustawiaj ile dni historii chcesz zachować, i w odpowiednich sekcjach kliknij:" + echo " 【 Usuń stare dokumenty 】" + echo " " + echo "2. Posprzątać nieużywane pliki na serwerze mikr.us:" + echo " - zaloguj się na swój mikr.us do panelu administracyjnego, przejdź do WebSSH" + echo " https://mikr.us/panel/?a=webssh" + echo " - zaloguj się, uruchom narzędzie komendą: nightscout-tool" + echo " - wybierz: 【 C) Sprztąj... 】" + echo " - wybierz: 【 A) Posprzątaj wszystko 】 i potwierdź 【 Tak 】" + echo " - cierpliwie poczekaj, po sprzątaniu narzędzie pokaże ile miejsca zwolniono" + } | pusher "🚨_Krytycznie_mało_miejsca_na_Twoim_serwerze_Nightscout!" + echo "Free space on server: CRITICALLY LOW (${remainingTxt}) - sending email to user" + else + echo "Free space on server: CRITICALLY LOW (${remainingTxt}) - user already notified" + fi + else + local lastCalled=$(get_since_last_time "disk_warning") + local domain=$(get_td_domain) + if ((lastCalled == -1)) || ((lastCalled > DISK_LOW_MAIL)); then + set_last_time "disk_warning" + { + echo "Na twoim serwerze mikr.us z Nightscout-em (https://$domain) powoli kończy się miejsce (${remainingTxt})!" + echo " " + echo "🧹 W wolnej chwili posprzątaj na serwerze, aby to zrobić możesz:" + echo " " + echo "1. Usunąć stare statusy i wpisy z poziomu strony Nightscout:" + echo " - wejdź do hamburger menu strony Nightscout i wybierz:【 Narzędzia administratora 】- wymaga zalogowania" + echo " to powinno otwórzyć adres: https://${domain}/admin" + echo " - w polach tekstowych poustawiaj ile dni historii chcesz zachować, i w odpowiednich sekcjach kliknij:" + echo " 【 Usuń stare dokumenty 】" + echo " " + echo "2. Posprzątać nieużywane pliki na serwerze mikr.us:" + echo " - zaloguj się na swój mikr.us do panelu administracyjnego, przejdź do WebSSH" + echo " https://mikr.us/panel/?a=webssh" + echo " - zaloguj się, uruchom narzędzie komendą: nightscout-tool" + echo " - wybierz: 【 C) Sprztąj... 】" + echo " - wybierz: 【 A) Posprzątaj wszystko 】 i potwierdź 【 Tak 】" + echo " - cierpliwie poczekaj, po sprzątaniu narzędzie pokaże ile miejsca zwolniono" + } | pusher "🧹_Powoli_kończy_sie_miejsce_na_Twoim_serwerze_Nightscout!" + echo "Free space on server: LOW (${remainingTxt}) - sending email to user" + else + echo "Free space on server: LOW (${remainingTxt}) - user already notified" + fi + fi + else + clear_last_time "disk_critical" + clear_last_time "disk_warning" + echo "Free space on server: OK (${remainingTxt})" + fi +} + +mail_restart_needed() { + local whyRestart="$1" + local mikrusSerwer=$(hostname) + { + echo "🛟 Twój serwer mikr.us z Nightscoutem potrzebuje restartu!" + echo " " + echo "🐕 Watchdog wykrył awarię której nie jest w stanie automatycznie naprawić:" + echo "$whyRestart" + echo " " + echo "Potrzebna będzie Twoja pomoc z ręcznym restartem serwera:" + echo " " + echo "1. Zaloguj się do panelu administracyjnego mikrusa" + echo " https://mikr.us/panel/" + echo " " + echo "2. Znajdź kafelek z nazwą serwera (${mikrusSerwer}) i kliknij na przycisk pod nim:" + echo " 【 Restart 】" + echo " " + echo "3. Potwierdź naciskając przycisk:" + echo " 【 Poproszę o restart VPSa 】" + echo " " + echo "==========================================================" + echo " " + echo "⏳ Restart serwera potrwa kilka minut, kolejne kilka minut potrwa uruchomienie serwera Nightscout" + echo "Jeśli po kilkunastu minutach serwer nie zacznie działać poprawnie:" + echo "Zaloguj się do panelu mikr.us-a, zaloguj się do WebSSH i w nightscout-tool sprawdź:" + echo "- czy kontenery są uruchomione - ich status i logi" + echo "- czy jest dosyć wolnego miejsca" + echo "W razie potrzeby - 🔄 zrestartuj kontenery i uruchom 🧹 sprzątanie (ale NIE usuwaj logów!)." + echo " " + echo "==========================================================" + echo " " + echo "Jeśli to nie pomoże, poszukaj wsparcia na grupie Technologie Diabetyka" + echo " 🙋 https://www.facebook.com/groups/techdiab" + echo "i - po uzgodnieniu!!! - wyślij diagnostykę do autora skryptu:" + echo " 📜 https://t1d.dzienia.pl/nightscout_mikrus_tutorial/stabilna/5.troubleshooting/#wysyanie-diagnostyki" + echo " " + } | pusher "🛟_Twoj_serwer_Nightscout_potrzebuje_ręcznego_restartu!" +} + + +#======================================= +# COMMANDLINE PARSER +#======================================= + +parse_commandline_args() { + + load_update_channel + + CMDARGS=$(getopt --quiet -o wvdpuc: --long watchdog,version,develop,production,update,channel: -n 'nightscout-tool' -- "$@") + + # shellcheck disable=SC2181 + if [ $? != 0 ]; then + echo "Invalid arguments: " "$@" >&2 + exit 1 + fi + + # Note the quotes around '$TEMP': they are essential! + eval set -- "$CMDARGS" + + WATCHDOGMODE=false + while true; do + case "$1" in + -w | --watchdog) + WATCHDOGMODE=true + shift + ;; + -v | --version) + echo "$SCRIPT_VERSION" + exit 0 + ;; + -d | --develop) + warn "Switching to DEVELOP update channel" + UPDATE_CHANNEL=develop + forceUpdateCheck=1 + echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" + shift + ;; + -p | --production) + warn "Switching to PRODUCTION update channel" + UPDATE_CHANNEL=master + forceUpdateCheck=1 + echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" + shift + ;; + -u | --update) + warn "Forcing update check" + forceUpdateCheck=1 + shift + ;; + -c | --channel) + shift # The arg is next in position args + UPDATE_CHANNEL_CANDIDATE=$1 + forceUpdateCheck=1 + + [[ ! "$UPDATE_CHANNEL_CANDIDATE" =~ ^[a-z]{3,}$ ]] && { + echo "Incorrect channel name provided: $UPDATE_CHANNEL_CANDIDATE" + exit 1 + } + + warn "Switching to $UPDATE_CHANNEL_CANDIDATE update channel" + UPDATE_CHANNEL="$UPDATE_CHANNEL_CANDIDATE" + echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" + shift + ;; + --) + shift + break + ;; + *) break ;; + esac + done + + if [ "$WATCHDOGMODE" = "true" ]; then + watchdog_check + fi + +} + + + + +#======================================= +# WATCHDOG UI +#======================================= + +show_watchdog_logs() { + local col=$((COLUMNS - 10)) + local rws=$((LINES - 3)) + if [ "$col" -gt 120 ]; then + col=160 + fi + if [ "$col" -lt 60 ]; then + col=60 + fi + if [ "$rws" -lt 12 ]; then + rws=12 + fi + + local tmpfile=$(mktemp) + { + echo "Ostatnie uruchomienie watchdoga:" + get_watchdog_age_string + echo "-------------------------------------------------------" + + if [[ -f $WATCHDOG_LOG_FILE ]]; then + echo "Statusy ostatnich przebiegów watchdoga:" + tail -5 "$WATCHDOG_LOG_FILE" + else + echo "Brak logów z ostatnich przebiegów watchdoga" + fi + echo "-------------------------------------------------------" + + if [[ -f $WATCHDOG_CRON_LOG ]]; then + echo "Log ostatniego przebiegu watchdoga:" + cat "$WATCHDOG_CRON_LOG" + fi + } >"$tmpfile" + + whiptail --title "Logi Watchdoga" --scrolltext --textbox "$tmpfile" "$rws" "$col" + rm "$tmpfile" +} + + + +#======================================= +# CLEANUP UI +#======================================= + +prompt_cleanup_container_logs() { + yesnodlg "Usunąć logi kontenerów?" "$uni_delete" "$uni_leave_logs" \ + "Czy chcesz usunąć logi kontenerów nightscout i bazy?" \ + "${TL}Jeśli Twój serwer działa poprawnie," \ + "${NL}- możesz spokojnie usunąć logi." \ + "${TL}Jeśli masz problem z serwerem - zostaw logi!" \ + "${NL}- logi mogą być niezbędne do diagnostyki" \ + "${TL}(ta operacja uruchomi ponownie kontenery)" +} + +cleanup_menu() { + + while :; do + + local spaceInfo=$(get_space_info) + local remainingTxt=$(echo "$spaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) + local totalTxt=$(echo "$spaceInfo" | awk '{print $2}' | numfmt --to iec-i --suffix=B) + local percTxt=$(echo "$spaceInfo" | awk '{print $4}') local fixedPerc=${percTxt/[%]/=} local nowB=$(echo "$spaceInfo" | awk '{print $3}') @@ -2224,42 +2358,78 @@ cleanup_menu() { done } -update_menu() { + +#======================================= +# CONFIG AND MANAGEMENT UI +#======================================= + +version_menu() { + + local tags=$(wget -q -O - "https://hub.docker.com/v2/namespaces/nightscout/repositories/cgm-remote-monitor/tags?page_size=100" | jq -r ".results[].name" | sed "/dev_[a-f0-9]*/d" | sort --version-sort -u -r | head -n 8) + while :; do - local CHOICE=$(whiptail --title "Aktualizuj" --menu "\n" 11 40 4 \ - "N)" "Aktualizuj to narzędzie" \ - "S)" "Aktualizuj system" \ - "K)" "Aktualizuj kontenery" \ - "M)" "Powrót do menu" \ - --ok-button="$uni_select" --cancel-button="$uni_back" \ + + local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") + local versions=() + + while read -r line; do + if [ "$line" == "$ns_tag" ]; then + continue + fi + + label=" - na sztywno $line " + + if [ "$line" == "latest_dev" ]; then + label=" - najnowsza wersja rozwojowa " + fi + + if [ "$line" == "latest" ]; then + label=" - aktualna wersja stabilna " + fi + + versions+=("$line") + versions+=("$label") + done <<<"$tags" + + versions+=("M)") + versions+=(" Powrót do poprzedniego menu") + + local CHOICE=$(whiptail --title "Wersja Nightscout" --menu "\nZmień wersję kontenera Nightscout z: $ns_tag na:\n\n" 20 60 10 \ + "${versions[@]}" \ + --ok-button="Zmień" --cancel-button="$uni_back" \ 3>&2 2>&1 1>&3) - case $CHOICE in - "S)") - ohai "Updating package list" - event_mark "update_system" - dialog --title " Aktualizacja systemu " --infobox "\n Pobieranie listy pakietów\n ..... Proszę czekać ....." 6 33 - apt-get -yq update >>"$LOGTO" 2>&1 - ohai "Upgrading system" - dialog --title " Aktualizacja systemu " --infobox "\n Instalowanie pakietów\n ... Proszę czekać ..." 6 33 - apt-get -yq upgrade >>"$LOGTO" 2>&1 - ;; - "N)") - event_mark "update_tool" - update_if_needed "Wszystkie pliki narzędzia są aktualne" - ;; - "K)") - event_mark "update_containers" - docker_compose_down - docker_compose_update - ;; - "M)") + if [ "$CHOICE" == "M)" ]; then break - ;; - "") + fi + + if [ "$CHOICE" == "" ]; then break - ;; - esac + fi + + if [ "$CHOICE" == "$ns_tag" ]; then + whiptail --title "Ta sama wersja!" --msgbox "Wybrano bieżącą wersję - brak zmiany" 7 50 + else + + whiptail --title "Zmienić wersję Nightscout?" --yesno --defaultno "Czy na pewno chcesz zmienić wersję z: $ns_tag na: $CHOICE?\n\n${uni_bullet}dane i konfiguracja NIE SĄ usuwane\n${uni_bullet}wersję można łatwo zmienić ponownie\n${uni_bullet}dane w bazie danych mogą ulec zmianie i NIE BYĆ kompatybilne" --yes-button "$uni_confirm_ch" --no-button "$uni_resign" 13 73 + if ! [ $? -eq 1 ]; then + event_mark "change_ns_version" + docker_compose_down + ohai "Changing Nightscout container tag from: $ns_tag to: $CHOICE" + dotenv-tool -pmr -i "$ENV_FILE_DEP" -- "NS_NIGHTSCOUT_TAG=$CHOICE" + docker_compose_update + whiptail --title "Zmieniono wersję Nightscout" --msgbox "$(center_multiline 65 \ + "Zmieniono wersję Nightscout na: $CHOICE" \ + "${TL}Sprawdź czy Nightscout działa poprawnie, w razie problemów:" \ + "${NL}${uni_bullet}aktualizuj kontenery" \ + "${NL}${uni_bullet}spróbuj wyczyścić bazę danych" \ + "${NL}${uni_bullet}wróć do poprzedniej wersji ($ns_tag)")" \ + 13 70 + break + fi + + fi + done } @@ -2411,673 +2581,434 @@ uninstall_menu() { done } -get_td_domain() { - local MHOST=$(hostname) - if ! [[ "$MHOST" =~ [a-zA-Z]{2,16}[0-9]{3} ]]; then - MIKRUS_APIKEY=$(cat "/klucz_api") - MIKRUS_INFO_HOST=$(curl -s -d "srv=$MHOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .imie_id) - if [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{2,16}[0-9]{3} ]]; then - MHOST="$MIKRUS_INFO_HOST" - fi - fi - local APIKEY=$(dotenv-tool -r get -f "$ENV_FILE_ADMIN" "MIKRUS_APIKEY") - curl -sd "srv=$MHOST&key=$APIKEY" https://api.mikr.us/domain | jq -r ".[].name" | grep ".ns.techdiab.pl" | head -n 1 -} - -get_domain_status() { - local domain=$(get_td_domain) - local domainLen=${#domain} - if ((domainLen > 15)); then - printf "\U1F7E2 %s" "$domain" - else - printf "\U26AA nie zarejestrowano" - fi -} - -gather_diagnostics() { - local maxNsLogs=$1 - local maxDbLogs=$2 - local curr_time=$3 - diagnosticsSizeOk=0 +#======================================= +# UPDATE UI +#======================================= - do_cleanup_diagnostics +update_if_needed() { - ohai "Zbieranie diagnostyki" + download_if_needed "$@" - local domain=$(get_td_domain) - local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") - local mikrus_h=$(hostname) + local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "???") local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") - local LOG_DIVIDER="=======================================================" - - { - echo "Dane diagnostyczne zebrane $curr_time" - echo " serwer : $mikrus_h" - echo " domena : $domain" - echo " wersja nightscout : $ns_tag" - echo " wersja nightscout-tool : $SCRIPT_VERSION ($SCRIPT_BUILD_TIME) $UPDATE_CHANNEL" - echo " build : ${updateInstalled}" - } >"$SUPPORT_LOG" - - ohai "Zbieranie statusu usług" - - { - echo "$LOG_DIVIDER" - echo " Statusy usług" - echo "$LOG_DIVIDER" - echo " Nightscout: $(get_container_status 'ns-server')" - echo " Baza danych: $(get_container_status 'ns-database')" - echo " Backup: $(get_container_status 'ns-backup')" - echo " Watchdog: $(get_watchdog_status "$(get_watchdog_status_code)" "$uni_watchdog_ok")" - } >>"$SUPPORT_LOG" - - local spaceInfo=$(get_space_info) - local remainingTxt=$(echo "$spaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) - local totalTxt=$(echo "$spaceInfo" | awk '{print $2}' | numfmt --to iec-i --suffix=B) - local percTxt=$(echo "$spaceInfo" | awk '{print $4}') - - { - echo "$LOG_DIVIDER" - echo " Miejsce na dysku" - echo "$LOG_DIVIDER" - echo " Dostępne: ${remainingTxt}" - echo " Zajęte: ${percTxt} (z ${totalTxt})" - } >>"$SUPPORT_LOG" - - ohai "Zbieranie zdarzeń" - { - echo "$LOG_DIVIDER" - echo " Zdarzenia" - echo "$LOG_DIVIDER" - event_list - } >>"$SUPPORT_LOG" - - ohai "Zbieranie logów watchdoga" - - if [[ -f $WATCHDOG_LOG_FILE ]]; then - { - echo "$LOG_DIVIDER" - echo " Watchdog log" - echo "$LOG_DIVIDER" - timeout -k 15 10 tail -n 200 "$WATCHDOG_LOG_FILE" - } >>"$SUPPORT_LOG" - fi - - if [[ -f $WATCHDOG_FAILURES_FILE ]]; then - { - echo "$LOG_DIVIDER" - echo " Watchdog failures log" - echo "$LOG_DIVIDER" - timeout -k 15 10 tail -n 200 "$WATCHDOG_FAILURES_FILE" - } >>"$SUPPORT_LOG" - fi + if [ "$lastDownload" == "$updateInstalled" ] && ((forceUpdateCheck == 0)) && [ $# -eq 0 ]; then + msgok "Scripts and config files are up to date" + else - ohai "Zbieranie logów usług" + if [ "$lastDownload" == "error" ]; then + msgerr "Download update failed" + if [ $# -eq 1 ]; then + okdlg "Aktualizacja niemożliwa" "Nie można w tej chwili aktualizować narzędzia.${TL}Spróbuj ponownie później.${NL}Jeśli problem nie ustąpi - sprawdź konfigurację kanału aktualizacji" + fi + else - { - echo "$LOG_DIVIDER" - echo " Nightscout log" - echo "$LOG_DIVIDER" - timeout -k 15 10 docker logs ns-server --tail "$maxNsLogs" >>"$SUPPORT_LOG" 2>&1 - echo "$LOG_DIVIDER" - echo " MongoDB database log" - echo "$LOG_DIVIDER" - timeout -k 15 10 docker logs ns-database --tail "$maxDbLogs" >>"$SUPPORT_LOG" 2>&1 - } >>"$SUPPORT_LOG" + if [ $# -eq 0 ] && [ "$UPDATE_CHANNEL" == "master" ] && [[ "$lastDownload" < "$updateInstalled" ]]; then + msgnote "Downgrade not possible on master channel" + forceUpdateCheck=1 + download_if_needed + else - ohai "Kompresowanie i szyfrowanie raportu" + local changed=0 + local redeploy=0 - gzip -9 "$SUPPORT_LOG" + local instOnlineVer=$(extract_version "$(<"$UPDATES_DIR/install.sh")") + local depEnvOnlineVer=$(extract_version "$(<"$UPDATES_DIR/deployment.env")") + local nsEnvOnlineVer=$(extract_version "$(<"$UPDATES_DIR/nightscout.env")") + local compOnlineVer=$(extract_version "$(<"$UPDATES_DIR/docker-compose.yml")") - local logkey=$(<"$LOG_ENCRYPTION_KEY_FILE") + local instLocalVer=$(extract_version "$(<"$TOOL_FILE")") + local depEnvLocalVer=$(extract_version "$(<"$ENV_FILE_DEP")") + local nsEnvLocalVer=$(extract_version "$(<"$ENV_FILE_NS")") + local compLocalVer=$(extract_version "$(<"$DOCKER_COMPOSE_FILE")") - gpg --passphrase "$logkey" --batch --quiet --yes -a -c "$SUPPORT_LOG.gz" -} + local msgInst="$(printf "\U1F7E2") $instLocalVer" + local msgDep="$(printf "\U1F7E2") $depEnvLocalVer" + local msgNs="$(printf "\U1F7E2") $nsEnvLocalVer" + local msgComp="$(printf "\U1F7E2") $compLocalVer" -retry_diagnostics() { - local maxNsLogs=$1 - local maxDbLogs=$2 - local curr_time=$3 + if ! [ "$instOnlineVer" == "$instLocalVer" ] || ! [ "$lastDownload" == "$updateInstalled" ]; then + changed=$((changed + 1)) + msgInst="$(printf "\U1F534") $instLocalVer $(printf "\U27A1") $instOnlineVer" + fi - if ((diagnosticsSizeOk == 0)); then + if ! [ "$depEnvLocalVer" == "$depEnvOnlineVer" ]; then + changed=$((changed + 1)) + redeploy=$((redeploy + 1)) + msgDep="$(printf "\U1F534") $depEnvLocalVer $(printf "\U27A1") $depEnvOnlineVer" + fi - ohai "Sprawdzanie rozmiaru raportu" + if ! [ "$nsEnvLocalVer" == "$nsEnvOnlineVer" ]; then + changed=$((changed + 1)) + redeploy=$((redeploy + 1)) + msgNs="$(printf "\U1F534") $nsEnvLocalVer $(printf "\U27A1") $nsEnvOnlineVer" + fi - local logSize=$(stat --printf="%s" "$SUPPORT_LOG.gz.asc") - local allowedTxt=$(echo "18000" | numfmt --to si --suffix=B) - local currentTxt=$(echo "$logSize" | numfmt --to si --suffix=B) + if ! [ "$compLocalVer" == "$compOnlineVer" ]; then + changed=$((changed + 1)) + redeploy=$((redeploy + 1)) + msgComp="$(printf "\U1F534") $compLocalVer $(printf "\U27A1") $compOnlineVer" + fi - if ((logSize > 18000)); then - msgerr "Zebrana diagnostyka jest zbyt duża do wysłania (${currentTxt})" - ohai "Spróbuję zebrać mniej danych aby zmieścić się w limicie (${allowedTxt})" - gather_diagnostics "$maxNsLogs" "$maxDbLogs" "$curr_time" - else - diagnosticsSizeOk=1 - msgok "Raport ma rozmiar ${currentTxt} i mieści się w limicie ${allowedTxt} dla usługi pusher-a" - fi - fi -} + if [ "$changed" -eq 0 ]; then + if [ $# -eq 1 ]; then + msgok "Scripts and config files are up to date" + okdlg "Aktualizacja skryptów" "$1" + fi + else + local okTxt="" + if [ "$redeploy" -gt 0 ]; then + okTxt="${TL}${uni_warn} Aktualizacja zrestartuje i zaktualizuje kontenery ${uni_warn}" + fi -send_diagnostics() { + local versionMsg="${TL}Build: ${updateInstalled}" + if [ ! "$lastDownload" == "$updateInstalled" ]; then + versionMsg="$(pad_multiline "${TL}Masz build: ${updateInstalled}${NL} Dostępny: ${lastDownload}")" + fi - setup_security + yesnodlg "Aktualizacja skryptów" "$uni_confirm_upd" "$uni_resign" \ + "Zalecana jest aktualizacja plików:${versionMsg}" \ + "$( + pad_multiline \ + "${TL}${uni_bullet}Skrypt instalacyjny: $msgInst" \ + "${NL}${uni_bullet}Konfiguracja deploymentu: $msgDep" \ + "${NL}${uni_bullet}Konfiguracja Nightscout: $msgNs" \ + "${NL}${uni_bullet}Kompozycja usług: $msgComp${NL}" + )" \ + "$okTxt" - LOG_KEY=$(<"$LOG_ENCRYPTION_KEY_FILE") + if ! [ $? -eq 1 ]; then - yesnodlg "Wysyłać diagnostykę?" \ - "$uni_send" "$uni_resign" \ - "Czy chcesz zgromadzić i wysłać sobie mailem dane diagnostyczne?" \ - "\n$( - pad_multiline \ - "\n${uni_bullet}diagnostyka zawiera logi i informacje o serwerze i usługach" \ - "\n${uni_bullet}wysyłka na e-mail na który zamówiono serwer Mikr.us" \ - "\n${uni_bullet}dane będą skompresowane i zaszyfrowane" \ - "\n${uni_bullet}maila prześlij dalej do zaufanej osoby wspierającej" \ - "\n${uni_bullet_pad}(z którą to wcześniej zaplanowano i uzgodniono!!!)" \ - "\n${uni_bullet}hasło przekaż INNĄ DROGĄ (komunikatorem, SMSem, osobiście)" \ - "\n\n${uni_bullet_pad}Hasło do logów: $LOG_KEY" - )" + clear_last_time "update_needed" - if ! [ $? -eq 1 ]; then + if [ "$redeploy" -gt 0 ]; then + docker_compose_down + fi - local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + if ! [ "$compOnlineVer" == "$compLocalVer" ]; then + ohai "Updating $DOCKER_COMPOSE_FILE" + cp -fr "$UPDATES_DIR/docker-compose.yml" "$DOCKER_COMPOSE_FILE" + fi - gather_diagnostics 500 100 "$curr_time" - retry_diagnostics 200 50 "$curr_time" - retry_diagnostics 100 50 "$curr_time" - retry_diagnostics 50 50 "$curr_time" - retry_diagnostics 50 20 "$curr_time" + if ! [ "$depEnvLocalVer" == "$depEnvOnlineVer" ]; then + ohai "Updating $ENV_FILE_DEP" + dotenv-tool -pr -o "$ENV_FILE_DEP" -i "$UPDATES_DIR/deployment.env" "$ENV_FILE_DEP" + fi - ohai "Wysyłanie maila" + if ! [ "$nsEnvLocalVer" == "$nsEnvOnlineVer" ]; then + ohai "Updating $ENV_FILE_NS" + dotenv-tool -pr -o "$ENV_FILE_NS" -i "$UPDATES_DIR/deployment.env" "$ENV_FILE_NS" + fi - local sentStatus=$({ - echo "Ta wiadomość zawiera poufne dane diagnostyczne Twojego serwera Nightscout." - echo "Mogą one pomóc Tobie lub zaufanej osobie w identyfikacji problemu." - echo " " - echo "Prześlij ten mail dalej do zaufanej osoby, umówionej na udzielenie wsparcia." - echo "Przekaż tej osobie w bezpieczny sposób hasło szyfrowania" - echo " (w narzędziu nightscout-tool można je znaleźć w pozycji 'O tym narzędziu...')." - echo "Do przekazania hasła użyj INNEJ metody (komunikator, SMS, osobiście...)." - echo "Nie przesyłaj tej wiadomości do administratorów grupy lub serwera bez wcześniejszego uzgodnienia!" - echo " " - echo "Instrukcje i narzędzie do odszyfrowania logów dostępne pod adresem: https://t1d.dzienia.pl/decoder/" - echo " " - echo " " - cat "$SUPPORT_LOG.gz.asc" - } | pusher "Diagnostyka_serwera_Nightscout_-_$curr_time") + echo "$lastDownload" >"$UPDATES_DIR/updated" - local regexEm='Email sent' - if [[ "$sentStatus" =~ $regexEm ]]; then - do_cleanup_diagnostics - msgcheck "Mail wysłany!" - okdlg "Diagnostyka wysłana" \ - "Sprawdź swoją skrzynkę pocztową,\n" \ - "otrzymanego maila przekaż zaufanemu wspierającemu.\n\n" \ - "Komunikatorem lub SMS przekaż hasło do logów:\n\n$LOG_KEY" - else - msgerr "Błąd podczas wysyłki maila: $sentStatus" - okdlg "Błąd wysyłki maila" \ - "Nieststy nie udało się wysłać diagnostyki" \ - "${NL}zgłoś poniższy błąd twórcom narzędzia (na grupie Technologie Diabetyka)" \ - "${TL}$sentStatus" + if ! [ "$instOnlineVer" == "$instLocalVer" ] || ! [ "$lastDownload" == "$updateInstalled" ]; then + ohai "Updating $TOOL_FILE" + cp -fr "$UPDATES_DIR/install.sh" "$TOOL_FILE" + okdlg "Aktualizacja zakończona" "Narzędzie zostanie uruchomione ponownie" + ohai "Restarting tool" + exec "$TOOL_FILE" + fi + fi + fi + fi fi - fi } -main_menu() { +update_menu() { while :; do - local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") - local quickStatus=$(center_text "Strona Nightscout: $(get_watchdog_status "$(get_watchdog_status_code_live)" "$uni_ns_ok")" 55) - local quickVersion=$(center_text "Wersja: $ns_tag" 55) - local quickDomain=$(center_text "Domena: $(get_domain_status 'ns-server')" 55) - local CHOICE=$(whiptail --title "Zarządzanie Nightscoutem :: $SCRIPT_VERSION" --menu "\n$quickStatus\n$quickVersion\n$quickDomain\n" 21 60 9 \ - "S)" "Status kontenerów i logi" \ - "P)" "Pokaż port i API SECRET" \ - "U)" "Aktualizuj..." \ - "C)" "Sprztąj..." \ - "R)" "Uruchom ponownie kontenery" \ - "D)" "Wyślij diagnostykę i logi" \ - "Z)" "Zmień lub odinstaluj..." \ - "I)" "O tym narzędziu..." \ - "X)" "Wyjście" \ - --ok-button="$uni_select" --cancel-button="$uni_exit" \ + local CHOICE=$(whiptail --title "Aktualizuj" --menu "\n" 11 40 4 \ + "N)" "Aktualizuj to narzędzie" \ + "S)" "Aktualizuj system" \ + "K)" "Aktualizuj kontenery" \ + "M)" "Powrót do menu" \ + --ok-button="$uni_select" --cancel-button="$uni_back" \ 3>&2 2>&1 1>&3) case $CHOICE in "S)") - status_menu - ;; - "P)") - local ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") - local ns_api_secret=$(dotenv-tool -r get -f "$ENV_FILE_NS" "API_SECRET") - whiptail --title "Podgląd konfiguracji Nightscout" --msgbox \ - "\n Port usługi Nightscout: $ns_external_port\n API_SECRET: $ns_api_secret" \ - 10 60 - ;; - "U)") - update_menu + ohai "Updating package list" + event_mark "update_system" + dialog --title " Aktualizacja systemu " --infobox "\n Pobieranie listy pakietów\n ..... Proszę czekać ....." 6 33 + apt-get -yq update >>"$LOGTO" 2>&1 + ohai "Upgrading system" + dialog --title " Aktualizacja systemu " --infobox "\n Instalowanie pakietów\n ... Proszę czekać ..." 6 33 + apt-get -yq upgrade >>"$LOGTO" 2>&1 ;; - "C)") - cleanup_menu + "N)") + event_mark "update_tool" + update_if_needed "Wszystkie pliki narzędzia są aktualne" ;; - "R)") + "K)") + event_mark "update_containers" docker_compose_down - docker_compose_up - ;; - "D)") - send_diagnostics - ;; - "Z)") - uninstall_menu - ;; - "I)") - about_dialog + docker_compose_update ;; - "X)") - exit 0 + "M)") + break ;; "") - exit 0 + break ;; esac done } -setup_done() { - whiptail --title "Gotowe!" --yesno --defaultno " Możesz teraz zamknąć to narzędzie lub wrócić do menu.\n Narzędzie dostępne jest też jako komenda konsoli:\n\n nightscout-tool" --yes-button "$uni_menu" --no-button "$uni_finish" 12 70 - exit_on_no_cancel - main_menu -} -install_or_menu() { - STATUS_NS=$(get_docker_status "ns-server") - lastTimeSpaceInfo=$(get_space_info) - if [ "$STATUS_NS" = "missing" ]; then +#======================================= +# DIAGNOSTICS UI +#======================================= + +send_diagnostics() { + + setup_security + + LOG_KEY=$(<"$LOG_ENCRYPTION_KEY_FILE") + + yesnodlg "Wysyłać diagnostykę?" \ + "$uni_send" "$uni_resign" \ + "Czy chcesz zgromadzić i wysłać sobie mailem dane diagnostyczne?" \ + "\n$( + pad_multiline \ + "\n${uni_bullet}diagnostyka zawiera logi i informacje o serwerze i usługach" \ + "\n${uni_bullet}wysyłka na e-mail na który zamówiono serwer Mikr.us" \ + "\n${uni_bullet}dane będą skompresowane i zaszyfrowane" \ + "\n${uni_bullet}maila prześlij dalej do zaufanej osoby wspierającej" \ + "\n${uni_bullet_pad}(z którą to wcześniej zaplanowano i uzgodniono!!!)" \ + "\n${uni_bullet}hasło przekaż INNĄ DROGĄ (komunikatorem, SMSem, osobiście)" \ + "\n\n${uni_bullet_pad}Hasło do logów: $LOG_KEY" + )" + + if ! [ $? -eq 1 ]; then - if [ "$freshInstall" -eq 0 ]; then - instal_now_prompt - if ! [ $? -eq 1 ]; then - freshInstall=1 - fi - fi + local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - if [ "$freshInstall" -gt 0 ]; then - ohai "Instalowanie Nightscout..." - event_mark "install_start" - docker_compose_update - setup_firewall_for_ns - domain_setup - # admin_panel_promo - event_mark "install_end" - setup_done - else - main_menu - fi - else - msgok "Wykryto uruchomiony Nightscout" - main_menu - fi -} + gather_diagnostics 500 100 "$curr_time" + retry_diagnostics 200 50 "$curr_time" + retry_diagnostics 100 50 "$curr_time" + retry_diagnostics 50 50 "$curr_time" + retry_diagnostics 50 20 "$curr_time" -free_space_check() { - lastTimeSpaceInfo=$(get_space_info) + ohai "Wysyłanie maila" - local remainingB=$(echo "$lastTimeSpaceInfo" | awk '{print $3}') - local remainingTxt=$(echo "$lastTimeSpaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) + local sentStatus=$({ + echo "Ta wiadomość zawiera poufne dane diagnostyczne Twojego serwera Nightscout." + echo "Mogą one pomóc Tobie lub zaufanej osobie w identyfikacji problemu." + echo " " + echo "Prześlij ten mail dalej do zaufanej osoby, umówionej na udzielenie wsparcia." + echo "Przekaż tej osobie w bezpieczny sposób hasło szyfrowania" + echo " (w narzędziu nightscout-tool można je znaleźć w pozycji 'O tym narzędziu...')." + echo "Do przekazania hasła użyj INNEJ metody (komunikator, SMS, osobiście...)." + echo "Nie przesyłaj tej wiadomości do administratorów grupy lub serwera bez wcześniejszego uzgodnienia!" + echo " " + echo "Instrukcje i narzędzie do odszyfrowania logów dostępne pod adresem: https://t1d.dzienia.pl/decoder/" + echo " " + echo " " + cat "$SUPPORT_LOG.gz.asc" + } | pusher "Diagnostyka_serwera_Nightscout_-_$curr_time") - if ((remainingB < DISK_LOW_WARNING)); then - if ((remainingB < DISK_CRITICAL_WARNING)); then - local lastCalled=$(get_since_last_time "disk_critical") - local domain=$(get_td_domain) - if ((lastCalled == -1)) || ((lastCalled > DISK_CRITICAL_MAIL)); then - set_last_time "disk_critical" - { - echo "Na twoim serwerze mikr.us z Nightscoutem (https://$domain) zostało krytycznie mało miejsca (${remainingTxt})!" - echo " " - echo "Tak mała ilość miejsca nie pozwala serwerowi na stabilne działanie!" - echo "🚨PILNIE🚨 posprzątaj na serwerze, aby to zrobić możesz:" - echo " " - echo "1. Usunąć stare statusy i wpisy z poziomu strony Nightscout:" - echo " - wejdź do hamburger menu strony Nightscout i wybierz: 【 Narzędzia administratora 】- wymaga zalogowania" - echo " to powinno otwórzyć adres: https://${domain}/admin" - echo " - w polach tekstowych poustawiaj ile dni historii chcesz zachować, i w odpowiednich sekcjach kliknij:" - echo " 【 Usuń stare dokumenty 】" - echo " " - echo "2. Posprzątać nieużywane pliki na serwerze mikr.us:" - echo " - zaloguj się na swój mikr.us do panelu administracyjnego, przejdź do WebSSH" - echo " https://mikr.us/panel/?a=webssh" - echo " - zaloguj się, uruchom narzędzie komendą: nightscout-tool" - echo " - wybierz: 【 C) Sprztąj... 】" - echo " - wybierz: 【 A) Posprzątaj wszystko 】 i potwierdź 【 Tak 】" - echo " - cierpliwie poczekaj, po sprzątaniu narzędzie pokaże ile miejsca zwolniono" - } | pusher "🚨_Krytycznie_mało_miejsca_na_Twoim_serwerze_Nightscout!" - echo "Free space on server: CRITICALLY LOW (${remainingTxt}) - sending email to user" - else - echo "Free space on server: CRITICALLY LOW (${remainingTxt}) - user already notified" - fi + local regexEm='Email sent' + if [[ "$sentStatus" =~ $regexEm ]]; then + do_cleanup_diagnostics + msgcheck "Mail wysłany!" + okdlg "Diagnostyka wysłana" \ + "Sprawdź swoją skrzynkę pocztową,\n" \ + "otrzymanego maila przekaż zaufanemu wspierającemu.\n\n" \ + "Komunikatorem lub SMS przekaż hasło do logów:\n\n$LOG_KEY" else - local lastCalled=$(get_since_last_time "disk_warning") - local domain=$(get_td_domain) - if ((lastCalled == -1)) || ((lastCalled > DISK_LOW_MAIL)); then - set_last_time "disk_warning" - { - echo "Na twoim serwerze mikr.us z Nightscout-em (https://$domain) powoli kończy się miejsce (${remainingTxt})!" - echo " " - echo "🧹 W wolnej chwili posprzątaj na serwerze, aby to zrobić możesz:" - echo " " - echo "1. Usunąć stare statusy i wpisy z poziomu strony Nightscout:" - echo " - wejdź do hamburger menu strony Nightscout i wybierz:【 Narzędzia administratora 】- wymaga zalogowania" - echo " to powinno otwórzyć adres: https://${domain}/admin" - echo " - w polach tekstowych poustawiaj ile dni historii chcesz zachować, i w odpowiednich sekcjach kliknij:" - echo " 【 Usuń stare dokumenty 】" - echo " " - echo "2. Posprzątać nieużywane pliki na serwerze mikr.us:" - echo " - zaloguj się na swój mikr.us do panelu administracyjnego, przejdź do WebSSH" - echo " https://mikr.us/panel/?a=webssh" - echo " - zaloguj się, uruchom narzędzie komendą: nightscout-tool" - echo " - wybierz: 【 C) Sprztąj... 】" - echo " - wybierz: 【 A) Posprzątaj wszystko 】 i potwierdź 【 Tak 】" - echo " - cierpliwie poczekaj, po sprzątaniu narzędzie pokaże ile miejsca zwolniono" - } | pusher "🧹_Powoli_kończy_sie_miejsce_na_Twoim_serwerze_Nightscout!" - echo "Free space on server: LOW (${remainingTxt}) - sending email to user" - else - echo "Free space on server: LOW (${remainingTxt}) - user already notified" - fi + msgerr "Błąd podczas wysyłki maila: $sentStatus" + okdlg "Błąd wysyłki maila" \ + "Nieststy nie udało się wysłać diagnostyki" \ + "${NL}zgłoś poniższy błąd twórcom narzędzia (na grupie Technologie Diabetyka)" \ + "${TL}$sentStatus" fi - else - clear_last_time "disk_critical" - clear_last_time "disk_warning" - echo "Free space on server: OK (${remainingTxt})" + fi } -mail_restart_needed() { - local whyRestart="$1" - local mikrusSerwer=$(hostname) - { - echo "🛟 Twój serwer mikr.us z Nightscoutem potrzebuje restartu!" - echo " " - echo "🐕 Watchdog wykrył awarię której nie jest w stanie automatycznie naprawić:" - echo "$whyRestart" - echo " " - echo "Potrzebna będzie Twoja pomoc z ręcznym restartem serwera:" - echo " " - echo "1. Zaloguj się do panelu administracyjnego mikrusa" - echo " https://mikr.us/panel/" - echo " " - echo "2. Znajdź kafelek z nazwą serwera (${mikrusSerwer}) i kliknij na przycisk pod nim:" - echo " 【 Restart 】" - echo " " - echo "3. Potwierdź naciskając przycisk:" - echo " 【 Poproszę o restart VPSa 】" - echo " " - echo "==========================================================" - echo " " - echo "⏳ Restart serwera potrwa kilka minut, kolejne kilka minut potrwa uruchomienie serwera Nightscout" - echo "Jeśli po kilkunastu minutach serwer nie zacznie działać poprawnie:" - echo "Zaloguj się do panelu mikr.us-a, zaloguj się do WebSSH i w nightscout-tool sprawdź:" - echo "- czy kontenery są uruchomione - ich status i logi" - echo "- czy jest dosyć wolnego miejsca" - echo "W razie potrzeby - 🔄 zrestartuj kontenery i uruchom 🧹 sprzątanie (ale NIE usuwaj logów!)." - echo " " - echo "==========================================================" - echo " " - echo "Jeśli to nie pomoże, poszukaj wsparcia na grupie Technologie Diabetyka" - echo " 🙋 https://www.facebook.com/groups/techdiab" - echo "i - po uzgodnieniu!!! - wyślij diagnostykę do autora skryptu:" - echo " 📜 https://t1d.dzienia.pl/nightscout_mikrus_tutorial/stabilna/5.troubleshooting/#wysyanie-diagnostyki" - echo " " - } | pusher "🛟_Twoj_serwer_Nightscout_potrzebuje_ręcznego_restartu!" -} -watchdog_check() { - echo "Nightscout Watchdog mode" - WATCHDOG_LAST_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - WATCHDOG_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - WATCHDOG_LAST_STATUS="unknown" - WATCHDOG_STATUS="unknown" +#======================================= +# MAIN APP UI +#======================================= - if [[ -f $WATCHDOG_TIME_FILE ]]; then - echo "Found $WATCHDOG_TIME_FILE" - WATCHDOG_LAST_TIME=$(cat "$WATCHDOG_TIME_FILE") - else - echo "First watchdog run" +show_logs() { + local col=$((COLUMNS - 10)) + local rws=$((LINES - 4)) + if [ "$col" -gt 120 ]; then + col=160 fi - - if [[ -f $WATCHDOG_STATUS_FILE ]]; then - echo "Found $WATCHDOG_STATUS_FILE" - WATCHDOG_LAST_STATUS=$(cat "$WATCHDOG_STATUS_FILE") + if [ "$col" -lt 60 ]; then + col=60 fi - - local STATUS_AGO=$(dateutils.ddiff "$WATCHDOG_TIME" "$WATCHDOG_LAST_TIME" -f '%S') - - if [ "$STATUS_AGO" -gt 900 ]; then - echo "Watchdog last status is $STATUS_AGO seconds old, ignoring" - WATCHDOG_LAST_STATUS="unknown" + if [ "$rws" -lt 12 ]; then + rws=12 fi - free_space_check - update_background_check - - local NS_STATUS=$(get_container_status_code 'ns-server') - local DB_STATUS=$(get_container_status_code 'ns-database') - local COMBINED_STATUS="$NS_STATUS $DB_STATUS" - - echo "Server container: $NS_STATUS" - echo "Database container: $DB_STATUS" - - if [ "$COMBINED_STATUS" = "running running" ]; then + local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') + if [ -n "$ID" ]; then + local tmpfile=$(mktemp) + docker logs "$ID" 2>&1 | tail $((rws * -6)) | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g' >"$tmpfile" + whiptail --title "Logi $2" --scrolltext --textbox "$tmpfile" "$rws" "$col" + rm "$tmpfile" + fi +} - clear_last_time "docker_down" - clear_last_time "server_restart_needed" +status_menu() { + while :; do + local CHOICE=$(whiptail --title "Status kontenerów" --menu "\n Aktualizacja: kontenery na żywo, watchdog co 5 minut\n\n Wybierz pozycję aby zobaczyć logi:\n" 18 60 6 \ + "1)" " Nightscout: $(get_container_status 'ns-server')" \ + "2)" " Baza danych: $(get_container_status 'ns-database')" \ + "3)" " Backup: $(get_container_status 'ns-backup')" \ + "4)" " Watchdog: $(get_watchdog_status "$(get_watchdog_status_code)" "$uni_watchdog_ok")" \ + "5)" " Zdarzenia: $(get_events_status)" \ + "M)" "Powrót do menu" \ + --ok-button="Zobacz logi" --cancel-button="$uni_back" \ + 3>&2 2>&1 1>&3) - echo "Will check page contents" - local domain=$(get_td_domain) + case $CHOICE in + "1)") + show_logs 'ns-server' 'Nightscouta' + ;; + "2)") + show_logs 'ns-database' 'bazy danych' + ;; + "3)") + show_logs 'ns-backup' 'usługi kopii zapasowych' + ;; + "4)") + show_watchdog_logs + ;; + "5)") + okdlg "Zdarzenia" \ + "$(pad_multiline "$(event_list)")" + ;; + "M)") + break + ;; + "") + break + ;; + esac + done +} - local domainLen=${#domain} - if ((domainLen > 15)); then - local html=$(curl -iLsk "$domain") +main_menu() { + while :; do + local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") + local quickStatus=$(center_text "Strona Nightscout: $(get_watchdog_status "$(get_watchdog_status_code_live)" "$uni_ns_ok")" 55) + local quickVersion=$(center_text "Wersja: $ns_tag" 55) + local quickDomain=$(center_text "Domena: $(get_domain_status 'ns-server')" 55) + local CHOICE=$(whiptail --title "Zarządzanie Nightscoutem :: $SCRIPT_VERSION" --menu "\n$quickStatus\n$quickVersion\n$quickDomain\n" 21 60 9 \ + "S)" "Status kontenerów i logi" \ + "P)" "Pokaż port i API SECRET" \ + "U)" "Aktualizuj..." \ + "C)" "Sprztąj..." \ + "R)" "Uruchom ponownie kontenery" \ + "D)" "Wyślij diagnostykę i logi" \ + "Z)" "Zmień lub odinstaluj..." \ + "I)" "O tym narzędziu..." \ + "X)" "Wyjście" \ + --ok-button="$uni_select" --cancel-button="$uni_exit" \ + 3>&2 2>&1 1>&3) - WATCHDOG_STATUS="detection_failed" + case $CHOICE in + "S)") + status_menu + ;; + "P)") + local ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") + local ns_api_secret=$(dotenv-tool -r get -f "$ENV_FILE_NS" "API_SECRET") + whiptail --title "Podgląd konfiguracji Nightscout" --msgbox \ + "\n Port usługi Nightscout: $ns_external_port\n API_SECRET: $ns_api_secret" \ + 10 60 + ;; + "U)") + update_menu + ;; + "C)") + cleanup_menu + ;; + "R)") + docker_compose_down + docker_compose_up + ;; + "D)") + send_diagnostics + ;; + "Z)") + uninstall_menu + ;; + "I)") + about_dialog + ;; + "X)") + exit 0 + ;; + "") + exit 0 + ;; + esac + done +} - if [[ "$html" =~ github.com/nightscout/cgm-remote-monitor ]]; then - echo "Nightscout is running" - WATCHDOG_STATUS="ok" - fi +install_or_menu() { + STATUS_NS=$(get_docker_status "ns-server") + lastTimeSpaceInfo=$(get_space_info) - if [[ "$html" =~ 'MongoDB connection failed' ]]; then - echo "Nightscout crash detected" - WATCHDOG_STATUS="restart" - if [ "$WATCHDOG_LAST_STATUS" == "restart_failed" ]; then - event_mark "restart_both" - echo "Restarting DB first..." - docker restart 'ns-database' - echo "Then, restarting Nightscout..." - docker restart 'ns-server' - echo "...done" - WATCHDOG_STATUS="full_restart" - else - if [ "$WATCHDOG_LAST_STATUS" != "restart" ]; then - event_mark "restart_ns" - echo "Restarting only Nightscout..." - docker restart 'ns-server' - echo "...done" - else - echo "Restart was tried but NS still crashed, will retry restart next time" - WATCHDOG_STATUS="restart_failed" - fi - fi - else - regex3='poszło nie tak' - if [[ "$html" =~ $regex3 ]]; then - echo "Nightscout is still restarting..." - WATCHDOG_STATUS="awaiting" - fi - fi + if [ "$STATUS_NS" = "missing" ]; then - if [ "$WATCHDOG_STATUS" = "detection_failed" ]; then - { - echo "----------------------------------------------------------------" - echo "[$WATCHDOG_TIME] Unknown server failure:" - echo "CONTAINERS:" - docker stats --no-stream - echo "HTTP DUMP:" - echo "$html" - } >>"$WATCHDOG_FAILURES_FILE" + if [ "$freshInstall" -eq 0 ]; then + install_now_prompt + if ! [ $? -eq 1 ]; then + freshInstall=1 fi - - else - WATCHDOG_STATUS="domain_failed" fi - else - if [ "$NS_STATUS" = "restarting" ] || [ "$DB_STATUS" = "restarting" ]; then - WATCHDOG_STATUS="awaiting" + if [ "$freshInstall" -gt 0 ]; then + ohai "Instalowanie Nightscout..." + event_mark "install_start" + docker_compose_update + setup_firewall_for_ns + domain_setup + # admin_panel_promo + event_mark "install_end" + setup_done else - WATCHDOG_STATUS="not_running" - - local logSample=$(timeout -k 15 10 docker logs ns-server --tail "10" 2>&1) - local regexSample='Cannot connect to the Docker daemon' - if [[ "$logSample" =~ $regexSample ]]; then - WATCHDOG_STATUS="docker_down" - if [ "$WATCHDOG_LAST_STATUS" != "docker_down" ]; then - echo "Cannot connect to Docker, will restart service..." - set_last_time "docker_down" - sudo systemctl restart docker - else - echo "Cannot connect to Docker, and service cannot be restarted" - local lastCalled=$(get_since_last_time "server_restart_needed") - if ((lastCalled == -1)) || ((lastCalled > DOCKER_DOWN_MAIL)); then - set_last_time "server_restart_needed" - echo "Sending mail to user - manual server restart needed" - mail_restart_needed "Usługa Docker uległa awarii i nie można automatycznie jej uruchomić" - else - echo "Mail for manual restart already recently sent" - fi - fi - fi + main_menu fi + else + msgok "Wykryto uruchomiony Nightscout" + main_menu fi +} + + + +#======================================= +# ACTIONS AND STEPS +#======================================= - echo "Watchdog observation: $WATCHDOG_STATUS" - # if [ "$WATCHDOG_LAST_STATUS" != "$WATCHDOG_STATUS" ]; then - echo "$WATCHDOG_TIME [$WATCHDOG_STATUS]" >>"$WATCHDOG_LOG_FILE" - LOGSIZE=$(wc -l <"$WATCHDOG_LOG_FILE") - if [ "$LOGSIZE" -gt 1000 ]; then - tail -1000 "$WATCHDOG_LOG_FILE" >"$WATCHDOG_LOG_FILE.tmp" - mv -f "$WATCHDOG_LOG_FILE.tmp" "$WATCHDOG_LOG_FILE" - fi - # fi - if [[ -f $WATCHDOG_FAILURES_FILE ]]; then - FAILSIZE=$(wc -l <"$WATCHDOG_FAILURES_FILE") - if [ "$FAILSIZE" -gt 10000 ]; then - tail -10000 "$WATCHDOG_FAILURES_FILE" >"$WATCHDOG_FAILURES_FILE.tmp" - mv -f "$WATCHDOG_FAILURES_FILE.tmp" "$WATCHDOG_FAILURES_FILE" - fi - fi - echo "$WATCHDOG_TIME" >"$WATCHDOG_TIME_FILE" - echo "$WATCHDOG_STATUS" >"$WATCHDOG_STATUS_FILE" - exit 0 -} -load_update_channel() { - if [[ -f $UPDATE_CHANNEL_FILE ]]; then - UPDATE_CHANNEL=$(cat "$UPDATE_CHANNEL_FILE") - msgok "Loaded update channel: $UPDATE_CHANNEL" - fi -} -startup_version() { - local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") - msgnote "nightscout-tool version $SCRIPT_VERSION ($SCRIPT_BUILD_TIME)" - msgnote "build ${updateInstalled}" - msgnote "$uni_copyright 2023-2025 Dominik Dzienia" - msgnote "Licensed under CC BY-NC-ND 4.0" -} -parse_commandline_args() { - load_update_channel - CMDARGS=$(getopt --quiet -o wvdpuc: --long watchdog,version,develop,production,update,channel: -n 'nightscout-tool' -- "$@") - # shellcheck disable=SC2181 - if [ $? != 0 ]; then - echo "Invalid arguments: " "$@" >&2 - exit 1 - fi - # Note the quotes around '$TEMP': they are essential! - eval set -- "$CMDARGS" - WATCHDOGMODE=false - while true; do - case "$1" in - -w | --watchdog) - WATCHDOGMODE=true - shift - ;; - -v | --version) - echo "$SCRIPT_VERSION" - exit 0 - ;; - -d | --develop) - warn "Switching to DEVELOP update channel" - UPDATE_CHANNEL=develop - forceUpdateCheck=1 - echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" - shift - ;; - -p | --production) - warn "Switching to PRODUCTION update channel" - UPDATE_CHANNEL=master - forceUpdateCheck=1 - echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" - shift - ;; - -u | --update) - warn "Forcing update check" - forceUpdateCheck=1 - shift - ;; - -c | --channel) - shift # The arg is next in position args - UPDATE_CHANNEL_CANDIDATE=$1 - forceUpdateCheck=1 - [[ ! "$UPDATE_CHANNEL_CANDIDATE" =~ ^[a-z]{3,}$ ]] && { - echo "Incorrect channel name provided: $UPDATE_CHANNEL_CANDIDATE" - exit 1 - } - warn "Switching to $UPDATE_CHANNEL_CANDIDATE update channel" - UPDATE_CHANNEL="$UPDATE_CHANNEL_CANDIDATE" - echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" - shift - ;; - --) - shift - break - ;; - *) break ;; - esac - done - if [ "$WATCHDOGMODE" = "true" ]; then - watchdog_check - fi -} diff --git a/src/app_cleanup.sh b/src/app_cleanup.sh new file mode 100644 index 0000000..007881b --- /dev/null +++ b/src/app_cleanup.sh @@ -0,0 +1,115 @@ +#======================================= +# CLEANUP UI +#======================================= + +prompt_cleanup_container_logs() { + yesnodlg "Usunąć logi kontenerów?" "$uni_delete" "$uni_leave_logs" \ + "Czy chcesz usunąć logi kontenerów nightscout i bazy?" \ + "${TL}Jeśli Twój serwer działa poprawnie," \ + "${NL}- możesz spokojnie usunąć logi." \ + "${TL}Jeśli masz problem z serwerem - zostaw logi!" \ + "${NL}- logi mogą być niezbędne do diagnostyki" \ + "${TL}(ta operacja uruchomi ponownie kontenery)" +} + +cleanup_menu() { + + while :; do + + local spaceInfo=$(get_space_info) + local remainingTxt=$(echo "$spaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) + local totalTxt=$(echo "$spaceInfo" | awk '{print $2}' | numfmt --to iec-i --suffix=B) + local percTxt=$(echo "$spaceInfo" | awk '{print $4}') + local fixedPerc=${percTxt/[%]/=} + + local nowB=$(echo "$spaceInfo" | awk '{print $3}') + local lastTimeB=$(echo "$lastTimeSpaceInfo" | awk '{print $3}') + local savedB=$((nowB - lastTimeB)) + local savedTxt=$(echo "$savedB" | numfmt --to iec-i --suffix=B) + + if ((savedB < 1)); then + savedTxt="---" + fi + + local statusTitle="\n$(center_multiline 45 "$( + pad_multiline \ + " Dostępne: ${remainingTxt}" \ + "\n Zwolniono: ${savedTxt}" \ + "\n Zajęte: ${fixedPerc} (z ${totalTxt})" + )")\n" + + local CHOICE=$(whiptail --title "Sprzątanie" --menu \ + "${statusTitle/=/%}" \ + 17 50 6 \ + "A)" "Posprzątaj wszystko" \ + "S)" "Posprzątaj zasoby systemowe" \ + "D)" "Usuń nieużywane obrazy Dockera" \ + "B)" "Usuń kopie zapasowe bazy danych" \ + "L)" "Usuń logi kontenerów" \ + "M)" "Powrót do menu" \ + --ok-button="Wybierz" --cancel-button="$uni_back" \ + 3>&2 2>&1 1>&3) + + case $CHOICE in + "A)") + noyesdlg "Posprzątać wszystko?" "$uni_confirm_del" "$uni_resign" \ + "Czy chcesz posprzątać i usunąć:" \ + "$(pad_multiline \ + "${TL}${uni_bullet}nieużywane pliki apt i dziennika" \ + "${NL}${uni_bullet}nieużywane obrazy Dockera" \ + "${NL}${uni_bullet}kopie zapasowe bazy danych" \ + "${NL}${uni_bullet}opcjonalnie - logi Nightscouta i bazy")${NL}" \ + "${TL}(☕ to może potrwać nawet kilkadziesiąt minut)" + if ! [ $? -eq 1 ]; then + prompt_cleanup_container_logs + if ! [ $? -eq 1 ]; then + do_cleanup_container_logs + do_cleanup_sys + do_cleanup_docker + do_cleanup_db + else + do_cleanup_sys + do_cleanup_docker + do_cleanup_db + fi + fi + ;; + "S)") + noyesdlg "Posprzątać zasoby systemowe?" "$uni_confirm_del" "$uni_resign" \ + "Czy chcesz usunąć nieużywane pakiety apt${NL}i poprzątać dziennik systemowy?" \ + "${TL}(☕ to może potrwać nawet kilkadziesiąt minut)" + if ! [ $? -eq 1 ]; then + do_cleanup_sys + fi + ;; + "D)") + noyesdlg "Posprzątać obrazy Dockera?" "$uni_confirm_del" "$uni_resign" \ + "Czy chcesz usunąć nieużywane obrazy Dockera?" \ + "${TL}(☕ to może potrwać kilka minut)" + if ! [ $? -eq 1 ]; then + do_cleanup_docker + fi + ;; + "B)") + noyesdlg "Usunąć kopie zapasowe bazy danych?" "$uni_confirm_del" "$uni_resign" \ + "Czy chcesz usunąć kopie zapasowe bazy danych?" \ + "${NL}(na razie i tak nie ma automatycznego mechanizmu ich wykorzystania)" + if ! [ $? -eq 1 ]; then + do_cleanup_db + fi + ;; + "L)") + prompt_cleanup_container_logs + if ! [ $? -eq 1 ]; then + do_cleanup_container_logs + fi + ;; + "M)") + break + ;; + "") + break + ;; + esac + done +} \ No newline at end of file diff --git a/src/app_config.sh b/src/app_config.sh new file mode 100644 index 0000000..8727b07 --- /dev/null +++ b/src/app_config.sh @@ -0,0 +1,221 @@ +#======================================= +# CONFIG AND MANAGEMENT UI +#======================================= + +version_menu() { + + local tags=$(wget -q -O - "https://hub.docker.com/v2/namespaces/nightscout/repositories/cgm-remote-monitor/tags?page_size=100" | jq -r ".results[].name" | sed "/dev_[a-f0-9]*/d" | sort --version-sort -u -r | head -n 8) + + while :; do + + local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") + local versions=() + + while read -r line; do + if [ "$line" == "$ns_tag" ]; then + continue + fi + + label=" - na sztywno $line " + + if [ "$line" == "latest_dev" ]; then + label=" - najnowsza wersja rozwojowa " + fi + + if [ "$line" == "latest" ]; then + label=" - aktualna wersja stabilna " + fi + + versions+=("$line") + versions+=("$label") + done <<<"$tags" + + versions+=("M)") + versions+=(" Powrót do poprzedniego menu") + + local CHOICE=$(whiptail --title "Wersja Nightscout" --menu "\nZmień wersję kontenera Nightscout z: $ns_tag na:\n\n" 20 60 10 \ + "${versions[@]}" \ + --ok-button="Zmień" --cancel-button="$uni_back" \ + 3>&2 2>&1 1>&3) + + if [ "$CHOICE" == "M)" ]; then + break + fi + + if [ "$CHOICE" == "" ]; then + break + fi + + if [ "$CHOICE" == "$ns_tag" ]; then + whiptail --title "Ta sama wersja!" --msgbox "Wybrano bieżącą wersję - brak zmiany" 7 50 + else + + whiptail --title "Zmienić wersję Nightscout?" --yesno --defaultno "Czy na pewno chcesz zmienić wersję z: $ns_tag na: $CHOICE?\n\n${uni_bullet}dane i konfiguracja NIE SĄ usuwane\n${uni_bullet}wersję można łatwo zmienić ponownie\n${uni_bullet}dane w bazie danych mogą ulec zmianie i NIE BYĆ kompatybilne" --yes-button "$uni_confirm_ch" --no-button "$uni_resign" 13 73 + if ! [ $? -eq 1 ]; then + event_mark "change_ns_version" + docker_compose_down + ohai "Changing Nightscout container tag from: $ns_tag to: $CHOICE" + dotenv-tool -pmr -i "$ENV_FILE_DEP" -- "NS_NIGHTSCOUT_TAG=$CHOICE" + docker_compose_update + whiptail --title "Zmieniono wersję Nightscout" --msgbox "$(center_multiline 65 \ + "Zmieniono wersję Nightscout na: $CHOICE" \ + "${TL}Sprawdź czy Nightscout działa poprawnie, w razie problemów:" \ + "${NL}${uni_bullet}aktualizuj kontenery" \ + "${NL}${uni_bullet}spróbuj wyczyścić bazę danych" \ + "${NL}${uni_bullet}wróć do poprzedniej wersji ($ns_tag)")" \ + 13 70 + break + fi + + fi + + done +} + +uninstall_menu() { + while :; do + local extraMenu=() + extraMenu+=("A)" "Ustaw adres strony (subdomenę)") + local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") + local CHOICE=$(whiptail --title "Zmień lub odinstaluj Nightscout" --menu "\n" 17 70 8 \ + "${extraMenu[@]}" \ + "W)" "Zmień wersję Nightscouta (bieżąca: $ns_tag)" \ + "E)" "Edytuj ustawienia (zmienne środowiskowe)" \ + "K)" "Usuń kontenery" \ + "B)" "Wyczyść bazę danych" \ + "D)" "Usuń kontenery, dane i konfigurację" \ + "U)" "Usuń wszystko - odinstaluj" \ + "M)" "Powrót do menu" \ + --ok-button="$uni_select" --cancel-button="$uni_back" \ + 3>&2 2>&1 1>&3) + + case $CHOICE in + "A)") + domain_setup + ;; + "W)") + version_menu + ;; + "E)") + + if ! [[ "$0" =~ .*"/usr/bin/nightscout-tool" ]]; then + okdlg "Opcja niedostępna" \ + "Edytor ustawień dostępny po uruchomieniu narzędzia komendą:" \ + "${TL}nightscout-tool" + else + yesnodlg "Edycja ustawień Nightscout" "$uni_confirm_ed" "$uni_resign" \ + "Za chwilę otworzę plik konfiguracji Nightscout w edytorze NANO" \ + "$(pad_multiline \ + "${TL}Wskazówki co do obsługi edytora:" \ + "${NL}${uni_bullet}Aby ZAPISAĆ zmiany naciśnij Ctrl+O" \ + "${NL}${uni_bullet}Aby ZAKOŃCZYĆ edycję naciśnij Ctrl+X")" \ + "${TL}${uni_warn} Edycja spowoduje też restart i aktualizację kontenerów ${uni_warn}" + + if ! [ $? -eq 1 ]; then + event_mark "edit_env_manual" + nano "$ENV_FILE_NS" + docker_compose_down + docker_compose_update + fi + fi + ;; + "K)") + noyesdlg "Usunąć kontenery?" "$uni_confirm_del" "$uni_resign" \ + "Czy na pewno chcesz usunąć kontenery powiązane z Nightscout?" \ + "$(pad_multiline \ + "${TL}${uni_bullet}dane i konfiguracja NIE SĄ usuwane" \ + "${NL}${uni_bullet}kontenery można łatwo odzyskać (opcja Aktualizuj kontenery)")" + + if ! [ $? -eq 1 ]; then + event_mark "remove_containers" + docker_compose_down + fi + ;; + "B)") + noyesdlg "Usunąć dane z bazy danych?" "$uni_confirm_del" "$uni_resign" \ + "Czy na pewno chcesz usunąć dane z bazy danych?" \ + "$(pad_multiline \ + "${TL}${uni_bullet}konfiguracja serwera NIE ZOSTANIE usunięta" \ + "${NL}${uni_bullet}usunięte zostaną wszystkie dane użytkownika" \ + "${NL}${uni_bullet_pad} (m.in. historia glikemii, wpisy, notatki, pomiary, profile)" \ + "${NL}${uni_bullet}kontenery zostaną zatrzymane i uruchomione ponownie (zaktualizowane)")" + + if ! [ $? -eq 1 ]; then + docker_compose_down + dialog --title " Czyszczenie bazy danych " --infobox "\n Usuwanie plików bazy\n ... Proszę czekać ..." 6 32 + rm -r "${MONGO_DB_DIR:?}/data" + event_mark "remove_db_data" + docker_compose_update + fi + ;; + "D)") + noyesdlg "Usunąć wszystkie dane?" "$uni_confirm_del" "$uni_resign" \ + "Czy na pewno chcesz usunąć wszystkie dane i konfigurację?" \ + "$(pad_multiline \ + "${TL}${uni_bullet}konfigurację panelu, ustawienia Nightscout" \ + "${NL}${uni_bullet}wszystkie dane użytkownika" \ + "${NL}${uni_bullet_pad}(m.in. glikemia, wpisy, notatki, pomiary, profile)" \ + "${NL}${uni_bullet}kontenery zostaną zatrzymane")" + + if ! [ $? -eq 1 ]; then + docker_compose_down + dialog --title " Czyszczenie bazy danych" --infobox "\n Usuwanie plików bazy\n ... Proszę czekać ..." 6 32 + rm -r "${MONGO_DB_DIR:?}/data" + event_mark "remove_all_data" + dialog --title " Czyszczenie konfiguracji" --infobox "\n Usuwanie konfiguracji\n ... Proszę czekać ..." 6 32 + rm -r "${CONFIG_ROOT_DIR:?}" + do_cleanup_diagnostics + do_cleanup_app_logs + + okdlg "Usunięto dane użytkownika" \ + "Usunęto dane użytkwnika i konfigurację." \ + "${TL}Aby zainstalować Nightscout od zera:" \ + "${NL}uruchom ponownie skrypt i podaj konfigurację" + + exit 0 + fi + ;; + "U)") + noyesdlg "Odinstalować?" "$uni_confirm_del" "$uni_resign" \ + "Czy na pewno chcesz usunąć wszystko?" \ + "$(pad_multiline \ + "${TL}${uni_bullet}konfigurację panelu, ustawienia Nightscout" \ + "${NL}${uni_bullet}wszystkie dane użytkownika (glikemia, status, profile)" \ + "${NL}${uni_bullet}kontenery, skrypt nightscout-tool")" \ + "${TL}NIE ZOSTANĄ USUNIĘTE/ODINSTALOWANE:" \ + "$(pad_multiline \ + "${TL}${uni_bullet}użytkownik mongo db, firewall, doinstalowane pakiety" \ + "${NL}${uni_bullet}kopie zapasowe bazy danych")" + + if ! [ $? -eq 1 ]; then + docker_compose_down + dialog --title " Odinstalowanie" --infobox "\n Usuwanie plików\n ... Proszę czekać ..." 6 32 + uninstall_cron + rm -r "${MONGO_DB_DIR:?}/data" + rm -r "${CONFIG_ROOT_DIR:?}" + rm "$TOOL_LINK" + rm -r "${NIGHTSCOUT_ROOT_DIR:?}/tools" + rm -r "${NIGHTSCOUT_ROOT_DIR:?}/updates" + do_cleanup_diagnostics + do_cleanup_app_logs + do_cleanup_app_state + event_mark "uninstall" + + okdlg "Odinstalowano" \ + "Odinstalowano Nightscout z Mikr.us-a" \ + "${TL}Aby ponownie zainstalować, postępuj według instrukcji na stronie:" \ + "${NL}https://t1d.dzienia.pl/nightscout_mikrus_tutorial" \ + "${TL}Dziękujemy i do zobaczenia!" + + exit 0 + fi + ;; + "M)") + break + ;; + "") + break + ;; + esac + done +} diff --git a/src/app_diagnostics.sh b/src/app_diagnostics.sh new file mode 100644 index 0000000..89cfd6f --- /dev/null +++ b/src/app_diagnostics.sh @@ -0,0 +1,70 @@ +#======================================= +# DIAGNOSTICS UI +#======================================= + +send_diagnostics() { + + setup_security + + LOG_KEY=$(<"$LOG_ENCRYPTION_KEY_FILE") + + yesnodlg "Wysyłać diagnostykę?" \ + "$uni_send" "$uni_resign" \ + "Czy chcesz zgromadzić i wysłać sobie mailem dane diagnostyczne?" \ + "\n$( + pad_multiline \ + "\n${uni_bullet}diagnostyka zawiera logi i informacje o serwerze i usługach" \ + "\n${uni_bullet}wysyłka na e-mail na który zamówiono serwer Mikr.us" \ + "\n${uni_bullet}dane będą skompresowane i zaszyfrowane" \ + "\n${uni_bullet}maila prześlij dalej do zaufanej osoby wspierającej" \ + "\n${uni_bullet_pad}(z którą to wcześniej zaplanowano i uzgodniono!!!)" \ + "\n${uni_bullet}hasło przekaż INNĄ DROGĄ (komunikatorem, SMSem, osobiście)" \ + "\n\n${uni_bullet_pad}Hasło do logów: $LOG_KEY" + )" + + if ! [ $? -eq 1 ]; then + + local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + gather_diagnostics 500 100 "$curr_time" + retry_diagnostics 200 50 "$curr_time" + retry_diagnostics 100 50 "$curr_time" + retry_diagnostics 50 50 "$curr_time" + retry_diagnostics 50 20 "$curr_time" + + ohai "Wysyłanie maila" + + local sentStatus=$({ + echo "Ta wiadomość zawiera poufne dane diagnostyczne Twojego serwera Nightscout." + echo "Mogą one pomóc Tobie lub zaufanej osobie w identyfikacji problemu." + echo " " + echo "Prześlij ten mail dalej do zaufanej osoby, umówionej na udzielenie wsparcia." + echo "Przekaż tej osobie w bezpieczny sposób hasło szyfrowania" + echo " (w narzędziu nightscout-tool można je znaleźć w pozycji 'O tym narzędziu...')." + echo "Do przekazania hasła użyj INNEJ metody (komunikator, SMS, osobiście...)." + echo "Nie przesyłaj tej wiadomości do administratorów grupy lub serwera bez wcześniejszego uzgodnienia!" + echo " " + echo "Instrukcje i narzędzie do odszyfrowania logów dostępne pod adresem: https://t1d.dzienia.pl/decoder/" + echo " " + echo " " + cat "$SUPPORT_LOG.gz.asc" + } | pusher "Diagnostyka_serwera_Nightscout_-_$curr_time") + + local regexEm='Email sent' + if [[ "$sentStatus" =~ $regexEm ]]; then + do_cleanup_diagnostics + msgcheck "Mail wysłany!" + okdlg "Diagnostyka wysłana" \ + "Sprawdź swoją skrzynkę pocztową,\n" \ + "otrzymanego maila przekaż zaufanemu wspierającemu.\n\n" \ + "Komunikatorem lub SMS przekaż hasło do logów:\n\n$LOG_KEY" + else + msgerr "Błąd podczas wysyłki maila: $sentStatus" + okdlg "Błąd wysyłki maila" \ + "Nieststy nie udało się wysłać diagnostyki" \ + "${NL}zgłoś poniższy błąd twórcom narzędzia (na grupie Technologie Diabetyka)" \ + "${TL}$sentStatus" + fi + + fi +} diff --git a/src/app_main.sh b/src/app_main.sh new file mode 100644 index 0000000..6d44398 --- /dev/null +++ b/src/app_main.sh @@ -0,0 +1,154 @@ +#======================================= +# MAIN APP UI +#======================================= + +show_logs() { + local col=$((COLUMNS - 10)) + local rws=$((LINES - 4)) + if [ "$col" -gt 120 ]; then + col=160 + fi + if [ "$col" -lt 60 ]; then + col=60 + fi + if [ "$rws" -lt 12 ]; then + rws=12 + fi + + local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') + if [ -n "$ID" ]; then + local tmpfile=$(mktemp) + docker logs "$ID" 2>&1 | tail $((rws * -6)) | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g' >"$tmpfile" + whiptail --title "Logi $2" --scrolltext --textbox "$tmpfile" "$rws" "$col" + rm "$tmpfile" + fi +} + +status_menu() { + while :; do + local CHOICE=$(whiptail --title "Status kontenerów" --menu "\n Aktualizacja: kontenery na żywo, watchdog co 5 minut\n\n Wybierz pozycję aby zobaczyć logi:\n" 18 60 6 \ + "1)" " Nightscout: $(get_container_status 'ns-server')" \ + "2)" " Baza danych: $(get_container_status 'ns-database')" \ + "3)" " Backup: $(get_container_status 'ns-backup')" \ + "4)" " Watchdog: $(get_watchdog_status "$(get_watchdog_status_code)" "$uni_watchdog_ok")" \ + "5)" " Zdarzenia: $(get_events_status)" \ + "M)" "Powrót do menu" \ + --ok-button="Zobacz logi" --cancel-button="$uni_back" \ + 3>&2 2>&1 1>&3) + + case $CHOICE in + "1)") + show_logs 'ns-server' 'Nightscouta' + ;; + "2)") + show_logs 'ns-database' 'bazy danych' + ;; + "3)") + show_logs 'ns-backup' 'usługi kopii zapasowych' + ;; + "4)") + show_watchdog_logs + ;; + "5)") + okdlg "Zdarzenia" \ + "$(pad_multiline "$(event_list)")" + ;; + "M)") + break + ;; + "") + break + ;; + esac + done +} + +main_menu() { + while :; do + local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") + local quickStatus=$(center_text "Strona Nightscout: $(get_watchdog_status "$(get_watchdog_status_code_live)" "$uni_ns_ok")" 55) + local quickVersion=$(center_text "Wersja: $ns_tag" 55) + local quickDomain=$(center_text "Domena: $(get_domain_status 'ns-server')" 55) + local CHOICE=$(whiptail --title "Zarządzanie Nightscoutem :: $SCRIPT_VERSION" --menu "\n$quickStatus\n$quickVersion\n$quickDomain\n" 21 60 9 \ + "S)" "Status kontenerów i logi" \ + "P)" "Pokaż port i API SECRET" \ + "U)" "Aktualizuj..." \ + "C)" "Sprztąj..." \ + "R)" "Uruchom ponownie kontenery" \ + "D)" "Wyślij diagnostykę i logi" \ + "Z)" "Zmień lub odinstaluj..." \ + "I)" "O tym narzędziu..." \ + "X)" "Wyjście" \ + --ok-button="$uni_select" --cancel-button="$uni_exit" \ + 3>&2 2>&1 1>&3) + + case $CHOICE in + "S)") + status_menu + ;; + "P)") + local ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") + local ns_api_secret=$(dotenv-tool -r get -f "$ENV_FILE_NS" "API_SECRET") + whiptail --title "Podgląd konfiguracji Nightscout" --msgbox \ + "\n Port usługi Nightscout: $ns_external_port\n API_SECRET: $ns_api_secret" \ + 10 60 + ;; + "U)") + update_menu + ;; + "C)") + cleanup_menu + ;; + "R)") + docker_compose_down + docker_compose_up + ;; + "D)") + send_diagnostics + ;; + "Z)") + uninstall_menu + ;; + "I)") + about_dialog + ;; + "X)") + exit 0 + ;; + "") + exit 0 + ;; + esac + done +} + +install_or_menu() { + STATUS_NS=$(get_docker_status "ns-server") + lastTimeSpaceInfo=$(get_space_info) + + if [ "$STATUS_NS" = "missing" ]; then + + if [ "$freshInstall" -eq 0 ]; then + install_now_prompt + if ! [ $? -eq 1 ]; then + freshInstall=1 + fi + fi + + if [ "$freshInstall" -gt 0 ]; then + ohai "Instalowanie Nightscout..." + event_mark "install_start" + docker_compose_update + setup_firewall_for_ns + domain_setup + # admin_panel_promo + event_mark "install_end" + setup_done + else + main_menu + fi + else + msgok "Wykryto uruchomiony Nightscout" + main_menu + fi +} \ No newline at end of file diff --git a/src/app_other_dialogs.sh b/src/app_other_dialogs.sh new file mode 100644 index 0000000..c127c66 --- /dev/null +++ b/src/app_other_dialogs.sh @@ -0,0 +1,53 @@ +#======================================= +# OTHER UI +#======================================= + +about_dialog() { + LOG_KEY=$(<"$LOG_ENCRYPTION_KEY_FILE") + okdlg "O tym narzędziu..." \ + "$(printf '\U1F9D1') (c) 2023-2026 Dominik Dzienia" \ + "${NL}$(printf '\U1F4E7') dominik.dzienia@gmail.com" \ + "${TL}$(printf '\U1F3DB') To narzędzie jest dystrybuowane na licencji CC BY-NC-ND 4.0" \ + "${NL}htps://creativecommons.org/licenses/by-nc-nd/4.0/deed.pl" \ + "${TL}wersja: $SCRIPT_VERSION ($SCRIPT_BUILD_TIME) $UPDATE_CHANNEL" \ + "${TL}hasło do logów: $LOG_KEY" +} + +prompt_welcome() { + yesnodlg "Witamy" "$uni_start" "$uni_exit" \ + "Ten skrypt zainstaluje Nightscout na bieżącym serwerze mikr.us" \ + "${TL}Jeśli na tym serwerze jest już Nightscout " \ + "${NL}- ten skrypt umożliwia jego aktualizację oraz diagnostykę.${TL}" + exit_on_no_cancel +} + +prompt_disclaimer() { + confirmdlg "Ostrzeżenie!" \ + "Zrozumiano!" \ + "Te narzędzie pozwala TOBIE zainstalować WŁASNĄ instancję Nightscout." \ + "${NL}Ty odpowiadasz za ten serwer i ewentualne skutki jego używania." \ + "${NL}Ty nim zarządzasz, to nie jest usługa czy produkt." \ + "${NL}To rozwiązanie \"Zrób to sam\" - SAM za nie odpowiadasz!" \ + "${TL}Autorzy skryptu nie ponoszą odpowiedzialności za skutki jego użycia!" \ + "${NL}Nie dajemy żadnych gwarancji co do jego poprawności czy dostępności!" \ + "${NL}Używasz go na własną odpowiedzialność!" \ + "${NL}Nie opieraj decyzji terapeutycznych na podstawie wskazań tego narzędzia!" \ + "${TL}Twórcy tego narzędzia NIE SĄ administratorami Mikr.us-ów ani Hetznera!" \ + "${NL}W razie problemów z dostępnością serwera najpierw sprawdź status Mikr.us-a!" +} + +install_now_prompt() { + yesnodlg "Instalować Nightscout?" "$uni_install" "$uni_noenter" \ + "Wykryto konfigurację ale brak uruchomionych usług" \ + "${NL}Czy chcesz zainstalować teraz kontenery Nightscout?" +} + +# Promocja panelu administracyjnego - nie jest używany +admin_panel_promo() { + whiptail --title "Panel zarządzania Mikr.us-em" --msgbox "$(center_multiline 70 \ + "Ta instalacja Nightscout dodaje dodatkowy panel administracyjny" \ + "${NL}do zarządzania serwerem i konfiguracją - online." \ + "${TL}Znajdziesz go klikając na ikonkę serwera w menu strony Nightscout" \ + "${NL}lub dodając /mikrus na końcu swojego adresu Nightscout")" \ + 12 75 +} \ No newline at end of file diff --git a/src/app_setup.sh b/src/app_setup.sh new file mode 100644 index 0000000..71ddb0c --- /dev/null +++ b/src/app_setup.sh @@ -0,0 +1,119 @@ +#======================================= +# SETUP UI +#======================================= + +docker_compose_up() { + process_gauge install_containers install_containers_progress "Uruchamianie Nightscouta" "Proszę czekać, trwa uruchamianie kontenerów..." +} + +docker_compose_update() { + process_gauge update_containers install_containers_progress "Uruchamianie Nightscouta" "Proszę czekać, trwa aktualizacja kontenerów..." +} + +docker_compose_down() { + process_gauge uninstall_containers uninstall_containers_progress "Zatrzymywanie Nightscouta" "Proszę czekać, trwa zatrzymywanie i usuwanie kontenerów..." +} + +setup_done() { + whiptail --title "Gotowe!" --yesno --defaultno " Możesz teraz zamknąć to narzędzie lub wrócić do menu.\n Narzędzie dostępne jest też jako komenda konsoli:\n\n nightscout-tool" --yes-button "$uni_menu" --no-button "$uni_finish" 12 70 + exit_on_no_cancel + main_menu +} + +domain_setup_manual() { + ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") + whiptail --title "Ustaw domenę" --msgbox "Aby Nightscout był widoczny z internetu ustaw subdomenę:\n\n${uni_bullet}otwórz nową zakładkę w przeglądarce,\n${uni_bullet}wejdź do panelu administracyjnego swojego Mikr.us-a,\n${uni_bullet}otwórz sekcję [Subdomeny], pod adresem:\n\n${uni_bullet_pad} https://mikr.us/panel/?a=domain\n\n${uni_bullet}w pole nazwy wpisz dowolną własną nazwę\n${uni_bullet_pad}(tylko małe litery i cyfry, max. 12 znaków)\n${uni_bullet}w pole numer portu wpisz:\n${uni_bullet_pad}\n $ns_external_port\n\n${uni_bullet}kliknij [Dodaj subdomenę] i poczekaj do kilku minut" 22 75 +} + +domain_setup() { + + local domain=$(get_td_domain) + local domainLen=${#domain} + if ((domainLen > 15)); then + msgcheck "Subdomena jest już skonfigurowana ($domain)" + okdlg "Subdomena już ustawiona" \ + "Wykryto poprzednio skonfigurowaną subdomenę:" \ + "${TL}$domain" \ + "${TL}Strona Nightscout powinna być widoczna z internetu." + return + fi + + ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") + whiptail --title "Ustaw subdomenę" --msgbox "Aby Nightscout był widoczny z internetu ustaw adres - subdomenę:\n\n [wybierz].ns.techdiab.pl\n\nWybrany początek subdomeny powinien:\n${uni_bullet}mieć długość od 4 do 12 znaków\n${uni_bullet}zaczynać się z małej litery,\n${uni_bullet}może składać się z małych liter i cyfr\n${uni_bullet}być unikalny, charakterystyczny i łatwa do zapamiętania" 16 75 + + while :; do + SUBDOMAIN='' + while :; do + SUBDOMAIN=$(whiptail --title "Podaj początek subdomeny" --inputbox "\n(4-12 znaków, tylko: małe litery i cyfry)\n\n" --cancel-button "Anuluj" 12 60 3>&1 1>&2 2>&3) + + if [ $? -eq 1 ]; then + break + fi + + if [[ "$SUBDOMAIN" =~ ^[a-z][a-z0-9]{3,11}$ ]]; then + + if printf "%s" "$SUBDOMAIN" | grep -f "$PROFANITY_DB_FILE" >>"$LOGTO" 2>&1; then + okdlg "$uni_excl Nieprawidłowa subdomena $uni_excl" \ + "Podana wartość:" \ + "${NL}$SUBDOMAIN" \ + "${TL}jest zajęta, zarezerwowana lub niedopuszczalna." \ + "${TL}Wymyśl coś innego" + SUBDOMAIN='' + continue + fi + + if printf "%s" "$SUBDOMAIN" | grep -xf "$RESERVED_DB_FILE" >>"$LOGTO" 2>&1; then + okdlg "$uni_excl Nieprawidłowa subdomena $uni_excl" \ + "Podana wartość:" \ + "${NL}$SUBDOMAIN" \ + "${TL}jest zajęta lub zarezerwowana." \ + "${TL}Wymyśl coś innego" + SUBDOMAIN='' + continue + fi + + break + + else + okdlg "$uni_excl Nieprawidłowy początek subdomeny $uni_excl" \ + "Podany początek subdomeny:" \ + "${NL}$SUBDOMAIN" \ + "${TL}ma nieprawidłowy format. Wymyśl coś innego" + if [ $? -eq 1 ]; then + SUBDOMAIN='' + continue + fi + fi + + done + + if [ "$SUBDOMAIN" == "" ]; then + domain_setup_manual + break + fi + + local MHOST=$(hostname) + local APISEC=$(dotenv-tool -r get -f "$ENV_FILE_ADMIN" "MIKRUS_APIKEY") + + ohai "Rejestrowanie subdomeny $SUBDOMAIN.ns.techdiab.pl" + local REGSTATUS=$(curl -sd "srv=$MHOST&key=$APISEC&domain=$SUBDOMAIN.ns.techdiab.pl" https://api.mikr.us/domain) + local STATOK=$(echo "$REGSTATUS" | jq -r ".status") + local STATERR=$(echo "$REGSTATUS" | jq -r ".error") + + if ! [ "$STATOK" == "null" ]; then + msgcheck "Subdomena ustawiona poprawnie ($STATOK)" + okdlg "Subdomena ustawiona" \ + "Ustawiono subdomenę:\n\n$SUBDOMAIN.ns.techdiab.pl\n($STATOK)\n\nZa kilka minut strona będzie widoczna z internetu." + break + else + msgerr "Nie udało się ustawić subdomeny ($STATERR)" + whiptail --title "$uni_excl Błąd rezerwacji domeny $uni_excl" --yesno "Nie udało się zarezerwować subdomeny:\n $STATERR\n\nChcesz podać inną subdomenę?" --yes-button "$uni_reenter" --no-button "$uni_noenter" 10 73 + if [ $? -eq 1 ]; then + SUBDOMAIN='' + domain_setup_manual + break + fi + fi + done + +} \ No newline at end of file diff --git a/src/app_setup_prompts.sh b/src/app_setup_prompts.sh new file mode 100644 index 0000000..f5e7347 --- /dev/null +++ b/src/app_setup_prompts.sh @@ -0,0 +1,137 @@ +#======================================= +# SETUP PROMPT DIALOGS +#======================================= + +prompt_mikrus_host() { + if ! [[ "$MIKRUS_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + MIKRUS_HOST=$(hostname) + while :; do + if [[ "$MIKRUS_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + break + else + MIKRUS_NEW_HOST=$(whiptail --title "Podaj identyfikator serwera" --inputbox "\nNie udało się wykryć identyfikatora serwera,\npodaj go poniżej ręcznie.\n\nIdentyfikator składa się z jednej litery i trzech cyfr\n" --cancel-button "Anuluj" 13 65 3>&1 1>&2 2>&3) + exit_on_no_cancel + if [[ "$MIKRUS_NEW_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + MIKRUS_HOST=$MIKRUS_NEW_HOST + break + else + whiptail --title "$uni_excl Nieprawidłowy identyfikator serwera $uni_excl" --yesno "Podany identyfikator serwera ma nieprawidłowy format.\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 + exit_on_no_cancel + fi + fi + done + + ohai "Updating admin config (host)" + dotenv-tool -pmr -i "$ENV_FILE_ADMIN" -- "MIKRUS_HOST=$MIKRUS_HOST" + fi +} + +prompt_mikrus_apikey() { + if ! [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then + freshInstall=$((freshInstall + 1)) + + if [ -f "/klucz_api" ]; then + MIKRUS_APIKEY=$(cat "/klucz_api") + MIKRUS_INFO_HOST=$(curl -s -d "srv=$MIKRUS_HOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .server_id) + + if [[ "$MIKRUS_INFO_HOST" == "$MIKRUS_HOST" ]] || [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + msgcheck "Mikrus OK" + else + MIKRUS_APIKEY="" + fi + fi + + if ! [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then + + whiptail --title "Przygotuj klucz API" --msgbox "Do zarządzania mikrusem [$MIKRUS_HOST] potrzebujemy klucz API.\n\n${uni_bullet}otwórz nową zakładkę w przeglądarce,\n${uni_bullet}wejdź do panelu administracyjnego swojego Mikr.us-a,\n${uni_bullet}otwórz sekcję API, pod adresem:\n\n${uni_bullet_pad}https://mikr.us/panel/?a=api\n\n${uni_bullet}skopiuj do schowka wartość klucza API" 16 70 + exit_on_no_cancel + + while :; do + MIKRUS_APIKEY=$(whiptail --title "Podaj klucz API" --passwordbox "\nWpisz klucz API. Jeśli masz go skopiowanego w schowku,\nkliknij prawym przyciskiem i wybierz z menu:" --cancel-button "Anuluj" 11 65 3>&1 1>&2 2>&3) + exit_on_no_cancel + if [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then + MIKRUS_INFO_HOST=$(curl -s -d "srv=$MIKRUS_HOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .server_id) + + if [[ "$MIKRUS_INFO_HOST" == "$MIKRUS_HOST" ]] || [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then + msgcheck "Mikrus OK" + break + else + whiptail --title "$uni_excl Nieprawidłowy API key $uni_excl" --yesno "Podany API key wydaje się mieć dobry format, ale NIE DZIAŁA!\nMoże to literówka lub podano API KEY z innego Mikr.us-a?.\n\nPotrzebujesz API KEY serwera [$MIKRUS_HOST]\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 + exit_on_no_cancel + fi + else + whiptail --title "$uni_excl Nieprawidłowy API key $uni_excl" --yesno "Podany API key ma nieprawidłowy format.\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 + exit_on_no_cancel + fi + done + + fi + + ohai "Updating admin config (api key)" + dotenv-tool -pmr -i "$ENV_FILE_ADMIN" -- "MIKRUS_APIKEY=$MIKRUS_APIKEY" + fi +} + +prompt_api_secret() { + API_SECRET=$(dotenv-tool -r get -f "$ENV_FILE_NS" "API_SECRET") + + if ! [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; then + freshInstall=$((freshInstall + 1)) + while :; do + CHOICE=$(whiptail --title "Ustal API SECRET" --menu "\nUstal bezpieczny API_SECRET, tajne główne hasło zabezpieczające dostęp do Twojego Nightscouta\n" 13 70 2 \ + "1)" "Wygeneruj losowo." \ + "2)" "Podaj własny." \ + --ok-button="$uni_select" --cancel-button="$uni_exit" \ + 3>&2 2>&1 1>&3) + exit_on_no_cancel + + case $CHOICE in + "1)") + API_SECRET=$(openssl rand -base64 100 | tr -dc '23456789@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | fold -w 16 | head -n 1) + whiptail --title "Zapisz API SECRET" --msgbox "Zapisz poniższy wygenerowany API SECRET w bezpiecznym miejscu, np.: managerze haseł:\n\n\n $API_SECRET" 12 50 + ;; + "2)") + while :; do + API_SECRET=$(whiptail --title "Podaj API SECRET" --passwordbox "\nWpisz API SECRET do serwera Nightscout:\n${uni_bullet}Upewnij się że masz go zapisanego np.: w managerze haseł\n${uni_bullet}Użyj conajmniej 12 znaków: małych i dużych liter i cyfr\n\n" --cancel-button "Anuluj" 12 75 3>&1 1>&2 2>&3) + + if [ $? -eq 1 ]; then + break + fi + + if [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; then + break + else + whiptail --title "$uni_excl Nieprawidłowy API SECRET $uni_excl" --yesno "Podany API SECRET ma nieprawidłowy format.\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_noenter" 10 73 + if [ $? -eq 1 ]; then + API_SECRET='' + break + fi + fi + done + + ;; + esac + + while [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; do + API_SECRET_CHECK=$(whiptail --title "Podaj ponownie API SECRET" --passwordbox "\nDla sprawdzenia, wpisz ustalony przed chwilą API SECRET\n\n" --cancel-button "Anuluj" 11 65 3>&1 1>&2 2>&3) + if [ $? -eq 1 ]; then + API_SECRET='' + break + fi + if [[ "$API_SECRET" == "$API_SECRET_CHECK" ]]; then + ohai "Updating nightscout config (api secret)" + dotenv-tool -pmr -i "$ENV_FILE_NS" -- "API_SECRET=$API_SECRET" + break 2 + else + whiptail --title "$uni_excl Nieprawidłowe API SECRET $uni_excl" --yesno "Podana wartości API SECRET różni się od poprzedniej!\nChcesz podać ponownie?\n" --yes-button "$uni_reenter" --no-button "$uni_noenter" 9 60 + if [ $? -eq 1 ]; then + API_SECRET='' + break + fi + fi + + done + + done + fi +} diff --git a/src/app_update.sh b/src/app_update.sh new file mode 100644 index 0000000..bb1a05b --- /dev/null +++ b/src/app_update.sh @@ -0,0 +1,173 @@ +#======================================= +# UPDATE UI +#======================================= + +update_if_needed() { + + download_if_needed "$@" + + local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "???") + local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") + + if [ "$lastDownload" == "$updateInstalled" ] && ((forceUpdateCheck == 0)) && [ $# -eq 0 ]; then + msgok "Scripts and config files are up to date" + else + + if [ "$lastDownload" == "error" ]; then + msgerr "Download update failed" + if [ $# -eq 1 ]; then + okdlg "Aktualizacja niemożliwa" "Nie można w tej chwili aktualizować narzędzia.${TL}Spróbuj ponownie później.${NL}Jeśli problem nie ustąpi - sprawdź konfigurację kanału aktualizacji" + fi + else + + if [ $# -eq 0 ] && [ "$UPDATE_CHANNEL" == "master" ] && [[ "$lastDownload" < "$updateInstalled" ]]; then + msgnote "Downgrade not possible on master channel" + forceUpdateCheck=1 + download_if_needed + else + + local changed=0 + local redeploy=0 + + local instOnlineVer=$(extract_version "$(<"$UPDATES_DIR/install.sh")") + local depEnvOnlineVer=$(extract_version "$(<"$UPDATES_DIR/deployment.env")") + local nsEnvOnlineVer=$(extract_version "$(<"$UPDATES_DIR/nightscout.env")") + local compOnlineVer=$(extract_version "$(<"$UPDATES_DIR/docker-compose.yml")") + + local instLocalVer=$(extract_version "$(<"$TOOL_FILE")") + local depEnvLocalVer=$(extract_version "$(<"$ENV_FILE_DEP")") + local nsEnvLocalVer=$(extract_version "$(<"$ENV_FILE_NS")") + local compLocalVer=$(extract_version "$(<"$DOCKER_COMPOSE_FILE")") + + local msgInst="$(printf "\U1F7E2") $instLocalVer" + local msgDep="$(printf "\U1F7E2") $depEnvLocalVer" + local msgNs="$(printf "\U1F7E2") $nsEnvLocalVer" + local msgComp="$(printf "\U1F7E2") $compLocalVer" + + if ! [ "$instOnlineVer" == "$instLocalVer" ] || ! [ "$lastDownload" == "$updateInstalled" ]; then + changed=$((changed + 1)) + msgInst="$(printf "\U1F534") $instLocalVer $(printf "\U27A1") $instOnlineVer" + fi + + if ! [ "$depEnvLocalVer" == "$depEnvOnlineVer" ]; then + changed=$((changed + 1)) + redeploy=$((redeploy + 1)) + msgDep="$(printf "\U1F534") $depEnvLocalVer $(printf "\U27A1") $depEnvOnlineVer" + fi + + if ! [ "$nsEnvLocalVer" == "$nsEnvOnlineVer" ]; then + changed=$((changed + 1)) + redeploy=$((redeploy + 1)) + msgNs="$(printf "\U1F534") $nsEnvLocalVer $(printf "\U27A1") $nsEnvOnlineVer" + fi + + if ! [ "$compLocalVer" == "$compOnlineVer" ]; then + changed=$((changed + 1)) + redeploy=$((redeploy + 1)) + msgComp="$(printf "\U1F534") $compLocalVer $(printf "\U27A1") $compOnlineVer" + fi + + if [ "$changed" -eq 0 ]; then + if [ $# -eq 1 ]; then + msgok "Scripts and config files are up to date" + okdlg "Aktualizacja skryptów" "$1" + fi + else + local okTxt="" + if [ "$redeploy" -gt 0 ]; then + okTxt="${TL}${uni_warn} Aktualizacja zrestartuje i zaktualizuje kontenery ${uni_warn}" + fi + + local versionMsg="${TL}Build: ${updateInstalled}" + if [ ! "$lastDownload" == "$updateInstalled" ]; then + versionMsg="$(pad_multiline "${TL}Masz build: ${updateInstalled}${NL} Dostępny: ${lastDownload}")" + fi + + yesnodlg "Aktualizacja skryptów" "$uni_confirm_upd" "$uni_resign" \ + "Zalecana jest aktualizacja plików:${versionMsg}" \ + "$( + pad_multiline \ + "${TL}${uni_bullet}Skrypt instalacyjny: $msgInst" \ + "${NL}${uni_bullet}Konfiguracja deploymentu: $msgDep" \ + "${NL}${uni_bullet}Konfiguracja Nightscout: $msgNs" \ + "${NL}${uni_bullet}Kompozycja usług: $msgComp${NL}" + )" \ + "$okTxt" + + if ! [ $? -eq 1 ]; then + + clear_last_time "update_needed" + + if [ "$redeploy" -gt 0 ]; then + docker_compose_down + fi + + if ! [ "$compOnlineVer" == "$compLocalVer" ]; then + ohai "Updating $DOCKER_COMPOSE_FILE" + cp -fr "$UPDATES_DIR/docker-compose.yml" "$DOCKER_COMPOSE_FILE" + fi + + if ! [ "$depEnvLocalVer" == "$depEnvOnlineVer" ]; then + ohai "Updating $ENV_FILE_DEP" + dotenv-tool -pr -o "$ENV_FILE_DEP" -i "$UPDATES_DIR/deployment.env" "$ENV_FILE_DEP" + fi + + if ! [ "$nsEnvLocalVer" == "$nsEnvOnlineVer" ]; then + ohai "Updating $ENV_FILE_NS" + dotenv-tool -pr -o "$ENV_FILE_NS" -i "$UPDATES_DIR/deployment.env" "$ENV_FILE_NS" + fi + + echo "$lastDownload" >"$UPDATES_DIR/updated" + + if ! [ "$instOnlineVer" == "$instLocalVer" ] || ! [ "$lastDownload" == "$updateInstalled" ]; then + ohai "Updating $TOOL_FILE" + cp -fr "$UPDATES_DIR/install.sh" "$TOOL_FILE" + okdlg "Aktualizacja zakończona" "Narzędzie zostanie uruchomione ponownie" + ohai "Restarting tool" + exec "$TOOL_FILE" + fi + fi + fi + fi + fi + fi +} + +update_menu() { + while :; do + local CHOICE=$(whiptail --title "Aktualizuj" --menu "\n" 11 40 4 \ + "N)" "Aktualizuj to narzędzie" \ + "S)" "Aktualizuj system" \ + "K)" "Aktualizuj kontenery" \ + "M)" "Powrót do menu" \ + --ok-button="$uni_select" --cancel-button="$uni_back" \ + 3>&2 2>&1 1>&3) + + case $CHOICE in + "S)") + ohai "Updating package list" + event_mark "update_system" + dialog --title " Aktualizacja systemu " --infobox "\n Pobieranie listy pakietów\n ..... Proszę czekać ....." 6 33 + apt-get -yq update >>"$LOGTO" 2>&1 + ohai "Upgrading system" + dialog --title " Aktualizacja systemu " --infobox "\n Instalowanie pakietów\n ... Proszę czekać ..." 6 33 + apt-get -yq upgrade >>"$LOGTO" 2>&1 + ;; + "N)") + event_mark "update_tool" + update_if_needed "Wszystkie pliki narzędzia są aktualne" + ;; + "K)") + event_mark "update_containers" + docker_compose_down + docker_compose_update + ;; + "M)") + break + ;; + "") + break + ;; + esac + done +} diff --git a/src/app_watchdog.sh b/src/app_watchdog.sh new file mode 100644 index 0000000..b129f53 --- /dev/null +++ b/src/app_watchdog.sh @@ -0,0 +1,40 @@ +#======================================= +# WATCHDOG UI +#======================================= + +show_watchdog_logs() { + local col=$((COLUMNS - 10)) + local rws=$((LINES - 3)) + if [ "$col" -gt 120 ]; then + col=160 + fi + if [ "$col" -lt 60 ]; then + col=60 + fi + if [ "$rws" -lt 12 ]; then + rws=12 + fi + + local tmpfile=$(mktemp) + { + echo "Ostatnie uruchomienie watchdoga:" + get_watchdog_age_string + echo "-------------------------------------------------------" + + if [[ -f $WATCHDOG_LOG_FILE ]]; then + echo "Statusy ostatnich przebiegów watchdoga:" + tail -5 "$WATCHDOG_LOG_FILE" + else + echo "Brak logów z ostatnich przebiegów watchdoga" + fi + echo "-------------------------------------------------------" + + if [[ -f $WATCHDOG_CRON_LOG ]]; then + echo "Log ostatniego przebiegu watchdoga:" + cat "$WATCHDOG_CRON_LOG" + fi + } >"$tmpfile" + + whiptail --title "Logi Watchdoga" --scrolltext --textbox "$tmpfile" "$rws" "$col" + rm "$tmpfile" +} diff --git a/src/commandline.sh b/src/commandline.sh new file mode 100644 index 0000000..7151594 --- /dev/null +++ b/src/commandline.sh @@ -0,0 +1,77 @@ +#======================================= +# COMMANDLINE PARSER +#======================================= + +parse_commandline_args() { + + load_update_channel + + CMDARGS=$(getopt --quiet -o wvdpuc: --long watchdog,version,develop,production,update,channel: -n 'nightscout-tool' -- "$@") + + # shellcheck disable=SC2181 + if [ $? != 0 ]; then + echo "Invalid arguments: " "$@" >&2 + exit 1 + fi + + # Note the quotes around '$TEMP': they are essential! + eval set -- "$CMDARGS" + + WATCHDOGMODE=false + while true; do + case "$1" in + -w | --watchdog) + WATCHDOGMODE=true + shift + ;; + -v | --version) + echo "$SCRIPT_VERSION" + exit 0 + ;; + -d | --develop) + warn "Switching to DEVELOP update channel" + UPDATE_CHANNEL=develop + forceUpdateCheck=1 + echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" + shift + ;; + -p | --production) + warn "Switching to PRODUCTION update channel" + UPDATE_CHANNEL=master + forceUpdateCheck=1 + echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" + shift + ;; + -u | --update) + warn "Forcing update check" + forceUpdateCheck=1 + shift + ;; + -c | --channel) + shift # The arg is next in position args + UPDATE_CHANNEL_CANDIDATE=$1 + forceUpdateCheck=1 + + [[ ! "$UPDATE_CHANNEL_CANDIDATE" =~ ^[a-z]{3,}$ ]] && { + echo "Incorrect channel name provided: $UPDATE_CHANNEL_CANDIDATE" + exit 1 + } + + warn "Switching to $UPDATE_CHANNEL_CANDIDATE update channel" + UPDATE_CHANNEL="$UPDATE_CHANNEL_CANDIDATE" + echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" + shift + ;; + --) + shift + break + ;; + *) break ;; + esac + done + + if [ "$WATCHDOGMODE" = "true" ]; then + watchdog_check + fi + +} diff --git a/src/lib.sh b/src/lib.sh index 286ce79..effa380 100644 --- a/src/lib.sh +++ b/src/lib.sh @@ -38,8 +38,8 @@ DISK_LOW_MAIL=5184000 # == 60 days in seconds DISK_CRITICAL_WARNING=104857600 # == 100 MiB DISK_CRITICAL_MAIL=604800 # == 7 days in seconds DOCKER_DOWN_MAIL=604800 # == 7 days in seconds -SCRIPT_VERSION="1.9.5" #auto-update -SCRIPT_BUILD_TIME="2025.03.19" #auto-update +SCRIPT_VERSION="1.10.1" #auto-update +SCRIPT_BUILD_TIME="2026.01.05" #auto-update #======================================= # DOWNLOAD CONFIG @@ -94,2380 +94,78 @@ MIKRUS_HOST='' # shellcheck source=/dev/null . "$DIR/logic_events.sh" -#dev-end - -#include logic_events.sh - -#======================================= -# ACTIONS AND STEPS -#======================================= - -check_interactive() { - - shopt -q login_shell && echo 'Login shell' || echo 'Not login shell' - - # if [[ $- == *i* ]]; then - # msgok "Interactive setup" - # else - # msgok "Non-interactive setup" - # fi -} - -setup_update_repo() { - if [ "$aptGetWasUpdated" -eq "0" ]; then - aptGetWasUpdated=1 - ohai "Updating package repository" - apt-get -yq update >>"$LOGTO" 2>&1 - fi -} - -test_node() { - local node_version_output - node_version_output="$(node -v 2>/dev/null)" - version_ge "$(major_minor "${node_version_output/v/}")" "$(major_minor "${REQUIRED_NODE_VERSION}")" -} - -# $1 lib name -# $2 package name -add_if_not_ok() { - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - msgcheck "$1 installed" - else - packages+=("$2") - fi -} - -add_if_not_ok_cmd() { - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - msgcheck "$1 installed" - else - ohai "Installing $1..." - eval "$2" >>"$LOGTO" 2>&1 && msgcheck "Installing $1 successfull" - fi -} - -add_if_not_ok_compose() { - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - msgcheck "$1 installed" - else - ohai "Installing $1..." - mkdir -p "~/.docker/cli-plugins" >> "$LOGTO" 2>&1 - curl -SL "https://github.com/docker/compose/releases/download/v2.29.7/docker-compose-linux-x86_64" -o "~/.docker/cli-plugins/docker-compose" >> "$LOGTO" 2>&1 - chmod +x "~/.docker/cli-plugins/docker-compose" >> "$LOGTO" 2>&1 - msgcheck "Installing $1 successfull" - fi -} - -check_git() { - git --version >/dev/null 2>&1 - add_if_not_ok "GIT" "git" -} - -check_docker() { - docker -v >/dev/null 2>&1 - add_if_not_ok "Docker" "docker.io" -} - -check_docker_compose() { - docker compose version >/dev/null 2>&1 - add_if_not_ok_compose "Docker compose" -} - -patch_docker_compose() { - if [[ -f $DOCKER_COMPOSE_FILE ]]; then - local patched=0 - local containers_running=0 - - # Check if containers are already running before patching - local ns_status=$(get_docker_status "ns-server") - local db_status=$(get_docker_status "ns-database") - if [[ "$ns_status" == "running" ]] || [[ "$db_status" == "running" ]]; then - containers_running=1 - fi - - # Check if mongodb image needs patching (bitnami/mongodb) - if grep -q "bitnami/mongodb" "$DOCKER_COMPOSE_FILE"; then - ohai "Patching docker-compose.yml MongoDB image..." - # Replace bitnami/mongodb with official mongo image - sed -i -E 's|image:\s*"*(bitnami/)?mongodb:.*"|image: "mongo:${NS_MONGODB_TAG}"|g' "$DOCKER_COMPOSE_FILE" - patched=1 - fi - # Check if volume path needs patching (bitnami/mongodb -> data/db) - if grep -q "/bitnami/mongodb" "$DOCKER_COMPOSE_FILE"; then - ohai "Patching docker-compose.yml MongoDB volume path..." - # Replace both host path and container path for mongodb volume - sed -i -E 's|(\$\{NS_DATA_DIR\}/mongodb):/bitnami/mongodb"|\1/data/db:/data/db"|g' "$DOCKER_COMPOSE_FILE" - patched=1 - fi - - if [ "$patched" -eq 1 ]; then - msgcheck "Docker compose file patched" - # Restart containers only if they were already running - if [ "$containers_running" -eq 1 ]; then - do_cleanup_sys - ohai "Restarting containers to apply patched configuration..." - update_containers - do_cleanup_docker - msgcheck "Containers restarted" - fi - fi - fi -} - -check_jq() { - jq --help >/dev/null 2>&1 - add_if_not_ok "JSON parser" "jq" -} - -check_dotenv() { - if dotenv-tool -v >/dev/null 2>&1; then - local dotEnvVersion="$(dotenv-tool -v 2>/dev/null)" - if version_ge "$(major_minor "${dotEnvVersion}")" \ - "$(major_minor "${REQUIRED_DOTENV_VERSION}")"; then - msgcheck "dotenv-tool installed (${dotEnvVersion})" - else - ohai "Updating dotenv-tool (from: ${dotEnvVersion})" - eval "npm install -g dotenv-tool --registry https://npm.dzienia.pl" >>"$LOGTO" 2>&1 && msgcheck "Updating dotenv-tool successfull" - fi - else - ohai "Installing dotenv-tool..." - eval "npm install -g dotenv-tool --registry https://npm.dzienia.pl" >>"$LOGTO" 2>&1 && msgcheck "Installing dotenv-tool successfull" - fi -} - -check_ufw() { - ufw --version >/dev/null 2>&1 - add_if_not_ok "Firewall" "ufw" -} - -check_nano() { - nano --version >/dev/null 2>&1 - add_if_not_ok "Text Editor" "nano" -} - -check_dateutils() { - dateutils.ddiff --version >/dev/null 2>&1 - add_if_not_ok "Date Utils" "dateutils" -} - -test_diceware() { - diceware --version >/dev/null 2>&1 -} - -check_diceware() { - test_diceware - add_if_not_ok "Secure Password Generator" "diceware" -} - -setup_provisional_key() { - ohai "Generating provisional log encryption key" - local randPass=$(openssl rand -base64 30) - local fixedPass=$(echo "$randPass" | sed -e 's/[+\/]/-/g') - echo "tymczasowe-${fixedPass}" >"$LOG_ENCRYPTION_KEY_FILE" - msgcheck "Provisional key generated" -} - -setup_security() { - if [[ -f $LOG_ENCRYPTION_KEY_FILE ]]; then - # -------------------- - # JAKIŚ klucz istnieje - # -------------------- - local logKey=$(<"$LOG_ENCRYPTION_KEY_FILE") - local regexTemp='tymczasowe-' - - # ----------------------- - # ...ale jest tymczasowy - # ----------------------- - if [[ "$logKey" =~ $regexTemp ]]; then - msgerr "Using provisional key" - test_diceware - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - ohai "Generating proper log encryption file..." - diceware -n 5 -d - >"$LOG_ENCRYPTION_KEY_FILE" - msgcheck "Key generated" - else - msgerr "Required tool (diceware) still cannot be installed - apt is locked!" - msgnote "Zrestartuj serwer mikr.us i sprawdź czy ten błąd nadal występuje - wtedy odbokuj apt-get i zainstaluj diceware (apt-get install diceware)" - fi - else - local keySize=${#logKey} - - # ---------------------- - # ...ale jest za krótki - # ---------------------- - if ((keySize < 12)); then - msgerr "Encryption key empty or too short, generating better one" - test_diceware - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - ohai "Generating proper log encryption file..." - diceware -n 5 -d - >"$LOG_ENCRYPTION_KEY_FILE" - msgcheck "Key generated" - else - msgerr "Generating provisional key while diceware tool is not installed" - setup_provisional_key - fi - else - msgok "Found log encryption key" - fi - fi - else - - # --------------------- - # jescze nie ma klucza - # --------------------- - - test_diceware - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - ohai "Generating log encryption key..." - diceware -n 5 -d - >"$LOG_ENCRYPTION_KEY_FILE" - msgcheck "Key generated" - else - msgerr "Generating provisional key while diceware tool is not installed" - setup_provisional_key - fi - - fi -} - -setup_packages() { - # shellcheck disable=SC2145 - # shellcheck disable=SC2068 - (if_is_set packages && setup_update_repo && - ohai "Installing packages: ${packages[@]}" && - apt-get -yq install ${packages[@]} >>"$LOGTO" 2>&1 && - msgcheck "Install successfull") || msgok "All required packages already installed" -} - -setup_node() { - test_node - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - msgcheck "Node installed in correct version" - else - ohai "Cleaning old Node.js" - { - rm -f /etc/apt/sources.list.d/nodesource.list - apt-get -yq --fix-broken install - apt-get -yq update - apt-get -yq remove nodejs nodejs-doc libnode* - } >>"$LOGTO" 2>&1 - - ohai "Preparing Node.js setup" - curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - >/dev/null 2>&1 - - ohai "Installing Node.js" - apt-get install -y nodejs >>"$LOGTO" 2>&1 - - test_node - local RECHECK=$? - if [ "$RECHECK" -ne 0 ]; then - - msgerr "Nie udało się zainstalować Node.js" - - msgerr "Instalacja Node.js jest skomplikowanym procesem i zależy od wersji systemu Linux i konfiguracji Mikr.us-a" - msgerr "Spróbuj ręcznie uruchomić instalację poniższą komendą i sprawdź czy pojawiają się błędy (i jakie):" - msgerr " apt-get install -y nodejs " - - exit 1 - fi - - fi -} - -setup_users() { - id -u mongodb &>/dev/null - local RESULT=$? - if [ "$RESULT" -eq 0 ]; then - msgcheck "Mongo DB user detected" - else - ohai "Configuring Mongo DB user" - useradd -u 1001 -g 0 mongodb - fi -} - -setup_dir_structure() { - ohai "Configuring folder structure" - mkdir -p "$MONGO_DB_DIR" - mkdir -p /srv/nightscout/config - mkdir -p /srv/nightscout/tools - mkdir -p /srv/nightscout/data - mkdir -p "$UPDATES_DIR" - chown -R mongodb:root "$MONGO_DB_DIR" -} - -setup_firewall() { - ohai "Configuring firewall" - - { - ufw default deny incoming - ufw default allow outgoing - - ufw allow OpenSSH - ufw allow ssh - } >>"$LOGTO" 2>&1 - - host=$(hostname) - - # Extract the last 3 digits from the hostname - port_number=$(echo "$host" | grep -oE '[0-9]{3}$') - - ohai "Firewall port: $port_number" - - port1=$((10000 + port_number)) - port2=$((20000 + port_number)) - port3=$((30000 + port_number)) - - if ufw allow "$port1" >>"$LOGTO" 2>&1; then - msgcheck "Do regul firewalla poprawnie dodano port $port1" - else - msgerr "Blad dodawania $port1 do regul firewalla" - fi - - if ufw allow "$port2" >>"$LOGTO" 2>&1; then - msgcheck "Do regul firewalla poprawnie dodano port $port2" - else - msgerr "Blad dodawania $port2 do regul firewalla" - fi - - if ufw allow "$port3" >>"$LOGTO" 2>&1; then - msgcheck "Do regul firewalla poprawnie dodano port $port3" - else - msgerr "Blad dodawania $port3 do regul firewalla" - fi - - ufw --force enable >>"$LOGTO" 2>&1 -} - -setup_firewall_for_ns() { - ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") - if ufw allow "$ns_external_port" >>"$LOGTO" 2>&1; then - msgcheck "Do regul firewalla poprawnie dodano port Nightscout: $ns_external_port" - else - msgerr "Blad dodawania portu Nightscout: $ns_external_port do reguł firewalla" - fi -} - -install_cron() { - local croncmd="$TOOL_LINK -w > $WATCHDOG_CRON_LOG 2>&1" - local cronjob="*/5 * * * * $croncmd" - msgok "Configuring watchdog..." - ( - crontab -l | grep -v -F "$croncmd" || : - echo "$cronjob" - ) | crontab - -} - -uninstall_cron() { - local croncmd="nightscout-tool" - (crontab -l | grep -v -F "$croncmd") | crontab - -} - -get_docker_status() { - local ID=$(docker ps -a --no-trunc --filter name="^$1" --format '{{ .ID }}') - if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then - docker inspect "$ID" | jq -r ".[0].State.Status" - else - echo 'missing' - fi -} - -get_space_info() { - df -B1 --output=target,size,avail,pcent | tail -n +2 | awk '$1 ~ /^\/$/' -} - -install_containers() { - docker compose --env-file /srv/nightscout/config/deployment.env -f /srv/nightscout/config/docker-compose.yml up --no-recreate -d >>"$LOGTO" 2>&1 -} - -update_containers() { - docker compose --env-file /srv/nightscout/config/deployment.env -f /srv/nightscout/config/docker-compose.yml pull >>"$LOGTO" 2>&1 - docker compose --env-file /srv/nightscout/config/deployment.env -f /srv/nightscout/config/docker-compose.yml up -d >>"$LOGTO" 2>&1 -} - -install_containers_progress() { - local created=$(docker container ls -f 'status=created' -f name=ns-server -f name=ns-database | wc -l) - local current=$(docker container ls -f 'status=running' -f name=ns-server -f name=ns-database | wc -l) - local progr=$(((current - 1) * 2 + (created - 1))) - echo_progress "$progr" 6 50 "$1" 60 -} - -uninstall_containers() { - docker compose --env-file /srv/nightscout/config/deployment.env -f /srv/nightscout/config/docker-compose.yml down >>"$LOGTO" 2>&1 -} - -uninstall_containers_progress() { - local running=$(docker container ls -f 'status=running' -f name=ns-server -f name=ns-database -f name=ns-backup | wc -l) - local current=$(docker container ls -f 'status=exited' -f name=ns-server -f name=ns-database -f name=ns-backup | wc -l) - local progr=$((current - 1)) - if [ "$(((running - 1) + (current - 1)))" -eq "0" ]; then - echo_progress 3 3 50 "$1" 15 - else - echo_progress "$progr" 3 50 "$1" 15 - fi -} - -source_admin() { - if [[ -f $ENV_FILE_ADMIN ]]; then - # shellcheck disable=SC1090 - source "$ENV_FILE_ADMIN" - msgok "Imported admin config" - fi -} - -download_if_not_exists() { - if [[ -f $2 ]]; then - msgok "Found $1" - else - ohai "Downloading $1..." - curl -fsSL -o "$2" "$3" - msgcheck "Downloaded $1" - fi -} - -download_conf() { - download_if_not_exists "deployment config" "$ENV_FILE_DEP" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/deployment.env" - download_if_not_exists "nightscout config" "$ENV_FILE_NS" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/nightscout.env" - download_if_not_exists "docker compose file" "$DOCKER_COMPOSE_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/docker-compose.yml" - download_if_not_exists "profanity database" "$PROFANITY_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/profanity.db" - download_if_not_exists "reservation database" "$RESERVED_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/reserved.db" -} - -download_tools() { - download_if_not_exists "update stamp" "$UPDATES_DIR/updated" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated" - - if ! [[ -f $TOOL_FILE ]]; then - download_if_not_exists "nightscout-tool file" "$TOOL_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/install.sh" - local timestamp=$(date +%s) - echo "$timestamp" >"$UPDATES_DIR/timestamp" - else - msgok "Found nightscout-tool" - fi - - if ! [[ -f $TOOL_LINK ]]; then - ohai "Linking nightscout-tool" - ln -s "$TOOL_FILE" "$TOOL_LINK" - fi - - chmod +x "$TOOL_FILE" - chmod +x "$TOOL_LINK" -} - -extract_version() { - regex='version:\s+([0-9]+\.[0-9]+\.[0-9]+)' - if [[ "$1" =~ $regex ]]; then - echo "${BASH_REMATCH[1]}" - else - echo "0.0.0" - fi -} - -read_or_default() { - if [ -f "$1" ]; then - cat "$1" - else - if [ $# -eq 2 ]; then - echo "$2" - else - echo "" - fi - fi -} - -download_updates() { - ohai "Downloading updated scripts and config files" - local onlineUpdated="$(curl -fsSL "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated")" - if [ ! "$onlineUpdated" == "" ]; then - curl -fsSL -o "$UPDATES_DIR/install.sh" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/install.sh" - curl -fsSL -o "$UPDATES_DIR/deployment.env" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/deployment.env" - curl -fsSL -o "$UPDATES_DIR/nightscout.env" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/nightscout.env" - curl -fsSL -o "$UPDATES_DIR/docker-compose.yml" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/docker-compose.yml" - curl -fsSL -o "$PROFANITY_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/profanity.db" - curl -fsSL -o "$RESERVED_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/reserved.db" - else - onlineUpdated="error" - fi - echo "$onlineUpdated" >"$UPDATES_DIR/downloaded" -} - -download_if_needed() { - local lastCheck=$(read_or_default "$UPDATES_DIR/timestamp") - local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "") - local timestampNow=$(date +%s) - local updateCheck=$UPDATE_CHECK - if (((timestampNow - lastCheck) > updateCheck)) || [ "$lastDownload" == "" ] || [ "$lastDownload" == "error" ] || ((forceUpdateCheck == 1)) || [ $# -eq 1 ]; then - echo "$timestampNow" >"$UPDATES_DIR/timestamp" - ohai "Checking if new version is available..." - local onlineUpdated="$(curl -fsSL "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated")" - local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded") - if [ "$onlineUpdated" == "$lastDownload" ] && ((forceUpdateCheck == 0)); then - msgok "Latest update already downloaded" - else - download_updates - fi - else - msgok "Too soon to download update, skipping..." - fi -} - -update_background_check() { - download_if_needed - - local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "") - local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "") - - if [ ! "$lastDownload" == "$updateInstalled" ] && [ ! "$lastDownload" == "" ] && [ ! "$lastDownload" == "error" ]; then - echo "Update needed" - local lastCalled=$(get_since_last_time "update_needed") - if ((lastCalled == -1)) || ((lastCalled > UPDATE_MAIL)); then - set_last_time "update_needed" - echo "Sending mail to user - tool update needed" - { - echo "✨ Na Twoim serwerze mikr.us z Nightscoutem można zaktualizować narzędzie nightscout-tool!" - echo " " - echo "🐕 Watchdog wykrył że dostępna jest nowa aktualizacja nightscout-tool." - echo "Na Twoim serwerze zainstalowana jest starsza wersja narzędzia - zaktualizuj go by poprawić stabilność systemu i uzyskać dostęp do nowych funkcji." - echo " " - echo "Aby zaktualizować narzędzie:" - echo " " - echo "1. Zaloguj się do panelu administracyjnego mikrusa i zaloguj się do WebSSH:" - echo " https://mikr.us/panel/?a=webssh" - echo " " - echo "2. Uruchom narzędzie komendą:" - echo " nightscout-tool" - echo " " - echo "3. Potwierdź naciskając przycisk:" - echo " 【 Aktualizacja 】" - echo " " - } | pusher "✨_Na_Twoim_serwerze_Nightscout_dostępna_jest_aktualizacja" - fi - fi -} - -update_if_needed() { - - download_if_needed "$@" - - local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "???") - local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") - - if [ "$lastDownload" == "$updateInstalled" ] && ((forceUpdateCheck == 0)) && [ $# -eq 0 ]; then - msgok "Scripts and config files are up to date" - else - - if [ "$lastDownload" == "error" ]; then - msgerr "Download update failed" - if [ $# -eq 1 ]; then - okdlg "Aktualizacja niemożliwa" "Nie można w tej chwili aktualizować narzędzia.${TL}Spróbuj ponownie później.${NL}Jeśli problem nie ustąpi - sprawdź konfigurację kanału aktualizacji" - fi - else - - if [ $# -eq 0 ] && [ "$UPDATE_CHANNEL" == "master" ] && [[ "$lastDownload" < "$updateInstalled" ]]; then - msgnote "Downgrade not possible on master channel" - forceUpdateCheck=1 - download_if_needed - else - - local changed=0 - local redeploy=0 - - local instOnlineVer=$(extract_version "$(<"$UPDATES_DIR/install.sh")") - local depEnvOnlineVer=$(extract_version "$(<"$UPDATES_DIR/deployment.env")") - local nsEnvOnlineVer=$(extract_version "$(<"$UPDATES_DIR/nightscout.env")") - local compOnlineVer=$(extract_version "$(<"$UPDATES_DIR/docker-compose.yml")") - - local instLocalVer=$(extract_version "$(<"$TOOL_FILE")") - local depEnvLocalVer=$(extract_version "$(<"$ENV_FILE_DEP")") - local nsEnvLocalVer=$(extract_version "$(<"$ENV_FILE_NS")") - local compLocalVer=$(extract_version "$(<"$DOCKER_COMPOSE_FILE")") - - local msgInst="$(printf "\U1F7E2") $instLocalVer" - local msgDep="$(printf "\U1F7E2") $depEnvLocalVer" - local msgNs="$(printf "\U1F7E2") $nsEnvLocalVer" - local msgComp="$(printf "\U1F7E2") $compLocalVer" - - if ! [ "$instOnlineVer" == "$instLocalVer" ] || ! [ "$lastDownload" == "$updateInstalled" ]; then - changed=$((changed + 1)) - msgInst="$(printf "\U1F534") $instLocalVer $(printf "\U27A1") $instOnlineVer" - fi - - if ! [ "$depEnvLocalVer" == "$depEnvOnlineVer" ]; then - changed=$((changed + 1)) - redeploy=$((redeploy + 1)) - msgDep="$(printf "\U1F534") $depEnvLocalVer $(printf "\U27A1") $depEnvOnlineVer" - fi - - if ! [ "$nsEnvLocalVer" == "$nsEnvOnlineVer" ]; then - changed=$((changed + 1)) - redeploy=$((redeploy + 1)) - msgNs="$(printf "\U1F534") $nsEnvLocalVer $(printf "\U27A1") $nsEnvOnlineVer" - fi - - if ! [ "$compLocalVer" == "$compOnlineVer" ]; then - changed=$((changed + 1)) - redeploy=$((redeploy + 1)) - msgComp="$(printf "\U1F534") $compLocalVer $(printf "\U27A1") $compOnlineVer" - fi - - if [ "$changed" -eq 0 ]; then - if [ $# -eq 1 ]; then - msgok "Scripts and config files are up to date" - okdlg "Aktualizacja skryptów" "$1" - fi - else - local okTxt="" - if [ "$redeploy" -gt 0 ]; then - okTxt="${TL}${uni_warn} Aktualizacja zrestartuje i zaktualizuje kontenery ${uni_warn}" - fi - - local versionMsg="${TL}Build: ${updateInstalled}" - if [ ! "$lastDownload" == "$updateInstalled" ]; then - versionMsg="$(pad_multiline "${TL}Masz build: ${updateInstalled}${NL} Dostępny: ${lastDownload}")" - fi - - yesnodlg "Aktualizacja skryptów" "$uni_confirm_upd" "$uni_resign" \ - "Zalecana jest aktualizacja plików:${versionMsg}" \ - "$( - pad_multiline \ - "${TL}${uni_bullet}Skrypt instalacyjny: $msgInst" \ - "${NL}${uni_bullet}Konfiguracja deploymentu: $msgDep" \ - "${NL}${uni_bullet}Konfiguracja Nightscout: $msgNs" \ - "${NL}${uni_bullet}Kompozycja usług: $msgComp${NL}" - )" \ - "$okTxt" - - if ! [ $? -eq 1 ]; then - - clear_last_time "update_needed" - - if [ "$redeploy" -gt 0 ]; then - docker_compose_down - fi - - if ! [ "$compOnlineVer" == "$compLocalVer" ]; then - ohai "Updating $DOCKER_COMPOSE_FILE" - cp -fr "$UPDATES_DIR/docker-compose.yml" "$DOCKER_COMPOSE_FILE" - fi - - if ! [ "$depEnvLocalVer" == "$depEnvOnlineVer" ]; then - ohai "Updating $ENV_FILE_DEP" - dotenv-tool -pr -o "$ENV_FILE_DEP" -i "$UPDATES_DIR/deployment.env" "$ENV_FILE_DEP" - fi - - if ! [ "$nsEnvLocalVer" == "$nsEnvOnlineVer" ]; then - ohai "Updating $ENV_FILE_NS" - dotenv-tool -pr -o "$ENV_FILE_NS" -i "$UPDATES_DIR/deployment.env" "$ENV_FILE_NS" - fi - - echo "$lastDownload" >"$UPDATES_DIR/updated" - - if ! [ "$instOnlineVer" == "$instLocalVer" ] || ! [ "$lastDownload" == "$updateInstalled" ]; then - ohai "Updating $TOOL_FILE" - cp -fr "$UPDATES_DIR/install.sh" "$TOOL_FILE" - okdlg "Aktualizacja zakończona" "Narzędzie zostanie uruchomione ponownie" - ohai "Restarting tool" - exec "$TOOL_FILE" - fi - fi - fi - fi - fi - fi - -} - -about_dialog() { - LOG_KEY=$(<"$LOG_ENCRYPTION_KEY_FILE") - okdlg "O tym narzędziu..." \ - "$(printf '\U1F9D1') (c) 2023 Dominik Dzienia" \ - "${NL}$(printf '\U1F4E7') dominik.dzienia@gmail.com" \ - "${TL}$(printf '\U1F3DB') To narzędzie jest dystrybuowane na licencji CC BY-NC-ND 4.0" \ - "${NL}htps://creativecommons.org/licenses/by-nc-nd/4.0/deed.pl" \ - "${TL}wersja: $SCRIPT_VERSION ($SCRIPT_BUILD_TIME) $UPDATE_CHANNEL" \ - "${TL}hasło do logów: $LOG_KEY" -} - -prompt_welcome() { - yesnodlg "Witamy" "$uni_start" "$uni_exit" \ - "Ten skrypt zainstaluje Nightscout na bieżącym serwerze mikr.us" \ - "${TL}Jeśli na tym serwerze jest już Nightscout " \ - "${NL}- ten skrypt umożliwia jego aktualizację oraz diagnostykę.${TL}" - exit_on_no_cancel -} -prompt_disclaimer() { - confirmdlg "Ostrzeżenie!" \ - "Zrozumiano!" \ - "Te narzędzie pozwala TOBIE zainstalować WŁASNĄ instancję Nightscout." \ - "${NL}Ty odpowiadasz za ten serwer i ewentualne skutki jego używania." \ - "${NL}Ty nim zarządzasz, to nie jest usługa czy produkt." \ - "${NL}To rozwiązanie \"Zrób to sam\" - SAM za nie odpowiadasz!" \ - "${TL}Autorzy skryptu nie ponoszą odpowiedzialności za skutki jego użycia!" \ - "${NL}Nie dajemy żadnych gwarancji co do jego poprawności czy dostępności!" \ - "${NL}Używasz go na własną odpowiedzialność!" \ - "${NL}Nie opieraj decyzji terapeutycznych na podstawie wskazań tego narzędzia!" \ - "${TL}Twórcy tego narzędzia NIE SĄ administratorami Mikr.us-ów ani Hetznera!" \ - "${NL}W razie problemów z dostępnością serwera najpierw sprawdź status Mikr.us-a!" -} - -instal_now_prompt() { - yesnodlg "Instalować Nightscout?" "$uni_install" "$uni_noenter" \ - "Wykryto konfigurację ale brak uruchomionych usług" \ - "${NL}Czy chcesz zainstalować teraz kontenery Nightscout?" -} - -prompt_mikrus_host() { - if ! [[ "$MIKRUS_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - MIKRUS_HOST=$(hostname) - while :; do - if [[ "$MIKRUS_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - break - else - MIKRUS_NEW_HOST=$(whiptail --title "Podaj identyfikator serwera" --inputbox "\nNie udało się wykryć identyfikatora serwera,\npodaj go poniżej ręcznie.\n\nIdentyfikator składa się z jednej litery i trzech cyfr\n" --cancel-button "Anuluj" 13 65 3>&1 1>&2 2>&3) - exit_on_no_cancel - if [[ "$MIKRUS_NEW_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - MIKRUS_HOST=$MIKRUS_NEW_HOST - break - else - whiptail --title "$uni_excl Nieprawidłowy identyfikator serwera $uni_excl" --yesno "Podany identyfikator serwera ma nieprawidłowy format.\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 - exit_on_no_cancel - fi - fi - done - - ohai "Updating admin config (host)" - dotenv-tool -pmr -i "$ENV_FILE_ADMIN" -- "MIKRUS_HOST=$MIKRUS_HOST" - fi -} - -prompt_mikrus_apikey() { - if ! [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then - freshInstall=$((freshInstall + 1)) - - if [ -f "/klucz_api" ]; then - MIKRUS_APIKEY=$(cat "/klucz_api") - MIKRUS_INFO_HOST=$(curl -s -d "srv=$MIKRUS_HOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .server_id) - - if [[ "$MIKRUS_INFO_HOST" == "$MIKRUS_HOST" ]] || [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - msgcheck "Mikrus OK" - else - MIKRUS_APIKEY="" - fi - fi - - if ! [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then - - whiptail --title "Przygotuj klucz API" --msgbox "Do zarządzania mikrusem [$MIKRUS_HOST] potrzebujemy klucz API.\n\n${uni_bullet}otwórz nową zakładkę w przeglądarce,\n${uni_bullet}wejdź do panelu administracyjnego swojego Mikr.us-a,\n${uni_bullet}otwórz sekcję API, pod adresem:\n\n${uni_bullet_pad}https://mikr.us/panel/?a=api\n\n${uni_bullet}skopiuj do schowka wartość klucza API" 16 70 - exit_on_no_cancel - - while :; do - MIKRUS_APIKEY=$(whiptail --title "Podaj klucz API" --passwordbox "\nWpisz klucz API. Jeśli masz go skopiowanego w schowku,\nkliknij prawym przyciskiem i wybierz z menu:" --cancel-button "Anuluj" 11 65 3>&1 1>&2 2>&3) - exit_on_no_cancel - if [[ "$MIKRUS_APIKEY" =~ [0-9a-fA-F]{40} ]]; then - MIKRUS_INFO_HOST=$(curl -s -d "srv=$MIKRUS_HOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .server_id) - - if [[ "$MIKRUS_INFO_HOST" == "$MIKRUS_HOST" ]] || [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{1,16}[0-9]{3} ]]; then - msgcheck "Mikrus OK" - break - else - whiptail --title "$uni_excl Nieprawidłowy API key $uni_excl" --yesno "Podany API key wydaje się mieć dobry format, ale NIE DZIAŁA!\nMoże to literówka lub podano API KEY z innego Mikr.us-a?.\n\nPotrzebujesz API KEY serwera [$MIKRUS_HOST]\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 - exit_on_no_cancel - fi - else - whiptail --title "$uni_excl Nieprawidłowy API key $uni_excl" --yesno "Podany API key ma nieprawidłowy format.\n\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_exit" 12 70 - exit_on_no_cancel - fi - done - - fi - - ohai "Updating admin config (api key)" - dotenv-tool -pmr -i "$ENV_FILE_ADMIN" -- "MIKRUS_APIKEY=$MIKRUS_APIKEY" - fi -} - -prompt_api_secret() { - API_SECRET=$(dotenv-tool -r get -f "$ENV_FILE_NS" "API_SECRET") - - if ! [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; then - freshInstall=$((freshInstall + 1)) - while :; do - CHOICE=$(whiptail --title "Ustal API SECRET" --menu "\nUstal bezpieczny API_SECRET, tajne główne hasło zabezpieczające dostęp do Twojego Nightscouta\n" 13 70 2 \ - "1)" "Wygeneruj losowo." \ - "2)" "Podaj własny." \ - --ok-button="$uni_select" --cancel-button="$uni_exit" \ - 3>&2 2>&1 1>&3) - exit_on_no_cancel - - case $CHOICE in - "1)") - API_SECRET=$(openssl rand -base64 100 | tr -dc '23456789@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | fold -w 16 | head -n 1) - whiptail --title "Zapisz API SECRET" --msgbox "Zapisz poniższy wygenerowany API SECRET w bezpiecznym miejscu, np.: managerze haseł:\n\n\n $API_SECRET" 12 50 - ;; - "2)") - while :; do - API_SECRET=$(whiptail --title "Podaj API SECRET" --passwordbox "\nWpisz API SECRET do serwera Nightscout:\n${uni_bullet}Upewnij się że masz go zapisanego np.: w managerze haseł\n${uni_bullet}Użyj conajmniej 12 znaków: małych i dużych liter i cyfr\n\n" --cancel-button "Anuluj" 12 75 3>&1 1>&2 2>&3) - - if [ $? -eq 1 ]; then - break - fi - - if [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; then - break - else - whiptail --title "$uni_excl Nieprawidłowy API SECRET $uni_excl" --yesno "Podany API SECRET ma nieprawidłowy format.\nChcesz podać go ponownie?" --yes-button "$uni_reenter" --no-button "$uni_noenter" 10 73 - if [ $? -eq 1 ]; then - API_SECRET='' - break - fi - fi - done - - ;; - esac - - while [[ "$API_SECRET" =~ [a-zA-Z0-9%+=./:=@_]{12,} ]]; do - API_SECRET_CHECK=$(whiptail --title "Podaj ponownie API SECRET" --passwordbox "\nDla sprawdzenia, wpisz ustalony przed chwilą API SECRET\n\n" --cancel-button "Anuluj" 11 65 3>&1 1>&2 2>&3) - if [ $? -eq 1 ]; then - API_SECRET='' - break - fi - if [[ "$API_SECRET" == "$API_SECRET_CHECK" ]]; then - ohai "Updating nightscout config (api secret)" - dotenv-tool -pmr -i "$ENV_FILE_NS" -- "API_SECRET=$API_SECRET" - break 2 - else - whiptail --title "$uni_excl Nieprawidłowe API SECRET $uni_excl" --yesno "Podana wartości API SECRET różni się od poprzedniej!\nChcesz podać ponownie?\n" --yes-button "$uni_reenter" --no-button "$uni_noenter" 9 60 - if [ $? -eq 1 ]; then - API_SECRET='' - break - fi - fi - - done - - done - fi -} - -docker_compose_up() { - process_gauge install_containers install_containers_progress "Uruchamianie Nightscouta" "Proszę czekać, trwa uruchamianie kontenerów..." -} - -docker_compose_update() { - process_gauge update_containers install_containers_progress "Uruchamianie Nightscouta" "Proszę czekać, trwa aktualizacja kontenerów..." -} - -docker_compose_down() { - process_gauge uninstall_containers uninstall_containers_progress "Zatrzymywanie Nightscouta" "Proszę czekać, trwa zatrzymywanie i usuwanie kontenerów..." -} - -domain_setup_manual() { - ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") - whiptail --title "Ustaw domenę" --msgbox "Aby Nightscout był widoczny z internetu ustaw subdomenę:\n\n${uni_bullet}otwórz nową zakładkę w przeglądarce,\n${uni_bullet}wejdź do panelu administracyjnego swojego Mikr.us-a,\n${uni_bullet}otwórz sekcję [Subdomeny], pod adresem:\n\n${uni_bullet_pad} https://mikr.us/panel/?a=domain\n\n${uni_bullet}w pole nazwy wpisz dowolną własną nazwę\n${uni_bullet_pad}(tylko małe litery i cyfry, max. 12 znaków)\n${uni_bullet}w pole numer portu wpisz:\n${uni_bullet_pad}\n $ns_external_port\n\n${uni_bullet}kliknij [Dodaj subdomenę] i poczekaj do kilku minut" 22 75 -} - -domain_setup() { - - local domain=$(get_td_domain) - local domainLen=${#domain} - if ((domainLen > 15)); then - msgcheck "Subdomena jest już skonfigurowana ($domain)" - okdlg "Subdomena już ustawiona" \ - "Wykryto poprzednio skonfigurowaną subdomenę:" \ - "${TL}$domain" \ - "${TL}Strona Nightscout powinna być widoczna z internetu." - return - fi - - ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") - whiptail --title "Ustaw subdomenę" --msgbox "Aby Nightscout był widoczny z internetu ustaw adres - subdomenę:\n\n [wybierz].ns.techdiab.pl\n\nWybrany początek subdomeny powinien:\n${uni_bullet}mieć długość od 4 do 12 znaków\n${uni_bullet}zaczynać się z małej litery,\n${uni_bullet}może składać się z małych liter i cyfr\n${uni_bullet}być unikalny, charakterystyczny i łatwa do zapamiętania" 16 75 - - while :; do - SUBDOMAIN='' - while :; do - SUBDOMAIN=$(whiptail --title "Podaj początek subdomeny" --inputbox "\n(4-12 znaków, tylko: małe litery i cyfry)\n\n" --cancel-button "Anuluj" 12 60 3>&1 1>&2 2>&3) - - if [ $? -eq 1 ]; then - break - fi - - if [[ "$SUBDOMAIN" =~ ^[a-z][a-z0-9]{3,11}$ ]]; then - - if printf "%s" "$SUBDOMAIN" | grep -f "$PROFANITY_DB_FILE" >>"$LOGTO" 2>&1; then - okdlg "$uni_excl Nieprawidłowa subdomena $uni_excl" \ - "Podana wartość:" \ - "${NL}$SUBDOMAIN" \ - "${TL}jest zajęta, zarezerwowana lub niedopuszczalna." \ - "${TL}Wymyśl coś innego" - SUBDOMAIN='' - continue - fi - - if printf "%s" "$SUBDOMAIN" | grep -xf "$RESERVED_DB_FILE" >>"$LOGTO" 2>&1; then - okdlg "$uni_excl Nieprawidłowa subdomena $uni_excl" \ - "Podana wartość:" \ - "${NL}$SUBDOMAIN" \ - "${TL}jest zajęta lub zarezerwowana." \ - "${TL}Wymyśl coś innego" - SUBDOMAIN='' - continue - fi - - break - - else - okdlg "$uni_excl Nieprawidłowy początek subdomeny $uni_excl" \ - "Podany początek subdomeny:" \ - "${NL}$SUBDOMAIN" \ - "${TL}ma nieprawidłowy format. Wymyśl coś innego" - if [ $? -eq 1 ]; then - SUBDOMAIN='' - continue - fi - fi - - done - - if [ "$SUBDOMAIN" == "" ]; then - domain_setup_manual - break - fi - - local MHOST=$(hostname) - local APISEC=$(dotenv-tool -r get -f "$ENV_FILE_ADMIN" "MIKRUS_APIKEY") - - ohai "Rejestrowanie subdomeny $SUBDOMAIN.ns.techdiab.pl" - local REGSTATUS=$(curl -sd "srv=$MHOST&key=$APISEC&domain=$SUBDOMAIN.ns.techdiab.pl" https://api.mikr.us/domain) - local STATOK=$(echo "$REGSTATUS" | jq -r ".status") - local STATERR=$(echo "$REGSTATUS" | jq -r ".error") - - if ! [ "$STATOK" == "null" ]; then - msgcheck "Subdomena ustawiona poprawnie ($STATOK)" - okdlg "Subdomena ustawiona" \ - "Ustawiono subdomenę:\n\n$SUBDOMAIN.ns.techdiab.pl\n($STATOK)\n\nZa kilka minut strona będzie widoczna z internetu." - break - else - msgerr "Nie udało się ustawić subdomeny ($STATERR)" - whiptail --title "$uni_excl Błąd rezerwacji domeny $uni_excl" --yesno "Nie udało się zarezerwować subdomeny:\n $STATERR\n\nChcesz podać inną subdomenę?" --yes-button "$uni_reenter" --no-button "$uni_noenter" 10 73 - if [ $? -eq 1 ]; then - SUBDOMAIN='' - domain_setup_manual - break - fi - fi - done - -} +. "$DIR/logic_setup_checks.sh" +. "$DIR/logic_patch.sh" +. "$DIR/logic_docker.sh" -admin_panel_promo() { - whiptail --title "Panel zarządzania Mikr.us-em" --msgbox "$(center_multiline 70 \ - "Ta instalacja Nightscout dodaje dodatkowy panel administracyjny" \ - "${NL}do zarządzania serwerem i konfiguracją - online." \ - "${TL}Znajdziesz go klikając na ikonkę serwera w menu strony Nightscout" \ - "${NL}lub dodając /mikrus na końcu swojego adresu Nightscout")" \ - 12 75 -} +. "$DIR/utils_app.sh" -get_watchdog_age_string() { - local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +. "$DIR/logic_watchdog.sh" +. "$DIR/logic_cleanup.sh" +. "$DIR/logic_config.sh" +. "$DIR/logic_update.sh" +. "$DIR/logic_app.sh" - if [[ -f $WATCHDOG_TIME_FILE ]]; then - last_time=$(cat "$WATCHDOG_TIME_FILE") - local status_ago=$(dateutils.ddiff "$last_time" "$curr_time" -f '%Mmin. %Ssek.') - echo "$last_time ($status_ago temu)" - else - echo "jescze nie uruchomiony" - fi -} +. "$DIR/app_other_dialogs.sh" +. "$DIR/app_setup_prompts.sh" +. "$DIR/app_setup.sh" -get_watchdog_status_code() { - local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local status="unknown" +. "$DIR/utils_reminders.sh" +. "$DIR/commandline.sh" - if [[ -f $WATCHDOG_TIME_FILE ]]; then - last_time=$(cat "$WATCHDOG_TIME_FILE") - fi +. "$DIR/app_watchdog.sh" +. "$DIR/app_cleanup.sh" +. "$DIR/app_config.sh" +. "$DIR/app_update.sh" +. "$DIR/app_main.sh" - if [[ -f $WATCHDOG_STATUS_FILE ]]; then - status=$(cat "$WATCHDOG_STATUS_FILE") - fi - - local status_ago=$(dateutils.ddiff "$curr_time" "$last_time" -f '%S') - - if [ "$status_ago" -gt 900 ]; then - status="unknown" - fi - - echo "$status" -} - -get_watchdog_status_code_live() { - local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - local status="unknown" - - if [[ -f $WATCHDOG_TIME_FILE ]]; then - last_time=$(cat "$WATCHDOG_TIME_FILE") - fi - - if [[ -f $WATCHDOG_STATUS_FILE ]]; then - status=$(cat "$WATCHDOG_STATUS_FILE") - fi - - local status_ago=$(dateutils.ddiff "$curr_time" "$last_time" -f '%S') - - if [ "$status_ago" -gt 900 ]; then - status="unknown" - fi - - local NS_STATUS=$(get_container_status_code 'ns-server') - local DB_STATUS=$(get_container_status_code 'ns-database') - local COMBINED_STATUS="$NS_STATUS $DB_STATUS" - - if [ "$COMBINED_STATUS" = "running running" ]; then - - status="detection_failed" - - local domain=$cachedMenuDomain - local cachedDomainLen=${#cachedMenuDomain} - if ((cachedDomainLen < 16)); then - domain=$(get_td_domain) - fi - - local domainLen=${#domain} - if ((domainLen > 15)); then - cachedMenuDomain=$domain - local html=$(curl -Lks "$domain") - - if [[ "$html" =~ github.com/nightscout/cgm-remote-monitor ]]; then - status="ok" - fi - - if [[ "$html" =~ 'MongoDB connection failed' ]]; then - status="crashed" - fi - - regex3='poszło nie tak' - if [[ "$html" =~ $regex3 ]]; then - status="awaiting" - fi - - else - status="domain_failed" - fi - - else - if [ "$NS_STATUS" = "restarting" ] || [ "$DB_STATUS" = "restarting" ]; then - status="awaiting" - else - local logSample=$(timeout -k 15 10 docker logs ns-server --tail "10" 2>&1) - local regexSample='Cannot connect to the Docker daemon' - if [[ "$logSample" =~ $regexSample ]]; then - status="docker_down" - else - status="not_running" - fi - fi - fi - - echo "$status" -} - -get_watchdog_status() { - local status="$1" - case "$status" in - "ok") - echo "$2" - ;; - "restart") - printf "\U1F680 wymuszono restart NS" - ;; - "awaiting") - printf "\U23F3 uruchamia się" - ;; - "restart_failed") - printf "\U1F680 restart NS to za mało" - ;; - "full_restart") - printf "\U1F680 restart NS i DB" - ;; - "unknown") - printf "\U1F4A4 brak statusu" - ;; - "not_running") - printf "\U1F534 serwer nie działa" - ;; - "detection_failed") - printf "\U2753 nieznany stan" - ;; - "domain_failed") - printf "\U2753 problem z domeną" - ;; - "crashed") - printf "\U1F4A5 awaria NS" - ;; - "docker_down") - printf "\U1F4A5 awaria Dockera" - ;; - esac - -} - -show_watchdog_logs() { - local col=$((COLUMNS - 10)) - local rws=$((LINES - 3)) - if [ "$col" -gt 120 ]; then - col=160 - fi - if [ "$col" -lt 60 ]; then - col=60 - fi - if [ "$rws" -lt 12 ]; then - rws=12 - fi - - local tmpfile=$(mktemp) - { - echo "Ostatnie uruchomienie watchdoga:" - get_watchdog_age_string - echo "-------------------------------------------------------" - - if [[ -f $WATCHDOG_LOG_FILE ]]; then - echo "Statusy ostatnich przebiegów watchdoga:" - tail -5 "$WATCHDOG_LOG_FILE" - else - echo "Brak logów z ostatnich przebiegów watchdoga" - fi - echo "-------------------------------------------------------" - - if [[ -f $WATCHDOG_CRON_LOG ]]; then - echo "Log ostatniego przebiegu watchdoga:" - cat "$WATCHDOG_CRON_LOG" - fi - } >"$tmpfile" - - whiptail --title "Logi Watchdoga" --scrolltext --textbox "$tmpfile" "$rws" "$col" - rm "$tmpfile" -} - -get_events_status() { - local count="$(event_count)" - if ((count == 0)); then - printf "\U2728 brak zdarzeń" - elif ((count == 1)); then - printf "\U1F4C5 jedno zdarzenie" - elif (((count % 10) > 1)) && (((count % 10) < 5)); then - printf "\U1F4C5 %s zdarzenia" "$count" - else - printf "\U1F4C5 %s zdarzeń" "$count" - fi -} - -get_container_status() { - local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') - if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then - local status=$(docker inspect "$ID" | jq -r ".[0].State.Status") - case "$status" in - "running") - printf "\U1F7E2 działa" - ;; - "restarting") - printf "\U1F7E3 restart" - ;; - "created") - printf "\U26AA utworzono" - ;; - "exited") - printf "\U1F534 wyłączono" - ;; - "paused") - printf "\U1F7E1 zapauzowano" - ;; - "dead") - printf "\U1F480 zablokowany" - ;; - esac - - else - printf '\U2753 nie odnaleziono' - fi -} - -get_container_status_code() { - local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') - if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then - local status=$(docker inspect "$ID" | jq -r ".[0].State.Status") - echo "$status" - else - echo "unknown" - fi -} - -show_logs() { - local col=$((COLUMNS - 10)) - local rws=$((LINES - 4)) - if [ "$col" -gt 120 ]; then - col=160 - fi - if [ "$col" -lt 60 ]; then - col=60 - fi - if [ "$rws" -lt 12 ]; then - rws=12 - fi - - local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') - if [ -n "$ID" ]; then - local tmpfile=$(mktemp) - docker logs "$ID" 2>&1 | tail $((rws * -6)) | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g' >"$tmpfile" - whiptail --title "Logi $2" --scrolltext --textbox "$tmpfile" "$rws" "$col" - rm "$tmpfile" - fi -} - -status_menu() { - while :; do - local CHOICE=$(whiptail --title "Status kontenerów" --menu "\n Aktualizacja: kontenery na żywo, watchdog co 5 minut\n\n Wybierz pozycję aby zobaczyć logi:\n" 18 60 6 \ - "1)" " Nightscout: $(get_container_status 'ns-server')" \ - "2)" " Baza danych: $(get_container_status 'ns-database')" \ - "3)" " Backup: $(get_container_status 'ns-backup')" \ - "4)" " Watchdog: $(get_watchdog_status "$(get_watchdog_status_code)" "$uni_watchdog_ok")" \ - "5)" " Zdarzenia: $(get_events_status)" \ - "M)" "Powrót do menu" \ - --ok-button="Zobacz logi" --cancel-button="$uni_back" \ - 3>&2 2>&1 1>&3) - - case $CHOICE in - "1)") - show_logs 'ns-server' 'Nightscouta' - ;; - "2)") - show_logs 'ns-database' 'bazy danych' - ;; - "3)") - show_logs 'ns-backup' 'usługi kopii zapasowych' - ;; - "4)") - show_watchdog_logs - ;; - "5)") - okdlg "Zdarzenia" \ - "$(pad_multiline "$(event_list)")" - ;; - "M)") - break - ;; - "") - break - ;; - esac - done -} - -version_menu() { - - local tags=$(wget -q -O - "https://hub.docker.com/v2/namespaces/nightscout/repositories/cgm-remote-monitor/tags?page_size=100" | jq -r ".results[].name" | sed "/dev_[a-f0-9]*/d" | sort --version-sort -u -r | head -n 8) - - while :; do - - local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") - local versions=() - - while read -r line; do - if [ "$line" == "$ns_tag" ]; then - continue - fi - - label=" - na sztywno $line " - - if [ "$line" == "latest_dev" ]; then - label=" - najnowsza wersja rozwojowa " - fi - - if [ "$line" == "latest" ]; then - label=" - aktualna wersja stabilna " - fi - - versions+=("$line") - versions+=("$label") - done <<<"$tags" - - versions+=("M)") - versions+=(" Powrót do poprzedniego menu") - - local CHOICE=$(whiptail --title "Wersja Nightscout" --menu "\nZmień wersję kontenera Nightscout z: $ns_tag na:\n\n" 20 60 10 \ - "${versions[@]}" \ - --ok-button="Zmień" --cancel-button="$uni_back" \ - 3>&2 2>&1 1>&3) - - if [ "$CHOICE" == "M)" ]; then - break - fi - - if [ "$CHOICE" == "" ]; then - break - fi - - if [ "$CHOICE" == "$ns_tag" ]; then - whiptail --title "Ta sama wersja!" --msgbox "Wybrano bieżącą wersję - brak zmiany" 7 50 - else - - whiptail --title "Zmienić wersję Nightscout?" --yesno --defaultno "Czy na pewno chcesz zmienić wersję z: $ns_tag na: $CHOICE?\n\n${uni_bullet}dane i konfiguracja NIE SĄ usuwane\n${uni_bullet}wersję można łatwo zmienić ponownie\n${uni_bullet}dane w bazie danych mogą ulec zmianie i NIE BYĆ kompatybilne" --yes-button "$uni_confirm_ch" --no-button "$uni_resign" 13 73 - if ! [ $? -eq 1 ]; then - event_mark "change_ns_version" - docker_compose_down - ohai "Changing Nightscout container tag from: $ns_tag to: $CHOICE" - dotenv-tool -pmr -i "$ENV_FILE_DEP" -- "NS_NIGHTSCOUT_TAG=$CHOICE" - docker_compose_update - whiptail --title "Zmieniono wersję Nightscout" --msgbox "$(center_multiline 65 \ - "Zmieniono wersję Nightscout na: $CHOICE" \ - "${TL}Sprawdź czy Nightscout działa poprawnie, w razie problemów:" \ - "${NL}${uni_bullet}aktualizuj kontenery" \ - "${NL}${uni_bullet}spróbuj wyczyścić bazę danych" \ - "${NL}${uni_bullet}wróć do poprzedniej wersji ($ns_tag)")" \ - 13 70 - break - fi - - fi - - done -} - -do_cleanup_sys() { - ohai "Sprzątanie dziennik systemowego..." - event_mark "cleanup" - journalctl --vacuum-size=50M >>"$LOGTO" 2>&1 - ohai "Czyszczenie systemu apt..." - msgnote "Ta operacja może TROCHĘ potrwać (od kilku do kilkudziesięciu minut...)" - apt-get -y autoremove >>"$LOGTO" 2>&1 && apt-get -y clean >>"$LOGTO" 2>&1 - msgcheck "Czyszczenie dziennika i apt zakończono" -} - -do_cleanup_docker() { - ohai "Usuwanie nieużywanych obrazów Dockera..." - event_mark "cleanup" - msgnote "Ta operacja może TROCHĘ potrwać (do kilku minut...)" - docker image prune -af >>"$LOGTO" 2>&1 - msgcheck "Czyszczenie Dockera zakończono" -} - -do_cleanup_db() { - ohai "Usuwanie kopii zapasowych bazy danych..." - event_mark "cleanup" - find /srv/nightscout/data/dbbackup ! -type d -delete - msgcheck "Czyszczenie kopii zapasowych zakończono" -} - -do_cleanup_container_logs() { - ohai "Zatrzymywanie kontenerów..." - event_mark "cleanup" - docker stop 'ns-server' - docker stop 'ns-database' - docker stop 'ns-backup' - ohai "Usuwanie logów kontenerów..." - truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-server')" - truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-database')" - truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-backup')" - ohai "Ponowne uruchamianie kontenerów..." - docker start 'ns-server' - docker start 'ns-database' - docker start 'ns-backup' - msgok "Logi usunięte" -} - -do_cleanup_diagnostics() { - ohai "Sprzątanie diagnostyki" - rm -f "$SUPPORT_LOG" - rm -f "$SUPPORT_LOG.gz" - rm -f "$SUPPORT_LOG.gz.asc" -} - -do_cleanup_app_state() { - ohai "Sprzątanie stanu aplikacji" - rm -f "$UPDATE_CHANNEL_FILE" - rm -f "$EVENTS_DB" -} - -do_cleanup_app_logs() { - ohai "Sprzątanie logów aplikacji" - rm -f "$WATCHDOG_STATUS_FILE" - rm -f "$WATCHDOG_TIME_FILE" - rm -f "$WATCHDOG_LOG_FILE" - rm -f "$WATCHDOG_FAILURES_FILE" - rm -f "$WATCHDOG_CRON_LOG" -} - -prompt_cleanup_container_logs() { - yesnodlg "Usunąć logi kontenerów?" "$uni_delete" "$uni_leave_logs" \ - "Czy chcesz usunąć logi kontenerów nightscout i bazy?" \ - "${TL}Jeśli Twój serwer działa poprawnie," \ - "${NL}- możesz spokojnie usunąć logi." \ - "${TL}Jeśli masz problem z serwerem - zostaw logi!" \ - "${NL}- logi mogą być niezbędne do diagnostyki" \ - "${TL}(ta operacja uruchomi ponownie kontenery)" -} - -cleanup_menu() { - - while :; do - - local spaceInfo=$(get_space_info) - local remainingTxt=$(echo "$spaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) - local totalTxt=$(echo "$spaceInfo" | awk '{print $2}' | numfmt --to iec-i --suffix=B) - local percTxt=$(echo "$spaceInfo" | awk '{print $4}') - local fixedPerc=${percTxt/[%]/=} - - local nowB=$(echo "$spaceInfo" | awk '{print $3}') - local lastTimeB=$(echo "$lastTimeSpaceInfo" | awk '{print $3}') - local savedB=$((nowB - lastTimeB)) - local savedTxt=$(echo "$savedB" | numfmt --to iec-i --suffix=B) - - if ((savedB < 1)); then - savedTxt="---" - fi - - local statusTitle="\n$(center_multiline 45 "$( - pad_multiline \ - " Dostępne: ${remainingTxt}" \ - "\n Zwolniono: ${savedTxt}" \ - "\n Zajęte: ${fixedPerc} (z ${totalTxt})" - )")\n" - - local CHOICE=$(whiptail --title "Sprzątanie" --menu \ - "${statusTitle/=/%}" \ - 17 50 6 \ - "A)" "Posprzątaj wszystko" \ - "S)" "Posprzątaj zasoby systemowe" \ - "D)" "Usuń nieużywane obrazy Dockera" \ - "B)" "Usuń kopie zapasowe bazy danych" \ - "L)" "Usuń logi kontenerów" \ - "M)" "Powrót do menu" \ - --ok-button="Wybierz" --cancel-button="$uni_back" \ - 3>&2 2>&1 1>&3) - - case $CHOICE in - "A)") - noyesdlg "Posprzątać wszystko?" "$uni_confirm_del" "$uni_resign" \ - "Czy chcesz posprzątać i usunąć:" \ - "$(pad_multiline \ - "${TL}${uni_bullet}nieużywane pliki apt i dziennika" \ - "${NL}${uni_bullet}nieużywane obrazy Dockera" \ - "${NL}${uni_bullet}kopie zapasowe bazy danych" \ - "${NL}${uni_bullet}opcjonalnie - logi Nightscouta i bazy")${NL}" \ - "${TL}(☕ to może potrwać nawet kilkadziesiąt minut)" - if ! [ $? -eq 1 ]; then - prompt_cleanup_container_logs - if ! [ $? -eq 1 ]; then - do_cleanup_container_logs - do_cleanup_sys - do_cleanup_docker - do_cleanup_db - else - do_cleanup_sys - do_cleanup_docker - do_cleanup_db - fi - fi - ;; - "S)") - noyesdlg "Posprzątać zasoby systemowe?" "$uni_confirm_del" "$uni_resign" \ - "Czy chcesz usunąć nieużywane pakiety apt${NL}i poprzątać dziennik systemowy?" \ - "${TL}(☕ to może potrwać nawet kilkadziesiąt minut)" - if ! [ $? -eq 1 ]; then - do_cleanup_sys - fi - ;; - "D)") - noyesdlg "Posprzątać obrazy Dockera?" "$uni_confirm_del" "$uni_resign" \ - "Czy chcesz usunąć nieużywane obrazy Dockera?" \ - "${TL}(☕ to może potrwać kilka minut)" - if ! [ $? -eq 1 ]; then - do_cleanup_docker - fi - ;; - "B)") - noyesdlg "Usunąć kopie zapasowe bazy danych?" "$uni_confirm_del" "$uni_resign" \ - "Czy chcesz usunąć kopie zapasowe bazy danych?" \ - "${NL}(na razie i tak nie ma automatycznego mechanizmu ich wykorzystania)" - if ! [ $? -eq 1 ]; then - do_cleanup_db - fi - ;; - "L)") - prompt_cleanup_container_logs - if ! [ $? -eq 1 ]; then - do_cleanup_container_logs - fi - ;; - "M)") - break - ;; - "") - break - ;; - esac - done -} - -update_menu() { - while :; do - local CHOICE=$(whiptail --title "Aktualizuj" --menu "\n" 11 40 4 \ - "N)" "Aktualizuj to narzędzie" \ - "S)" "Aktualizuj system" \ - "K)" "Aktualizuj kontenery" \ - "M)" "Powrót do menu" \ - --ok-button="$uni_select" --cancel-button="$uni_back" \ - 3>&2 2>&1 1>&3) - - case $CHOICE in - "S)") - ohai "Updating package list" - event_mark "update_system" - dialog --title " Aktualizacja systemu " --infobox "\n Pobieranie listy pakietów\n ..... Proszę czekać ....." 6 33 - apt-get -yq update >>"$LOGTO" 2>&1 - ohai "Upgrading system" - dialog --title " Aktualizacja systemu " --infobox "\n Instalowanie pakietów\n ... Proszę czekać ..." 6 33 - apt-get -yq upgrade >>"$LOGTO" 2>&1 - ;; - "N)") - event_mark "update_tool" - update_if_needed "Wszystkie pliki narzędzia są aktualne" - ;; - "K)") - event_mark "update_containers" - docker_compose_down - docker_compose_update - ;; - "M)") - break - ;; - "") - break - ;; - esac - done -} - -uninstall_menu() { - while :; do - local extraMenu=() - extraMenu+=("A)" "Ustaw adres strony (subdomenę)") - local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") - local CHOICE=$(whiptail --title "Zmień lub odinstaluj Nightscout" --menu "\n" 17 70 8 \ - "${extraMenu[@]}" \ - "W)" "Zmień wersję Nightscouta (bieżąca: $ns_tag)" \ - "E)" "Edytuj ustawienia (zmienne środowiskowe)" \ - "K)" "Usuń kontenery" \ - "B)" "Wyczyść bazę danych" \ - "D)" "Usuń kontenery, dane i konfigurację" \ - "U)" "Usuń wszystko - odinstaluj" \ - "M)" "Powrót do menu" \ - --ok-button="$uni_select" --cancel-button="$uni_back" \ - 3>&2 2>&1 1>&3) - - case $CHOICE in - "A)") - domain_setup - ;; - "W)") - version_menu - ;; - "E)") - - if ! [[ "$0" =~ .*"/usr/bin/nightscout-tool" ]]; then - okdlg "Opcja niedostępna" \ - "Edytor ustawień dostępny po uruchomieniu narzędzia komendą:" \ - "${TL}nightscout-tool" - else - yesnodlg "Edycja ustawień Nightscout" "$uni_confirm_ed" "$uni_resign" \ - "Za chwilę otworzę plik konfiguracji Nightscout w edytorze NANO" \ - "$(pad_multiline \ - "${TL}Wskazówki co do obsługi edytora:" \ - "${NL}${uni_bullet}Aby ZAPISAĆ zmiany naciśnij Ctrl+O" \ - "${NL}${uni_bullet}Aby ZAKOŃCZYĆ edycję naciśnij Ctrl+X")" \ - "${TL}${uni_warn} Edycja spowoduje też restart i aktualizację kontenerów ${uni_warn}" - - if ! [ $? -eq 1 ]; then - event_mark "edit_env_manual" - nano "$ENV_FILE_NS" - docker_compose_down - docker_compose_update - fi - fi - ;; - "K)") - noyesdlg "Usunąć kontenery?" "$uni_confirm_del" "$uni_resign" \ - "Czy na pewno chcesz usunąć kontenery powiązane z Nightscout?" \ - "$(pad_multiline \ - "${TL}${uni_bullet}dane i konfiguracja NIE SĄ usuwane" \ - "${NL}${uni_bullet}kontenery można łatwo odzyskać (opcja Aktualizuj kontenery)")" - - if ! [ $? -eq 1 ]; then - event_mark "remove_containers" - docker_compose_down - fi - ;; - "B)") - noyesdlg "Usunąć dane z bazy danych?" "$uni_confirm_del" "$uni_resign" \ - "Czy na pewno chcesz usunąć dane z bazy danych?" \ - "$(pad_multiline \ - "${TL}${uni_bullet}konfiguracja serwera NIE ZOSTANIE usunięta" \ - "${NL}${uni_bullet}usunięte zostaną wszystkie dane użytkownika" \ - "${NL}${uni_bullet_pad} (m.in. historia glikemii, wpisy, notatki, pomiary, profile)" \ - "${NL}${uni_bullet}kontenery zostaną zatrzymane i uruchomione ponownie (zaktualizowane)")" - - if ! [ $? -eq 1 ]; then - docker_compose_down - dialog --title " Czyszczenie bazy danych " --infobox "\n Usuwanie plików bazy\n ... Proszę czekać ..." 6 32 - rm -r "${MONGO_DB_DIR:?}/data" - event_mark "remove_db_data" - docker_compose_update - fi - ;; - "D)") - noyesdlg "Usunąć wszystkie dane?" "$uni_confirm_del" "$uni_resign" \ - "Czy na pewno chcesz usunąć wszystkie dane i konfigurację?" \ - "$(pad_multiline \ - "${TL}${uni_bullet}konfigurację panelu, ustawienia Nightscout" \ - "${NL}${uni_bullet}wszystkie dane użytkownika" \ - "${NL}${uni_bullet_pad}(m.in. glikemia, wpisy, notatki, pomiary, profile)" \ - "${NL}${uni_bullet}kontenery zostaną zatrzymane")" - - if ! [ $? -eq 1 ]; then - docker_compose_down - dialog --title " Czyszczenie bazy danych" --infobox "\n Usuwanie plików bazy\n ... Proszę czekać ..." 6 32 - rm -r "${MONGO_DB_DIR:?}/data" - event_mark "remove_all_data" - dialog --title " Czyszczenie konfiguracji" --infobox "\n Usuwanie konfiguracji\n ... Proszę czekać ..." 6 32 - rm -r "${CONFIG_ROOT_DIR:?}" - do_cleanup_diagnostics - do_cleanup_app_logs - - okdlg "Usunięto dane użytkownika" \ - "Usunęto dane użytkwnika i konfigurację." \ - "${TL}Aby zainstalować Nightscout od zera:" \ - "${NL}uruchom ponownie skrypt i podaj konfigurację" - - exit 0 - fi - ;; - "U)") - noyesdlg "Odinstalować?" "$uni_confirm_del" "$uni_resign" \ - "Czy na pewno chcesz usunąć wszystko?" \ - "$(pad_multiline \ - "${TL}${uni_bullet}konfigurację panelu, ustawienia Nightscout" \ - "${NL}${uni_bullet}wszystkie dane użytkownika (glikemia, status, profile)" \ - "${NL}${uni_bullet}kontenery, skrypt nightscout-tool")" \ - "${TL}NIE ZOSTANĄ USUNIĘTE/ODINSTALOWANE:" \ - "$(pad_multiline \ - "${TL}${uni_bullet}użytkownik mongo db, firewall, doinstalowane pakiety" \ - "${NL}${uni_bullet}kopie zapasowe bazy danych")" - - if ! [ $? -eq 1 ]; then - docker_compose_down - dialog --title " Odinstalowanie" --infobox "\n Usuwanie plików\n ... Proszę czekać ..." 6 32 - uninstall_cron - rm -r "${MONGO_DB_DIR:?}/data" - rm -r "${CONFIG_ROOT_DIR:?}" - rm "$TOOL_LINK" - rm -r "${NIGHTSCOUT_ROOT_DIR:?}/tools" - rm -r "${NIGHTSCOUT_ROOT_DIR:?}/updates" - do_cleanup_diagnostics - do_cleanup_app_logs - do_cleanup_app_state - event_mark "uninstall" - - okdlg "Odinstalowano" \ - "Odinstalowano Nightscout z Mikr.us-a" \ - "${TL}Aby ponownie zainstalować, postępuj według instrukcji na stronie:" \ - "${NL}https://t1d.dzienia.pl/nightscout_mikrus_tutorial" \ - "${TL}Dziękujemy i do zobaczenia!" - - exit 0 - fi - ;; - "M)") - break - ;; - "") - break - ;; - esac - done -} - -get_td_domain() { - local MHOST=$(hostname) - if ! [[ "$MHOST" =~ [a-zA-Z]{2,16}[0-9]{3} ]]; then - MIKRUS_APIKEY=$(cat "/klucz_api") - MIKRUS_INFO_HOST=$(curl -s -d "srv=$MHOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .imie_id) - if [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{2,16}[0-9]{3} ]]; then - MHOST="$MIKRUS_INFO_HOST" - fi - fi - local APIKEY=$(dotenv-tool -r get -f "$ENV_FILE_ADMIN" "MIKRUS_APIKEY") - curl -sd "srv=$MHOST&key=$APIKEY" https://api.mikr.us/domain | jq -r ".[].name" | grep ".ns.techdiab.pl" | head -n 1 -} - -get_domain_status() { - local domain=$(get_td_domain) - local domainLen=${#domain} - if ((domainLen > 15)); then - printf "\U1F7E2 %s" "$domain" - else - printf "\U26AA nie zarejestrowano" - fi -} - -gather_diagnostics() { - - local maxNsLogs=$1 - local maxDbLogs=$2 - local curr_time=$3 - - diagnosticsSizeOk=0 - - do_cleanup_diagnostics - - ohai "Zbieranie diagnostyki" - - local domain=$(get_td_domain) - local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") - local mikrus_h=$(hostname) - local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") - - local LOG_DIVIDER="=======================================================" - - { - echo "Dane diagnostyczne zebrane $curr_time" - echo " serwer : $mikrus_h" - echo " domena : $domain" - echo " wersja nightscout : $ns_tag" - echo " wersja nightscout-tool : $SCRIPT_VERSION ($SCRIPT_BUILD_TIME) $UPDATE_CHANNEL" - echo " build : ${updateInstalled}" - } >"$SUPPORT_LOG" - - ohai "Zbieranie statusu usług" - - { - echo "$LOG_DIVIDER" - echo " Statusy usług" - echo "$LOG_DIVIDER" - echo " Nightscout: $(get_container_status 'ns-server')" - echo " Baza danych: $(get_container_status 'ns-database')" - echo " Backup: $(get_container_status 'ns-backup')" - echo " Watchdog: $(get_watchdog_status "$(get_watchdog_status_code)" "$uni_watchdog_ok")" - } >>"$SUPPORT_LOG" - - local spaceInfo=$(get_space_info) - local remainingTxt=$(echo "$spaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) - local totalTxt=$(echo "$spaceInfo" | awk '{print $2}' | numfmt --to iec-i --suffix=B) - local percTxt=$(echo "$spaceInfo" | awk '{print $4}') - - { - echo "$LOG_DIVIDER" - echo " Miejsce na dysku" - echo "$LOG_DIVIDER" - echo " Dostępne: ${remainingTxt}" - echo " Zajęte: ${percTxt} (z ${totalTxt})" - } >>"$SUPPORT_LOG" - - ohai "Zbieranie zdarzeń" - { - echo "$LOG_DIVIDER" - echo " Zdarzenia" - echo "$LOG_DIVIDER" - event_list - } >>"$SUPPORT_LOG" - - ohai "Zbieranie logów watchdoga" - - if [[ -f $WATCHDOG_LOG_FILE ]]; then - { - echo "$LOG_DIVIDER" - echo " Watchdog log" - echo "$LOG_DIVIDER" - timeout -k 15 10 tail -n 200 "$WATCHDOG_LOG_FILE" - } >>"$SUPPORT_LOG" - fi - - if [[ -f $WATCHDOG_FAILURES_FILE ]]; then - { - echo "$LOG_DIVIDER" - echo " Watchdog failures log" - echo "$LOG_DIVIDER" - timeout -k 15 10 tail -n 200 "$WATCHDOG_FAILURES_FILE" - } >>"$SUPPORT_LOG" - fi - - ohai "Zbieranie logów usług" - - { - echo "$LOG_DIVIDER" - echo " Nightscout log" - echo "$LOG_DIVIDER" - timeout -k 15 10 docker logs ns-server --tail "$maxNsLogs" >>"$SUPPORT_LOG" 2>&1 - echo "$LOG_DIVIDER" - echo " MongoDB database log" - echo "$LOG_DIVIDER" - timeout -k 15 10 docker logs ns-database --tail "$maxDbLogs" >>"$SUPPORT_LOG" 2>&1 - } >>"$SUPPORT_LOG" - - ohai "Kompresowanie i szyfrowanie raportu" - - gzip -9 "$SUPPORT_LOG" - - local logkey=$(<"$LOG_ENCRYPTION_KEY_FILE") - - gpg --passphrase "$logkey" --batch --quiet --yes -a -c "$SUPPORT_LOG.gz" -} - -retry_diagnostics() { - local maxNsLogs=$1 - local maxDbLogs=$2 - local curr_time=$3 - - if ((diagnosticsSizeOk == 0)); then - - ohai "Sprawdzanie rozmiaru raportu" - - local logSize=$(stat --printf="%s" "$SUPPORT_LOG.gz.asc") - local allowedTxt=$(echo "18000" | numfmt --to si --suffix=B) - local currentTxt=$(echo "$logSize" | numfmt --to si --suffix=B) - - if ((logSize > 18000)); then - msgerr "Zebrana diagnostyka jest zbyt duża do wysłania (${currentTxt})" - ohai "Spróbuję zebrać mniej danych aby zmieścić się w limicie (${allowedTxt})" - gather_diagnostics "$maxNsLogs" "$maxDbLogs" "$curr_time" - else - diagnosticsSizeOk=1 - msgok "Raport ma rozmiar ${currentTxt} i mieści się w limicie ${allowedTxt} dla usługi pusher-a" - fi - fi -} - -send_diagnostics() { - - setup_security - - LOG_KEY=$(<"$LOG_ENCRYPTION_KEY_FILE") - - yesnodlg "Wysyłać diagnostykę?" \ - "$uni_send" "$uni_resign" \ - "Czy chcesz zgromadzić i wysłać sobie mailem dane diagnostyczne?" \ - "\n$( - pad_multiline \ - "\n${uni_bullet}diagnostyka zawiera logi i informacje o serwerze i usługach" \ - "\n${uni_bullet}wysyłka na e-mail na który zamówiono serwer Mikr.us" \ - "\n${uni_bullet}dane będą skompresowane i zaszyfrowane" \ - "\n${uni_bullet}maila prześlij dalej do zaufanej osoby wspierającej" \ - "\n${uni_bullet_pad}(z którą to wcześniej zaplanowano i uzgodniono!!!)" \ - "\n${uni_bullet}hasło przekaż INNĄ DROGĄ (komunikatorem, SMSem, osobiście)" \ - "\n\n${uni_bullet_pad}Hasło do logów: $LOG_KEY" - )" - - if ! [ $? -eq 1 ]; then - - local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - - gather_diagnostics 500 100 "$curr_time" - retry_diagnostics 200 50 "$curr_time" - retry_diagnostics 100 50 "$curr_time" - retry_diagnostics 50 50 "$curr_time" - retry_diagnostics 50 20 "$curr_time" - - ohai "Wysyłanie maila" - - local sentStatus=$({ - echo "Ta wiadomość zawiera poufne dane diagnostyczne Twojego serwera Nightscout." - echo "Mogą one pomóc Tobie lub zaufanej osobie w identyfikacji problemu." - echo " " - echo "Prześlij ten mail dalej do zaufanej osoby, umówionej na udzielenie wsparcia." - echo "Przekaż tej osobie w bezpieczny sposób hasło szyfrowania" - echo " (w narzędziu nightscout-tool można je znaleźć w pozycji 'O tym narzędziu...')." - echo "Do przekazania hasła użyj INNEJ metody (komunikator, SMS, osobiście...)." - echo "Nie przesyłaj tej wiadomości do administratorów grupy lub serwera bez wcześniejszego uzgodnienia!" - echo " " - echo "Instrukcje i narzędzie do odszyfrowania logów dostępne pod adresem: https://t1d.dzienia.pl/decoder/" - echo " " - echo " " - cat "$SUPPORT_LOG.gz.asc" - } | pusher "Diagnostyka_serwera_Nightscout_-_$curr_time") - - local regexEm='Email sent' - if [[ "$sentStatus" =~ $regexEm ]]; then - do_cleanup_diagnostics - msgcheck "Mail wysłany!" - okdlg "Diagnostyka wysłana" \ - "Sprawdź swoją skrzynkę pocztową,\n" \ - "otrzymanego maila przekaż zaufanemu wspierającemu.\n\n" \ - "Komunikatorem lub SMS przekaż hasło do logów:\n\n$LOG_KEY" - else - msgerr "Błąd podczas wysyłki maila: $sentStatus" - okdlg "Błąd wysyłki maila" \ - "Nieststy nie udało się wysłać diagnostyki" \ - "${NL}zgłoś poniższy błąd twórcom narzędzia (na grupie Technologie Diabetyka)" \ - "${TL}$sentStatus" - fi - - fi -} - -main_menu() { - while :; do - local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") - local quickStatus=$(center_text "Strona Nightscout: $(get_watchdog_status "$(get_watchdog_status_code_live)" "$uni_ns_ok")" 55) - local quickVersion=$(center_text "Wersja: $ns_tag" 55) - local quickDomain=$(center_text "Domena: $(get_domain_status 'ns-server')" 55) - local CHOICE=$(whiptail --title "Zarządzanie Nightscoutem :: $SCRIPT_VERSION" --menu "\n$quickStatus\n$quickVersion\n$quickDomain\n" 21 60 9 \ - "S)" "Status kontenerów i logi" \ - "P)" "Pokaż port i API SECRET" \ - "U)" "Aktualizuj..." \ - "C)" "Sprztąj..." \ - "R)" "Uruchom ponownie kontenery" \ - "D)" "Wyślij diagnostykę i logi" \ - "Z)" "Zmień lub odinstaluj..." \ - "I)" "O tym narzędziu..." \ - "X)" "Wyjście" \ - --ok-button="$uni_select" --cancel-button="$uni_exit" \ - 3>&2 2>&1 1>&3) - - case $CHOICE in - "S)") - status_menu - ;; - "P)") - local ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") - local ns_api_secret=$(dotenv-tool -r get -f "$ENV_FILE_NS" "API_SECRET") - whiptail --title "Podgląd konfiguracji Nightscout" --msgbox \ - "\n Port usługi Nightscout: $ns_external_port\n API_SECRET: $ns_api_secret" \ - 10 60 - ;; - "U)") - update_menu - ;; - "C)") - cleanup_menu - ;; - "R)") - docker_compose_down - docker_compose_up - ;; - "D)") - send_diagnostics - ;; - "Z)") - uninstall_menu - ;; - "I)") - about_dialog - ;; - "X)") - exit 0 - ;; - "") - exit 0 - ;; - esac - done -} - -setup_done() { - whiptail --title "Gotowe!" --yesno --defaultno " Możesz teraz zamknąć to narzędzie lub wrócić do menu.\n Narzędzie dostępne jest też jako komenda konsoli:\n\n nightscout-tool" --yes-button "$uni_menu" --no-button "$uni_finish" 12 70 - exit_on_no_cancel - main_menu -} - -install_or_menu() { - STATUS_NS=$(get_docker_status "ns-server") - lastTimeSpaceInfo=$(get_space_info) - - if [ "$STATUS_NS" = "missing" ]; then - - if [ "$freshInstall" -eq 0 ]; then - instal_now_prompt - if ! [ $? -eq 1 ]; then - freshInstall=1 - fi - fi - - if [ "$freshInstall" -gt 0 ]; then - ohai "Instalowanie Nightscout..." - event_mark "install_start" - docker_compose_update - setup_firewall_for_ns - domain_setup - # admin_panel_promo - event_mark "install_end" - setup_done - else - main_menu - fi - else - msgok "Wykryto uruchomiony Nightscout" - main_menu - fi -} - -free_space_check() { - lastTimeSpaceInfo=$(get_space_info) - - local remainingB=$(echo "$lastTimeSpaceInfo" | awk '{print $3}') - local remainingTxt=$(echo "$lastTimeSpaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) - - if ((remainingB < DISK_LOW_WARNING)); then - if ((remainingB < DISK_CRITICAL_WARNING)); then - local lastCalled=$(get_since_last_time "disk_critical") - local domain=$(get_td_domain) - if ((lastCalled == -1)) || ((lastCalled > DISK_CRITICAL_MAIL)); then - set_last_time "disk_critical" - { - echo "Na twoim serwerze mikr.us z Nightscoutem (https://$domain) zostało krytycznie mało miejsca (${remainingTxt})!" - echo " " - echo "Tak mała ilość miejsca nie pozwala serwerowi na stabilne działanie!" - echo "🚨PILNIE🚨 posprzątaj na serwerze, aby to zrobić możesz:" - echo " " - echo "1. Usunąć stare statusy i wpisy z poziomu strony Nightscout:" - echo " - wejdź do hamburger menu strony Nightscout i wybierz: 【 Narzędzia administratora 】- wymaga zalogowania" - echo " to powinno otwórzyć adres: https://${domain}/admin" - echo " - w polach tekstowych poustawiaj ile dni historii chcesz zachować, i w odpowiednich sekcjach kliknij:" - echo " 【 Usuń stare dokumenty 】" - echo " " - echo "2. Posprzątać nieużywane pliki na serwerze mikr.us:" - echo " - zaloguj się na swój mikr.us do panelu administracyjnego, przejdź do WebSSH" - echo " https://mikr.us/panel/?a=webssh" - echo " - zaloguj się, uruchom narzędzie komendą: nightscout-tool" - echo " - wybierz: 【 C) Sprztąj... 】" - echo " - wybierz: 【 A) Posprzątaj wszystko 】 i potwierdź 【 Tak 】" - echo " - cierpliwie poczekaj, po sprzątaniu narzędzie pokaże ile miejsca zwolniono" - } | pusher "🚨_Krytycznie_mało_miejsca_na_Twoim_serwerze_Nightscout!" - echo "Free space on server: CRITICALLY LOW (${remainingTxt}) - sending email to user" - else - echo "Free space on server: CRITICALLY LOW (${remainingTxt}) - user already notified" - fi - else - local lastCalled=$(get_since_last_time "disk_warning") - local domain=$(get_td_domain) - if ((lastCalled == -1)) || ((lastCalled > DISK_LOW_MAIL)); then - set_last_time "disk_warning" - { - echo "Na twoim serwerze mikr.us z Nightscout-em (https://$domain) powoli kończy się miejsce (${remainingTxt})!" - echo " " - echo "🧹 W wolnej chwili posprzątaj na serwerze, aby to zrobić możesz:" - echo " " - echo "1. Usunąć stare statusy i wpisy z poziomu strony Nightscout:" - echo " - wejdź do hamburger menu strony Nightscout i wybierz:【 Narzędzia administratora 】- wymaga zalogowania" - echo " to powinno otwórzyć adres: https://${domain}/admin" - echo " - w polach tekstowych poustawiaj ile dni historii chcesz zachować, i w odpowiednich sekcjach kliknij:" - echo " 【 Usuń stare dokumenty 】" - echo " " - echo "2. Posprzątać nieużywane pliki na serwerze mikr.us:" - echo " - zaloguj się na swój mikr.us do panelu administracyjnego, przejdź do WebSSH" - echo " https://mikr.us/panel/?a=webssh" - echo " - zaloguj się, uruchom narzędzie komendą: nightscout-tool" - echo " - wybierz: 【 C) Sprztąj... 】" - echo " - wybierz: 【 A) Posprzątaj wszystko 】 i potwierdź 【 Tak 】" - echo " - cierpliwie poczekaj, po sprzątaniu narzędzie pokaże ile miejsca zwolniono" - } | pusher "🧹_Powoli_kończy_sie_miejsce_na_Twoim_serwerze_Nightscout!" - echo "Free space on server: LOW (${remainingTxt}) - sending email to user" - else - echo "Free space on server: LOW (${remainingTxt}) - user already notified" - fi - fi - else - clear_last_time "disk_critical" - clear_last_time "disk_warning" - echo "Free space on server: OK (${remainingTxt})" - fi -} - -mail_restart_needed() { - local whyRestart="$1" - local mikrusSerwer=$(hostname) - { - echo "🛟 Twój serwer mikr.us z Nightscoutem potrzebuje restartu!" - echo " " - echo "🐕 Watchdog wykrył awarię której nie jest w stanie automatycznie naprawić:" - echo "$whyRestart" - echo " " - echo "Potrzebna będzie Twoja pomoc z ręcznym restartem serwera:" - echo " " - echo "1. Zaloguj się do panelu administracyjnego mikrusa" - echo " https://mikr.us/panel/" - echo " " - echo "2. Znajdź kafelek z nazwą serwera (${mikrusSerwer}) i kliknij na przycisk pod nim:" - echo " 【 Restart 】" - echo " " - echo "3. Potwierdź naciskając przycisk:" - echo " 【 Poproszę o restart VPSa 】" - echo " " - echo "==========================================================" - echo " " - echo "⏳ Restart serwera potrwa kilka minut, kolejne kilka minut potrwa uruchomienie serwera Nightscout" - echo "Jeśli po kilkunastu minutach serwer nie zacznie działać poprawnie:" - echo "Zaloguj się do panelu mikr.us-a, zaloguj się do WebSSH i w nightscout-tool sprawdź:" - echo "- czy kontenery są uruchomione - ich status i logi" - echo "- czy jest dosyć wolnego miejsca" - echo "W razie potrzeby - 🔄 zrestartuj kontenery i uruchom 🧹 sprzątanie (ale NIE usuwaj logów!)." - echo " " - echo "==========================================================" - echo " " - echo "Jeśli to nie pomoże, poszukaj wsparcia na grupie Technologie Diabetyka" - echo " 🙋 https://www.facebook.com/groups/techdiab" - echo "i - po uzgodnieniu!!! - wyślij diagnostykę do autora skryptu:" - echo " 📜 https://t1d.dzienia.pl/nightscout_mikrus_tutorial/stabilna/5.troubleshooting/#wysyanie-diagnostyki" - echo " " - } | pusher "🛟_Twoj_serwer_Nightscout_potrzebuje_ręcznego_restartu!" -} - -watchdog_check() { - echo "Nightscout Watchdog mode" - - WATCHDOG_LAST_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - WATCHDOG_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - WATCHDOG_LAST_STATUS="unknown" - WATCHDOG_STATUS="unknown" - - if [[ -f $WATCHDOG_TIME_FILE ]]; then - echo "Found $WATCHDOG_TIME_FILE" - WATCHDOG_LAST_TIME=$(cat "$WATCHDOG_TIME_FILE") - else - echo "First watchdog run" - fi - - if [[ -f $WATCHDOG_STATUS_FILE ]]; then - echo "Found $WATCHDOG_STATUS_FILE" - WATCHDOG_LAST_STATUS=$(cat "$WATCHDOG_STATUS_FILE") - fi - - local STATUS_AGO=$(dateutils.ddiff "$WATCHDOG_TIME" "$WATCHDOG_LAST_TIME" -f '%S') - - if [ "$STATUS_AGO" -gt 900 ]; then - echo "Watchdog last status is $STATUS_AGO seconds old, ignoring" - WATCHDOG_LAST_STATUS="unknown" - fi - - free_space_check - update_background_check - - local NS_STATUS=$(get_container_status_code 'ns-server') - local DB_STATUS=$(get_container_status_code 'ns-database') - local COMBINED_STATUS="$NS_STATUS $DB_STATUS" - - echo "Server container: $NS_STATUS" - echo "Database container: $DB_STATUS" - - if [ "$COMBINED_STATUS" = "running running" ]; then - - clear_last_time "docker_down" - clear_last_time "server_restart_needed" - - echo "Will check page contents" - local domain=$(get_td_domain) - - local domainLen=${#domain} - if ((domainLen > 15)); then - local html=$(curl -iLsk "$domain") +#dev-end - WATCHDOG_STATUS="detection_failed" +#include logic_events.sh +#include logic_setup_checks.sh +#include logic_patch.sh +#include logic_docker.sh - if [[ "$html" =~ github.com/nightscout/cgm-remote-monitor ]]; then - echo "Nightscout is running" - WATCHDOG_STATUS="ok" - fi +#include utils_app.sh - if [[ "$html" =~ 'MongoDB connection failed' ]]; then - echo "Nightscout crash detected" - WATCHDOG_STATUS="restart" - if [ "$WATCHDOG_LAST_STATUS" == "restart_failed" ]; then - event_mark "restart_both" - echo "Restarting DB first..." - docker restart 'ns-database' - echo "Then, restarting Nightscout..." - docker restart 'ns-server' - echo "...done" - WATCHDOG_STATUS="full_restart" - else - if [ "$WATCHDOG_LAST_STATUS" != "restart" ]; then - event_mark "restart_ns" - echo "Restarting only Nightscout..." - docker restart 'ns-server' - echo "...done" - else - echo "Restart was tried but NS still crashed, will retry restart next time" - WATCHDOG_STATUS="restart_failed" - fi - fi - else - regex3='poszło nie tak' - if [[ "$html" =~ $regex3 ]]; then - echo "Nightscout is still restarting..." - WATCHDOG_STATUS="awaiting" - fi - fi +#include logic_watchdog.sh +#include logic_cleanup.sh +#include logic_config.sh +#include logic_update.sh +#include logic_diagnostics.sh +#include logic_app.sh - if [ "$WATCHDOG_STATUS" = "detection_failed" ]; then - { - echo "----------------------------------------------------------------" - echo "[$WATCHDOG_TIME] Unknown server failure:" - echo "CONTAINERS:" - docker stats --no-stream - echo "HTTP DUMP:" - echo "$html" - } >>"$WATCHDOG_FAILURES_FILE" - fi +#include app_other_dialogs.sh +#include app_setup_prompts.sh +#include app_setup.sh - else - WATCHDOG_STATUS="domain_failed" - fi +#include utils_reminders.sh +#include commandline.sh - else - if [ "$NS_STATUS" = "restarting" ] || [ "$DB_STATUS" = "restarting" ]; then - WATCHDOG_STATUS="awaiting" - else - WATCHDOG_STATUS="not_running" +#include app_watchdog.sh +#include app_cleanup.sh +#include app_config.sh +#include app_update.sh +#include app_diagnostics.sh +#include app_main.sh - local logSample=$(timeout -k 15 10 docker logs ns-server --tail "10" 2>&1) - local regexSample='Cannot connect to the Docker daemon' - if [[ "$logSample" =~ $regexSample ]]; then - WATCHDOG_STATUS="docker_down" - if [ "$WATCHDOG_LAST_STATUS" != "docker_down" ]; then - echo "Cannot connect to Docker, will restart service..." - set_last_time "docker_down" - sudo systemctl restart docker - else - echo "Cannot connect to Docker, and service cannot be restarted" - local lastCalled=$(get_since_last_time "server_restart_needed") - if ((lastCalled == -1)) || ((lastCalled > DOCKER_DOWN_MAIL)); then - set_last_time "server_restart_needed" - echo "Sending mail to user - manual server restart needed" - mail_restart_needed "Usługa Docker uległa awarii i nie można automatycznie jej uruchomić" - else - echo "Mail for manual restart already recently sent" - fi - fi - fi - fi - fi +#======================================= +# ACTIONS AND STEPS +#======================================= - echo "Watchdog observation: $WATCHDOG_STATUS" - # if [ "$WATCHDOG_LAST_STATUS" != "$WATCHDOG_STATUS" ]; then - echo "$WATCHDOG_TIME [$WATCHDOG_STATUS]" >>"$WATCHDOG_LOG_FILE" - LOGSIZE=$(wc -l <"$WATCHDOG_LOG_FILE") - if [ "$LOGSIZE" -gt 1000 ]; then - tail -1000 "$WATCHDOG_LOG_FILE" >"$WATCHDOG_LOG_FILE.tmp" - mv -f "$WATCHDOG_LOG_FILE.tmp" "$WATCHDOG_LOG_FILE" - fi - # fi - if [[ -f $WATCHDOG_FAILURES_FILE ]]; then - FAILSIZE=$(wc -l <"$WATCHDOG_FAILURES_FILE") - if [ "$FAILSIZE" -gt 10000 ]; then - tail -10000 "$WATCHDOG_FAILURES_FILE" >"$WATCHDOG_FAILURES_FILE.tmp" - mv -f "$WATCHDOG_FAILURES_FILE.tmp" "$WATCHDOG_FAILURES_FILE" - fi - fi - echo "$WATCHDOG_TIME" >"$WATCHDOG_TIME_FILE" - echo "$WATCHDOG_STATUS" >"$WATCHDOG_STATUS_FILE" - exit 0 -} -load_update_channel() { - if [[ -f $UPDATE_CHANNEL_FILE ]]; then - UPDATE_CHANNEL=$(cat "$UPDATE_CHANNEL_FILE") - msgok "Loaded update channel: $UPDATE_CHANNEL" - fi -} -startup_version() { - local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") - msgnote "nightscout-tool version $SCRIPT_VERSION ($SCRIPT_BUILD_TIME)" - msgnote "build ${updateInstalled}" - msgnote "$uni_copyright 2023-2025 Dominik Dzienia" - msgnote "Licensed under CC BY-NC-ND 4.0" -} -parse_commandline_args() { - load_update_channel - CMDARGS=$(getopt --quiet -o wvdpuc: --long watchdog,version,develop,production,update,channel: -n 'nightscout-tool' -- "$@") - # shellcheck disable=SC2181 - if [ $? != 0 ]; then - echo "Invalid arguments: " "$@" >&2 - exit 1 - fi - # Note the quotes around '$TEMP': they are essential! - eval set -- "$CMDARGS" - WATCHDOGMODE=false - while true; do - case "$1" in - -w | --watchdog) - WATCHDOGMODE=true - shift - ;; - -v | --version) - echo "$SCRIPT_VERSION" - exit 0 - ;; - -d | --develop) - warn "Switching to DEVELOP update channel" - UPDATE_CHANNEL=develop - forceUpdateCheck=1 - echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" - shift - ;; - -p | --production) - warn "Switching to PRODUCTION update channel" - UPDATE_CHANNEL=master - forceUpdateCheck=1 - echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" - shift - ;; - -u | --update) - warn "Forcing update check" - forceUpdateCheck=1 - shift - ;; - -c | --channel) - shift # The arg is next in position args - UPDATE_CHANNEL_CANDIDATE=$1 - forceUpdateCheck=1 - [[ ! "$UPDATE_CHANNEL_CANDIDATE" =~ ^[a-z]{3,}$ ]] && { - echo "Incorrect channel name provided: $UPDATE_CHANNEL_CANDIDATE" - exit 1 - } - warn "Switching to $UPDATE_CHANNEL_CANDIDATE update channel" - UPDATE_CHANNEL="$UPDATE_CHANNEL_CANDIDATE" - echo "$UPDATE_CHANNEL" >"$UPDATE_CHANNEL_FILE" - shift - ;; - --) - shift - break - ;; - *) break ;; - esac - done - if [ "$WATCHDOGMODE" = "true" ]; then - watchdog_check - fi -} diff --git a/src/logic_app.sh b/src/logic_app.sh new file mode 100644 index 0000000..e051f33 --- /dev/null +++ b/src/logic_app.sh @@ -0,0 +1,41 @@ +#======================================= +# APP LOGIC +#======================================= + +get_td_domain() { + local MHOST=$(hostname) + if ! [[ "$MHOST" =~ [a-zA-Z]{2,16}[0-9]{3} ]]; then + MIKRUS_APIKEY=$(cat "/klucz_api") + MIKRUS_INFO_HOST=$(curl -s -d "srv=$MHOST&key=$MIKRUS_APIKEY" -X POST https://api.mikr.us/info | jq -r .imie_id) + if [[ "$MIKRUS_INFO_HOST" =~ [a-zA-Z]{2,16}[0-9]{3} ]]; then + MHOST="$MIKRUS_INFO_HOST" + fi + fi + local APIKEY=$(dotenv-tool -r get -f "$ENV_FILE_ADMIN" "MIKRUS_APIKEY") + curl -sd "srv=$MHOST&key=$APIKEY" https://api.mikr.us/domain | jq -r ".[].name" | grep ".ns.techdiab.pl" | head -n 1 +} + +get_domain_status() { + local domain=$(get_td_domain) + local domainLen=${#domain} + if ((domainLen > 15)); then + printf "\U1F7E2 %s" "$domain" + else + printf "\U26AA nie zarejestrowano" + fi +} + +load_update_channel() { + if [[ -f $UPDATE_CHANNEL_FILE ]]; then + UPDATE_CHANNEL=$(cat "$UPDATE_CHANNEL_FILE") + msgok "Loaded update channel: $UPDATE_CHANNEL" + fi +} + +startup_version() { + local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") + msgnote "nightscout-tool version $SCRIPT_VERSION ($SCRIPT_BUILD_TIME)" + msgnote "build ${updateInstalled}" + msgnote "$uni_copyright 2023-2026 Dominik Dzienia" + msgnote "Licensed under CC BY-NC-ND 4.0" +} diff --git a/src/logic_cleanup.sh b/src/logic_cleanup.sh new file mode 100644 index 0000000..61e5cb8 --- /dev/null +++ b/src/logic_cleanup.sh @@ -0,0 +1,67 @@ +#======================================= +# CLEANUP LOGIC +#======================================= + +do_cleanup_sys() { + ohai "Sprzątanie dziennik systemowego..." + event_mark "cleanup" + journalctl --vacuum-size=50M >>"$LOGTO" 2>&1 + ohai "Czyszczenie systemu apt..." + msgnote "Ta operacja może TROCHĘ potrwać (od kilku do kilkudziesięciu minut...)" + apt-get -y autoremove >>"$LOGTO" 2>&1 && apt-get -y clean >>"$LOGTO" 2>&1 + msgcheck "Czyszczenie dziennika i apt zakończono" +} + +do_cleanup_docker() { + ohai "Usuwanie nieużywanych obrazów Dockera..." + event_mark "cleanup" + msgnote "Ta operacja może TROCHĘ potrwać (do kilku minut...)" + docker image prune -af >>"$LOGTO" 2>&1 + msgcheck "Czyszczenie Dockera zakończono" +} + +do_cleanup_db() { + ohai "Usuwanie kopii zapasowych bazy danych..." + event_mark "cleanup" + find /srv/nightscout/data/dbbackup ! -type d -delete + msgcheck "Czyszczenie kopii zapasowych zakończono" +} + +do_cleanup_container_logs() { + ohai "Zatrzymywanie kontenerów..." + event_mark "cleanup" + docker stop 'ns-server' + docker stop 'ns-database' + docker stop 'ns-backup' + ohai "Usuwanie logów kontenerów..." + truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-server')" + truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-database')" + truncate -s 0 "$(docker inspect --format='{{.LogPath}}' 'ns-backup')" + ohai "Ponowne uruchamianie kontenerów..." + docker start 'ns-server' + docker start 'ns-database' + docker start 'ns-backup' + msgok "Logi usunięte" +} + +do_cleanup_diagnostics() { + ohai "Sprzątanie diagnostyki" + rm -f "$SUPPORT_LOG" + rm -f "$SUPPORT_LOG.gz" + rm -f "$SUPPORT_LOG.gz.asc" +} + +do_cleanup_app_state() { + ohai "Sprzątanie stanu aplikacji" + rm -f "$UPDATE_CHANNEL_FILE" + rm -f "$EVENTS_DB" +} + +do_cleanup_app_logs() { + ohai "Sprzątanie logów aplikacji" + rm -f "$WATCHDOG_STATUS_FILE" + rm -f "$WATCHDOG_TIME_FILE" + rm -f "$WATCHDOG_LOG_FILE" + rm -f "$WATCHDOG_FAILURES_FILE" + rm -f "$WATCHDOG_CRON_LOG" +} diff --git a/src/logic_config.sh b/src/logic_config.sh new file mode 100644 index 0000000..58725bb --- /dev/null +++ b/src/logic_config.sh @@ -0,0 +1,11 @@ +#======================================= +# CONFIGURATION +#======================================= + +source_admin() { + if [[ -f $ENV_FILE_ADMIN ]]; then + # shellcheck disable=SC1090 + source "$ENV_FILE_ADMIN" + msgok "Imported admin config" + fi +} \ No newline at end of file diff --git a/src/logic_diagnostics.sh b/src/logic_diagnostics.sh new file mode 100644 index 0000000..4e585bb --- /dev/null +++ b/src/logic_diagnostics.sh @@ -0,0 +1,130 @@ +#======================================= +# DIAGNOSTICS +#======================================= + +gather_diagnostics() { + + local maxNsLogs=$1 + local maxDbLogs=$2 + local curr_time=$3 + + diagnosticsSizeOk=0 + + do_cleanup_diagnostics + + ohai "Zbieranie diagnostyki" + + local domain=$(get_td_domain) + local ns_tag=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_NIGHTSCOUT_TAG") + local mikrus_h=$(hostname) + local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "???") + + local LOG_DIVIDER="=======================================================" + + { + echo "Dane diagnostyczne zebrane $curr_time" + echo " serwer : $mikrus_h" + echo " domena : $domain" + echo " wersja nightscout : $ns_tag" + echo " wersja nightscout-tool : $SCRIPT_VERSION ($SCRIPT_BUILD_TIME) $UPDATE_CHANNEL" + echo " build : ${updateInstalled}" + } >"$SUPPORT_LOG" + + ohai "Zbieranie statusu usług" + + { + echo "$LOG_DIVIDER" + echo " Statusy usług" + echo "$LOG_DIVIDER" + echo " Nightscout: $(get_container_status 'ns-server')" + echo " Baza danych: $(get_container_status 'ns-database')" + echo " Backup: $(get_container_status 'ns-backup')" + echo " Watchdog: $(get_watchdog_status "$(get_watchdog_status_code)" "$uni_watchdog_ok")" + } >>"$SUPPORT_LOG" + + local spaceInfo=$(get_space_info) + local remainingTxt=$(echo "$spaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) + local totalTxt=$(echo "$spaceInfo" | awk '{print $2}' | numfmt --to iec-i --suffix=B) + local percTxt=$(echo "$spaceInfo" | awk '{print $4}') + + { + echo "$LOG_DIVIDER" + echo " Miejsce na dysku" + echo "$LOG_DIVIDER" + echo " Dostępne: ${remainingTxt}" + echo " Zajęte: ${percTxt} (z ${totalTxt})" + } >>"$SUPPORT_LOG" + + ohai "Zbieranie zdarzeń" + { + echo "$LOG_DIVIDER" + echo " Zdarzenia" + echo "$LOG_DIVIDER" + event_list + } >>"$SUPPORT_LOG" + + ohai "Zbieranie logów watchdoga" + + if [[ -f $WATCHDOG_LOG_FILE ]]; then + { + echo "$LOG_DIVIDER" + echo " Watchdog log" + echo "$LOG_DIVIDER" + timeout -k 15 10 tail -n 200 "$WATCHDOG_LOG_FILE" + } >>"$SUPPORT_LOG" + fi + + if [[ -f $WATCHDOG_FAILURES_FILE ]]; then + { + echo "$LOG_DIVIDER" + echo " Watchdog failures log" + echo "$LOG_DIVIDER" + timeout -k 15 10 tail -n 200 "$WATCHDOG_FAILURES_FILE" + } >>"$SUPPORT_LOG" + fi + + ohai "Zbieranie logów usług" + + { + echo "$LOG_DIVIDER" + echo " Nightscout log" + echo "$LOG_DIVIDER" + timeout -k 15 10 docker logs ns-server --tail "$maxNsLogs" >>"$SUPPORT_LOG" 2>&1 + echo "$LOG_DIVIDER" + echo " MongoDB database log" + echo "$LOG_DIVIDER" + timeout -k 15 10 docker logs ns-database --tail "$maxDbLogs" >>"$SUPPORT_LOG" 2>&1 + } >>"$SUPPORT_LOG" + + ohai "Kompresowanie i szyfrowanie raportu" + + gzip -9 "$SUPPORT_LOG" + + local logkey=$(<"$LOG_ENCRYPTION_KEY_FILE") + + gpg --passphrase "$logkey" --batch --quiet --yes -a -c "$SUPPORT_LOG.gz" +} + +retry_diagnostics() { + local maxNsLogs=$1 + local maxDbLogs=$2 + local curr_time=$3 + + if ((diagnosticsSizeOk == 0)); then + + ohai "Sprawdzanie rozmiaru raportu" + + local logSize=$(stat --printf="%s" "$SUPPORT_LOG.gz.asc") + local allowedTxt=$(echo "18000" | numfmt --to si --suffix=B) + local currentTxt=$(echo "$logSize" | numfmt --to si --suffix=B) + + if ((logSize > 18000)); then + msgerr "Zebrana diagnostyka jest zbyt duża do wysłania (${currentTxt})" + ohai "Spróbuję zebrać mniej danych aby zmieścić się w limicie (${allowedTxt})" + gather_diagnostics "$maxNsLogs" "$maxDbLogs" "$curr_time" + else + diagnosticsSizeOk=1 + msgok "Raport ma rozmiar ${currentTxt} i mieści się w limicie ${allowedTxt} dla usługi pusher-a" + fi + fi +} \ No newline at end of file diff --git a/src/logic_docker.sh b/src/logic_docker.sh new file mode 100644 index 0000000..be66ee5 --- /dev/null +++ b/src/logic_docker.sh @@ -0,0 +1,83 @@ +#======================================= +# DOCKER +#======================================= + +get_docker_status() { + local ID=$(docker ps -a --no-trunc --filter name="^$1" --format '{{ .ID }}') + if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then + docker inspect "$ID" | jq -r ".[0].State.Status" + else + echo 'missing' + fi +} + +install_containers() { + docker compose --env-file $ENV_FILE_DEP -f $DOCKER_COMPOSE_FILE up --no-recreate -d >>"$LOGTO" 2>&1 +} + +update_containers() { + docker compose --env-file $ENV_FILE_DEP -f $DOCKER_COMPOSE_FILE pull >>"$LOGTO" 2>&1 + docker compose --env-file $ENV_FILE_DEP -f $DOCKER_COMPOSE_FILE up -d >>"$LOGTO" 2>&1 +} + +install_containers_progress() { + local created=$(docker container ls -f 'status=created' -f name=ns-server -f name=ns-database | wc -l) + local current=$(docker container ls -f 'status=running' -f name=ns-server -f name=ns-database | wc -l) + local progr=$(((current - 1) * 2 + (created - 1))) + echo_progress "$progr" 6 50 "$1" 60 +} + +uninstall_containers() { + docker compose --env-file $ENV_FILE_DEP -f $DOCKER_COMPOSE_FILE down >>"$LOGTO" 2>&1 +} + +uninstall_containers_progress() { + local running=$(docker container ls -f 'status=running' -f name=ns-server -f name=ns-database -f name=ns-backup | wc -l) + local current=$(docker container ls -f 'status=exited' -f name=ns-server -f name=ns-database -f name=ns-backup | wc -l) + local progr=$((current - 1)) + if [ "$(((running - 1) + (current - 1)))" -eq "0" ]; then + echo_progress 3 3 50 "$1" 15 + else + echo_progress "$progr" 3 50 "$1" 15 + fi +} + +get_container_status() { + local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') + if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then + local status=$(docker inspect "$ID" | jq -r ".[0].State.Status") + case "$status" in + "running") + printf "\U1F7E2 działa" + ;; + "restarting") + printf "\U1F7E3 restart" + ;; + "created") + printf "\U26AA utworzono" + ;; + "exited") + printf "\U1F534 wyłączono" + ;; + "paused") + printf "\U1F7E1 zapauzowano" + ;; + "dead") + printf "\U1F480 zablokowany" + ;; + esac + + else + printf '\U2753 nie odnaleziono' + fi +} + +get_container_status_code() { + local ID=$(docker ps -a --no-trunc --filter name="^$1$" --format '{{ .ID }}') + if [[ "$ID" =~ [0-9a-fA-F]{12,} ]]; then + local status=$(docker inspect "$ID" | jq -r ".[0].State.Status") + echo "$status" + else + echo "unknown" + fi +} diff --git a/src/logic_events.sh b/src/logic_events.sh index e34f51f..bf4b748 100644 --- a/src/logic_events.sh +++ b/src/logic_events.sh @@ -205,3 +205,16 @@ clear_last_time() { rm -f "$actionFile" event_mark "last_${actionName}_clear" } + +get_events_status() { + local count="$(event_count)" + if ((count == 0)); then + printf "\U2728 brak zdarzeń" + elif ((count == 1)); then + printf "\U1F4C5 jedno zdarzenie" + elif (((count % 10) > 1)) && (((count % 10) < 5)); then + printf "\U1F4C5 %s zdarzenia" "$count" + else + printf "\U1F4C5 %s zdarzeń" "$count" + fi +} \ No newline at end of file diff --git a/src/logic_patch.sh b/src/logic_patch.sh new file mode 100644 index 0000000..c6f16c8 --- /dev/null +++ b/src/logic_patch.sh @@ -0,0 +1,44 @@ +#======================================= +# PATCH OLDER CONFIGS +#======================================= + +patch_docker_compose() { + if [[ -f $DOCKER_COMPOSE_FILE ]]; then + local patched=0 + local containers_running=0 + + # Check if containers are already running before patching + local ns_status=$(get_docker_status "ns-server") + local db_status=$(get_docker_status "ns-database") + if [[ "$ns_status" == "running" ]] || [[ "$db_status" == "running" ]]; then + containers_running=1 + fi + + # Check if mongodb image needs patching (bitnami/mongodb) + if grep -q "bitnami/mongodb" "$DOCKER_COMPOSE_FILE"; then + ohai "Patching docker-compose.yml MongoDB image..." + # Replace bitnami/mongodb with official mongo image + sed -i -E 's|image:\s*"*(bitnami/)?mongodb:.*"|image: "mongo:${NS_MONGODB_TAG}"|g' "$DOCKER_COMPOSE_FILE" + patched=1 + fi + # Check if volume path needs patching (bitnami/mongodb -> data/db) + if grep -q "/bitnami/mongodb" "$DOCKER_COMPOSE_FILE"; then + ohai "Patching docker-compose.yml MongoDB volume path..." + # Replace both host path and container path for mongodb volume + sed -i -E 's|(\$\{NS_DATA_DIR\}/mongodb):/bitnami/mongodb"|\1/data/db:/data/db"|g' "$DOCKER_COMPOSE_FILE" + patched=1 + fi + + if [ "$patched" -eq 1 ]; then + msgcheck "Docker compose file patched" + # Restart containers only if they were already running + if [ "$containers_running" -eq 1 ]; then + do_cleanup_sys + ohai "Restarting containers to apply patched configuration..." + update_containers + do_cleanup_docker + msgcheck "Containers restarted" + fi + fi + fi +} \ No newline at end of file diff --git a/src/logic_setup.sh b/src/logic_setup.sh new file mode 100644 index 0000000..f86bc31 --- /dev/null +++ b/src/logic_setup.sh @@ -0,0 +1,217 @@ +#======================================= +# SETUP +#======================================= + +setup_update_repo() { + if [ "$aptGetWasUpdated" -eq "0" ]; then + aptGetWasUpdated=1 + ohai "Updating package repository" + apt-get -yq update >>"$LOGTO" 2>&1 + fi +} + +setup_provisional_key() { + ohai "Generating provisional log encryption key" + local randPass=$(openssl rand -base64 30) + local fixedPass=$(echo "$randPass" | sed -e 's/[+\/]/-/g') + echo "tymczasowe-${fixedPass}" >"$LOG_ENCRYPTION_KEY_FILE" + msgcheck "Provisional key generated" +} + +setup_security() { + if [[ -f $LOG_ENCRYPTION_KEY_FILE ]]; then + # -------------------- + # JAKIŚ klucz istnieje + # -------------------- + local logKey=$(<"$LOG_ENCRYPTION_KEY_FILE") + local regexTemp='tymczasowe-' + + # ----------------------- + # ...ale jest tymczasowy + # ----------------------- + if [[ "$logKey" =~ $regexTemp ]]; then + msgerr "Using provisional key" + test_diceware + local RESULT=$? + if [ "$RESULT" -eq 0 ]; then + ohai "Generating proper log encryption file..." + diceware -n 5 -d - >"$LOG_ENCRYPTION_KEY_FILE" + msgcheck "Key generated" + else + msgerr "Required tool (diceware) still cannot be installed - apt is locked!" + msgnote "Zrestartuj serwer mikr.us i sprawdź czy ten błąd nadal występuje - wtedy odbokuj apt-get i zainstaluj diceware (apt-get install diceware)" + fi + else + local keySize=${#logKey} + + # ---------------------- + # ...ale jest za krótki + # ---------------------- + if ((keySize < 12)); then + msgerr "Encryption key empty or too short, generating better one" + test_diceware + local RESULT=$? + if [ "$RESULT" -eq 0 ]; then + ohai "Generating proper log encryption file..." + diceware -n 5 -d - >"$LOG_ENCRYPTION_KEY_FILE" + msgcheck "Key generated" + else + msgerr "Generating provisional key while diceware tool is not installed" + setup_provisional_key + fi + else + msgok "Found log encryption key" + fi + fi + else + + # --------------------- + # jescze nie ma klucza + # --------------------- + + test_diceware + local RESULT=$? + if [ "$RESULT" -eq 0 ]; then + ohai "Generating log encryption key..." + diceware -n 5 -d - >"$LOG_ENCRYPTION_KEY_FILE" + msgcheck "Key generated" + else + msgerr "Generating provisional key while diceware tool is not installed" + setup_provisional_key + fi + + fi +} + +setup_packages() { + # shellcheck disable=SC2145 + # shellcheck disable=SC2068 + (if_is_set packages && setup_update_repo && + ohai "Installing packages: ${packages[@]}" && + apt-get -yq install ${packages[@]} >>"$LOGTO" 2>&1 && + msgcheck "Install successfull") || msgok "All required packages already installed" +} + +setup_node() { + test_node + local RESULT=$? + if [ "$RESULT" -eq 0 ]; then + msgcheck "Node installed in correct version" + else + ohai "Cleaning old Node.js" + { + rm -f /etc/apt/sources.list.d/nodesource.list + apt-get -yq --fix-broken install + apt-get -yq update + apt-get -yq remove nodejs nodejs-doc libnode* + } >>"$LOGTO" 2>&1 + + ohai "Preparing Node.js setup" + curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - >/dev/null 2>&1 + + ohai "Installing Node.js" + apt-get install -y nodejs >>"$LOGTO" 2>&1 + + test_node + local RECHECK=$? + if [ "$RECHECK" -ne 0 ]; then + + msgerr "Nie udało się zainstalować Node.js" + + msgerr "Instalacja Node.js jest skomplikowanym procesem i zależy od wersji systemu Linux i konfiguracji Mikr.us-a" + msgerr "Spróbuj ręcznie uruchomić instalację poniższą komendą i sprawdź czy pojawiają się błędy (i jakie):" + msgerr " apt-get install -y nodejs " + + exit 1 + fi + + fi +} + +setup_users() { + id -u mongodb &>/dev/null + local RESULT=$? + if [ "$RESULT" -eq 0 ]; then + msgcheck "Mongo DB user detected" + else + ohai "Configuring Mongo DB user" + useradd -u 1001 -g 0 mongodb + fi +} + +setup_dir_structure() { + ohai "Configuring folder structure" + mkdir -p "$MONGO_DB_DIR" + mkdir -p /srv/nightscout/config + mkdir -p /srv/nightscout/tools + mkdir -p /srv/nightscout/data + mkdir -p "$UPDATES_DIR" + chown -R mongodb:root "$MONGO_DB_DIR" +} + +setup_firewall() { + ohai "Configuring firewall" + + { + ufw default deny incoming + ufw default allow outgoing + + ufw allow OpenSSH + ufw allow ssh + } >>"$LOGTO" 2>&1 + + host=$(hostname) + + # Extract the last 3 digits from the hostname + port_number=$(echo "$host" | grep -oE '[0-9]{3}$') + + ohai "Firewall port: $port_number" + + port1=$((10000 + port_number)) + port2=$((20000 + port_number)) + port3=$((30000 + port_number)) + + if ufw allow "$port1" >>"$LOGTO" 2>&1; then + msgcheck "Do regul firewalla poprawnie dodano port $port1" + else + msgerr "Blad dodawania $port1 do regul firewalla" + fi + + if ufw allow "$port2" >>"$LOGTO" 2>&1; then + msgcheck "Do regul firewalla poprawnie dodano port $port2" + else + msgerr "Blad dodawania $port2 do regul firewalla" + fi + + if ufw allow "$port3" >>"$LOGTO" 2>&1; then + msgcheck "Do regul firewalla poprawnie dodano port $port3" + else + msgerr "Blad dodawania $port3 do regul firewalla" + fi + + ufw --force enable >>"$LOGTO" 2>&1 +} + +setup_firewall_for_ns() { + ns_external_port=$(dotenv-tool -r get -f "$ENV_FILE_DEP" "NS_PORT") + if ufw allow "$ns_external_port" >>"$LOGTO" 2>&1; then + msgcheck "Do regul firewalla poprawnie dodano port Nightscout: $ns_external_port" + else + msgerr "Blad dodawania portu Nightscout: $ns_external_port do reguł firewalla" + fi +} + +install_cron() { + local croncmd="$TOOL_LINK -w > $WATCHDOG_CRON_LOG 2>&1" + local cronjob="*/5 * * * * $croncmd" + msgok "Configuring watchdog..." + ( + crontab -l | grep -v -F "$croncmd" || : + echo "$cronjob" + ) | crontab - +} + +uninstall_cron() { + local croncmd="nightscout-tool" + (crontab -l | grep -v -F "$croncmd") | crontab - +} diff --git a/src/logic_setup_checks.sh b/src/logic_setup_checks.sh new file mode 100644 index 0000000..bd529a4 --- /dev/null +++ b/src/logic_setup_checks.sh @@ -0,0 +1,103 @@ +#======================================= +# SETUP CHECKS +#======================================= + +# $1 lib name +# $2 package name +add_if_not_ok() { + local RESULT=$? + if [ "$RESULT" -eq 0 ]; then + msgcheck "$1 installed" + else + packages+=("$2") + fi +} + +add_if_not_ok_cmd() { + local RESULT=$? + if [ "$RESULT" -eq 0 ]; then + msgcheck "$1 installed" + else + ohai "Installing $1..." + eval "$2" >>"$LOGTO" 2>&1 && msgcheck "Installing $1 successfull" + fi +} + +add_if_not_ok_compose() { + local RESULT=$? + if [ "$RESULT" -eq 0 ]; then + msgcheck "$1 installed" + else + ohai "Installing $1..." + mkdir -p "~/.docker/cli-plugins" >> "$LOGTO" 2>&1 + curl -SL "https://github.com/docker/compose/releases/download/v2.29.7/docker-compose-linux-x86_64" -o "~/.docker/cli-plugins/docker-compose" >> "$LOGTO" 2>&1 + chmod +x "~/.docker/cli-plugins/docker-compose" >> "$LOGTO" 2>&1 + msgcheck "Installing $1 successfull" + fi +} + +test_node() { + local node_version_output + node_version_output="$(node -v 2>/dev/null)" + version_ge "$(major_minor "${node_version_output/v/}")" "$(major_minor "${REQUIRED_NODE_VERSION}")" +} + +check_git() { + git --version >/dev/null 2>&1 + add_if_not_ok "GIT" "git" +} + +check_docker() { + docker -v >/dev/null 2>&1 + add_if_not_ok "Docker" "docker.io" +} + +check_docker_compose() { + docker compose version >/dev/null 2>&1 + add_if_not_ok_compose "Docker compose" +} + +check_jq() { + jq --help >/dev/null 2>&1 + add_if_not_ok "JSON parser" "jq" +} + +check_dotenv() { + if dotenv-tool -v >/dev/null 2>&1; then + local dotEnvVersion="$(dotenv-tool -v 2>/dev/null)" + if version_ge "$(major_minor "${dotEnvVersion}")" \ + "$(major_minor "${REQUIRED_DOTENV_VERSION}")"; then + msgcheck "dotenv-tool installed (${dotEnvVersion})" + else + ohai "Updating dotenv-tool (from: ${dotEnvVersion})" + eval "npm install -g dotenv-tool --registry https://npm.dzienia.pl" >>"$LOGTO" 2>&1 && msgcheck "Updating dotenv-tool successfull" + fi + else + ohai "Installing dotenv-tool..." + eval "npm install -g dotenv-tool --registry https://npm.dzienia.pl" >>"$LOGTO" 2>&1 && msgcheck "Installing dotenv-tool successfull" + fi +} + +check_ufw() { + ufw --version >/dev/null 2>&1 + add_if_not_ok "Firewall" "ufw" +} + +check_nano() { + nano --version >/dev/null 2>&1 + add_if_not_ok "Text Editor" "nano" +} + +check_dateutils() { + dateutils.ddiff --version >/dev/null 2>&1 + add_if_not_ok "Date Utils" "dateutils" +} + +test_diceware() { + diceware --version >/dev/null 2>&1 +} + +check_diceware() { + test_diceware + add_if_not_ok "Secure Password Generator" "diceware" +} \ No newline at end of file diff --git a/src/logic_update.sh b/src/logic_update.sh new file mode 100644 index 0000000..cd37dea --- /dev/null +++ b/src/logic_update.sh @@ -0,0 +1,111 @@ +#======================================= +# UPGRADE +#======================================= + +download_if_not_exists() { + if [[ -f $2 ]]; then + msgok "Found $1" + else + ohai "Downloading $1..." + curl -fsSL -o "$2" "$3" + msgcheck "Downloaded $1" + fi +} + +download_conf() { + download_if_not_exists "deployment config" "$ENV_FILE_DEP" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/deployment.env" + download_if_not_exists "nightscout config" "$ENV_FILE_NS" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/nightscout.env" + download_if_not_exists "docker compose file" "$DOCKER_COMPOSE_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/docker-compose.yml" + download_if_not_exists "profanity database" "$PROFANITY_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/profanity.db" + download_if_not_exists "reservation database" "$RESERVED_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/reserved.db" +} + +download_tools() { + download_if_not_exists "update stamp" "$UPDATES_DIR/updated" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated" + + if ! [[ -f $TOOL_FILE ]]; then + download_if_not_exists "nightscout-tool file" "$TOOL_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/install.sh" + local timestamp=$(date +%s) + echo "$timestamp" >"$UPDATES_DIR/timestamp" + else + msgok "Found nightscout-tool" + fi + + if ! [[ -f $TOOL_LINK ]]; then + ohai "Linking nightscout-tool" + ln -s "$TOOL_FILE" "$TOOL_LINK" + fi + + chmod +x "$TOOL_FILE" + chmod +x "$TOOL_LINK" +} + +download_updates() { + ohai "Downloading updated scripts and config files" + local onlineUpdated="$(curl -fsSL "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated")" + if [ ! "$onlineUpdated" == "" ]; then + curl -fsSL -o "$UPDATES_DIR/install.sh" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/install.sh" + curl -fsSL -o "$UPDATES_DIR/deployment.env" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/deployment.env" + curl -fsSL -o "$UPDATES_DIR/nightscout.env" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/nightscout.env" + curl -fsSL -o "$UPDATES_DIR/docker-compose.yml" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/templates/docker-compose.yml" + curl -fsSL -o "$PROFANITY_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/profanity.db" + curl -fsSL -o "$RESERVED_DB_FILE" "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/profanity/templates/reserved.db" + else + onlineUpdated="error" + fi + echo "$onlineUpdated" >"$UPDATES_DIR/downloaded" +} + +download_if_needed() { + local lastCheck=$(read_or_default "$UPDATES_DIR/timestamp") + local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "") + local timestampNow=$(date +%s) + local updateCheck=$UPDATE_CHECK + if (((timestampNow - lastCheck) > updateCheck)) || [ "$lastDownload" == "" ] || [ "$lastDownload" == "error" ] || ((forceUpdateCheck == 1)) || [ $# -eq 1 ]; then + echo "$timestampNow" >"$UPDATES_DIR/timestamp" + ohai "Checking if new version is available..." + local onlineUpdated="$(curl -fsSL "https://gitea.dzienia.pl/shared/mikrus-installer/raw/branch/$UPDATE_CHANNEL/updated")" + local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded") + if [ "$onlineUpdated" == "$lastDownload" ] && ((forceUpdateCheck == 0)); then + msgok "Latest update already downloaded" + else + download_updates + fi + else + msgok "Too soon to download update, skipping..." + fi +} + +update_background_check() { + download_if_needed + + local lastDownload=$(read_or_default "$UPDATES_DIR/downloaded" "") + local updateInstalled=$(read_or_default "$UPDATES_DIR/updated" "") + + if [ ! "$lastDownload" == "$updateInstalled" ] && [ ! "$lastDownload" == "" ] && [ ! "$lastDownload" == "error" ]; then + echo "Update needed" + local lastCalled=$(get_since_last_time "update_needed") + if ((lastCalled == -1)) || ((lastCalled > UPDATE_MAIL)); then + set_last_time "update_needed" + echo "Sending mail to user - tool update needed" + { + echo "✨ Na Twoim serwerze mikr.us z Nightscoutem można zaktualizować narzędzie nightscout-tool!" + echo " " + echo "🐕 Watchdog wykrył że dostępna jest nowa aktualizacja nightscout-tool." + echo "Na Twoim serwerze zainstalowana jest starsza wersja narzędzia - zaktualizuj go by poprawić stabilność systemu i uzyskać dostęp do nowych funkcji." + echo " " + echo "Aby zaktualizować narzędzie:" + echo " " + echo "1. Zaloguj się do panelu administracyjnego mikrusa i zaloguj się do WebSSH:" + echo " https://mikr.us/panel/?a=webssh" + echo " " + echo "2. Uruchom narzędzie komendą:" + echo " nightscout-tool" + echo " " + echo "3. Potwierdź naciskając przycisk:" + echo " 【 Aktualizacja 】" + echo " " + } | pusher "✨_Na_Twoim_serwerze_Nightscout_dostępna_jest_aktualizacja" + fi + fi +} diff --git a/src/logic_watchdog.sh b/src/logic_watchdog.sh new file mode 100644 index 0000000..fd7a9ef --- /dev/null +++ b/src/logic_watchdog.sh @@ -0,0 +1,305 @@ +#======================================= +# WATCHDOG LOGIC +#======================================= + +get_watchdog_age_string() { + local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + if [[ -f $WATCHDOG_TIME_FILE ]]; then + last_time=$(cat "$WATCHDOG_TIME_FILE") + local status_ago=$(dateutils.ddiff "$last_time" "$curr_time" -f '%Mmin. %Ssek.') + echo "$last_time ($status_ago temu)" + else + echo "jescze nie uruchomiony" + fi +} + +get_watchdog_status_code() { + local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local status="unknown" + + if [[ -f $WATCHDOG_TIME_FILE ]]; then + last_time=$(cat "$WATCHDOG_TIME_FILE") + fi + + if [[ -f $WATCHDOG_STATUS_FILE ]]; then + status=$(cat "$WATCHDOG_STATUS_FILE") + fi + + local status_ago=$(dateutils.ddiff "$curr_time" "$last_time" -f '%S') + + if [ "$status_ago" -gt 900 ]; then + status="unknown" + fi + + echo "$status" +} + +get_watchdog_status_code_live() { + local curr_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local last_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local status="unknown" + + if [[ -f $WATCHDOG_TIME_FILE ]]; then + last_time=$(cat "$WATCHDOG_TIME_FILE") + fi + + if [[ -f $WATCHDOG_STATUS_FILE ]]; then + status=$(cat "$WATCHDOG_STATUS_FILE") + fi + + local status_ago=$(dateutils.ddiff "$curr_time" "$last_time" -f '%S') + + if [ "$status_ago" -gt 900 ]; then + status="unknown" + fi + + local NS_STATUS=$(get_container_status_code 'ns-server') + local DB_STATUS=$(get_container_status_code 'ns-database') + local COMBINED_STATUS="$NS_STATUS $DB_STATUS" + + if [ "$COMBINED_STATUS" = "running running" ]; then + + status="detection_failed" + + local domain=$cachedMenuDomain + local cachedDomainLen=${#cachedMenuDomain} + if ((cachedDomainLen < 16)); then + domain=$(get_td_domain) + fi + + local domainLen=${#domain} + if ((domainLen > 15)); then + cachedMenuDomain=$domain + local html=$(curl -Lks "$domain") + + if [[ "$html" =~ github.com/nightscout/cgm-remote-monitor ]]; then + status="ok" + fi + + if [[ "$html" =~ 'MongoDB connection failed' ]]; then + status="crashed" + fi + + regex3='poszło nie tak' + if [[ "$html" =~ $regex3 ]]; then + status="awaiting" + fi + + else + status="domain_failed" + fi + + else + if [ "$NS_STATUS" = "restarting" ] || [ "$DB_STATUS" = "restarting" ]; then + status="awaiting" + else + local logSample=$(timeout -k 15 10 docker logs ns-server --tail "10" 2>&1) + local regexSample='Cannot connect to the Docker daemon' + if [[ "$logSample" =~ $regexSample ]]; then + status="docker_down" + else + status="not_running" + fi + fi + fi + + echo "$status" +} + +get_watchdog_status() { + local status="$1" + case "$status" in + "ok") + echo "$2" + ;; + "restart") + printf "\U1F680 wymuszono restart NS" + ;; + "awaiting") + printf "\U23F3 uruchamia się" + ;; + "restart_failed") + printf "\U1F680 restart NS to za mało" + ;; + "full_restart") + printf "\U1F680 restart NS i DB" + ;; + "unknown") + printf "\U1F4A4 brak statusu" + ;; + "not_running") + printf "\U1F534 serwer nie działa" + ;; + "detection_failed") + printf "\U2753 nieznany stan" + ;; + "domain_failed") + printf "\U2753 problem z domeną" + ;; + "crashed") + printf "\U1F4A5 awaria NS" + ;; + "docker_down") + printf "\U1F4A5 awaria Dockera" + ;; + esac + +} + +watchdog_check() { + echo "Nightscout Watchdog mode" + + WATCHDOG_LAST_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + WATCHDOG_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + WATCHDOG_LAST_STATUS="unknown" + WATCHDOG_STATUS="unknown" + + if [[ -f $WATCHDOG_TIME_FILE ]]; then + echo "Found $WATCHDOG_TIME_FILE" + WATCHDOG_LAST_TIME=$(cat "$WATCHDOG_TIME_FILE") + else + echo "First watchdog run" + fi + + if [[ -f $WATCHDOG_STATUS_FILE ]]; then + echo "Found $WATCHDOG_STATUS_FILE" + WATCHDOG_LAST_STATUS=$(cat "$WATCHDOG_STATUS_FILE") + fi + + local STATUS_AGO=$(dateutils.ddiff "$WATCHDOG_TIME" "$WATCHDOG_LAST_TIME" -f '%S') + + if [ "$STATUS_AGO" -gt 900 ]; then + echo "Watchdog last status is $STATUS_AGO seconds old, ignoring" + WATCHDOG_LAST_STATUS="unknown" + fi + + free_space_check + update_background_check + + local NS_STATUS=$(get_container_status_code 'ns-server') + local DB_STATUS=$(get_container_status_code 'ns-database') + local COMBINED_STATUS="$NS_STATUS $DB_STATUS" + + echo "Server container: $NS_STATUS" + echo "Database container: $DB_STATUS" + + if [ "$COMBINED_STATUS" = "running running" ]; then + + clear_last_time "docker_down" + clear_last_time "server_restart_needed" + + echo "Will check page contents" + local domain=$(get_td_domain) + + local domainLen=${#domain} + if ((domainLen > 15)); then + local html=$(curl -iLsk "$domain") + + WATCHDOG_STATUS="detection_failed" + + if [[ "$html" =~ github.com/nightscout/cgm-remote-monitor ]]; then + echo "Nightscout is running" + WATCHDOG_STATUS="ok" + fi + + if [[ "$html" =~ 'MongoDB connection failed' ]]; then + echo "Nightscout crash detected" + WATCHDOG_STATUS="restart" + if [ "$WATCHDOG_LAST_STATUS" == "restart_failed" ]; then + event_mark "restart_both" + echo "Restarting DB first..." + docker restart 'ns-database' + echo "Then, restarting Nightscout..." + docker restart 'ns-server' + echo "...done" + WATCHDOG_STATUS="full_restart" + else + if [ "$WATCHDOG_LAST_STATUS" != "restart" ]; then + event_mark "restart_ns" + echo "Restarting only Nightscout..." + docker restart 'ns-server' + echo "...done" + else + echo "Restart was tried but NS still crashed, will retry restart next time" + WATCHDOG_STATUS="restart_failed" + fi + fi + else + regex3='poszło nie tak' + if [[ "$html" =~ $regex3 ]]; then + echo "Nightscout is still restarting..." + WATCHDOG_STATUS="awaiting" + fi + fi + + if [ "$WATCHDOG_STATUS" = "detection_failed" ]; then + { + echo "----------------------------------------------------------------" + echo "[$WATCHDOG_TIME] Unknown server failure:" + echo "CONTAINERS:" + docker stats --no-stream + echo "HTTP DUMP:" + echo "$html" + } >>"$WATCHDOG_FAILURES_FILE" + fi + + else + WATCHDOG_STATUS="domain_failed" + fi + + else + if [ "$NS_STATUS" = "restarting" ] || [ "$DB_STATUS" = "restarting" ]; then + WATCHDOG_STATUS="awaiting" + else + WATCHDOG_STATUS="not_running" + + local logSample=$(timeout -k 15 10 docker logs ns-server --tail "10" 2>&1) + local regexSample='Cannot connect to the Docker daemon' + if [[ "$logSample" =~ $regexSample ]]; then + WATCHDOG_STATUS="docker_down" + if [ "$WATCHDOG_LAST_STATUS" != "docker_down" ]; then + echo "Cannot connect to Docker, will restart service..." + set_last_time "docker_down" + sudo systemctl restart docker + else + echo "Cannot connect to Docker, and service cannot be restarted" + local lastCalled=$(get_since_last_time "server_restart_needed") + if ((lastCalled == -1)) || ((lastCalled > DOCKER_DOWN_MAIL)); then + set_last_time "server_restart_needed" + echo "Sending mail to user - manual server restart needed" + mail_restart_needed "Usługa Docker uległa awarii i nie można automatycznie jej uruchomić" + else + echo "Mail for manual restart already recently sent" + fi + fi + fi + fi + fi + + echo "Watchdog observation: $WATCHDOG_STATUS" + + # if [ "$WATCHDOG_LAST_STATUS" != "$WATCHDOG_STATUS" ]; then + echo "$WATCHDOG_TIME [$WATCHDOG_STATUS]" >>"$WATCHDOG_LOG_FILE" + LOGSIZE=$(wc -l <"$WATCHDOG_LOG_FILE") + if [ "$LOGSIZE" -gt 1000 ]; then + tail -1000 "$WATCHDOG_LOG_FILE" >"$WATCHDOG_LOG_FILE.tmp" + mv -f "$WATCHDOG_LOG_FILE.tmp" "$WATCHDOG_LOG_FILE" + fi + # fi + + if [[ -f $WATCHDOG_FAILURES_FILE ]]; then + FAILSIZE=$(wc -l <"$WATCHDOG_FAILURES_FILE") + if [ "$FAILSIZE" -gt 10000 ]; then + tail -10000 "$WATCHDOG_FAILURES_FILE" >"$WATCHDOG_FAILURES_FILE.tmp" + mv -f "$WATCHDOG_FAILURES_FILE.tmp" "$WATCHDOG_FAILURES_FILE" + fi + fi + + echo "$WATCHDOG_TIME" >"$WATCHDOG_TIME_FILE" + echo "$WATCHDOG_STATUS" >"$WATCHDOG_STATUS_FILE" + + exit 0 +} \ No newline at end of file diff --git a/src/setup.sh b/src/setup.sh index 917f3b8..88b20d9 100755 --- a/src/setup.sh +++ b/src/setup.sh @@ -1,11 +1,11 @@ #!/bin/bash -### version: 1.9.5 +### version: 1.10.1 # ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.# # Nightscout Mikr.us setup script # # ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.# -# (c)2025 by Dominik Dzienia # +# (c)2023-2026 by Dominik Dzienia # # # # Licensed under MIT license # # ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.# @@ -13,6 +13,8 @@ # https://github.com/Homebrew/brew # # ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.# +###autogen-warning + #dev-begin DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi diff --git a/src/utils.sh b/src/utils.sh index 92f6019..c1b7118 100644 --- a/src/utils.sh +++ b/src/utils.sh @@ -15,6 +15,15 @@ major_minor() { )" } +extract_version() { + regex='version:\s+([0-9]+\.[0-9]+\.[0-9]+)' + if [[ "$1" =~ $regex ]]; then + echo "${BASH_REMATCH[1]}" + else + echo "0.0.0" + fi +} + version_gt() { [[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -gt "${2#*.}" ]] } @@ -34,3 +43,25 @@ exit_on_no_cancel() { exit 0 fi } + +check_interactive() { + shopt -q login_shell && echo 'Login shell' || echo 'Not login shell' + + # if [[ $- == *i* ]]; then + # msgok "Interactive setup" + # else + # msgok "Non-interactive setup" + # fi +} + +read_or_default() { + if [ -f "$1" ]; then + cat "$1" + else + if [ $# -eq 2 ]; then + echo "$2" + else + echo "" + fi + fi +} diff --git a/src/utils_app.sh b/src/utils_app.sh new file mode 100644 index 0000000..a62b10f --- /dev/null +++ b/src/utils_app.sh @@ -0,0 +1,7 @@ +#======================================= +# APP +#======================================= + +get_space_info() { + df -B1 --output=target,size,avail,pcent | tail -n +2 | awk '$1 ~ /^\/$/' +} diff --git a/src/utils_reminders.sh b/src/utils_reminders.sh new file mode 100644 index 0000000..c24922f --- /dev/null +++ b/src/utils_reminders.sh @@ -0,0 +1,114 @@ +#======================================= +# REMINDERS +#======================================= + +free_space_check() { + lastTimeSpaceInfo=$(get_space_info) + + local remainingB=$(echo "$lastTimeSpaceInfo" | awk '{print $3}') + local remainingTxt=$(echo "$lastTimeSpaceInfo" | awk '{print $3}' | numfmt --to iec-i --suffix=B) + + if ((remainingB < DISK_LOW_WARNING)); then + if ((remainingB < DISK_CRITICAL_WARNING)); then + local lastCalled=$(get_since_last_time "disk_critical") + local domain=$(get_td_domain) + if ((lastCalled == -1)) || ((lastCalled > DISK_CRITICAL_MAIL)); then + set_last_time "disk_critical" + { + echo "Na twoim serwerze mikr.us z Nightscoutem (https://$domain) zostało krytycznie mało miejsca (${remainingTxt})!" + echo " " + echo "Tak mała ilość miejsca nie pozwala serwerowi na stabilne działanie!" + echo "🚨PILNIE🚨 posprzątaj na serwerze, aby to zrobić możesz:" + echo " " + echo "1. Usunąć stare statusy i wpisy z poziomu strony Nightscout:" + echo " - wejdź do hamburger menu strony Nightscout i wybierz: 【 Narzędzia administratora 】- wymaga zalogowania" + echo " to powinno otwórzyć adres: https://${domain}/admin" + echo " - w polach tekstowych poustawiaj ile dni historii chcesz zachować, i w odpowiednich sekcjach kliknij:" + echo " 【 Usuń stare dokumenty 】" + echo " " + echo "2. Posprzątać nieużywane pliki na serwerze mikr.us:" + echo " - zaloguj się na swój mikr.us do panelu administracyjnego, przejdź do WebSSH" + echo " https://mikr.us/panel/?a=webssh" + echo " - zaloguj się, uruchom narzędzie komendą: nightscout-tool" + echo " - wybierz: 【 C) Sprztąj... 】" + echo " - wybierz: 【 A) Posprzątaj wszystko 】 i potwierdź 【 Tak 】" + echo " - cierpliwie poczekaj, po sprzątaniu narzędzie pokaże ile miejsca zwolniono" + } | pusher "🚨_Krytycznie_mało_miejsca_na_Twoim_serwerze_Nightscout!" + echo "Free space on server: CRITICALLY LOW (${remainingTxt}) - sending email to user" + else + echo "Free space on server: CRITICALLY LOW (${remainingTxt}) - user already notified" + fi + else + local lastCalled=$(get_since_last_time "disk_warning") + local domain=$(get_td_domain) + if ((lastCalled == -1)) || ((lastCalled > DISK_LOW_MAIL)); then + set_last_time "disk_warning" + { + echo "Na twoim serwerze mikr.us z Nightscout-em (https://$domain) powoli kończy się miejsce (${remainingTxt})!" + echo " " + echo "🧹 W wolnej chwili posprzątaj na serwerze, aby to zrobić możesz:" + echo " " + echo "1. Usunąć stare statusy i wpisy z poziomu strony Nightscout:" + echo " - wejdź do hamburger menu strony Nightscout i wybierz:【 Narzędzia administratora 】- wymaga zalogowania" + echo " to powinno otwórzyć adres: https://${domain}/admin" + echo " - w polach tekstowych poustawiaj ile dni historii chcesz zachować, i w odpowiednich sekcjach kliknij:" + echo " 【 Usuń stare dokumenty 】" + echo " " + echo "2. Posprzątać nieużywane pliki na serwerze mikr.us:" + echo " - zaloguj się na swój mikr.us do panelu administracyjnego, przejdź do WebSSH" + echo " https://mikr.us/panel/?a=webssh" + echo " - zaloguj się, uruchom narzędzie komendą: nightscout-tool" + echo " - wybierz: 【 C) Sprztąj... 】" + echo " - wybierz: 【 A) Posprzątaj wszystko 】 i potwierdź 【 Tak 】" + echo " - cierpliwie poczekaj, po sprzątaniu narzędzie pokaże ile miejsca zwolniono" + } | pusher "🧹_Powoli_kończy_sie_miejsce_na_Twoim_serwerze_Nightscout!" + echo "Free space on server: LOW (${remainingTxt}) - sending email to user" + else + echo "Free space on server: LOW (${remainingTxt}) - user already notified" + fi + fi + else + clear_last_time "disk_critical" + clear_last_time "disk_warning" + echo "Free space on server: OK (${remainingTxt})" + fi +} + +mail_restart_needed() { + local whyRestart="$1" + local mikrusSerwer=$(hostname) + { + echo "🛟 Twój serwer mikr.us z Nightscoutem potrzebuje restartu!" + echo " " + echo "🐕 Watchdog wykrył awarię której nie jest w stanie automatycznie naprawić:" + echo "$whyRestart" + echo " " + echo "Potrzebna będzie Twoja pomoc z ręcznym restartem serwera:" + echo " " + echo "1. Zaloguj się do panelu administracyjnego mikrusa" + echo " https://mikr.us/panel/" + echo " " + echo "2. Znajdź kafelek z nazwą serwera (${mikrusSerwer}) i kliknij na przycisk pod nim:" + echo " 【 Restart 】" + echo " " + echo "3. Potwierdź naciskając przycisk:" + echo " 【 Poproszę o restart VPSa 】" + echo " " + echo "==========================================================" + echo " " + echo "⏳ Restart serwera potrwa kilka minut, kolejne kilka minut potrwa uruchomienie serwera Nightscout" + echo "Jeśli po kilkunastu minutach serwer nie zacznie działać poprawnie:" + echo "Zaloguj się do panelu mikr.us-a, zaloguj się do WebSSH i w nightscout-tool sprawdź:" + echo "- czy kontenery są uruchomione - ich status i logi" + echo "- czy jest dosyć wolnego miejsca" + echo "W razie potrzeby - 🔄 zrestartuj kontenery i uruchom 🧹 sprzątanie (ale NIE usuwaj logów!)." + echo " " + echo "==========================================================" + echo " " + echo "Jeśli to nie pomoże, poszukaj wsparcia na grupie Technologie Diabetyka" + echo " 🙋 https://www.facebook.com/groups/techdiab" + echo "i - po uzgodnieniu!!! - wyślij diagnostykę do autora skryptu:" + echo " 📜 https://t1d.dzienia.pl/nightscout_mikrus_tutorial/stabilna/5.troubleshooting/#wysyanie-diagnostyki" + echo " " + } | pusher "🛟_Twoj_serwer_Nightscout_potrzebuje_ręcznego_restartu!" +} \ No newline at end of file diff --git a/tools/build.js b/tools/build.js index bf72521..d72c3a3 100644 --- a/tools/build.js +++ b/tools/build.js @@ -7,25 +7,39 @@ const regexScrVer = /(SCRIPT_VERSION\s*=\s*\")([0-9]+\.[0-9]+\.[0-9]+)(\"\s+#aut const regexDate = /(SCRIPT_BUILD_TIME\s*=\s*\")([0-9.]+)(\"\s+#auto-update)/mgs; const srcDir = fs.realpathSync(__dirname + path.sep + '..' + path.sep + 'src') -const baseDir = fs.realpathSync(__dirname + path.sep + '..' ) -const manifest = require(baseDir + path.sep + 'package.json' ) +const baseDir = fs.realpathSync(__dirname + path.sep + '..') +const manifest = require(baseDir + path.sep + 'package.json') console.log(`Version from manifest: ${manifest.version}`) const event = new Date(); const dateFormated = event.toISOString().substr(0, 10).replaceAll('-', '.'); +function toLocalDateStr(date = new Date()) { + const pad = n => String(Math.floor(Math.abs(n))).padStart(2, '0'); + return date.getFullYear() + '.' + + pad(date.getMonth() + 1) + '.' + + pad(date.getDate()) + ' ' + + pad(date.getHours()) + ':' + + pad(date.getMinutes()); +} + try { let data = fs.readFileSync(srcDir + path.sep + 'setup.sh', 'utf8'); + data = data.replace(/###autogen-warning/mgs, + "# This file is automatically generated. Do not modify it directly!\n" + + "# Instead, modify the source files in the src directory and run the build script!\n#\n" + + "# Build time: " + toLocalDateStr(event) + "\n" + ); while (data.includes('#include') || data.includes('#dev-begin')) { data = data.replace(regexDevRemove, ''); data = data.replace(regexInclude, (_, _2, fileName) => { const included = fs.readFileSync(srcDir + path.sep + fileName, 'utf8'); - return included+"\n\n" + return included + "\n\n" }); - - data = data.replace(regexVer, '$1'+manifest.version); - data = data.replace(regexScrVer, '$1'+manifest.version+'$3'); - data = data.replace(regexDate, '$1'+dateFormated+'$3'); + + data = data.replace(regexVer, '$1' + manifest.version); + data = data.replace(regexScrVer, '$1' + manifest.version + '$3'); + data = data.replace(regexDate, '$1' + dateFormated + '$3'); } fs.writeFileSync(baseDir + path.sep + 'install.sh', data); } catch (err) { diff --git a/updated b/updated index d1c3dd2..0a0bb57 100644 --- a/updated +++ b/updated @@ -1 +1 @@ -2026-01-05T15:40:04.465Z \ No newline at end of file +2026-01-05T21:10:02.232Z \ No newline at end of file