The app no longer dumps Postgres, restores it, or checks copies in CI. Co-authored-by: Cursor <cursoragent@cursor.com>
122 lines
5.7 KiB
Bash
Executable File
122 lines
5.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Первичная подготовка сервера: каталоги данных, общий корень библиотеки с mount propagation, .env с секретами.
|
||
# Запуск: sudo ./install.sh (повторный запуск безопасен)
|
||
set -euo pipefail
|
||
cd "$(dirname "$0")"
|
||
|
||
# .env и каталоги создаются от root, но сам репозиторий должен остаться у пользователя, который делает git pull.
|
||
OWNER="${SUDO_USER:-root}"
|
||
|
||
DATA="${PIPU_DATA:-/srv/pipu}"
|
||
|
||
echo "→ каталоги в $DATA"
|
||
mkdir -p "$DATA"/{library,library-host,cache/transcode,cache/covers,postgres,meili,redis,models,updates,uploads,caddy/data,caddy/config}
|
||
|
||
# Каталоги, куда пишут сервисы без root: api (distroless nonroot, uid 65532) и analyzer (uid 10001).
|
||
# Без рекурсии: файлы внутри создают сами сервисы, а повторный запуск не обходит весь кэш.
|
||
chown 65532:65532 "$DATA"/{cache,cache/transcode,cache/covers,updates,uploads}
|
||
chown 10001:10001 "$DATA/models"
|
||
|
||
# Папка с локальной музыкой: sudo LIBRARY_HOST=/mnt/music ./install.sh (или LIBRARY_HOST в .env)
|
||
LIBRARY_HOST="${LIBRARY_HOST:-$( [ -f .env ] && sed -n 's/^LIBRARY_HOST=//p' .env || true )}"
|
||
if [ -n "$LIBRARY_HOST" ]; then
|
||
if [ ! -d "$LIBRARY_HOST" ]; then
|
||
echo "LIBRARY_HOST=$LIBRARY_HOST: каталога нет — смонтируйте диск с музыкой (docs/install-guide.md, шаг 4)" >&2
|
||
exit 1
|
||
fi
|
||
findmnt -n --target "$LIBRARY_HOST" -o TARGET,SOURCE | sed 's/^/→ музыка: /'
|
||
fi
|
||
|
||
# storage-agent монтирует NFS внутрь $DATA/library; чтобы точки монтирования видели другие контейнеры,
|
||
# каталог должен быть отдельной точкой монтирования с shared propagation.
|
||
if ! findmnt -n "$DATA/library" >/dev/null 2>&1; then
|
||
echo "→ bind-mount $DATA/library (rshared)"
|
||
mount --bind "$DATA/library" "$DATA/library"
|
||
fi
|
||
mount --make-rshared "$DATA/library"
|
||
|
||
UNIT=/etc/systemd/system/pipu-library-mount.service
|
||
if [ -d /etc/systemd/system ] && [ ! -f "$UNIT" ]; then
|
||
echo "→ systemd-юнит $UNIT (повторяет bind-mount после перезагрузки)"
|
||
cat > "$UNIT" <<EOF
|
||
[Unit]
|
||
Description=Pipu Music library mount propagation
|
||
Before=docker.service
|
||
|
||
[Service]
|
||
Type=oneshot
|
||
ExecStart=/bin/sh -c 'findmnt -n "$DATA/library" || mount --bind "$DATA/library" "$DATA/library"; mount --make-rshared "$DATA/library"'
|
||
RemainAfterExit=yes
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
systemctl daemon-reload
|
||
systemctl enable pipu-library-mount.service
|
||
fi
|
||
|
||
secret() { head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 40; }
|
||
|
||
if [ ! -f .env ]; then
|
||
echo "→ .env из .env.example"
|
||
cp .env.example .env
|
||
sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$(secret)|; s|^MEILI_MASTER_KEY=.*|MEILI_MASTER_KEY=$(secret)|; s|^INTERNAL_TOKEN=.*|INTERNAL_TOKEN=$(secret)|; s|^PIPU_DATA=.*|PIPU_DATA=$DATA|; s|^LIBRARY_HOST=.*|LIBRARY_HOST=$LIBRARY_HOST|" .env
|
||
chmod 600 .env
|
||
chown "$OWNER" .env
|
||
echo " Заполните в .env: DOMAIN и ACME_EMAIL (или настройте свой прокси — docs/install-guide.md)"
|
||
fi
|
||
|
||
# Ключи подписи (токены — M1, ссылки на обложки и аудио — M2): создаются один раз,
|
||
# в том числе в .env, сделанном до их появления.
|
||
for key in JWT_PRIVATE_KEY STREAM_SIGNING_KEY; do
|
||
if ! grep -q "^$key=." .env; then
|
||
sed -i "/^$key=\$/d" .env
|
||
echo "$key=$(head -c 32 /dev/urandom | base64)" >> .env
|
||
echo "→ в .env добавлен $key"
|
||
fi
|
||
done
|
||
|
||
# Самообновление Android: адрес Gitea и репозиторий — из origin этого клона, если в .env пусто
|
||
# или осталась заглушка из .env.example (https://host/owner/repo, git@host:owner/repo, ssh://git@host:port/owner/repo).
|
||
fill_gitea() {
|
||
local url repo origin host path
|
||
url="$(sed -n 's/^GITEA_URL=//p' .env)"
|
||
repo="$(sed -n 's/^GITEA_REPO=//p' .env)"
|
||
case "$url" in "" | *example.*) url="" ;; esac
|
||
case "$repo" in "" | owner/*) repo="" ;; esac
|
||
[ -n "$url" ] && [ -n "$repo" ] && return 0
|
||
origin="$(git -c safe.directory='*' remote get-url origin 2>/dev/null || true)"
|
||
[ -n "$origin" ] || return 0
|
||
case "$origin" in
|
||
http://* | https://*)
|
||
host="$(printf '%s' "$origin" | sed -E 's#^(https?://)([^@/]*@)?([^/]+)/.*#\1\3#')"
|
||
path="$(printf '%s' "$origin" | sed -E 's#^https?://[^/]+/##')" ;;
|
||
ssh://*)
|
||
host="https://$(printf '%s' "$origin" | sed -E 's#^ssh://([^@/]*@)?([^:/]+).*#\2#')"
|
||
path="$(printf '%s' "$origin" | sed -E 's#^ssh://[^/]+/##')" ;;
|
||
*:*)
|
||
host="https://$(printf '%s' "$origin" | sed -E 's#^([^@]*@)?([^:]+):.*#\2#')"
|
||
path="${origin#*:}" ;;
|
||
*) return 0 ;;
|
||
esac
|
||
path="${path%.git}"
|
||
[ -n "$url" ] || url="$host"
|
||
[ -n "$repo" ] || repo="$path"
|
||
for kv in "GITEA_URL=$url" "GITEA_REPO=$repo"; do
|
||
if grep -q "^${kv%%=*}=" .env; then sed -i "s|^${kv%%=*}=.*|$kv|" .env; else echo "$kv" >> .env; fi
|
||
done
|
||
echo "→ самообновление Android: $url, $repo (из git remote; поменять — в .env)"
|
||
}
|
||
fill_gitea
|
||
|
||
# LIBRARY_HOST, переданный при повторном запуске, попадает и в уже существующий .env
|
||
if [ -n "$LIBRARY_HOST" ]; then
|
||
if grep -q '^LIBRARY_HOST=' .env; then
|
||
sed -i "s|^LIBRARY_HOST=.*|LIBRARY_HOST=$LIBRARY_HOST|" .env
|
||
else
|
||
echo "LIBRARY_HOST=$LIBRARY_HOST" >> .env
|
||
fi
|
||
fi
|
||
|
||
echo "Готово. Сборка и запуск последнего релиза: ./update.sh"
|