# Подготовка диска под медиахранилище (tvvm)

Runbook: разметка, форматирование и монтирование диска `sdb` (700G) под хранилище TeleWave.
Выполняется **один раз** на сервере `tvvm` перед запуском фичи медиа. Все команды — от root
(`sudo -i` или с `sudo` перед каждой).

> ⚠️ Команды разметки **уничтожают данные** на целевом диске. Диск `sdb` сейчас пустой (нет
> разделов), но каждый шаг ниже содержит проверку — не пропускайте их.

Итог: раздел `sdb1` (ext4) смонтирован в `/srv/telewave/media`, внутри созданы рабочие каталоги,
запись из контейнера (работает под непривилегированным uid 1654) возможна.

---

## 0. Проверить, что это тот самый диск

```bash
lsblk -o NAME,SIZE,TYPE,MOUNTPOINTS,FSTYPE /dev/sdb
```

Ожидается: `sdb` размером `700G`, тип `disk`, **без разделов и без точек монтирования**. Убедитесь
дополнительно, что на диске нет файловой системы и подписей:

```bash
sudo wipefs -n /dev/sdb          # -n = «сухой прогон», ничего не меняет
```

Если вывод пустой — диск чист, продолжаем. Если что-то нашлось (следы старой ФС/RAID) —
**остановитесь** и разберитесь, что это, прежде чем идти дальше.

---

## 1. Создать таблицу разделов и один раздел

GPT + один раздел на весь диск:

```bash
sudo parted -s /dev/sdb mklabel gpt
sudo parted -s -a optimal /dev/sdb mkpart primary ext4 0% 100%
sudo partprobe /dev/sdb
lsblk /dev/sdb
```

Должен появиться `sdb1` размером ~700G.

---

## 2. Отформатировать в ext4

`-m 1` уменьшает резерв под root с 5% до 1% (на диске данных резерв в 35 ГБ не нужен), `-L` даёт
метку тома:

```bash
sudo mkfs.ext4 -m 1 -L telewave-media /dev/sdb1
```

---

## 3. Создать точку монтирования

```bash
sudo mkdir -p /srv/telewave/media
```

---

## 4. Прописать в /etc/fstab (монтирование по UUID)

Монтируем по UUID, а не по имени `sdb1` — имя может измениться при добавлении дисков. Узнать UUID:

```bash
sudo blkid /dev/sdb1
```

Скопируйте значение `UUID="..."` и добавьте строку в `/etc/fstab` (подставьте свой UUID):

```
UUID=ВАШ-UUID  /srv/telewave/media  ext4  defaults,noatime,nofail,x-systemd.device-timeout=10  0  2
```

Пояснения к опциям:
- `noatime` — не обновлять время доступа при чтении сегментов; заметно снижает лишние записи при
  раздаче видео.
- `nofail` — если диск не подключился, система всё равно загрузится (приложение просто не сможет
  писать, а не «висит» на загрузке).
- `x-systemd.device-timeout=10` — не ждать диск дольше 10с при старте.

Проверить, что fstab корректен, и смонтировать:

```bash
sudo systemctl daemon-reload
sudo mount -a
findmnt /srv/telewave/media
```

`findmnt` должен показать смонтированный `ext4` на `/dev/sdb1`. **Ошибка на этом шаге лучше, чем
на следующей перезагрузке** — если `mount -a` ругается, чините fstab сейчас.

---

## 5. Рабочие каталоги и права

Создать структуру, ожидаемую приложением:

```bash
sudo mkdir -p /srv/telewave/media/{inbox,manual,uploads,originals,assets}
```

**Владелец.** Контейнер работает под непривилегированным пользователем `app` образа aspnet —
**uid/gid 1654** (`USER $APP_UID` в Dockerfile). Bind-mount пробрасывает права хоста внутрь как
есть, никакого маппинга uid не происходит: контейнер увидит ровно те номера, что стоят на хосте.
Значит, владельцем хранилища должен быть 1654.

Имени `app` на хосте нет — заводим группу с этим gid, чтобы права были читаемыми в `ls` и чтобы
в неё можно было добавить себя:

```bash
sudo groupadd -g 1654 telewave          # «уже существует» — не ошибка, идём дальше
sudo usermod -aG telewave "$USER"       # чтобы класть файлы в inbox/manual под собой
```

> Членство в группе применяется **только к новым сессиям**: перелогиньтесь (или `newgrp telewave`),
> иначе следующая команда отработает, а записать файл вы всё равно не сможете. Проверка — `id`
> должен показать `telewave` в списке групп.

Теперь владелец и базовые права:

```bash
sudo chown -R 1654:1654 /srv/telewave/media
sudo chmod -R 750 /srv/telewave/media
```

Два каталога наполняете вы, а не приложение:

- `inbox/` — разбирается сканером автоматически: файл с допустимым расширением и стабильным
  размером регистрируется сам и уходит в обработку;
- `manual/` — **ручной разбор**: сканер сюда не заглядывает. Файлы видны в админке
  (Медиа → «Из папки manual»), выбираются галочками и сразу привязываются к шоу. Импортированные
  файлы уходят из каталога так же, как из `inbox/`.

Их открываем группе на запись, плюс setgid (`2` в начале режима) — чтобы файлы, положенные вами по
SFTP/rsync, наследовали группу `telewave`, а не вашу личную, и приложение их видело:

