docs: add product idea, design prototypes and development documentation
- IDEA.md: product vision from the user's perspective (input for design)
- Architecture, ADRs, data model, REST API overview
- Auth and roles, library storage (NFS, path regexes, scanner, search),
playback, recommendation system ("My Wave"), track upload queue
- Web, Android (offline, self-update), i18n, design system
- Testing strategy, Gitea Actions CI/CD, deployment, conventions, roadmap
- Clickable web/Android design prototypes and wave button variants
- .claude/launch.json to serve the prototypes locally
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
36e99fe960
commit
9eb1c25147
30 files changed
+7538
No files matched your search
@@ -0,0 +1,144 @@
|
||||
# Развёртывание
|
||||
|
||||
## Требования к серверу
|
||||
|
||||
- Linux x86_64 (нужен mount propagation для storage-agent), Docker 24+, Docker Compose v2.
|
||||
- 4+ ядра, 8+ ГБ RAM (аудиоанализ при первом запуске ест CPU; TensorFlow-модели ~1–2 ГБ RAM).
|
||||
- Диск: ~5 ГБ под систему и БД + кэш транскодов (по умолчанию 50 ГБ) + обложки (~1 ГБ на 10k альбомов).
|
||||
- Сетевой доступ к NFS-серверам.
|
||||
|
||||
## Каталоги на хосте
|
||||
|
||||
```
|
||||
/srv/pipu/
|
||||
├── library/ # точки монтирования источников (storage-agent, rshared)
|
||||
├── library-host/ # опционально: заранее смонтированные папки для type=local
|
||||
├── cache/
|
||||
│ ├── transcode/
|
||||
│ └── covers/
|
||||
├── postgres/
|
||||
├── meili/
|
||||
├── redis/
|
||||
├── models/ # модели Essentia (скачиваются при первом старте analyzer)
|
||||
├── updates/ # кэш APK релизов
|
||||
├── uploads/ # staging очереди на добавление (размер ≈ лимит очереди, по умолчанию 10 ГБ)
|
||||
└── backups/
|
||||
```
|
||||
|
||||
Перед первым запуском: `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/compose.yaml` (сокращённо)
|
||||
|
||||
```yaml
|
||||
x-library-ro: &library-ro
|
||||
type: bind
|
||||
source: /srv/pipu/library
|
||||
target: /library
|
||||
read_only: true
|
||||
bind: { propagation: rslave }
|
||||
|
||||
services:
|
||||
nginx:
|
||||
image: ${REGISTRY}/pipu-music-web:${PIPU_VERSION}
|
||||
ports: ["443:443", "80:80"]
|
||||
volumes:
|
||||
- *library-ro
|
||||
- /srv/pipu/cache:/cache:ro
|
||||
- /srv/pipu/uploads:/uploads:ro # прослушивание очереди админом (internal location)
|
||||
- ./certs:/etc/nginx/certs:ro
|
||||
depends_on: [api]
|
||||
|
||||
api:
|
||||
image: ${REGISTRY}/pipu-music-api:${PIPU_VERSION}
|
||||
env_file: .env
|
||||
volumes:
|
||||
- *library-ro
|
||||
- /srv/pipu/cache:/cache
|
||||
- /srv/pipu/updates:/updates
|
||||
- /srv/pipu/uploads:/uploads
|
||||
depends_on: [postgres, redis, meilisearch, storage-agent]
|
||||
|
||||
storage-agent:
|
||||
image: ${REGISTRY}/pipu-music-storage-agent:${PIPU_VERSION}
|
||||
cap_add: [SYS_ADMIN]
|
||||
security_opt: ["apparmor:unconfined"]
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /srv/pipu/library
|
||||
target: /library
|
||||
bind: { propagation: rshared }
|
||||
- /srv/pipu/uploads:/uploads:ro # читает staging при импорте
|
||||
# /library-rw/<id> монтируется агентом внутри своего контейнера, наружу не пробрасывается
|
||||
environment: [INTERNAL_TOKEN, DATABASE_URL, PIPU_FILE_UID, PIPU_FILE_GID]
|
||||
|
||||
recommender:
|
||||
image: ${REGISTRY}/pipu-music-recommender:${PIPU_VERSION}
|
||||
command: ["serve"]
|
||||
env_file: .env
|
||||
depends_on: [postgres, redis]
|
||||
|
||||
analyzer:
|
||||
image: ${REGISTRY}/pipu-music-recommender:${PIPU_VERSION}
|
||||
command: ["analyze", "--workers", "4"]
|
||||
env_file: .env
|
||||
volumes: [*library-ro, /srv/pipu/models:/models]
|
||||
deploy: { resources: { limits: { cpus: "4" } } }
|
||||
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg17
|
||||
volumes: [/srv/pipu/postgres:/var/lib/postgresql/data]
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
command: ["redis-server", "--appendonly", "yes"]
|
||||
volumes: [/srv/pipu/redis:/data]
|
||||
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1.11
|
||||
environment: [MEILI_MASTER_KEY, MEILI_ENV=production]
|
||||
volumes: [/srv/pipu/meili:/meili_data]
|
||||
```
|
||||
|
||||
Наружу открыт только `nginx`. Все остальные — во внутренней сети compose.
|
||||
|
||||
## Конфигурация (`.env`)
|
||||
|
||||
| Переменная | Пример / по умолчанию | Описание |
|
||||
|---|---|---|
|
||||
| `PIPU_VERSION` | `1.0.0` | Версия образов |
|
||||
| `PUBLIC_URL` | `https://music.home.lan` | Внешний адрес |
|
||||
| `DATABASE_URL` | `postgres://pipu:…@postgres:5432/pipu` | |
|
||||
| `REDIS_URL` | `redis://redis:6379/0` | |
|
||||
| `MEILI_URL`, `MEILI_MASTER_KEY` | | |
|
||||
| `JWT_PRIVATE_KEY_FILE` | `/run/secrets/jwt_ed25519.pem` | Генерируется `install.sh` |
|
||||
| `STREAM_SIGNING_KEY` | 32 байта base64 | HMAC подписанных ссылок |
|
||||
| `INTERNAL_TOKEN` | случайный | Межсервисная аутентификация |
|
||||
| `BOOTSTRAP_ADMIN_USERNAME` | `admin` | |
|
||||
| `BOOTSTRAP_ADMIN_PASSWORD` | пусто → сгенерировать и вывести в лог | |
|
||||
| `TRANSCODE_CACHE_LIMIT_GB` | `50` | |
|
||||
| `TRANSCODE_CONCURRENCY` | `2` | |
|
||||
| `GITEA_URL`, `GITEA_REPO`, `GITEA_TOKEN` | `https://git.home.lan`, `owner/pipu-music`, read-only токен | Для самообновления Android |
|
||||
| `PIPU_FILE_UID`, `PIPU_FILE_GID` | `1000` | Владелец файлов, которые агент записывает в принимающие хранилища |
|
||||
| `UPLOADS_DIR` | `/uploads` | Staging очереди на добавление (лимит очереди задаётся в админке) |
|
||||
| `LOG_LEVEL` | `info` | |
|
||||
|
||||
## TLS
|
||||
|
||||
- Свой домен — Let's Encrypt (через отдельный certbot / acme.sh или Caddy перед nginx).
|
||||
- Локальная сеть — свой CA; сертификат CA нужно установить на устройства или включить пиннинг в мобильном (см. mobile.md).
|
||||
|
||||
## Бэкапы
|
||||
|
||||
- `pg_dump` ежедневно (cron-контейнер `backup`), хранить 14 дней + еженедельные 8 недель → `/srv/pipu/backups`.
|
||||
- Meilisearch, кэши и модели **не бэкапятся** — восстанавливаются (`reindex`, повторный транскод / анализ).
|
||||
- Признаки анализа хранятся в Postgres — после восстановления из бэкапа повторный многочасовой анализ не нужен.
|
||||
- Сама музыка — на NFS, вне зоны ответственности приложения.
|
||||
|
||||
## Обновление
|
||||
|
||||
```bash
|
||||
cd /opt/pipu && ./update.sh 1.3.0 # pull образов, up -d, миграции при старте api, проверка /readyz
|
||||
```
|
||||
Откат: `./update.sh 1.2.4` (миграции пишутся обратно-совместимыми в пределах минорной версии: сначала добавить,
|
||||
потом — в следующем релизе — удалить).
|
||||
Reference in new issue
Block a user