Files
pipu-music/deploy/install.sh
T
mrleo1nidandCursor dfc72b1d69
ci / ts / ts (packages + web) (push) Successful in 54s
ci / db / db (push) Successful in 1m40s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 3m51s
chore(deploy): leave database backups to external tools
The app no longer dumps Postgres, restores it, or checks copies in CI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 09:03:06 +03:00

122 lines
5.7 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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"