testsAndMisc/linux_configuration/scripts/single_use/features/setup_syncyomi.sh
Krzysztof kuhy Rudnicki 1a75ed5bf5 Split three more linux_configuration feature and fix scripts
setup_syncyomi.sh 408 -> 138 (two cuts), setup_personal_website.sh
396 -> 192, fix_thorium.sh 377 -> 208.

All three already declared SCRIPT_DIR and the splitter added a second
one, which aborts at runtime with "readonly variable" while bash -n and
shellcheck stay clean. The stubbed run caught it; the splitter now
reuses an existing declaration.

Fixed SC2164 in build_website rather than suppressing it: a bare
cd "$WEBSITE_SRC" inside a subshell, with pnpm install and pnpm run
build after it. A failed cd would have built in the caller's directory.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-16 18:48:01 +02:00

141 lines
5.1 KiB
Bash
Executable File

#!/bin/bash
# setup_syncyomi.sh — Self-hosted SyncYomi manga-sync server on this Arch PC.
#
# Stands up SyncYomi (github.com/SyncYomi/SyncYomi) in Docker, bound to
# 127.0.0.1:8282, and fronts it with the EXISTING Caddy reverse proxy (from
# setup_gitea.sh) for automatic public HTTPS at a new DuckDNS subdomain, so
# TachiyomiSY on the phone can sync its library from a mobile network.
#
# It reuses the host's existing exposure stack — it does NOT add a second Caddy,
# a second DuckDNS updater, or hand-edit /etc/nftables.conf. It also creates the
# first SyncYomi account and mints an API key over the local REST API, then
# prints the host address + API key ready to paste into TachiyomiSY.
#
# Run as your normal user (NOT root). It uses sudo only for pacman, the firewall
# flag and enabling docker. Idempotent — safe to re-run.
#
# Usage:
# ./setup_syncyomi.sh Install / update everything (default)
# ./setup_syncyomi.sh setup Same as above
# ./setup_syncyomi.sh status Show health / exposure diagnostics
# ./setup_syncyomi.sh help Show this help
set -euo pipefail
# --- Pinned SyncYomi image ---------------------------------------------------
readonly SYNCYOMI_IMAGE="ghcr.io/syncyomi/syncyomi:v1.1.11"
readonly SYNCYOMI_PORT=8282
readonly SYNCYOMI_LOCAL="http://127.0.0.1:${SYNCYOMI_PORT}"
# --- Data dir + secrets (outside the git repo, like ~/gitea) -----------------
readonly DATA_DIR="${HOME}/syncyomi"
readonly COMPOSE_FILE="${DATA_DIR}/docker-compose.yml"
readonly CONFIG_TOML="${DATA_DIR}/config/config.toml"
readonly CONF_FILE="${DATA_DIR}/.syncyomi.conf"
readonly TOKEN_FILE="${DATA_DIR}/.api_token"
# --- Existing host infrastructure (from setup_gitea.sh / install_joplin.sh) --
readonly CADDY_CONTAINER="gitea-caddy"
readonly CADDYFILE="${HOME}/gitea/Caddyfile"
readonly CADDYFILE_IN_CONTAINER="/etc/caddy/Caddyfile"
readonly DUCKDNS_UPDATER="${HOME}/.joplin-server/duckdns-update.sh"
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
readonly SCRIPT_DIR
readonly WG_SCRIPT="${SCRIPT_DIR}/setup_wireguard_ssh.sh"
# shellcheck source=../../lib/common.sh
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/../../lib/common.sh"
# Config values (populated from CONF_FILE / defaults).
SYNCYOMI_USER=""
SYNCYOMI_PASSWORD=""
SYNCYOMI_SUBDOMAIN=""
# Local timezone for the container (falls back if timedatectl is unavailable).
TZ_VALUE="$(timedatectl show -p Timezone --value 2>/dev/null || echo 'Europe/Warsaw')"
readonly TZ_VALUE
die() {
log_error "$1"
exit 1
}
usage() {
grep -E '^#( |$)' "$0" | sed -E 's/^# ?//'
exit 0
}
# --- Step 1: preflight -------------------------------------------------------
preflight() {
log_info "Preflight checks"
[[ ${EUID} -ne 0 ]] || die "Run as your normal user, not root (it will sudo where needed)."
# Runtime deps; install any that are missing from the official repos.
install_missing_pacman_packages docker docker-compose jq curl openssl
# Ensure the docker daemon is enabled+running (auto-start after reboot). Only
# touch systemd via sudo when it is not already active, to stay a no-op on the
# common case where docker is already up (Gitea/dufs run on it).
has_cmd docker || die "docker not found — run setup_gitea.sh first."
if ! is_service_active docker; then
log_info "Enabling and starting docker.service"
sudo systemctl enable --now docker
fi
# The public-exposure stack must already exist (Caddy from setup_gitea.sh).
docker ps --format '{{.Names}}' | grep -qx "${CADDY_CONTAINER}" ||
die "Caddy container '${CADDY_CONTAINER}' not running — run setup_gitea.sh first."
[[ -f ${CADDYFILE} ]] || die "Caddyfile ${CADDYFILE} missing — run setup_gitea.sh first."
[[ -x ${DUCKDNS_UPDATER} ]] || die "DuckDNS updater ${DUCKDNS_UPDATER} missing — run install_joplin.sh first."
log_ok "Host exposure stack present (docker + ${CADDY_CONTAINER} + DuckDNS updater)"
}
# --- Step 2: config (auto-generate credentials, store 0600) ------------------
load_config() {
ensure_dir "${DATA_DIR}"
chmod 700 "${DATA_DIR}"
if [[ -f ${CONF_FILE} ]]; then
# shellcheck source=/dev/null
source "${CONF_FILE}"
fi
SYNCYOMI_USER="${SYNCYOMI_USER:-$(get_actual_user)}"
SYNCYOMI_SUBDOMAIN="${SYNCYOMI_SUBDOMAIN:-kuhy-sync.duckdns.org}"
# Generate the web/API password once; never regenerate on re-run.
if [[ -z ${SYNCYOMI_PASSWORD:-} ]]; then
SYNCYOMI_PASSWORD="$(openssl rand -base64 24)"
fi
save_config
}
save_config() {
# 0600 — holds the plaintext SyncYomi password so re-runs stay
# non-interactive. Lives under ~/syncyomi (outside the git repo).
umask 077
{
printf 'SYNCYOMI_USER=%q\n' "${SYNCYOMI_USER}"
printf 'SYNCYOMI_PASSWORD=%q\n' "${SYNCYOMI_PASSWORD}"
printf 'SYNCYOMI_SUBDOMAIN=%q\n' "${SYNCYOMI_SUBDOMAIN}"
} >"${CONF_FILE}"
chmod 600 "${CONF_FILE}"
log_ok "Saved config to ${CONF_FILE} (0600)"
}
# shellcheck source=lib/syncyomi_stack.sh
source "$SCRIPT_DIR/lib/syncyomi_stack.sh"
# shellcheck source=lib/syncyomi_expose.sh
source "$SCRIPT_DIR/lib/syncyomi_expose.sh"
main() {
local cmd="${1:-setup}"
case "${cmd}" in
setup) setup_cmd ;;
status) status_cmd ;;
help | -h | --help) usage ;;
*) die "Unknown command '${cmd}' (use: setup | status | help)" ;;
esac
}
main "$@"