#!/bin/bash # deploy-immich-credentials.sh — деплой кредов Immich и immich-deduper на VM 200 # Секреты из Vaultwarden (объекты IMMICH, IMMICH_DEDUPER). # # Использование: # /root/scripts/deploy-immich-credentials.sh # /root/scripts/deploy-immich-credentials.sh --dry-run # # Требования: bw, jq, /root/.bw-master, SSH без пароля root@host → admin@192.168.1.200 # # Vaultwarden: IMMICH — поля DB_PASSWORD, IMMICH_API_KEY, GEMINI_API_KEY и др. (см. .env). # IMMICH_DEDUPER — поля PSQL_PASS, DEDUP_*, IMMICH_PATH, PSQL_*. set -e VM_SSH="admin@192.168.1.200" IMMICH_PATH="/opt/immich" DEDUPER_PATH="/opt/immich-deduper" BW_MASTER_FILE="${BW_MASTER_PASSWORD_FILE:-/root/.bw-master}" DRY_RUN=false for arg in "$@"; do case "$arg" in --dry-run) DRY_RUN=true ;; esac done export PATH="/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PATH}" log() { echo "[$(date -Iseconds)] $*"; } err() { echo "[$(date -Iseconds)] ERROR: $*" >&2; } ensure_bw_unlocked() { local status status=$(bw status 2>/dev/null | jq -r '.status' 2>/dev/null || echo "unknown") if [ "$status" = "unlocked" ]; then log "bw already unlocked, reusing session" return 0 fi if [ ! -f "$BW_MASTER_FILE" ]; then err "Missing $BW_MASTER_FILE" exit 1 fi export BW_SESSION=$(bw unlock --passwordfile "$BW_MASTER_FILE" --raw 2>/dev/null) || { err "bw unlock failed" exit 1 } log "bw unlocked" } get_field() { local item="$1" name="$2" echo "$item" | jq -r ".fields[] | select(.name==\"$name\") | .value // empty" } get_immich_secrets() { local id id=$(bw list items --search IMMICH 2>/dev/null | jq -r '.[] | select(.name=="IMMICH") | .id' | head -1) || true [ -z "$id" ] && id=$(bw list items 2>/dev/null | jq -r '.[] | select(.name=="IMMICH") | .id' | head -1) || true [ -z "$id" ] && { err "IMMICH not found in Vaultwarden"; exit 1; } IMMICH_ITEM=$(bw get item "$id" 2>/dev/null) || { err "IMMICH get item failed for id=$id"; exit 1; } DB_PASSWORD=$(get_field "$IMMICH_ITEM" "DB_PASSWORD") IMMICH_API_KEY=$(get_field "$IMMICH_ITEM" "IMMICH_API_KEY") GEMINI_API_KEY=$(get_field "$IMMICH_ITEM" "GEMINI_API_KEY") if [ -z "$DB_PASSWORD" ]; then err "IMMICH: missing DB_PASSWORD field"; exit 1; fi if [ -z "$IMMICH_API_KEY" ]; then err "IMMICH: missing IMMICH_API_KEY field"; exit 1; fi } get_deduper_secrets() { local id id=$(bw list items 2>/dev/null | jq -r '.[] | select(.name=="IMMICH_DEDUPER") | .id' | head -1) [ -z "$id" ] && { err "IMMICH_DEDUPER not found in Vaultwarden"; exit 1; } DEDUP_ITEM=$(bw get item "$id" 2>/dev/null) || { err "IMMICH_DEDUPER not found in Vaultwarden" exit 1 } PSQL_PASS=$(get_field "$DEDUP_ITEM" "PSQL_PASS") [ -z "$PSQL_PASS" ] && PSQL_PASS=$(echo "$DEDUP_ITEM" | jq -r '.login.password // empty') DEDUP_PORT=$(get_field "$DEDUP_ITEM" "DEDUP_PORT") DEDUP_DATA=$(get_field "$DEDUP_ITEM" "DEDUP_DATA") DEDUP_IMAGE=$(get_field "$DEDUP_ITEM" "DEDUP_IMAGE") IMMICH_PATH_FIELD=$(get_field "$DEDUP_ITEM" "IMMICH_PATH") PSQL_HOST=$(get_field "$DEDUP_ITEM" "PSQL_HOST") PSQL_PORT=$(get_field "$DEDUP_ITEM" "PSQL_PORT") PSQL_DB=$(get_field "$DEDUP_ITEM" "PSQL_DB") [ -z "$PSQL_PASS" ] && PSQL_PASS="${DB_PASSWORD:-}" DEDUP_PORT="${DEDUP_PORT:-8086}" DEDUP_DATA="${DEDUP_DATA:-/opt/immich-deduper/data}" DEDUP_IMAGE="${DEDUP_IMAGE:-razgrizhsu/immich-deduper:latest-cpu}" IMMICH_PATH_FIELD="${IMMICH_PATH_FIELD:-/mnt/data/library}" PSQL_HOST="${PSQL_HOST:-database}" PSQL_PORT="${PSQL_PORT:-5432}" PSQL_DB="${PSQL_DB:-immich}" } gen_immich_env() { local tmp tmp=$(mktemp) cat > "$tmp" << EOF # Immich .env (generated from Vaultwarden) UPLOAD_LOCATION=/mnt/data/library DB_DATA_LOCATION=/mnt/data/postgres IMMICH_VERSION=v2 DB_PASSWORD=${DB_PASSWORD} DB_USERNAME=postgres DB_DATABASE_NAME=immich IMMICH_URL=http://immich-server:2283 IMMICH_API_KEY=${IMMICH_API_KEY} DB_HOST=immich_postgres DB_PORT=5432 EXTERNAL_IMMICH_URL=https://immich.katykhin.ru GEMINI_API_KEY=${GEMINI_API_KEY} EOF echo "$tmp" } gen_deduper_env() { local tmp tmp=$(mktemp) cat > "$tmp" << EOF # Deduper .env (generated from Vaultwarden) DEDUP_PORT=${DEDUP_PORT} DEDUP_DATA=${DEDUP_DATA} DEDUP_IMAGE=${DEDUP_IMAGE} IMMICH_PATH=${IMMICH_PATH_FIELD} PSQL_HOST=${PSQL_HOST} PSQL_PORT=${PSQL_PORT} PSQL_DB=${PSQL_DB} PSQL_USER=postgres PSQL_PASS=${PSQL_PASS} EOF echo "$tmp" } push_to_vm() { local local_file="$1" remote_path="$2" scp -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -q "$local_file" "${VM_SSH}:/tmp/deploy-env.tmp" || { err "scp to ${VM_SSH} failed. Ensure SSH key from Proxmox: ssh-copy-id ${VM_SSH}" exit 1 } ssh -o BatchMode=yes -o ConnectTimeout=10 "$VM_SSH" "sudo mv /tmp/deploy-env.tmp ${remote_path} && sudo chmod 600 ${remote_path}" || { err "ssh to ${VM_SSH} failed" exit 1 } } run_compose() { ssh -o BatchMode=yes "$VM_SSH" "cd ${IMMICH_PATH} && sudo docker compose up -d --force-recreate" ssh -o BatchMode=yes "$VM_SSH" "cd ${DEDUPER_PATH} && sudo docker compose up -d --force-recreate" log "Immich and deduper started" } main() { log "deploy-immich-credentials start (dry_run=$DRY_RUN)" ensure_bw_unlocked get_immich_secrets get_deduper_secrets if [ "$DRY_RUN" = true ]; then log "DRY-RUN: would push .env files and run compose" log " DB_PASSWORD=*** IMMICH_API_KEY=***" exit 0 fi tmp_immich=$(gen_immich_env) tmp_deduper=$(gen_deduper_env) trap "rm -f $tmp_immich $tmp_deduper" EXIT push_to_vm "$tmp_immich" "${IMMICH_PATH}/.env" log "Immich .env written" push_to_vm "$tmp_deduper" "${DEDUPER_PATH}/.env" log "Deduper .env written" run_compose log "done" } main