```bash
sudo chmod 2775 /srv/telewave/media/inbox /srv/telewave/media/manual
```

Забрать **файл** из этих каталогов приложение сможет в любом случае: удаление зависит от прав на
каталог (владелец — 1654), а не на сам файл, поэтому чужой umask у ваших загрузок импорту не мешает.

А вот **вложенные папки** — мешают, и одним `chmod` это не закрыть. В `manual/` обычно кладут не
файлы, а каталог раздачи целиком. Владельцем такого каталога станете вы, режим ему выставит umask
клиента (у SFTP это обычно 755), и группе достанется `r-x` — приложение не сможет ни вынести файл
в `originals/` при импорте, ни подчистить спутники и опустевший каталог. Права на новое внутри
задаются дефолтными ACL — они действуют независимо от того, каким клиентом и с каким umask файлы
приехали:

```bash
sudo apt install -y acl
sudo setfacl -R -m g:telewave:rwx -m d:g:telewave:rwx \
  /srv/telewave/media/inbox /srv/telewave/media/manual
```

Проверить — `getfacl /srv/telewave/media/manual`: нужны строки `group:telewave:rwx` и
`default:group:telewave:rwx`. Без ACL альтернатива только клиентская (принудительные права `775`
на загрузку в WinSCP, umask 002 у rsync) — держится на настройке каждого, кто заливает файлы,
и ломается при первой же загрузке другим инструментом.

**Обновление существующей установки.** Если хранилище было заведено раньше, когда контейнер работал
под root, — те же команды и есть вся миграция: выполните их на остановленном контейнере
(`docker compose down`), затем поднимайте новый образ. До смены владельца приложение стартует, но
любая запись в `/media` будет падать с `Permission denied`.

---

## 6. Проброс в контейнер (bind mount)

В `docker-compose.yml`, в сервис `app`, добавить том (этого пока **нет** в репозитории — появится
вместе с реализацией фичи, здесь для справки):

```yaml
services:
  app:
    # ... существующая конфигурация ...
    volumes:
      - /srv/telewave/media:/media
    mem_limit: 3g
```

И переменные окружения в `.env` (см. `docs/media-storage-and-streaming.md`):

```
Storage__RootPath=/media
```

Выбран **bind mount**: диск смонтирован в ОС через fstab (шаги 1–5) и виден всегда, независимо от
Docker — это важно, потому что файлы в `inbox/` кладутся вручную по SFTP/rsync, в том числе когда
контейнер остановлен.

> **Альтернатива — named volume, монтируемый самим Docker** (не используем, для справки). Позволяет
> пропустить шаги 3–5, но диск тогда доступен только при запущенном контейнере, а подкаталоги всё
> равно надо создавать заранее. Пример:
>
> ```yaml
> services:
>   app:
>     volumes:
>       - media:/media
>     mem_limit: 3g
> volumes:
>   media:
>     driver: local
>     driver_opts:
>       type: ext4
>       device: /dev/disk/by-uuid/ВАШ-UUID
>       o: noatime
> ```
>
> Разметка и `mkfs.ext4` (шаги 1–2) обязательны в любом случае.

---

## 7. Проверка записи из контейнера

После добавления тома и пересборки образа убедиться, что контейнер работает под нужным uid и пишет
на диск:

```bash
docker compose exec app id     # ожидается uid=1654 gid=1654
docker compose exec app sh -c 'touch /media/.wtest && ls -l /media/.wtest && rm /media/.wtest'
```

И что вы сами можете класть файлы вручную (под своим пользователем, не через sudo), а приложение —
убирать созданное вами, в том числе внутри вложенных каталогов:

```bash
touch /srv/telewave/media/inbox/.wtest && rm /srv/telewave/media/inbox/.wtest
mkdir -p /srv/telewave/media/manual/.wtest && docker compose exec app sh -c 'rmdir /media/manual/.wtest'
```

Все команды должны отработать без ошибок доступа. `Permission denied` в первой — не сделан
`chown -R 1654:1654` из шага 5; во второй — вы не в группе `telewave` либо не перелогинились после
`usermod` (в SFTP-клиенте — не переподключили сессию); в третьей — не выставлены дефолтные ACL,
и приложение не достаёт файлы из ваших подкаталогов. На этом подготовка хоста завершена — дальнейшее (создание `MediaAsset`, нарезка ffmpeg)
делает уже само приложение.

---

## 8. Что дальше смотреть в админке

Заполненность диска видна в разделе **Админка → Хранилище**: сколько занято на томе целиком,
сколько из этого приходится на TeleWave, и на что именно оно ушло — сегменты программ и заставок,
исходники, inbox, изображения. Отдельной строкой считается «прочее под корнем»: если там не ноль,
значит на томе лежит что-то, чего приложение не создавало.

Цифра считается обходом дерева и потому кэшируется на пять минут; кнопка «Пересчитать» заставляет
пересчитать сразу. Когда свободного места остаётся меньше `Storage:MinFreeSpaceBytes`, страница
показывает предупреждение — с этого порога сервер начинает отклонять загрузку новых файлов.

---

## Приложение: если диск нужно расширить в будущем

Если VM отдаст диску больше места (например `sdb` вырастет с 700G), после увеличения на стороне
гипервизора:

```bash
sudo growpart /dev/sdb 1        # расширить раздел на весь диск
sudo resize2fs /dev/sdb1        # расширить ext4 (можно на смонтированном)
df -h /srv/telewave/media
```
