Compare commits

...
3 Commits
Author SHA1 Message Date
mrleo1nidandClaude Opus 5.5 12fc866c26 ci(release): cap memory of the Android release build
ci / api (push) Canceled after 5m58s
ci / recommender (push) Canceled after 25s
ci / ts (packages + web) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
release / verify (push) Successful in 1m21s
ci / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 45s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (apps/web/Dockerfile, web, ) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, api, api) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/recommender/Dockerfile, recommender, ) (push) Canceled after 0s
ci / mobile (Metro-бандл Android) (push) Canceled after 0s
release / android + release (push) Failing after 9m39s
v0.1.5 got past Metro, but the Gradle daemon was killed mid-bundle on the
8 GB runner. Compile Kotlin inside the Gradle JVM instead of a second
daemon, limit Gradle and Metro to two workers, set the heap explicitly and
print free/dmesg when the build fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:55:48 +03:00
mrleo1nidandClaude Opus 5.5 f76b3c89b3 fix(mobile): hide Gradle outputs inside node_modules from Metro
ci / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m6s
ci / api (push) Successful in 51s
ci / recommender (push) Successful in 36s
ci / ts (packages + web) (push) Successful in 39s
ci / mobile (Metro-бандл Android) (push) Successful in 52s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (apps/web/Dockerfile, web, ) (push) Successful in 43s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, api, api) (push) Successful in 51s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Successful in 48s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/recommender/Dockerfile, recommender, ) (push) Successful in 28s
release / verify (push) Successful in 1m14s
release / android + release (push) Failing after 7m41s
Gradle builds native modules in place, under node_modules/<pkg>/android/
{build,.cxx,.gradle}. The root node_modules is a watch folder, so Metro still
crawled and hashed those artifacts and the release bundle step stalled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:09:32 +03:00
mrleo1nidandClaude Opus 5.5 38edcd3e0e docs(install): music on a local disk at /mnt/music, repo in /opt/pipu-music
ci / docker (образы собираются — их сервер собирает сам при обновлении) (apps/web/Dockerfile, web, ) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, api, api) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/recommender/Dockerfile, recommender, ) (push) Canceled after 0s
ci / mobile (Metro-бандл Android) (push) Canceled after 0s
ci / contract (сгенерированный код совпадает с openapi.yaml) (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / recommender (push) Canceled after 0s
ci / ts (packages + web) (push) Canceled after 0s
Add an install step for mounting the music disk via fstab and wire it into
the stack: LIBRARY_HOST in .env points at the host folder, compose mounts it
as /library-host (read-only for api, nginx and analyzer; writable only for
storage-agent), nginx serves it through X-Accel, and install.sh validates the
path and records it in .env.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:51:26 +03:00
9 changed files with 136 additions and 24 deletions

No files matched your search

+9 -3
View File
@@ -70,18 +70,24 @@ jobs:
- name: Сборка APK - name: Сборка APK
working-directory: apps/mobile working-directory: apps/mobile
env: env:
# CI=1 — Metro без file watcher; NODE_ENV нужен expo для выбора .env-файлов # CI=1 — Metro без file watcher и с 2 воркерами (metro.config.js); NODE_ENV нужен expo для .env-файлов
CI: "1" CI: "1"
NODE_ENV: production NODE_ENV: production
# Только arm64-v8a: C++-часть React Native собирается под каждую ABI, и четыре ABI # Только arm64-v8a: C++-часть React Native собирается под каждую ABI, и четыре ABI
# вместе с NDK (~2.5 ГБ) не помещаются на диск раннера. arm64 — все устройства с 2017+. # вместе с NDK (~2.5 ГБ) не помещаются на диск раннера. arm64 — все устройства с 2017+.
# Память: на раннере 8 ГБ, и без ограничений Gradle + демон Kotlin + Metro вызывают OOM killer.
# Kotlin компилируется внутри JVM Gradle (без второго демона, поэтому heap 2.5 ГБ вместо 2), задач — не больше 2.
run: | run: |
npx expo prebuild --platform android --clean --no-install npx expo prebuild --platform android --clean --no-install
cd android && ./gradlew --no-daemon assembleRelease -PreactNativeArchitectures=arm64-v8a cd android
./gradlew --no-daemon --max-workers=2 assembleRelease -PreactNativeArchitectures=arm64-v8a -Pkotlin.compiler.execution.strategy=in-process -Dorg.gradle.jvmargs="-Xmx2560m -XX:MaxMetaspaceSize=512m"
- name: Свободное место после сбоя - name: Свободное место после сбоя
if: failure() if: failure()
run: df -h / /tmp; du -sh ~/.gradle ~/.android apps/mobile/android 2>/dev/null || true run: |
df -h / /tmp; free -m || true
du -sh ~/.gradle ~/.android apps/mobile/android 2>/dev/null || true
dmesg 2>/dev/null | grep -iE "killed process|out of memory" | tail -5 || true
- name: Подпись APK - name: Подпись APK
env: env:
+7 -1
View File
@@ -9,7 +9,13 @@ const config = getDefaultConfig(__dirname);
// в release-сборке Metro на них «висит». В JS-бандл из этих папок ничего не попадает. // в release-сборке Metro на них «висит». В JS-бандл из этих папок ничего не попадает.
const escape = (p) => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const escape = (p) => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const nativeDirs = ['android', 'ios'].map((d) => new RegExp(`^${escape(path.join(__dirname, d) + path.sep)}.*`)); const nativeDirs = ['android', 'ios'].map((d) => new RegExp(`^${escape(path.join(__dirname, d) + path.sep)}.*`));
// Gradle собирает нативные модули прямо в node_modules/<пакет>/android/{build,.cxx,.gradle},
// а корневой node_modules тоже в watchFolders.
const nativeBuildOutputs = /[\\/]android[\\/](build|\.cxx|\.gradle)[\\/].*/;
config.resolver.blockList = [...[].concat(config.resolver.blockList ?? []), ...nativeDirs]; config.resolver.blockList = [...[].concat(config.resolver.blockList ?? []), ...nativeDirs, nativeBuildOutputs];
// В CI раннер делит 8 ГБ RAM с Gradle: по умолчанию Metro запускает воркер на каждое ядро.
if (process.env.CI) config.maxWorkers = 2;
module.exports = config; module.exports = config;
+3
View File
@@ -4,6 +4,9 @@
# PIPU_VERSION проставляет update.sh по git-тегу — вручную не менять # PIPU_VERSION проставляет update.sh по git-тегу — вручную не менять
PIPU_VERSION=dev PIPU_VERSION=dev
PIPU_DATA=/srv/pipu PIPU_DATA=/srv/pipu
# Папка с музыкой на локальном диске сервера (источники type=local), например смонтированный диск.
# Пусто — $PIPU_DATA/library-host. NFS-хранилища сюда не относятся: их подключает админка.
LIBRARY_HOST=
# --- Домен и HTTPS (docs/install-guide.md) --- # --- Домен и HTTPS (docs/install-guide.md) ---
# Вариант А: встроенный Caddy с Let's Encrypt — оставьте строку COMPOSE_FILE и заполните DOMAIN, ACME_EMAIL. # Вариант А: встроенный Caddy с Let's Encrypt — оставьте строку COMPOSE_FILE и заполните DOMAIN, ACME_EMAIL.
+14
View File
@@ -17,6 +17,14 @@ x-library-ro: &library-ro
read_only: true read_only: true
bind: { propagation: rslave } bind: { propagation: rslave }
# Локальная музыка (источники type=local): каталог хоста, например смонтированный диск /mnt/music.
# Пишет в него только storage-agent (импорт одобренных загрузок), остальным — только чтение.
x-library-host-ro: &library-host-ro
type: bind
source: ${LIBRARY_HOST:-${PIPU_DATA:-/srv/pipu}/library-host}
target: /library-host
read_only: true
x-restart: &restart x-restart: &restart
restart: unless-stopped restart: unless-stopped
@@ -31,6 +39,7 @@ services:
ports: ["${HTTP_PORT:-80}:80"] ports: ["${HTTP_PORT:-80}:80"]
volumes: volumes:
- *library-ro - *library-ro
- *library-host-ro
- ${PIPU_DATA:-/srv/pipu}/cache:/cache:ro - ${PIPU_DATA:-/srv/pipu}/cache:/cache:ro
- ${PIPU_DATA:-/srv/pipu}/uploads:/uploads:ro - ${PIPU_DATA:-/srv/pipu}/uploads:/uploads:ro
depends_on: [api] depends_on: [api]
@@ -48,6 +57,7 @@ services:
DATABASE_URL: postgres://pipu:${POSTGRES_PASSWORD:?заполните POSTGRES_PASSWORD в .env (sudo ./install.sh)}@postgres:5432/pipu?sslmode=disable DATABASE_URL: postgres://pipu:${POSTGRES_PASSWORD:?заполните POSTGRES_PASSWORD в .env (sudo ./install.sh)}@postgres:5432/pipu?sslmode=disable
volumes: volumes:
- *library-ro - *library-ro
- *library-host-ro
- ${PIPU_DATA:-/srv/pipu}/cache:/cache - ${PIPU_DATA:-/srv/pipu}/cache:/cache
- ${PIPU_DATA:-/srv/pipu}/updates:/updates - ${PIPU_DATA:-/srv/pipu}/updates:/updates
- ${PIPU_DATA:-/srv/pipu}/uploads:/uploads - ${PIPU_DATA:-/srv/pipu}/uploads:/uploads
@@ -75,6 +85,9 @@ services:
source: ${PIPU_DATA:-/srv/pipu}/library source: ${PIPU_DATA:-/srv/pipu}/library
target: /library target: /library
bind: { propagation: rshared } bind: { propagation: rshared }
- type: bind
source: ${LIBRARY_HOST:-${PIPU_DATA:-/srv/pipu}/library-host}
target: /library-host
- ${PIPU_DATA:-/srv/pipu}/uploads:/uploads:ro - ${PIPU_DATA:-/srv/pipu}/uploads:/uploads:ro
recommender: recommender:
@@ -102,6 +115,7 @@ services:
LOG_LEVEL: ${LOG_LEVEL:-info} LOG_LEVEL: ${LOG_LEVEL:-info}
volumes: volumes:
- *library-ro - *library-ro
- *library-host-ro
- ${PIPU_DATA:-/srv/pipu}/models:/models - ${PIPU_DATA:-/srv/pipu}/models:/models
deploy: { resources: { limits: { cpus: "4" } } } deploy: { resources: { limits: { cpus: "4" } } }
depends_on: depends_on:
+20 -1
View File
@@ -17,6 +17,16 @@ mkdir -p "$DATA"/{library,library-host,cache/transcode,cache/covers,postgres,mei
chown 65532:65532 "$DATA"/{cache,cache/transcode,cache/covers,updates,uploads} chown 65532:65532 "$DATA"/{cache,cache/transcode,cache/covers,updates,uploads}
chown 10001:10001 "$DATA/models" 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; чтобы точки монтирования видели другие контейнеры, # storage-agent монтирует NFS внутрь $DATA/library; чтобы точки монтирования видели другие контейнеры,
# каталог должен быть отдельной точкой монтирования с shared propagation. # каталог должен быть отдельной точкой монтирования с shared propagation.
if ! findmnt -n "$DATA/library" >/dev/null 2>&1; then if ! findmnt -n "$DATA/library" >/dev/null 2>&1; then
@@ -50,10 +60,19 @@ secret() { head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 40; }
if [ ! -f .env ]; then if [ ! -f .env ]; then
echo "→ .env из .env.example" echo "→ .env из .env.example"
cp .env.example .env 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|" .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 chmod 600 .env
chown "$OWNER" .env chown "$OWNER" .env
echo " Заполните в .env: DOMAIN и ACME_EMAIL (или настройте свой прокси — docs/install-guide.md)" echo " Заполните в .env: DOMAIN и ACME_EMAIL (или настройте свой прокси — docs/install-guide.md)"
fi fi
# 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" echo "Готово. Сборка и запуск последнего релиза: ./update.sh"
+1
View File
@@ -48,6 +48,7 @@ server {
# Аудио и обложки отдаёт nginx по заголовку X-Accel-Redirect от api (docs/playback.md). # Аудио и обложки отдаёт nginx по заголовку X-Accel-Redirect от api (docs/playback.md).
location /_internal/library/ { internal; alias /library/; sendfile on; aio threads; } location /_internal/library/ { internal; alias /library/; sendfile on; aio threads; }
location /_internal/library-host/ { internal; alias /library-host/; sendfile on; aio threads; }
location /_internal/cache/ { internal; alias /cache/transcode/; sendfile on; } location /_internal/cache/ { internal; alias /cache/transcode/; sendfile on; }
location /_internal/covers/ { internal; alias /cache/covers/; expires 30d; } location /_internal/covers/ { internal; alias /cache/covers/; expires 30d; }
location /_internal/uploads/ { internal; alias /uploads/; sendfile on; } location /_internal/uploads/ { internal; alias /uploads/; sendfile on; }
+5 -1
View File
@@ -15,7 +15,7 @@
``` ```
/srv/pipu/ /srv/pipu/
├── library/ # точки монтирования источников (storage-agent, rshared) ├── library/ # точки монтирования источников (storage-agent, rshared)
├── library-host/ # опционально: заранее смонтированные папки для type=local ├── library-host/ # папка для type=local по умолчанию; обычно вместо неё LIBRARY_HOST=/mnt/music
├── cache/ ├── cache/
│ ├── transcode/ │ ├── transcode/
│ └── covers/ │ └── covers/
@@ -29,6 +29,10 @@
└── caddy/ # сертификаты Let's Encrypt (если используется compose.tls.yaml) └── caddy/ # сертификаты Let's Encrypt (если используется compose.tls.yaml)
``` ```
Музыка на локальном диске сервера подключается через `LIBRARY_HOST` в `.env` (например, `/mnt/music`):
каталог хоста монтируется в `api`, `nginx` и `analyzer` только на чтение, в `storage-agent` — на запись
(импорт одобренных загрузок), внутри контейнеров он виден как `/library-host`.
Перед первым запуском: `mkdir -p /srv/pipu/library && mount --bind /srv/pipu/library /srv/pipu/library && mount --make-rshared /srv/pipu/library` Перед первым запуском: `mkdir -p /srv/pipu/library && mount --bind /srv/pipu/library /srv/pipu/library && mount --make-rshared /srv/pipu/library`
(делает `deploy/install.sh`, плюс systemd-unit для повторения после перезагрузки). (делает `deploy/install.sh`, плюс systemd-unit для повторения после перезагрузки).
+76 -17
View File
@@ -6,8 +6,9 @@
В примерах: В примерах:
- домен — `music.example.com`, замените на свой; - домен — `music.example.com`, замените на свой;
- репозиторий — `gitea.hsrv.site/mrleo1nid/pipu-music`; - репозиторий — `gitea.hsrv.site/mrleo1nid/pipu-music`, клонируется в `/opt/pipu-music`;
- каталог установки — `/opt/pipu-music`. - диск с музыкой смонтирован в `/mnt/music`;
- данные сервиса (БД, кэши, сертификаты) — в `/srv/pipu`.
Готовые Docker-образы не публикуются: сервер клонирует репозиторий и собирает образы сам Готовые Docker-образы не публикуются: сервер клонирует репозиторий и собирает образы сам
(`docker compose build`). Релизы в Gitea содержат только APK для Android. (`docker compose build`). Релизы в Gitea содержат только APK для Android.
@@ -22,11 +23,11 @@
| Что | Требование | | Что | Требование |
|---|---| |---|---|
| Сервер | Linux x86_64 (Debian 12 / Ubuntu 22.04+), 4+ ядра, 8+ ГБ RAM, 30+ ГБ на диске (система, образы, кэш сборки, данные) | | Сервер | Linux x86_64 (Debian 12 / Ubuntu 22.04+), 4+ ядра, 8+ ГБ RAM, 30+ ГБ на системном диске (система, образы, кэш сборки, данные) |
| Доступ | root или пользователь с `sudo` | | Доступ | root или пользователь с `sudo` |
| Домен | Поддомен, которым вы управляете, например `music.example.com` | | Домен | Поддомен, которым вы управляете, например `music.example.com` |
| Сеть | Входящие **80/tcp** и **443/tcp** (+ **443/udp** для HTTP/3) из интернета на сервер | | Сеть | Входящие **80/tcp** и **443/tcp** (+ **443/udp** для HTTP/3) из интернета на сервер |
| Музыка | NFS-сервер с библиотекой (подключается позже из админки) | | Музыка | Отдельный диск с библиотекой, подключённый к серверу (шаг 4). NFS-хранилища можно добавить позже из админки |
| Интернет на сервере | Нужен при сборке: скачиваются базовые образы и зависимости Go / npm / Python | | Интернет на сервере | Нужен при сборке: скачиваются базовые образы и зависимости Go / npm / Python |
| Gitea | Доступ к репозиторию на чтение (если он приватный — токен или SSH-ключ) | | Gitea | Доступ к репозиторию на чтение (если он приватный — токен или SSH-ключ) |
@@ -50,7 +51,7 @@ dig +short music.example.com
``` ```
> Сертификат Let's Encrypt выдаётся, только когда домен уже указывает на сервер и порт 80 доступен из интернета. > Сертификат Let's Encrypt выдаётся, только когда домен уже указывает на сервер и порт 80 доступен из интернета.
> Не запускайте шаг 6, пока эта проверка не проходит. > Не запускайте шаг 7, пока эта проверка не проходит.
--- ---
@@ -86,7 +87,51 @@ sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw allow 443/udp
--- ---
## 4. Клонирование репозитория ## 4. Диск с музыкой
Музыка лежит на отдельном диске, смонтированном в `/mnt/music`. Если диск уже смонтирован — проверьте его
командой из конца шага и переходите к шагу 5.
Найдите диск и его UUID (раздел с музыкой — например, `/dev/sdb1`):
```bash
lsblk -f
```
Создайте точку монтирования:
```bash
sudo mkdir -p /mnt/music
```
Добавьте в `/etc/fstab` строку (подставьте свой UUID и файловую систему из вывода `lsblk -f`):
```ini
UUID=1234abcd-0000-0000-0000-000000000000 /mnt/music ext4 defaults,nofail,noatime 0 2
```
`nofail` нужен, чтобы сервер загружался, даже если диск не подключён. Смонтируйте и проверьте:
```bash
sudo mount -a && findmnt /mnt/music && ls /mnt/music | head
```
Сервисы Pipu работают в контейнерах не от root, поэтому музыка должна быть доступна на чтение всем:
файлы — `644`, каталоги — `755`. Если раньше права были закрытыми:
```bash
sudo chmod -R a+rX /mnt/music
```
> Для дисков с NTFS / exFAT права задаются опциями монтирования, а не `chmod`: добавьте в fstab
> `uid=0,gid=0,umask=022` вместо `defaults`.
Pipu только читает этот диск. Записывать в него будет лишь импорт одобренных треков из очереди на добавление,
и только если в админке разрешить это для хранилища ([uploads.md](uploads.md)).
---
## 5. Клонирование репозитория
```bash ```bash
sudo mkdir -p /opt/pipu-music && sudo chown "$USER" /opt/pipu-music sudo mkdir -p /opt/pipu-music && sudo chown "$USER" /opt/pipu-music
@@ -104,16 +149,18 @@ git clone https://gitea.hsrv.site/mrleo1nid/pipu-music.git /opt/pipu-music && cd
--- ---
## 5. Подготовка сервера ## 6. Подготовка сервера
```bash ```bash
sudo ./install.sh sudo LIBRARY_HOST=/mnt/music ./install.sh
``` ```
Скрипт: Скрипт:
- создаёт каталоги данных в `/srv/pipu` (БД, кэши, загрузки, сертификаты); - создаёт каталоги данных в `/srv/pipu` (БД, кэши, загрузки, сертификаты);
- делает `/srv/pipu/library` точкой монтирования с shared propagation, чтобы NFS-хранилища, которые потом - делает `/srv/pipu/library` точкой монтирования с shared propagation, чтобы NFS-хранилища, которые потом
подключит админ, были видны всем контейнерам, и ставит systemd-юнит, повторяющий это после перезагрузки; подключит админ, были видны всем контейнерам, и ставит systemd-юнит, повторяющий это после перезагрузки;
- проверяет, что `/mnt/music` существует, и записывает его в `.env` как `LIBRARY_HOST` — эту папку получат
контейнеры `api`, `nginx` и `analyzer` (внутри они видят её как `/library-host`);
- создаёт `.env` со случайными паролями БД, ключом поиска и межсервисным токеном. - создаёт `.env` со случайными паролями БД, ключом поиска и межсервисным токеном.
Заполните в `/opt/pipu-music/deploy/.env`: Заполните в `/opt/pipu-music/deploy/.env`:
@@ -124,6 +171,14 @@ DOMAIN=music.example.com
ACME_EMAIL=you@example.com ACME_EMAIL=you@example.com
``` ```
Проверьте, что там же стоит путь к музыке (его вписал `install.sh`):
```ini
LIBRARY_HOST=/mnt/music
```
Если диск смонтирован в другое место — поправьте путь здесь и перезапустите сервисы (`./update.sh`).
`ACME_EMAIL` — адрес, на который Let's Encrypt пишет, если с сертификатом что-то не так. `ACME_EMAIL` — адрес, на который Let's Encrypt пишет, если с сертификатом что-то не так.
Остальные значения менять не нужно; `PIPU_VERSION` проставляет `update.sh`. Остальные значения менять не нужно; `PIPU_VERSION` проставляет `update.sh`.
@@ -131,7 +186,7 @@ ACME_EMAIL=you@example.com
--- ---
## 6. Сборка и запуск ## 7. Сборка и запуск
```bash ```bash
./update.sh ./update.sh
@@ -161,7 +216,7 @@ curl -s https://music.example.com/readyz
--- ---
## 7. Веб-клиент ## 8. Веб-клиент
Откройте `https://music.example.com` в браузере. Откройте `https://music.example.com` в браузере.
@@ -176,12 +231,14 @@ curl -s https://music.example.com/readyz
Сразу после входа приложение попросит сменить логин и пароль администратора Сразу после входа приложение попросит сменить логин и пароль администратора
([auth-and-roles.md](auth-and-roles.md#первый-запуск-bootstrap)). Чтобы задать пароль заранее, впишите ([auth-and-roles.md](auth-and-roles.md#первый-запуск-bootstrap)). Чтобы задать пароль заранее, впишите
`BOOTSTRAP_ADMIN_PASSWORD` в `.env` **до** первого запуска. `BOOTSTRAP_ADMIN_PASSWORD` в `.env` **до** первого запуска.
- **С версии 0.3 (M2):** Админка → Хранилища → «Добавить хранилище» → адрес NFS-сервера и путь экспорта - **С версии 0.3 (M2):** Админка → Хранилища → «Добавить хранилище» → тип «Локальная папка» → выберите
→ «Проверить» → шаблоны путей → «Сохранить и пересканировать» ([library-and-storage.md](library-and-storage.md)). подпапку на диске `/mnt/music` (в списке она видна как `/library-host/...`) → «Проверить» → шаблоны путей
→ «Сохранить и пересканировать» ([library-and-storage.md](library-and-storage.md)).
Там же можно добавить NFS-хранилище: адрес сервера и путь экспорта.
--- ---
## 8. Android-приложение ## 9. Android-приложение
1. На телефоне откройте страницу релизов 1. На телефоне откройте страницу релизов
`https://gitea.hsrv.site/mrleo1nid/pipu-music/releases` и скачайте `pipu-music-<версия>.apk`. `https://gitea.hsrv.site/mrleo1nid/pipu-music/releases` и скачайте `pipu-music-<версия>.apk`.
@@ -201,7 +258,7 @@ curl -s https://music.example.com/readyz
--- ---
## 9. Вариант: свой reverse proxy вместо Caddy ## 10. Вариант: свой reverse proxy вместо Caddy
Если на сервере уже есть Traefik, Nginx Proxy Manager или свой nginx с сертификатами: Если на сервере уже есть Traefik, Nginx Proxy Manager или свой nginx с сертификатами:
@@ -238,14 +295,14 @@ curl -s https://music.example.com/readyz
} }
``` ```
3. Запуск и проверки — как в шагах 6–8. 3. Запуск и проверки — как в шагах 7–9.
Если прокси работает на другой машине, укажите в `HTTP_PORT` внутренний адрес сервера (`192.168.1.10:8081`) и Если прокси работает на другой машине, укажите в `HTTP_PORT` внутренний адрес сервера (`192.168.1.10:8081`) и
закройте этот порт от интернета файрволом. закройте этот порт от интернета файрволом.
--- ---
## 10. Обновление, бэкапы, удаление ## 11. Обновление, бэкапы, удаление
Обновление до последнего релиза: Обновление до последнего релиза:
@@ -271,7 +328,7 @@ cd /opt/pipu-music/deploy && docker compose down
--- ---
## 11. Если что-то не работает ## 12. Если что-то не работает
| Симптом | Что проверить | | Симптом | Что проверить |
|---|---| |---|---|
@@ -282,6 +339,8 @@ cd /opt/pipu-music/deploy && docker compose down
| Сборка падает на скачивании зависимостей | Нет интернета или DNS на сервере; повторите `./update.sh` — уже собранные слои берутся из кэша | | Сборка падает на скачивании зависимостей | Нет интернета или DNS на сервере; повторите `./update.sh` — уже собранные слои берутся из кэша |
| Сборка падает: «no space left on device» | Очистите кэш сборки: `docker builder prune` | | Сборка падает: «no space left on device» | Очистите кэш сборки: `docker builder prune` |
| `permission denied … docker.sock` | Пользователь не в группе `docker` (шаг 3) или вы не перелогинились | | `permission denied … docker.sock` | Пользователь не в группе `docker` (шаг 3) или вы не перелогинились |
| Сервисы не стартуют: `bind source path does not exist: /mnt/music` | Диск не смонтирован: `findmnt /mnt/music`, `sudo mount -a`; путь в `LIBRARY_HOST` в `.env` |
| Хранилище «Локальная папка» пустое или ошибка доступа | Диск смонтирован (`ls /mnt/music`); права на чтение для всех — `sudo chmod -R a+rX /mnt/music` (шаг 4) |
| `storage-agent` не стартует, ошибка про propagation / shared mount | Ещё раз `sudo ./install.sh`; на хосте `findmnt -o TARGET,PROPAGATION /srv/pipu/library` должно показать `shared` | | `storage-agent` не стартует, ошибка про propagation / shared mount | Ещё раз `sudo ./install.sh`; на хосте `findmnt -o TARGET,PROPAGATION /srv/pipu/library` должно показать `shared` |
| Android: «Сервер не отвечает» | Тот же адрес открывается в браузере телефона; в адресе нет опечатки; телефон в сети с доступом к серверу | | Android: «Сервер не отвечает» | Тот же адрес открывается в браузере телефона; в адресе нет опечатки; телефон в сети с доступом к серверу |
| Android: «Эта версия приложения слишком старая» | Установите APK из последнего релиза | | Android: «Эта версия приложения слишком старая» | Установите APK из последнего релиза |
+1 -1
View File
@@ -7,7 +7,7 @@
| Тип | Как подключается | | Тип | Как подключается |
|---|---| |---|---|
| `nfs` | Админ указывает `server`, `export`, опции. `storage-agent` выполняет `mount -t nfs -o <opts> server:export /library/<id>`. | | `nfs` | Админ указывает `server`, `export`, опции. `storage-agent` выполняет `mount -t nfs -o <opts> server:export /library/<id>`. |
| `local` | Папка, уже смонтированная на хосте в `/srv/pipu/library-host` (например, через fstab). Админ выбирает подпапку. Нужен как запасной путь и для dev. | | `local` | Папка на диске сервера, заранее смонтированная на хосте (например, диск `/mnt/music` через fstab) и указанная в `LIBRARY_HOST`. В контейнерах видна как `/library-host`; админ выбирает подпапку. |
### Монтирование (storage-agent) ### Монтирование (storage-agent)