mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 18:07:06 +01:00
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
177 lines
6.7 KiB
Bash
Executable File
177 lines
6.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# Compose file, container startup and readiness polling for SyncYomi.
|
|
#
|
|
# Sourced by setup_syncyomi.sh; split out to keep it under the 250-line cap.
|
|
# Sourced rather than run, so it inherits the caller's strict mode and
|
|
# the helper functions and variables defined above the source line.
|
|
|
|
# --- Step 3: docker-compose stack (host networking, loopback bind) -----------
|
|
write_compose() {
|
|
ensure_dir "${DATA_DIR}/config"
|
|
# network_mode: host + SyncYomi's host=127.0.0.1 keeps it loopback-only
|
|
# (reached by the host-networked Caddy) and dodges this host's default-drop
|
|
# nftables FORWARD chain that would kill a bridge-networked container.
|
|
umask 077
|
|
cat >"${COMPOSE_FILE}" <<EOF
|
|
# Generated by setup_syncyomi.sh — do not edit by hand.
|
|
services:
|
|
syncyomi:
|
|
container_name: syncyomi
|
|
image: ${SYNCYOMI_IMAGE}
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
environment:
|
|
- TZ=${TZ_VALUE}
|
|
volumes:
|
|
- ./config:/config
|
|
EOF
|
|
log_ok "Wrote ${COMPOSE_FILE}"
|
|
}
|
|
|
|
# Build the local image the compose file asks for. A local tag exists in no
|
|
# registry, so `docker compose up` would fail with a pull error rather than an
|
|
# explanation -- and a fork whose changes are never rebuilt is worse than no
|
|
# fork at all. Builds whatever is checked out in ${SYNCYOMI_SRC}; never moves
|
|
# that working tree. Set SYNCYOMI_REBUILD=1 to rebuild after editing it.
|
|
ensure_local_image() {
|
|
if [[ ${SYNCYOMI_IMAGE} == */* ]]; then
|
|
log_info "Using registry image ${SYNCYOMI_IMAGE}"
|
|
return 0
|
|
fi
|
|
if [[ ${SYNCYOMI_REBUILD:-0} != "1" ]] && docker image inspect "${SYNCYOMI_IMAGE}" >/dev/null 2>&1; then
|
|
log_ok "Local image ${SYNCYOMI_IMAGE} already built"
|
|
return 0
|
|
fi
|
|
if [[ ! -d ${SYNCYOMI_SRC}/.git ]]; then
|
|
log_info "Cloning ${SYNCYOMI_FORK_URL} into ${SYNCYOMI_SRC}"
|
|
git clone "${SYNCYOMI_FORK_URL}" "${SYNCYOMI_SRC}"
|
|
fi
|
|
local version revision
|
|
version="$(git -C "${SYNCYOMI_SRC}" describe --tags --always)"
|
|
revision="$(git -C "${SYNCYOMI_SRC}" rev-parse HEAD)"
|
|
log_info "Building ${SYNCYOMI_IMAGE} from ${SYNCYOMI_SRC} (${version})"
|
|
# BuildKit is required: the Dockerfile reads $BUILDPLATFORM, which the
|
|
# legacy builder leaves empty and then rejects as an invalid platform.
|
|
DOCKER_BUILDKIT=1 docker build \
|
|
-t "${SYNCYOMI_IMAGE}" \
|
|
--build-arg VERSION="${version}-kuhy" \
|
|
--build-arg REVISION="${revision}" \
|
|
--build-arg BUILDTIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
"${SYNCYOMI_SRC}"
|
|
log_ok "Built ${SYNCYOMI_IMAGE} from ${version}"
|
|
}
|
|
|
|
start_containers() {
|
|
ensure_local_image
|
|
log_info "Starting SyncYomi container"
|
|
docker compose -f "${COMPOSE_FILE}" up -d
|
|
}
|
|
|
|
# Readiness probe. SyncYomi (v1.1.x) has no public /healthz, but GET
|
|
# /api/auth/onboard answers 204 (no user yet) or 403 (a user exists) once the
|
|
# API is serving — either means "up". Echoes the HTTP code; returns 0 if ready.
|
|
# Usage: syncyomi_ready "http://127.0.0.1:8282"
|
|
syncyomi_ready() {
|
|
local base="$1" code
|
|
code="$(curl -sS -o /dev/null -w '%{http_code}' "${base}/api/auth/onboard" 2>/dev/null || true)"
|
|
echo "${code}"
|
|
[[ ${code} == "204" || ${code} == "403" ]]
|
|
}
|
|
|
|
# --- Step 4: wait until the local API answers --------------------------------
|
|
wait_for_syncyomi() {
|
|
log_info "Waiting for SyncYomi to come up on ${SYNCYOMI_LOCAL}"
|
|
local i
|
|
for ((i = 0; i < 60; i++)); do
|
|
if syncyomi_ready "${SYNCYOMI_LOCAL}" >/dev/null; then
|
|
log_ok "SyncYomi is up (API answering)"
|
|
return 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
die "SyncYomi did not become healthy in 120s — check 'docker logs syncyomi'."
|
|
}
|
|
|
|
# Defensive: SyncYomi generates config/config.toml on first run. Ensure it binds
|
|
# loopback-only (never 0.0.0.0, which would bypass Caddy/TLS) and serves at root.
|
|
ensure_local_config() {
|
|
[[ -f ${CONFIG_TOML} ]] || return 0
|
|
local changed=0
|
|
if grep -qE '^\s*host\s*=' "${CONFIG_TOML}"; then
|
|
if ! grep -qE '^\s*host\s*=\s*"127\.0\.0\.1"' "${CONFIG_TOML}"; then
|
|
sed -i -E 's|^\s*host\s*=.*|host = "127.0.0.1"|' "${CONFIG_TOML}"
|
|
changed=1
|
|
fi
|
|
fi
|
|
# Force an empty baseUrl (dedicated subdomain, not a sub-path).
|
|
if grep -qE '^\s*baseUrl\s*=' "${CONFIG_TOML}" &&
|
|
! grep -qE '^\s*baseUrl\s*=\s*""' "${CONFIG_TOML}"; then
|
|
sed -i -E 's|^\s*baseUrl\s*=.*|baseUrl = ""|' "${CONFIG_TOML}"
|
|
changed=1
|
|
fi
|
|
if [[ ${changed} -eq 1 ]]; then
|
|
log_info "Patched ${CONFIG_TOML} (host=127.0.0.1, baseUrl empty) — restarting"
|
|
docker compose -f "${COMPOSE_FILE}" restart
|
|
wait_for_syncyomi
|
|
fi
|
|
}
|
|
|
|
# --- Step 5: create first account + mint API key over the local REST API -----
|
|
mint_api_key() {
|
|
if [[ -f ${TOKEN_FILE} ]]; then
|
|
log_ok "API key already minted ($(<"${TOKEN_FILE}") stored in ${TOKEN_FILE})"
|
|
return 0
|
|
fi
|
|
log_info "Creating SyncYomi account and minting API key"
|
|
|
|
local jar body http key
|
|
jar="$(mktemp)"
|
|
# shellcheck disable=SC2064
|
|
trap "rm -f '${jar}'" RETURN
|
|
|
|
# JSON with proper escaping (password may contain +,/,= from base64).
|
|
body="$(jq -n --arg u "${SYNCYOMI_USER}" --arg p "${SYNCYOMI_PASSWORD}" \
|
|
'{username:$u,password:$p}')"
|
|
|
|
# Onboard the first user only if none exists yet (GET returns 204 when empty).
|
|
http="$(curl -fsS -o /dev/null -w '%{http_code}' "${SYNCYOMI_LOCAL}/api/auth/onboard" 2>/dev/null || true)"
|
|
if [[ ${http} == "204" ]]; then
|
|
http="$(curl -sS -o /dev/null -w '%{http_code}' \
|
|
-H 'Content-Type: application/json' -d "${body}" \
|
|
"${SYNCYOMI_LOCAL}/api/auth/onboard" 2>/dev/null || true)"
|
|
[[ ${http} =~ ^2 ]] || die "Onboard failed (HTTP ${http}) — check 'docker logs syncyomi'."
|
|
log_ok "Created first account '${SYNCYOMI_USER}'"
|
|
else
|
|
log_warn "An account already exists (onboard HTTP ${http}); trying stored credentials"
|
|
fi
|
|
|
|
# Log in to get a session cookie.
|
|
http="$(curl -sS -c "${jar}" -o /dev/null -w '%{http_code}' \
|
|
-H 'Content-Type: application/json' -d "${body}" \
|
|
"${SYNCYOMI_LOCAL}/api/auth/login" 2>/dev/null || true)"
|
|
if [[ ! ${http} =~ ^2 ]]; then
|
|
log_warn "Login failed (HTTP ${http}). A different account may already own this server."
|
|
log_warn "Create an API key manually at ${SYNCYOMI_LOCAL} → Settings → API Keys."
|
|
return 0
|
|
fi
|
|
|
|
# Reuse an existing 'tachiyomisy' key if one exists (idempotent); otherwise
|
|
# create one. scopes must be a non-empty array — SyncYomi rejects a null.
|
|
key="$(curl -sS -b "${jar}" "${SYNCYOMI_LOCAL}/api/keys" 2>/dev/null |
|
|
jq -r '[.[] | select(.name=="tachiyomisy")][0].key // empty')"
|
|
if [[ -n ${key} ]]; then
|
|
log_ok "Reusing existing 'tachiyomisy' API key"
|
|
else
|
|
key="$(curl -sS -b "${jar}" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"name":"tachiyomisy","scopes":["read","write"]}' \
|
|
"${SYNCYOMI_LOCAL}/api/keys" 2>/dev/null | jq -r '.key // empty')"
|
|
[[ -n ${key} ]] || die "API key creation returned no key — check 'docker logs syncyomi'."
|
|
fi
|
|
|
|
umask 077
|
|
printf '%s\n' "${key}" >"${TOKEN_FILE}"
|
|
chmod 600 "${TOKEN_FILE}"
|
|
log_ok "Minted API key and stored it in ${TOKEN_FILE} (0600)"
|
|
}
|