From 10ec52e9cbd5018f32f5dc0f05e5cc0628144ad8 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 24 Feb 2026 12:11:50 +0100 Subject: [PATCH] feat: add Docker-based tool addons for dockge, komodo, dokploy, npmplus Create addon scripts following the arcane.sh pattern for Docker-based tools that can be installed on any existing Docker LXC: - dockge: Docker Compose stack manager (port 5001) - komodo: Build/deployment system with MongoDB/FerretDB (port 9120) - dokploy: PaaS via external installer with Redis (port 3000) - npmplus: Nginx Proxy Manager Plus via Compose (port 81) Each addon includes: - Docker availability check - Install with full configuration - Update via docker compose pull - Uninstall with container cleanup - ASCII header files Original ct/ and install/ scripts are preserved for now. --- tools/addon/dockge.sh | 179 +++++++++++++++++++++++++++ tools/addon/dokploy.sh | 176 +++++++++++++++++++++++++++ tools/addon/komodo.sh | 267 +++++++++++++++++++++++++++++++++++++++++ tools/addon/npmplus.sh | 255 +++++++++++++++++++++++++++++++++++++++ tools/headers/dockge | 6 + tools/headers/dokploy | 6 + tools/headers/komodo | 5 + tools/headers/npmplus | 6 + 8 files changed, 900 insertions(+) create mode 100644 tools/addon/dockge.sh create mode 100644 tools/addon/dokploy.sh create mode 100644 tools/addon/komodo.sh create mode 100644 tools/addon/npmplus.sh create mode 100644 tools/headers/dockge create mode 100644 tools/headers/dokploy create mode 100644 tools/headers/komodo create mode 100644 tools/headers/npmplus diff --git a/tools/addon/dockge.sh b/tools/addon/dockge.sh new file mode 100644 index 000000000..233217dcf --- /dev/null +++ b/tools/addon/dockge.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: tteck (tteckster) | Addon: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://dockge.kuma.pet/ +if ! command -v curl &>/dev/null; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true + +# Enable error handling +set -Eeuo pipefail +trap 'error_handler' ERR + +# ============================================================================== +# CONFIGURATION +# ============================================================================== +APP="Dockge" +APP_TYPE="addon" +INSTALL_PATH="/opt/dockge" +STACKS_PATH="/opt/stacks" +COMPOSE_FILE="${INSTALL_PATH}/compose.yaml" +DEFAULT_PORT=5001 + +# Initialize all core functions (colors, formatting, icons, STD mode) +load_functions + +# ============================================================================== +# HEADER +# ============================================================================== +function header_info { + clear + cat <<"EOF" + ____ __ + / __ \____ _____/ /______ ____ + / / / / __ \/ ___/ //_/ __ `/ _ \ + / /_/ / /_/ / /__/ ,< / /_/ / __/ +/_____/\____/\___/_/|_|\__, /\___/ + /____/ + +EOF +} + +# ============================================================================== +# UNINSTALL +# ============================================================================== +function uninstall() { + msg_info "Uninstalling ${APP}" + + if [[ -f "$COMPOSE_FILE" ]]; then + msg_info "Stopping and removing Docker containers" + cd "$INSTALL_PATH" + $STD docker compose down --remove-orphans + msg_ok "Stopped and removed Docker containers" + fi + + rm -rf "$INSTALL_PATH" + msg_ok "${APP} has been uninstalled" + msg_warn "Stacks directory ${STACKS_PATH} was NOT removed. Delete manually if no longer needed." +} + +# ============================================================================== +# UPDATE +# ============================================================================== +function update() { + msg_info "Pulling latest ${APP} image" + cd "$INSTALL_PATH" + $STD docker compose pull + msg_ok "Pulled latest image" + + msg_info "Restarting ${APP}" + $STD docker compose up -d --remove-orphans + msg_ok "Restarted ${APP}" + + msg_ok "Updated successfully" + exit +} + +# ============================================================================== +# CHECK DOCKER +# ============================================================================== +function check_docker() { + if ! command -v docker &>/dev/null; then + msg_error "Docker is not installed. This addon requires an existing Docker LXC. Exiting." + exit 1 + fi + if ! docker compose version &>/dev/null; then + msg_error "Docker Compose plugin is not available. Please install it before running this script. Exiting." + exit 1 + fi + msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') and Docker Compose are available" +} + +# ============================================================================== +# INSTALL +# ============================================================================== +function install() { + check_docker + + msg_info "Creating install directories" + mkdir -p "$INSTALL_PATH" "$STACKS_PATH" + msg_ok "Created ${INSTALL_PATH} and ${STACKS_PATH}" + + msg_info "Downloading Docker Compose file" + curl -fsSL "https://raw.githubusercontent.com/louislam/dockge/master/compose.yaml" -o "$COMPOSE_FILE" + msg_ok "Downloaded Docker Compose file" + + msg_info "Starting ${APP}" + cd "$INSTALL_PATH" + $STD docker compose up -d + msg_ok "Started ${APP}" + + echo "" + msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" +} + +# ============================================================================== +# MAIN +# ============================================================================== + +# Handle type=update (called from update script) +if [[ "${type:-}" == "update" ]]; then + header_info + if [[ -f "$COMPOSE_FILE" ]]; then + update + else + msg_error "${APP} is not installed. Nothing to update." + exit 1 + fi + exit 0 +fi + +header_info +get_lxc_ip + +# Check if already installed +if [[ -f "$COMPOSE_FILE" ]]; then + msg_warn "${APP} is already installed." + echo "" + + echo -n "${TAB}Uninstall ${APP}? (y/N): " + read -r uninstall_prompt + if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then + uninstall + exit 0 + fi + + echo -n "${TAB}Update ${APP}? (y/N): " + read -r update_prompt + if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then + update + exit 0 + fi + + msg_warn "No action selected. Exiting." + exit 0 +fi + +# Fresh installation +msg_warn "${APP} is not installed." +echo "" +echo -e "${TAB}${INFO} This will install:" +echo -e "${TAB} - Dockge (via Docker Compose)" +echo "" + +echo -n "${TAB}Install ${APP}? (y/N): " +read -r install_prompt +if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then + install +else + msg_warn "Installation cancelled. Exiting." + exit 0 +fi diff --git a/tools/addon/dokploy.sh b/tools/addon/dokploy.sh new file mode 100644 index 000000000..64d8beecd --- /dev/null +++ b/tools/addon/dokploy.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://dokploy.com/ +if ! command -v curl &>/dev/null; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true + +# Enable error handling +set -Eeuo pipefail +trap 'error_handler' ERR + +# ============================================================================== +# CONFIGURATION +# ============================================================================== +APP="Dokploy" +APP_TYPE="addon" +INSTALL_PATH="/etc/dokploy" +DEFAULT_PORT=3000 + +# Initialize all core functions (colors, formatting, icons, STD mode) +load_functions + +# ============================================================================== +# HEADER +# ============================================================================== +function header_info { + clear + cat <<"EOF" + ____ __ __ + / __ \____ / /______ / /___ __ __ + / / / / __ \/ //_/ __ \/ / __ \/ / / / + / /_/ / /_/ / ,< / /_/ / / /_/ / /_/ / +/_____/\____/_/|_| .___/_/\____/\__, / + /_/ /____/ + +EOF +} + +# ============================================================================== +# UNINSTALL +# ============================================================================== +function uninstall() { + msg_info "Uninstalling ${APP}" + + if command -v docker &>/dev/null; then + msg_info "Stopping and removing Docker containers" + $STD docker stop $(docker ps -aq) 2>/dev/null || true + $STD docker rm $(docker ps -aq) 2>/dev/null || true + $STD docker network prune -f 2>/dev/null || true + msg_ok "Stopped and removed Docker containers" + fi + + rm -rf "$INSTALL_PATH" + msg_ok "${APP} has been uninstalled" +} + +# ============================================================================== +# UPDATE +# ============================================================================== +function update() { + msg_info "Updating ${APP}" + $STD curl -sSL https://dokploy.com/install.sh | bash -s update + msg_ok "Updated ${APP}" + + msg_ok "Updated successfully" + exit +} + +# ============================================================================== +# CHECK DOCKER +# ============================================================================== +function check_docker() { + if ! command -v docker &>/dev/null; then + msg_warn "Docker is not installed — Dokploy installer will set it up." + return + fi + msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') is available" +} + +# ============================================================================== +# INSTALL +# ============================================================================== +function install() { + check_docker + + msg_info "Installing dependencies" + $STD apt-get update + $STD apt-get install -y git openssl redis + msg_ok "Installed dependencies" + + msg_warn "WARNING: This will run an external installer from https://dokploy.com/" + msg_warn "The following code is NOT maintained or audited by our repository." + echo "" + echo -n "${TAB}Do you want to continue? (y/N): " + read -r confirm + if [[ ! "${confirm,,}" =~ ^(y|yes)$ ]]; then + msg_warn "Installation cancelled. Exiting." + exit 0 + fi + + msg_info "Installing ${APP} (this installs Docker and pulls containers)" + $STD bash <(curl -sSL https://dokploy.com/install.sh) + msg_ok "Installed ${APP}" + + echo "" + msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" +} + +# ============================================================================== +# MAIN +# ============================================================================== + +# Handle type=update (called from update script) +if [[ "${type:-}" == "update" ]]; then + header_info + if [[ -d "$INSTALL_PATH" ]]; then + update + else + msg_error "${APP} is not installed. Nothing to update." + exit 1 + fi + exit 0 +fi + +header_info +get_lxc_ip + +# Check if already installed +if [[ -d "$INSTALL_PATH" ]]; then + msg_warn "${APP} is already installed." + echo "" + + echo -n "${TAB}Uninstall ${APP}? (y/N): " + read -r uninstall_prompt + if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then + uninstall + exit 0 + fi + + echo -n "${TAB}Update ${APP}? (y/N): " + read -r update_prompt + if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then + update + exit 0 + fi + + msg_warn "No action selected. Exiting." + exit 0 +fi + +# Fresh installation +msg_warn "${APP} is not installed." +echo "" +echo -e "${TAB}${INFO} This will install:" +echo -e "${TAB} - Dokploy (via external installer)" +echo -e "${TAB} - Docker (if not already installed)" +echo -e "${TAB} - Redis" +echo "" + +echo -n "${TAB}Install ${APP}? (y/N): " +read -r install_prompt +if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then + install +else + msg_warn "Installation cancelled. Exiting." + exit 0 +fi diff --git a/tools/addon/komodo.sh b/tools/addon/komodo.sh new file mode 100644 index 000000000..9fed8af8a --- /dev/null +++ b/tools/addon/komodo.sh @@ -0,0 +1,267 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://komo.do/ +if ! command -v curl &>/dev/null; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 || apk update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 || apk add --no-cache curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true + +# Enable error handling +set -Eeuo pipefail +trap 'error_handler' ERR + +# ============================================================================== +# CONFIGURATION +# ============================================================================== +APP="Komodo" +APP_TYPE="addon" +INSTALL_PATH="/opt/komodo" +COMPOSE_ENV="${INSTALL_PATH}/compose.env" +DEFAULT_PORT=9120 + +# Initialize all core functions (colors, formatting, icons, STD mode) +load_functions + +# ============================================================================== +# HEADER +# ============================================================================== +function header_info { + clear + cat <<"EOF" + __ __ __ + / //_/___ ____ ___ ____ ____/ /___ + / ,< / __ \/ __ `__ \/ __ \/ __ / __ \ + / /| |/ /_/ / / / / / / /_/ / /_/ / /_/ / +/_/ |_|\____/_/ /_/ /_/\____/\__,_/\____/ + +EOF +} + +# ============================================================================== +# HELPERS +# ============================================================================== +function find_compose_file() { + COMPOSE_FILE=$(find "$INSTALL_PATH" -maxdepth 1 -type f -name '*.compose.yaml' ! -name 'compose.env' | head -n1) + if [[ -z "${COMPOSE_FILE:-}" ]]; then + msg_error "No valid compose file found in ${INSTALL_PATH}!" + exit 1 + fi + COMPOSE_BASENAME=$(basename "$COMPOSE_FILE") +} + +function check_legacy_db() { + if [[ "$COMPOSE_BASENAME" == "sqlite.compose.yaml" || "$COMPOSE_BASENAME" == "postgres.compose.yaml" ]]; then + msg_error "Detected outdated Komodo setup using SQLite or PostgreSQL (FerretDB v1)." + echo -e "${YW}This configuration is no longer supported since Komodo v1.18.0.${CL}" + echo -e "${YW}Please follow the migration guide:${CL}" + echo -e "${BGN}https://github.com/community-scripts/ProxmoxVE/discussions/5689${CL}\n" + exit 1 + fi +} + +# ============================================================================== +# UNINSTALL +# ============================================================================== +function uninstall() { + msg_info "Uninstalling ${APP}" + + find_compose_file + msg_info "Stopping and removing Docker containers" + cd "$INSTALL_PATH" + $STD docker compose -p komodo -f "$COMPOSE_FILE" --env-file "$COMPOSE_ENV" down --volumes --remove-orphans + msg_ok "Stopped and removed Docker containers" + + rm -rf "$INSTALL_PATH" + msg_ok "${APP} has been uninstalled" +} + +# ============================================================================== +# UPDATE +# ============================================================================== +function update() { + find_compose_file + check_legacy_db + + msg_info "Updating ${APP}" + BACKUP_FILE="${INSTALL_PATH}/${COMPOSE_BASENAME}.bak_$(date +%Y%m%d_%H%M%S)" + cp "$COMPOSE_FILE" "$BACKUP_FILE" || { + msg_error "Failed to create backup of ${COMPOSE_BASENAME}!" + exit 1 + } + + GITHUB_URL="https://raw.githubusercontent.com/moghtech/komodo/main/compose/${COMPOSE_BASENAME}" + if ! curl -fsSL "$GITHUB_URL" -o "$COMPOSE_FILE"; then + msg_error "Failed to download ${COMPOSE_BASENAME} from GitHub!" + mv "$BACKUP_FILE" "$COMPOSE_FILE" + exit 1 + fi + + if ! grep -qxF 'COMPOSE_KOMODO_BACKUPS_PATH=/etc/komodo/backups' "$COMPOSE_ENV"; then + sed -i '/^COMPOSE_KOMODO_IMAGE_TAG=latest$/a COMPOSE_KOMODO_BACKUPS_PATH=/etc/komodo/backups' "$COMPOSE_ENV" + fi + + $STD docker compose -p komodo -f "$COMPOSE_FILE" --env-file "$COMPOSE_ENV" pull + $STD docker compose -p komodo -f "$COMPOSE_FILE" --env-file "$COMPOSE_ENV" up -d + msg_ok "Updated ${APP}" + + msg_ok "Updated successfully" + exit +} + +# ============================================================================== +# CHECK DOCKER +# ============================================================================== +function check_docker() { + if ! command -v docker &>/dev/null; then + msg_error "Docker is not installed. This addon requires an existing Docker LXC. Exiting." + exit 1 + fi + if ! docker compose version &>/dev/null; then + msg_error "Docker Compose plugin is not available. Please install it before running this script. Exiting." + exit 1 + fi + msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') and Docker Compose are available" +} + +# ============================================================================== +# INSTALL +# ============================================================================== +function install() { + check_docker + + echo -e "${TAB}Choose the database for Komodo:" + echo -e "${TAB} 1) MongoDB (recommended)" + echo -e "${TAB} 2) FerretDB" + echo -n "${TAB}Enter your choice (default: 1): " + read -r DB_CHOICE + DB_CHOICE=${DB_CHOICE:-1} + + case $DB_CHOICE in + 1) DB_COMPOSE_FILE="mongo.compose.yaml" ;; + 2) DB_COMPOSE_FILE="ferretdb.compose.yaml" ;; + *) + msg_warn "Invalid choice. Defaulting to MongoDB." + DB_COMPOSE_FILE="mongo.compose.yaml" + ;; + esac + + msg_info "Creating install directory" + mkdir -p "$INSTALL_PATH" + msg_ok "Created ${INSTALL_PATH}" + + msg_info "Downloading Docker Compose file" + curl -fsSL "https://raw.githubusercontent.com/moghtech/komodo/main/compose/$DB_COMPOSE_FILE" -o "${INSTALL_PATH}/${DB_COMPOSE_FILE}" + msg_ok "Downloaded ${DB_COMPOSE_FILE}" + + msg_info "Configuring environment" + curl -fsSL "https://raw.githubusercontent.com/moghtech/komodo/main/compose/compose.env" -o "$COMPOSE_ENV" + + DB_PASSWORD=$(openssl rand -base64 16 | tr -d '/+=') + ADMIN_PASSWORD=$(openssl rand -base64 8 | tr -d '/+=') + PASSKEY=$(openssl rand -base64 24 | tr -d '/+=') + WEBHOOK_SECRET=$(openssl rand -base64 24 | tr -d '/+=') + JWT_SECRET=$(openssl rand -base64 24 | tr -d '/+=') + + sed -i "s/^KOMODO_DB_USERNAME=.*/KOMODO_DB_USERNAME=komodo_admin/" "$COMPOSE_ENV" + sed -i "s/^KOMODO_DB_PASSWORD=.*/KOMODO_DB_PASSWORD=${DB_PASSWORD}/" "$COMPOSE_ENV" + sed -i "s/^KOMODO_INIT_ADMIN_PASSWORD=changeme/KOMODO_INIT_ADMIN_PASSWORD=${ADMIN_PASSWORD}/" "$COMPOSE_ENV" + sed -i "s/^KOMODO_PASSKEY=.*/KOMODO_PASSKEY=${PASSKEY}/" "$COMPOSE_ENV" + sed -i "s/^KOMODO_WEBHOOK_SECRET=.*/KOMODO_WEBHOOK_SECRET=${WEBHOOK_SECRET}/" "$COMPOSE_ENV" + sed -i "s/^KOMODO_JWT_SECRET=.*/KOMODO_JWT_SECRET=${JWT_SECRET}/" "$COMPOSE_ENV" + msg_ok "Configured environment" + + msg_info "Starting ${APP}" + cd "$INSTALL_PATH" + $STD docker compose -p komodo -f "${INSTALL_PATH}/${DB_COMPOSE_FILE}" --env-file "$COMPOSE_ENV" up -d + msg_ok "Started ${APP}" + + { + echo "Komodo Credentials" + echo "" + echo "Admin User : admin" + echo "Admin Password: $ADMIN_PASSWORD" + } >>~/komodo.creds + + echo "" + msg_ok "${APP} is reachable at: ${BL}http://${LOCAL_IP}:${DEFAULT_PORT}${CL}" + echo "" + echo -e " Komodo Credentials" + echo -e " ==================" + echo -e " User : admin" + echo -e " Password: ${ADMIN_PASSWORD}" + echo "" + msg_info "Credentials saved to ~/komodo.creds" +} + +# ============================================================================== +# MAIN +# ============================================================================== + +# Handle type=update (called from update script) +if [[ "${type:-}" == "update" ]]; then + header_info + COMPOSE_FILE="" + COMPOSE_BASENAME="" + if [[ -d "$INSTALL_PATH" ]]; then + update + else + msg_error "${APP} is not installed. Nothing to update." + exit 1 + fi + exit 0 +fi + +header_info +get_lxc_ip + +# Declare variables used by find_compose_file +COMPOSE_FILE="" +COMPOSE_BASENAME="" + +# Check if already installed +if [[ -d "$INSTALL_PATH" ]]; then + msg_warn "${APP} is already installed." + echo "" + + echo -n "${TAB}Uninstall ${APP}? (y/N): " + read -r uninstall_prompt + if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then + uninstall + exit 0 + fi + + echo -n "${TAB}Update ${APP}? (y/N): " + read -r update_prompt + if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then + update + exit 0 + fi + + msg_warn "No action selected. Exiting." + exit 0 +fi + +# Fresh installation +msg_warn "${APP} is not installed." +echo "" +echo -e "${TAB}${INFO} This will install:" +echo -e "${TAB} - Komodo (via Docker Compose)" +echo -e "${TAB} - MongoDB or FerretDB (your choice)" +echo "" + +echo -n "${TAB}Install ${APP}? (y/N): " +read -r install_prompt +if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then + install +else + msg_warn "Installation cancelled. Exiting." + exit 0 +fi diff --git a/tools/addon/npmplus.sh b/tools/addon/npmplus.sh new file mode 100644 index 000000000..4d7e82510 --- /dev/null +++ b/tools/addon/npmplus.sh @@ -0,0 +1,255 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/ZoeyVid/NPMplus +if ! command -v curl &>/dev/null; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 || apk update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 || apk add --no-cache curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true + +# Enable error handling +set -Eeuo pipefail +trap 'error_handler' ERR + +# ============================================================================== +# CONFIGURATION +# ============================================================================== +APP="NPMplus" +APP_TYPE="addon" +INSTALL_PATH="/opt/npmplus" +COMPOSE_FILE="${INSTALL_PATH}/compose.yaml" +DEFAULT_PORT=81 + +# Initialize all core functions (colors, formatting, icons, STD mode) +load_functions + +# ============================================================================== +# HEADER +# ============================================================================== +function header_info { + clear + cat <<"EOF" + _ ______ __ ___ __ + / | / / __ \/ |/ /____ / /_ _______ + / |/ / /_/ / /|_/ / __ \/ / / / / ___/ + / /| / ____/ / / / /_/ / / /_/ (__ ) +/_/ |_/_/ /_/ /_/ .___/_/\__,_/____/ + /_/ + +EOF +} + +# ============================================================================== +# UNINSTALL +# ============================================================================== +function uninstall() { + msg_info "Uninstalling ${APP}" + + if [[ -f "$COMPOSE_FILE" ]]; then + msg_info "Stopping and removing Docker containers" + cd "$INSTALL_PATH" + $STD docker compose down --volumes --remove-orphans + msg_ok "Stopped and removed Docker containers" + fi + + rm -rf "$INSTALL_PATH" + msg_ok "${APP} has been uninstalled" +} + +# ============================================================================== +# UPDATE +# ============================================================================== +function update() { + msg_info "Pulling latest ${APP} image" + cd "$INSTALL_PATH" + $STD docker compose pull + msg_ok "Pulled latest image" + + msg_info "Recreating container" + $STD docker compose up -d --remove-orphans + msg_ok "Recreated container" + + msg_ok "Updated successfully" + exit +} + +# ============================================================================== +# CHECK DOCKER +# ============================================================================== +function check_docker() { + if ! command -v docker &>/dev/null; then + msg_error "Docker is not installed. This addon requires an existing Docker LXC. Exiting." + exit 1 + fi + if ! docker compose version &>/dev/null; then + msg_error "Docker Compose plugin is not available. Please install it before running this script. Exiting." + exit 1 + fi + msg_ok "Docker $(docker --version | cut -d' ' -f3 | tr -d ',') and Docker Compose are available" +} + +# ============================================================================== +# HELPERS +# ============================================================================== +function validate_tz() { + local tz="$1" + [[ -f "/usr/share/zoneinfo/$tz" ]] +} + +# ============================================================================== +# INSTALL +# ============================================================================== +function install() { + check_docker + + msg_info "Creating install directory" + mkdir -p "$INSTALL_PATH" + msg_ok "Created ${INSTALL_PATH}" + + # Install yq if not available + if ! command -v yq &>/dev/null; then + msg_info "Installing yq" + if command -v apt-get &>/dev/null; then + $STD apt-get install -y gawk + YQ_URL="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64" + elif command -v apk &>/dev/null; then + $STD apk add --no-cache gawk yq + fi + if ! command -v yq &>/dev/null && [[ -n "${YQ_URL:-}" ]]; then + curl -fsSL "$YQ_URL" -o /usr/local/bin/yq + chmod +x /usr/local/bin/yq + fi + msg_ok "Installed yq" + fi + + msg_info "Downloading Docker Compose file" + curl -fsSL "https://raw.githubusercontent.com/ZoeyVid/NPMplus/refs/heads/develop/compose.yaml" -o "$COMPOSE_FILE" + msg_ok "Downloaded Docker Compose file" + + # Timezone configuration + local attempts=0 + while true; do + echo -n "${TAB}Enter your TZ Identifier (e.g., Europe/Berlin): " + read -r TZ_INPUT + if validate_tz "$TZ_INPUT"; then + break + fi + msg_error "Invalid timezone! Please enter a valid TZ identifier." + attempts=$((attempts + 1)) + if [[ "$attempts" -ge 3 ]]; then + msg_error "Maximum attempts reached. Exiting." + exit 1 + fi + done + + echo -n "${TAB}Enter your ACME Email: " + read -r ACME_EMAIL_INPUT + + msg_info "Configuring NPMplus" + yq -i " + .services.npmplus.environment |= + (map(select(. != \"TZ=*\" and . != \"ACME_EMAIL=*\" and . != \"INITIAL_ADMIN_EMAIL=*\" and . != \"INITIAL_ADMIN_PASSWORD=*\")) + + [\"TZ=$TZ_INPUT\", \"ACME_EMAIL=$ACME_EMAIL_INPUT\", \"INITIAL_ADMIN_EMAIL=admin@local.com\", \"INITIAL_ADMIN_PASSWORD=helper-scripts.com\"]) + " "$COMPOSE_FILE" + msg_ok "Configured NPMplus" + + msg_info "Starting ${APP} (pulling images, please wait)" + cd "$INSTALL_PATH" + $STD docker compose up -d + + # Wait for healthy container + local CONTAINER_ID="" + for i in {1..60}; do + CONTAINER_ID=$(docker ps --filter "name=npmplus" --format "{{.ID}}" 2>/dev/null || echo "") + if [[ -n "$CONTAINER_ID" ]]; then + local STATUS + STATUS=$(docker inspect --format '{{.State.Health.Status}}' "$CONTAINER_ID" 2>/dev/null || echo "starting") + if [[ "$STATUS" == "healthy" ]]; then + msg_ok "${APP} is running and healthy" + break + elif [[ "$STATUS" == "unhealthy" ]]; then + msg_error "${APP} container is unhealthy! Check: docker logs $CONTAINER_ID" + exit 1 + fi + fi + sleep 2 + if [[ $i -eq 60 ]]; then + msg_error "${APP} container did not become healthy within 120s." + exit 1 + fi + done + msg_ok "Started ${APP}" + + echo "" + msg_ok "${APP} is reachable at: ${BL}https://${LOCAL_IP}:${DEFAULT_PORT}${CL}" + echo "" + echo -e " NPMplus Credentials" + echo -e " ===================" + echo -e " Email : admin@local.com" + echo -e " Password: helper-scripts.com" +} + +# ============================================================================== +# MAIN +# ============================================================================== + +# Handle type=update (called from update script) +if [[ "${type:-}" == "update" ]]; then + header_info + if [[ -f "$COMPOSE_FILE" ]]; then + update + else + msg_error "${APP} is not installed. Nothing to update." + exit 1 + fi + exit 0 +fi + +header_info +get_lxc_ip + +# Check if already installed +if [[ -f "$COMPOSE_FILE" ]]; then + msg_warn "${APP} is already installed." + echo "" + + echo -n "${TAB}Uninstall ${APP}? (y/N): " + read -r uninstall_prompt + if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then + uninstall + exit 0 + fi + + echo -n "${TAB}Update ${APP}? (y/N): " + read -r update_prompt + if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then + update + exit 0 + fi + + msg_warn "No action selected. Exiting." + exit 0 +fi + +# Fresh installation +msg_warn "${APP} is not installed." +echo "" +echo -e "${TAB}${INFO} This will install:" +echo -e "${TAB} - NPMplus (via Docker Compose)" +echo "" + +echo -n "${TAB}Install ${APP}? (y/N): " +read -r install_prompt +if [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then + install +else + msg_warn "Installation cancelled. Exiting." + exit 0 +fi diff --git a/tools/headers/dockge b/tools/headers/dockge new file mode 100644 index 000000000..dc84960a8 --- /dev/null +++ b/tools/headers/dockge @@ -0,0 +1,6 @@ + ____ __ + / __ \____ _____/ /______ ____ + / / / / __ \/ ___/ //_/ __ `/ _ \ + / /_/ / /_/ / /__/ ,< / /_/ / __/ +/_____/\____/\___/_/|_|\__, /\___/ + /____/ diff --git a/tools/headers/dokploy b/tools/headers/dokploy new file mode 100644 index 000000000..1104db800 --- /dev/null +++ b/tools/headers/dokploy @@ -0,0 +1,6 @@ + ____ __ __ + / __ \____ / /______ / /___ __ __ + / / / / __ \/ //_/ __ \/ / __ \/ / / / + / /_/ / /_/ / ,< / /_/ / / /_/ / /_/ / +/_____/\____/_/|_| .___/_/\____/\__, / + /_/ /____/ diff --git a/tools/headers/komodo b/tools/headers/komodo new file mode 100644 index 000000000..d7c7a6991 --- /dev/null +++ b/tools/headers/komodo @@ -0,0 +1,5 @@ + __ __ __ + / //_/___ ____ ___ ____ ____/ /___ + / ,< / __ \/ __ `__ \/ __ \/ __ / __ \ + / /| |/ /_/ / / / / / / /_/ / /_/ / /_/ / +/_/ |_|\____/_/ /_/ /_/\____/\__,_/\____/ diff --git a/tools/headers/npmplus b/tools/headers/npmplus new file mode 100644 index 000000000..caa2d98ac --- /dev/null +++ b/tools/headers/npmplus @@ -0,0 +1,6 @@ + _ ______ __ ___ __ + / | / / __ \/ |/ /____ / /_ _______ + / |/ / /_/ / /|_/ / __ \/ / / / / ___/ + / /| / ____/ / / / /_/ / / /_/ (__ ) +/_/ |_/_/ /_/ /_/ .___/_/\__,_/____/ + /_/