feat: self-host Hobbs Family Organizer (Shield TV dashboard) on hf-pdocker-02:8340
- tv app/Hobbs-Family-Organizer-Self-Hosted-v1.0.0: release bundle (Next.js app + Shield APK v1.4 + source) - compose/hf-pdocker-02/hobbs-organizer: override (local-disk secrets, watchtower off), .env (port 8340), Shield provisioning script - CLAUDE.md: service tree + internal ports Trusted-LAN mode. Secrets live only on .100 /var/lib/hobbs-organizer/secrets (not in git). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E9NnVEico3WvFxLyjy1iW3
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
umask 077
|
||||
|
||||
SCRIPT_DIRECTORY="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
PROJECT_ROOT="$(cd -- "$SCRIPT_DIRECTORY/.." && pwd -P)"
|
||||
FORCE=0
|
||||
|
||||
usage() {
|
||||
printf '%s\n' "Usage: $0 [--force] BACKUP.tar.gz" >&2
|
||||
}
|
||||
|
||||
if [[ "${1:-}" == "--force" ]]; then
|
||||
FORCE=1
|
||||
shift
|
||||
fi
|
||||
[[ $# -eq 1 ]] || { usage; exit 2; }
|
||||
|
||||
archive_argument="$1"
|
||||
archive_directory="$(cd -- "$(dirname -- "$archive_argument")" && pwd -P)"
|
||||
archive_path="$archive_directory/$(basename -- "$archive_argument")"
|
||||
[[ -f "$archive_path" ]] || { printf '%s\n' "The backup archive does not exist." >&2; exit 1; }
|
||||
command -v tar >/dev/null 2>&1 || { printf '%s\n' "tar is required to restore a backup." >&2; exit 1; }
|
||||
|
||||
if ((FORCE == 0)); then
|
||||
read -r -p "Restore will overwrite the current .env and runtime secrets. Continue? [y/N] " restore_answer
|
||||
case "${restore_answer,,}" in
|
||||
y|yes) ;;
|
||||
*) printf '%s\n' "Restore cancelled." >&2; exit 1 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
while IFS= read -r archive_entry; do
|
||||
normalized_entry="${archive_entry%/}"
|
||||
case "$normalized_entry" in
|
||||
config|config/environment.env|secrets|secrets/calendar_ical_urls.json|secrets/organizer_access_token.txt|manifest.txt) ;;
|
||||
*) printf '%s\n' "The archive contains an unexpected path and will not be restored." >&2; exit 1 ;;
|
||||
esac
|
||||
done < <(tar -tzf "$archive_path")
|
||||
|
||||
staging_path="$(mktemp -d "$PROJECT_ROOT/.restore-staging-XXXXXXXX")"
|
||||
cleanup_staging() {
|
||||
[[ -n "${staging_path:-}" && -d "$staging_path" ]] && rm -rf -- "$staging_path"
|
||||
}
|
||||
trap cleanup_staging EXIT HUP INT TERM
|
||||
|
||||
tar -xzf "$archive_path" -C "$staging_path"
|
||||
for required_file in \
|
||||
"$staging_path/config/environment.env" \
|
||||
"$staging_path/secrets/calendar_ical_urls.json" \
|
||||
"$staging_path/secrets/organizer_access_token.txt"; do
|
||||
[[ -f "$required_file" && ! -L "$required_file" ]] || {
|
||||
printf '%s\n' "The archive is missing a required regular file." >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
mkdir -p -- "$PROJECT_ROOT/secrets"
|
||||
cp -- "$staging_path/config/environment.env" "$PROJECT_ROOT/.env"
|
||||
cp -- "$staging_path/secrets/calendar_ical_urls.json" "$PROJECT_ROOT/secrets/calendar_ical_urls.json"
|
||||
cp -- "$staging_path/secrets/organizer_access_token.txt" "$PROJECT_ROOT/secrets/organizer_access_token.txt"
|
||||
chmod 600 -- "$PROJECT_ROOT/.env" "$PROJECT_ROOT/secrets/calendar_ical_urls.json" "$PROJECT_ROOT/secrets/organizer_access_token.txt"
|
||||
|
||||
rm -rf -- "$staging_path"
|
||||
staging_path=""
|
||||
trap - EXIT HUP INT TERM
|
||||
|
||||
printf '%s\n' "Configuration restored. Restart with: docker compose up -d --build"
|
||||
Reference in New Issue
Block a user