Add auto-update system for DashCaddy instances

- self-updater.js: polls for new versions, downloads/verifies tarballs,
  triggers host-side rebuild via systemd path unit
- dashcaddy-update.sh + systemd units: host-side container rebuild with
  automatic rollback on health check failure
- 7 new /api/v1/system/* endpoints for version info, update check/apply,
  rollback, and update history
- Frontend: DashCaddy tab in Updates modal with version display,
  changelog, update button, rollback, and notification dot
- install.sh: updater service installation, volume mounts, env vars
- build-release.sh + webhook-handler.js: release server pipeline
  (Gitea webhook → build tarball → deploy to get.dashcaddy.net)
- Dockerfile: DASHCADDY_COMMIT build arg → VERSION file
- Version bump to 1.1.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 03:11:35 -08:00
parent 9a0abc02d1
commit ffa6966fd3
14 changed files with 1395 additions and 4 deletions

View File

@@ -17,7 +17,7 @@
set -euo pipefail
# ---- Constants -------------------------------------------------------------
readonly DASHCADDY_VERSION="1.0.0"
readonly DASHCADDY_VERSION="1.1.0"
readonly DASHCADDY_DOWNLOAD="https://get.dashcaddy.net/release/latest.tar.gz"
readonly DASHCADDY_REPO="" # Set to a git URL to clone instead of downloading
readonly INSTALL_DIR="/etc/dashcaddy"
@@ -388,6 +388,7 @@ EOF
create_directories() {
mkdir -p "$INSTALL_DIR" "$DOCKER_DATA" "$SITES_DIR" "$API_DIR" "$DASHBOARD_DIR" "${DASHBOARD_DIR}/assets"
mkdir -p /opt/dashcaddy/updates /opt/dashcaddy/scripts
ok "Directories created"
}
@@ -444,6 +445,19 @@ fetch_source() {
done
ok "Dashboard files deployed"
# Deploy updater scripts
local scripts_src=""
for try in "${api_src}/scripts" "${tmp_src}/scripts"; do
[[ -d "$try" ]] && scripts_src="$try" && break
done
if [[ -n "$scripts_src" ]]; then
cp -f "${scripts_src}/dashcaddy-update.sh" /opt/dashcaddy/scripts/ 2>/dev/null || true
cp -f "${scripts_src}/dashcaddy-updater.path" /opt/dashcaddy/scripts/ 2>/dev/null || true
cp -f "${scripts_src}/dashcaddy-updater.service" /opt/dashcaddy/scripts/ 2>/dev/null || true
chmod +x /opt/dashcaddy/scripts/dashcaddy-update.sh 2>/dev/null || true
ok "Updater scripts deployed"
fi
# Cleanup
[[ -z "$SOURCE_PATH" ]] && rm -rf "$tmp_src"
}
@@ -630,6 +644,8 @@ services:
- ${INSTALL_DIR}/totp-config.json:/app/totp-config.json:rw
- ${INSTALL_DIR}/notifications.json:/app/notifications.json:rw
- ${DASHBOARD_DIR}/assets:/app/assets:rw
- ${DASHBOARD_DIR}:/app/dashboard:rw
- /opt/dashcaddy/updates:/app/updates:rw
- /var/run/docker.sock:/var/run/docker.sock
environment:
- CADDYFILE_PATH=/caddyfile
@@ -641,6 +657,12 @@ services:
- DNS_CREDENTIALS_FILE=/app/dns-credentials.json
- HOST_LAN_IP=${LAN_IP}
- NODE_ENV=production
- DASHCADDY_UPDATE_ENABLED=true
- DASHCADDY_UPDATE_URL=https://get.dashcaddy.net/release
- DASHCADDY_MIRROR_URL=https://get2.dashcaddy.net/release
- DASHCADDY_UPDATES_DIR=/app/updates
- DASHCADDY_API_SOURCE_DIR=${API_DIR}
- DASHCADDY_FRONTEND_DIR=/app/dashboard
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
@@ -654,6 +676,57 @@ DCEOF
ok "docker-compose.yml generated"
}
# ============================================================================
# Auto-Updater Service
# ============================================================================
install_updater_service() {
local scripts_dir="/opt/dashcaddy/scripts"
if [[ ! -f "${scripts_dir}/dashcaddy-update.sh" ]]; then
warn "Updater script not found — skipping auto-update service"
return
fi
# Install systemd units
if [[ -f "${scripts_dir}/dashcaddy-updater.path" ]]; then
cp -f "${scripts_dir}/dashcaddy-updater.path" /etc/systemd/system/
else
cat > /etc/systemd/system/dashcaddy-updater.path <<'PATHEOF'
[Unit]
Description=Watch for DashCaddy update trigger
[Path]
PathChanged=/opt/dashcaddy/updates/trigger.json
MakeDirectory=yes
[Install]
WantedBy=multi-user.target
PATHEOF
fi
if [[ -f "${scripts_dir}/dashcaddy-updater.service" ]]; then
cp -f "${scripts_dir}/dashcaddy-updater.service" /etc/systemd/system/
else
cat > /etc/systemd/system/dashcaddy-updater.service <<'SVCEOF'
[Unit]
Description=DashCaddy auto-update handler
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
ExecStart=/opt/dashcaddy/scripts/dashcaddy-update.sh
TimeoutStartSec=300
StandardOutput=journal
StandardError=journal
SyslogIdentifier=dashcaddy-update
SVCEOF
fi
systemctl daemon-reload
systemctl enable dashcaddy-updater.path >/dev/null 2>&1
systemctl start dashcaddy-updater.path >/dev/null 2>&1
ok "Auto-updater service installed"
}
# ============================================================================
# Build & Launch
# ============================================================================
@@ -759,6 +832,13 @@ do_uninstall() {
docker rm -f "$CONTAINER_NAME" 2>/dev/null && ok "Container removed" || true
# Stop and remove updater service
systemctl stop dashcaddy-updater.path 2>/dev/null || true
systemctl disable dashcaddy-updater.path 2>/dev/null || true
rm -f /etc/systemd/system/dashcaddy-updater.path /etc/systemd/system/dashcaddy-updater.service
systemctl daemon-reload 2>/dev/null || true
rm -rf /opt/dashcaddy && ok "Updater service removed" || true
if [[ -L /etc/caddy/Caddyfile ]] && readlink /etc/caddy/Caddyfile | grep -q dashcaddy; then
rm -f /etc/caddy/Caddyfile
[[ -f /etc/caddy/Caddyfile.original ]] && mv /etc/caddy/Caddyfile.original /etc/caddy/Caddyfile
@@ -949,6 +1029,7 @@ main() {
# ---- Step 6: Build & start ----
step "Building & starting services"
build_and_start
install_updater_service
# ---- Step 7: Start Caddy ----
step "Starting web server"