diff --git a/tests/duplicati.conf b/tests/duplicati.conf new file mode 100644 index 00000000..4187ccb2 --- /dev/null +++ b/tests/duplicati.conf @@ -0,0 +1,11 @@ +ENABLED=true +RELEASE="noble" +TESTNAME="Duplicati install" + +testcase() {( + set -e + ./bin/armbian-config --api module_duplicati remove + ./bin/armbian-config --api module_duplicati install "unittestkey123" "unittestpass123" + ./bin/armbian-config --api module_duplicati status + ./bin/armbian-config --api module_duplicati remove +)} diff --git a/tools/include/images/DPL001.png b/tools/include/images/DPL001.png new file mode 100644 index 00000000..b997d476 Binary files /dev/null and b/tools/include/images/DPL001.png differ diff --git a/tools/include/markdown/DPL001-footer.md b/tools/include/markdown/DPL001-footer.md new file mode 100644 index 00000000..ee5fb7a6 --- /dev/null +++ b/tools/include/markdown/DPL001-footer.md @@ -0,0 +1,17 @@ +=== "Access to the web interface" + + The web interface is accessible via port **8200**: + + - URL: `http://:8200` + +=== "Directories" + + - Install directory: `/armbian/duplicati` + - Configuration directory: `/armbian/duplicati/config` + - Backup target directory: `/armbian/duplicati/backups` + +=== "View logs" + + ```sh + docker logs -f duplicati + ``` diff --git a/tools/include/markdown/DPL001-header.md b/tools/include/markdown/DPL001-header.md new file mode 100644 index 00000000..c2c6b15e --- /dev/null +++ b/tools/include/markdown/DPL001-header.md @@ -0,0 +1,10 @@ +Duplicati is a versatile and secure backup tool designed for everyone, including: + +- Users new to backup systems who need a simple and reliable solution. +- Experienced users who want full control over encrypted backups and storage destinations. +- System administrators who require automated, encrypted backups across multiple platforms. + +Duplicati offers powerful features such as strong AES-256 encryption, backup scheduling, and flexible storage support (local folders, NAS, cloud providers like Google Drive, Dropbox, S3, and more). +Through its web-based interface, users can easily configure, monitor, and restore backups from any browser. + +Thanks to Duplicati’s smart design — working through standard protocols and containerized deployment — it fits seamlessly into any environment, from personal setups to enterprise infrastructures. diff --git a/tools/json/config.software.json b/tools/json/config.software.json index 549dc46b..5f0d5da0 100644 --- a/tools/json/config.software.json +++ b/tools/json/config.software.json @@ -627,6 +627,46 @@ } ] }, + { + "id": "Backup", + "description": "Backup solutions for your data", + "sub": [ + { + "id": "DUP001", + "description": "Duplicati install", + "short": "Duplicati", + "about": "This operation will install Duplicati backup software", + "command": [ + "module_duplicati install" + ], + "status": "Stable", + "author": "@yourname", + "condition": "! module_duplicati status" + }, + { + "id": "DUP002", + "description": "Duplicati remove", + "about": "This operation will remove Duplicati backup software", + "command": [ + "module_duplicati remove" + ], + "status": "Stable", + "author": "@yourname", + "condition": "module_duplicati status" + }, + { + "id": "DUP003", + "description": "Duplicati purge with data folder", + "about": "This operation will purge Duplicati data and remove the software", + "command": [ + "module_duplicati purge" + ], + "status": "Stable", + "author": "@yourname", + "condition": "module_duplicati status" + } + ] + }, { "id": "Downloaders", "description": "Download apps for movies, TV shows, music and subtitles", diff --git a/tools/modules/runtime/config.runtime.sh b/tools/modules/runtime/config.runtime.sh index b266a21d..98dac962 100644 --- a/tools/modules/runtime/config.runtime.sh +++ b/tools/modules/runtime/config.runtime.sh @@ -120,6 +120,9 @@ update_sub_submenu_data "Software" "Media" "JMS002" "http://$LOCALIPADD:${module # Containers update_sub_submenu_data "Software" "Containers" "POR002" "http://$LOCALIPADD:${module_options["module_portainer,port"]%% *}" # removing second port from url +# Backup +update_sub_submenu_data "Software" "Backup" "DUP002" "http://$LOCALIPADD:${module_options["module_duplicati,port"]%% *}" # removing second port from url + # Printing update_sub_submenu_data "Software" "Printing" "OCT002" "http://$LOCALIPADD:${module_options["module_octoprint,port"]}" diff --git a/tools/modules/software/module_duplicati.sh b/tools/modules/software/module_duplicati.sh new file mode 100644 index 00000000..626772a6 --- /dev/null +++ b/tools/modules/software/module_duplicati.sh @@ -0,0 +1,117 @@ +module_options+=( + ["module_duplicati,author"]="" + ["module_duplicati,maintainer"]="@igorpecovnik" + ["module_duplicati,feature"]="module_duplicati" + ["module_duplicati,example"]="install remove purge status help" + ["module_duplicati,desc"]="Install duplicati container" + ["module_duplicati,status"]="Active" + ["module_duplicati,doc_link"]="https://prev-docs.duplicati.com/en/latest/" + ["module_duplicati,group"]="Backup" + ["module_duplicati,port"]="8200" + ["module_duplicati,arch"]="x86-64 arm64" +) +# +# Module duplicati +# +function module_duplicati () { + local title="duplicati" + local condition=$(which "$title" 2>/dev/null) + + if pkg_installed docker-ce; then + local container=$(docker container ls -a | mawk '/duplicati?( |$)/{print $1}') + local image=$(docker image ls -a | mawk '/duplicati?( |$)/{print $3}') + fi + + local commands + IFS=' ' read -r -a commands <<< "${module_options["module_duplicati,example"]}" + + DUPLICATI_BASE="${SOFTWARE_FOLDER}/duplicati" + + case "$1" in + "${commands[0]}") + shift + # Accept encryption key and WebUI password from parameters if provided + local DUPLICATI_ENCRYPTION_KEY="$1" + local DUPLICATI_WEBUI_PASSWORD="$2" + + pkg_installed docker-ce || module_docker install + [[ -d "$DUPLICATI_BASE" ]] || mkdir -p "$DUPLICATI_BASE" || { echo "Couldn't create storage directory: $DUPLICATI_BASE"; exit 1; } + + # If no encryption key provided, prompt for it + if [[ -z "${DUPLICATI_ENCRYPTION_KEY}" ]]; then + DUPLICATI_ENCRYPTION_KEY=$($DIALOG --title "Duplicati Encryption Key" --inputbox "\nEnter an encryption key for Duplicati (at least 8 characters):" 9 60 "" 3>&1 1>&2 2>&3) + fi + + # Check encryption key length + if [[ -z "${DUPLICATI_ENCRYPTION_KEY}" || ${#DUPLICATI_ENCRYPTION_KEY} -lt 8 ]]; then + echo -e "\nError: Encryption key must be at least 8 characters long!" + exit 1 + fi + + # If no WebUI password provided, prompt for it + if [[ -z "${DUPLICATI_WEBUI_PASSWORD}" ]]; then + DUPLICATI_WEBUI_PASSWORD=$($DIALOG --title "Duplicati WebUI Password" --inputbox "\nEnter a password for Duplicati WebUI (at least 8 characters):" 9 60 "" 3>&1 1>&2 2>&3) + fi + + # Check WebUI password length + if [[ -z "${DUPLICATI_WEBUI_PASSWORD}" || ${#DUPLICATI_WEBUI_PASSWORD} -lt 8 ]]; then + echo -e "\nError: WebUI password must be at least 8 characters long!" + exit 1 + fi + + docker run -d \ + --name=duplicati \ + --net=lsio \ + -e PUID=1000 \ + -e PGID=1000 \ + -e TZ="$(cat /etc/timezone)" \ + -e SETTINGS_ENCRYPTION_KEY="${DUPLICATI_ENCRYPTION_KEY}" \ + -e DUPLICATI__WEBSERVICE_PASSWORD="${DUPLICATI_WEBUI_PASSWORD}" \ + -p 8200:8200 \ + -v "${DUPLICATI_BASE}/config:/config" \ + -v "${DUPLICATI_BASE}/backups:/backups" \ + -v /:/source:ro \ + --restart unless-stopped \ + lscr.io/linuxserver/duplicati:latest + for i in $(seq 1 20); do + if docker inspect -f '{{ index .Config.Labels "build_version" }}' duplicati >/dev/null 2>&1 ; then + break + else + sleep 3 + fi + if [ $i -eq 20 ]; then + echo -e "\nTimed out waiting for ${title} to start, consult your container logs for more info (\`docker logs duplicati\`)" + exit 1 + fi + done + ;; + "${commands[1]}") + [[ "${container}" ]] && docker container rm -f "$container" >/dev/null + [[ "${image}" ]] && docker image rm "$image" >/dev/null + ;; + "${commands[2]}") + ${module_options["module_duplicati,feature"]} ${commands[1]} + [[ -n "${DUPLICATI_BASE}" && "${DUPLICATI_BASE}" != "/" ]] && rm -rf "${DUPLICATI_BASE}" + ;; + "${commands[3]}") + if [[ "${container}" && "${image}" ]]; then + return 0 + else + return 1 + fi + ;; + "${commands[4]}") + echo -e "\nUsage: ${module_options["module_duplicati,feature"]} " + echo -e "Commands: ${module_options["module_duplicati,example"]}" + echo "Available commands:" + echo -e "\tinstall [key] [password] - Install $title. (parameters optional)" + echo -e "\tremove\t- Remove $title." + echo -e "\tpurge\t- Purge $title data folder." + echo -e "\tstatus\t- Installation status $title." + echo + ;; + *) + ${module_options["module_duplicati,feature"]} ${commands[4]} + ;; + esac +}