#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" PLATFORM_REPO="$(cd -- "${SCRIPT_DIR}/../.." && pwd)" NAS_ROOT="${NAS_ROOT:-/Volumes/docker/nodedc-platform}" LAUNCHER_REPO="${LAUNCHER_REPO:-}" TASKER_REPO="${TASKER_REPO:-}" TASKER_SYNC_SOURCE="${TASKER_SYNC_SOURCE:-0}" TASKER_CHANGED_BASE="${TASKER_CHANGED_BASE:-}" GATEWAY_REPO="${GATEWAY_REPO:-}" if [[ ! -d "${NAS_ROOT}" ]]; then echo "NAS_ROOT not found: ${NAS_ROOT}" >&2 echo "Set NAS_ROOT=/path/to/nodedc-platform when the Synology share is mounted elsewhere." >&2 exit 1 fi mkdir -p "${NAS_ROOT}/platform" "${NAS_ROOT}/authentik/custom-templates" rsync -av \ "${PLATFORM_REPO}/infra/synology/docker-compose.platform-http.yml" \ "${NAS_ROOT}/platform/docker-compose.platform-http.yml" rsync -av \ "${PLATFORM_REPO}/infra/synology/Caddyfile.http" \ "${NAS_ROOT}/platform/Caddyfile.http" rsync -av \ "${PLATFORM_REPO}/infra/synology/deploy-current.sh" \ "${PLATFORM_REPO}/infra/synology/backup-current.sh" \ "${NAS_ROOT}/platform/" rsync -av --delete \ "${PLATFORM_REPO}/infra/authentik/custom-templates/" \ "${NAS_ROOT}/authentik/custom-templates/" if [[ -n "${LAUNCHER_REPO}" ]]; then if [[ ! -d "${LAUNCHER_REPO}" ]]; then echo "LAUNCHER_REPO not found: ${LAUNCHER_REPO}" >&2 exit 1 fi mkdir -p "${NAS_ROOT}/launcher/source" rsync -av --delete \ --exclude='.git/' \ --exclude='node_modules/' \ --exclude='dist/' \ --exclude='server/storage/*' \ --exclude='public/storage/uploads/*' \ "${LAUNCHER_REPO}/" \ "${NAS_ROOT}/launcher/source/" else echo "LAUNCHER_REPO is not set; launcher source was not synced." fi if [[ -n "${TASKER_REPO}" ]]; then if [[ ! -d "${TASKER_REPO}/plane-src" || ! -d "${TASKER_REPO}/plane-app" ]]; then echo "TASKER_REPO must point to NODEDC_TASKMANAGER with plane-src and plane-app: ${TASKER_REPO}" >&2 exit 1 fi mkdir -p "${NAS_ROOT}/tasker/plane-src" "${NAS_ROOT}/tasker/plane-app" rsync -av \ "${TASKER_REPO}/plane-app/docker-compose.yaml" \ "${NAS_ROOT}/tasker/plane-app/docker-compose.yaml" if [[ -n "${TASKER_CHANGED_BASE}" ]]; then echo "Syncing Tasker changed files from ${TASKER_CHANGED_BASE}..HEAD" git -C "${TASKER_REPO}" diff --name-only "${TASKER_CHANGED_BASE}..HEAD" -- plane-src plane-app/docker-compose.yaml | while IFS= read -r changed_file; do [[ -z "${changed_file}" ]] && continue case "${changed_file}" in plane-src/*) source_path="${TASKER_REPO}/${changed_file}" target_path="${NAS_ROOT}/tasker/plane-src/${changed_file#plane-src/}" ;; plane-app/docker-compose.yaml) source_path="${TASKER_REPO}/${changed_file}" target_path="${NAS_ROOT}/tasker/plane-app/docker-compose.yaml" ;; *) continue ;; esac if [[ -f "${source_path}" ]]; then mkdir -p "$(dirname -- "${target_path}")" rsync -av "${source_path}" "${target_path}" else echo "skip deleted Tasker file in changed sync: ${changed_file}" fi done elif [[ "${TASKER_SYNC_SOURCE}" == "1" ]]; then echo "TASKER_SYNC_SOURCE=1: syncing full Tasker source without delete" rsync -av \ --exclude='.git/' \ --exclude='node_modules/' \ --exclude='.pnpm-store/' \ --exclude='.turbo/' \ --exclude='.cache/' \ --exclude='.react-router/' \ --exclude='build/' \ --exclude='dist/' \ --exclude='coverage/' \ --exclude='playwright-report/' \ --exclude='test-results/' \ --exclude='__pycache__/' \ --exclude='@eaDir/' \ --exclude='.DS_Store' \ --exclude='.env' \ --exclude='.env.*' \ --exclude='*.pyc' \ "${TASKER_REPO}/plane-src/" \ "${NAS_ROOT}/tasker/plane-src/" else echo "TASKER_REPO set; synced compose only. Set TASKER_CHANGED_BASE= or TASKER_SYNC_SOURCE=1 to sync source." fi if [[ -f "${TASKER_REPO}/plane-app/docker-compose.synology.override.yml" ]]; then rsync -av \ "${TASKER_REPO}/plane-app/docker-compose.synology.override.yml" \ "${NAS_ROOT}/tasker/plane-app/docker-compose.synology.override.yml" else echo "TASKER_REPO has no plane-app/docker-compose.synology.override.yml; existing NAS override, if any, was preserved." fi else echo "TASKER_REPO is not set; tasker source and compose were not synced." fi if [[ -n "${GATEWAY_REPO}" ]]; then if [[ ! -f "${GATEWAY_REPO}/docker-compose.synology.yml" || ! -f "${GATEWAY_REPO}/.env.synology.example" ]]; then echo "GATEWAY_REPO must point to NODEDC_TASKMANAGER_CODEXAPI with docker-compose.synology.yml: ${GATEWAY_REPO}" >&2 exit 1 fi mkdir -p "${NAS_ROOT}/ops-agents" rsync -av --delete \ --exclude='.git/' \ --exclude='node_modules/' \ --exclude='dist/' \ --include='.env.synology.example' \ --exclude='.env' \ --exclude='.env.*' \ "${GATEWAY_REPO}/" \ "${NAS_ROOT}/ops-agents/" else echo "GATEWAY_REPO is not set; Ops Agents Gateway source was not synced." fi cat <<'EOF' Synced files to NAS mount. Run on Synology to apply runtime changes: cd /volume1/docker/nodedc-platform/platform sudo mkdir -p ../launcher/server-storage ../launcher/uploads sudo chown -R 1000:1000 ../launcher/server-storage ../launcher/uploads sudo chmod -R u+rwX,g+rwX ../launcher/server-storage ../launcher/uploads cd /volume1/docker/nodedc-platform/launcher/source sudo /usr/local/bin/docker build -t nodedc/launcher:local . cd /volume1/docker/nodedc-platform/platform sudo /usr/local/bin/docker compose \ --env-file /volume1/docker/nodedc-platform/platform/.env.synology \ -f /volume1/docker/nodedc-platform/platform/docker-compose.platform-http.yml \ up -d --force-recreate launcher reverse-proxy authentik-server authentik-worker Verify: sudo /usr/local/bin/docker exec nodedc-platform-launcher-1 sh -lc \ 'touch /app/server/storage/.write-test /app/server/storage/uploads/.write-test && rm /app/server/storage/.write-test /app/server/storage/uploads/.write-test && echo storage-ok' curl -k -sS --compressed https://id.nodedc.ru/if/flow/default-authentication-flow/ \ | grep -aE 'hub.nodedc.ru|launcher.local|getLauncherBaseUrl|Запросить доступ' Optional Tasker apply after TASKER_REPO sync: cd /volume1/docker/nodedc-platform/tasker/plane-src BUILD_BACKEND=1 BUILD_WEB=1 BUILD_ADMIN=0 sh rebuild-nas-legacy.sh Optional Ops Agents Gateway first deploy after GATEWAY_REPO sync: cd /volume1/docker/nodedc-platform/ops-agents cp -n .env.synology.example .env # Edit .env before first start: # - NODEDC_AGENT_GATEWAY_INTERNAL_TOKEN # - NODEDC_INTERNAL_ACCESS_TOKEN # - POSTGRES_PASSWORD sudo /usr/local/bin/docker compose \ --env-file .env \ -f docker-compose.synology.yml \ up -d --build Verify Ops Agents Gateway: curl -fsS http://172.22.0.222:18190/healthz curl -fsS http://172.22.0.222:18190/readyz curl -fsS -i http://172.22.0.222:18190/mcp | head EOF