Files
lampac/AGENT.md
T
prodik 25c591b45a Секреты вынесены в config/init.custom.conf + config/secrets.env
- config/init.conf — шаблон (секреты всегда пустые), в git
- config/init.custom.conf — боевой конфиг, монтируется в /lampac/init.conf,
  собирается scripts/config-secrets.py apply из шаблона + secrets.env
- docker-compose.yaml: монтирует ./config/init.custom.conf:/lampac/init.conf
- scripts/config-secrets.py: apply/export/check/status
- scripts/install-git-hooks.sh: post-merge/post-checkout -> apply,
  pre-commit -> check --staged
- scripts/restore-lampa-main.sh: восстановление веб-клиента из архива
- .gitignore: init.custom.conf, secrets.env, lampa-main/, restore.sh zip
- lampa-main смонтирован как bind-mount ./lampa-main:/lampac/wwwroot/lampa-main,
  переживает docker compose up -d (раньше исчезал из writable-слоя)
- README: раздел «Секреты», обновлена «Структура проекта» и «Обновление»,
  «Безопасность» переписана под новую схему
- AGENT: правило 10 дополнено init.custom.conf/secrets.env/хуками,
  §11 Tg-notify ссылается на apply, §1 упоминает lampa-main bind-mount
2026-10-04 15:42:16 +00:00

141 lines
17 KiB
Markdown
Raw 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.
# AGENT.md — инструкции для ИИ-агентов
Работа с проектом: локальный Docker-развёртывание [Lampac NextGen](https://github.com/lampac-nextgen/lampac). Человек читает ответы **только на русском**.
## Источники правды (порядок приоритета)
1. **Официальная документация** — [docs.lampac.dev](https://docs.lampac.dev) (полный текст страниц: `https://docs.lampac.dev/llms-full.txt`, индекс: `llms.txt`). Не гадать по памяти — проверять в доках.
2. **Живой контейнер** — состояние модулей, манифестов и конфигов брать из контейнера:
```bash
docker exec lampac sh -c 'cat /lampac/module/<Имя>/manifest.json'
docker exec lampac cat /lampac/current.conf # merged-конфиг: base + init
```
3. **Логи контейнера** — `docker logs lampac`.
4. Локальные файлы проекта: `docker-compose.yaml`, `config/init.conf`, `mods/`.
## Ключевые правила
### 1. Никогда не правь файлы внутри контейнера/образа
Файлы в `/lampac/module/` (и любые другие в образе) **сгорают при обновлении** (`docker compose pull && up -d`). Включение встроенного модуля делается только пользовательской копией:
```bash
docker cp lampac:/lampac/module/<Имя> "mods/<Имя>"
# в mods/<Имя>/manifest.json -> "enable": true
# в docker-compose.yaml -> "- ./mods/<Имя>:/lampac/mods/<Имя>"
# docker compose up -d
```
См. также официальные шаги: [Включить модуль](https://docs.lampac.dev/modules/overview), [Пользовательские модули](https://docs.lampac.dev/maintenance/custom-modules).
Сейчас в `mods/`: `AdminPanel`, `Tracks`, `Tg-notify.bot`, `TelegramAuth`, `TelegramAuthBot`. **`mods/Music` удалён** (remote-коммит `remove Music`): в образе у Music `manifest.enable: false`, поэтому модуль не грузится, `/music.js` → 404, секция `"Music"` и плагин убраны из `init.conf`. `mods/WatchTogether` тоже удалён (вернуть — той же механикой, он в списке «ещё можно включить» в README).
**Веб-клиент `lampa-main` — bind-mount из `./lampa-main/`** (в .gitignore). В образе его нет, `LampaCron` качает с GitHub; при пересоздании контейнера без mount клиент исчезает. См. `scripts/restore-lampa-main.sh`.
### 2. Механика загрузки модуля (4 независимых фильтра)
Модуль грузится, только если выполнены **все**:
| Фильтр | Где |
|---|---|
| `"enable": true` | `manifest.json` модуля (копия в `mods/` для встроенных) |
| Имя **не** в `BaseModule.SkipModules` | `base.conf` (в образе) + `config/init.conf` |
| Имя попадает в `BaseModule.LoadModules` | если allowlist задан (у нас не задан — грузится всё остальное) |
| Runtime-флаг | секция модуля в `config/init.conf`, например `"gst": { "enable": true }` |
`SkipModules` имеет приоритет над `LoadModules` — модуль из обоих списков **не** грузится. Паттерны: точное имя, группа (`OnlineUKR`), regex (`LME.*`), `.*`.
### 3. `config/init.conf` — валидный JSON
JSON **без** trailing comma. Перед применением правок проверять:
```bash
python3 -m json.tool config/init.conf > /dev/null && echo OK
```
После правки конфига: `docker compose restart` (том смонтирован, `up -d` не нужен). После правки `manifest.json` в `mods/` с `dynamic: true` — тоже `restart` (или ждать hot-rebuild). После изменения томов в compose — `docker compose up -d`.
### 4. Клиентские плагины Lampa
- Встроенные плагины: флаги `LampaWeb.initPlugins` (`tracks`, `dlna`, `catalog`, `sync`, `backup`, ...).
- Дополнительные: `LampaWeb.customPlugins` — записи со `status: 1` добавляются как есть; поддерживается плейсхолдер `{localhost}` (подставляется реальный адрес сервера).
- Если плагин «не виден» в веб-клиенте — сначала проверить HTTP-статус его URL на сервере (`curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9118/<плагин>.js`), а не лезть в клиент.
### 4.1 Каталог плагинов (store) `/extensions`
- Раздел «Плагины» веб-клиента грузит каталог с `/extensions` (при `LampaWeb.initPlugins.pirate_store: true`).
- Источник: `/lampac/module/LampaWeb/plugins/extensions.json` — переопределён mount из `./plugins/extensions.json`. Править хост-файл, в контейнер не залезать.
- `{localhost}` в ссылках store сервер заменяет своим адресом при выдаче.
- Статика: `/lampac/plugins/` **не раздаётся** (проверено: файл → 404), корень `wwwroot` — раздаётся. Все плагины каталога — **локальный mirror** в `./plugins/`, смонтирован **одним directory-mount** в `/lampac/wwwroot/plugins/` → ссылки store `{localhost}/plugins/<имя>.js`. Не монтировать каталог целиком в корень `wwwroot` — скроется веб-клиент `lampa-main`.
- Обновление плагина: исходный URL — в `plugins/sources.json`; скачать, перезаписать файл, перезапуск не нужен (статика).
- При проверке скачанного плагина отличать: (а) реальный код, (б) лоадер `putScriptAsync` (абсолютные URL — безопасен локально; host от currentScript — **требуется патч**, см. showy.js), (в) пустой stub/placeholder (мёртвый плагин, из каталога удалять).
- **Кеш-ловушка**: ответ `/extensions` (и других LampaWeb-маршрутов) кешируется на диск в `/lampac/cache/static/<bucket>/<id>.json` (named volume `lampac_cache`); кеш переживает `docker compose restart`. После правки store: `docker exec lampac rm -rf /lampac/cache/static/59` (bucket `/extensions`), перезапуск не нужен. Заголовок `X-StatiCache-Status: HIT/MISS` показывает состояние.
### 5. AdminPanel API из скриптов (root-авторизация)
- Root-пароль = содержимое `config/passwd`. Механизм: cookie `accspasswd=<пароль>` (таков штатный сценарий: страница входа `auth.html` сама кладёт пароль в эту cookie). Заголовок `lcrqpasswd` — для внутренних вызовов, на `/adminpanel/api/*` с ним уходит 302 на `/adminpanel/auth`.
- Не передавать root-пароль в query string (предупреждение доков).
- Сервер может отвечать 404 вместо 401/403, чтобы не раскрывать админский endpoint.
- Основные маршруты: `GET /adminpanel/api/init` (чтение), `POST /adminpanel/api/init` (полная запись), `POST /adminpanel/api/init/section/{key}` (секция), `GET /adminpanel/api/current` (merged-конфиг), `GET /adminpanel/api/users-json`.
- Правка секций через API пишется в `init.conf` — watcher подхватит без рестарта (runtime-параметры). `customPlugins` — runtime-параметр: плагин появляется в `/lampainit.js` через ~1 с.
### 6. Диагностика «модуль не найден»
Чек-лист (в этом порядке):
1. `curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9118/<маршрут>` — 404 подтверждает, что модуль не загружен.
2. `docker logs lampac | grep -E "compilation|loaded module" | grep <Имя>` — скомпилирован ли и загружен ли.
3. Манифест: `enable: true`? (в `mods/` для встроенных)
4. Фильтры `SkipModules`/`LoadModules` в `config/init.conf`.
5. Runtime-секция модуля в `config/init.conf`.
6. В логе после старта ожидаются строки `compilation <Имя>`, `configure module: <Имя>` (если есть конфиг-секция), `loaded module: <Имя>`.
### 7. Команды эксплуатации
```bash
docker compose up -d # запуск / пересоздание (при изменении compose)
docker compose restart # после правки init.conf / mods
docker compose logs -f # логи
docker compose pull && docker compose up -d # обновление (сначала поменять тег в compose)
docker exec lampac sh -c '...' # осмотр состояния в контейнере
```
`container_name: lampac` — контейнер один; одновременный запуск нескольких compose-файлов с одним именем невозможен. `docker-compose.override.yml` (gitignore) живёт только на сервере Proxmox и не коммитится.
### 8. Чего не трогать
- `config/passwd`, `dlna/` — в .gitignore, не коммитить. `cache`/`database` — **named Docker volumes** (`lampac_cache`, `lampac_database`), в git не попадают. Ловушка `.gitignore`: паттерны без якоря (`cache/`) матчат каталоги на любой глубине и без учёта регистра — из-за этого `mods/Music/Services/Cache/` не попадал в git и ломала Roslyn-компицию на сервере (CS0103, crash-loop). Новые данные/каталоги проверять через `git status`/`git check-ignore -v`.
- В named volume данные принадлежат тому, кто их туда положил: после миграции/копии `chown -R 1000:1000` (процесс в контейнере — `lampac:1000`), иначе SQLite «attempt to write a readonly database».
Исключение: `cache/static/` (в volume `lampac_cache`) — одноразовый диск-кеш ответов LampaWeb; очистка (`docker exec lampac rm -rf /lampac/cache/static/<bucket>`) — штатное действие после правки store/статических маршрутов (см. 4.1).
- Тег образа в `docker-compose.yaml` закреплён **точным digest** (`1.68.0@sha256:752dafd8…`): тег 1.68.0 на ghcr mutable, его переиздавали, и новая сборка ломает Roslyn-компиляцию наших копий `mods/` (пример: `Music` → `CS0103 MusicMetadataCacheService`, crash-loop). Обновление образа = новый digest + пересборка копий `mods/` из нового образа. Не делать «просто» `docker compose pull` без фиксации digest.
- `LampaWeb.autoupdate` — веб-клиент Lampa сам обновляется из git (`yumata/lampa`), `wwwroot/lampa-main` в контейнере перезаписывается — туда не править.
### 9. После изменений в проекте
Обновлять `README.md` и `AGENT.md` в той же ревизии: структура, включённые модули, секции конфига, новые правила.
### 10. Безопасность (не публиковать)
`config/passwd`, cookie `accspasswd`, заголовок `lcrqpasswd`, `bot_token` в `TelegramBot`, `bot_token` в `TelegramAuthBot`, `mutations_api_secret`. Маршруты `/adminpanel`, `/weblog`, `Tracks`, `Catalog`, `DLNA`, proxy-модули — только для доверенной сети без WAF не открывать.
**Секреты:** в git `config/init.conf` — секреты **пустые**. Реальные значения — в `config/secrets.env` (gitignored), боевой конфиг — `config/init.custom.conf` (gitignored). Собирается скриптом: `python3 scripts/config-secrets.py apply`. Перед коммитом: `python3 scripts/config-secrets.py check` (должен выдать `секретов в config/init.conf нет — ok`). После `git pull`/смены ветки `apply` запускается автоматически через git-хуки (`scripts/install-git-hooks.sh`).
### 11. Tg-notify.bot (Telegram-бот)
- Механика: long polling по `TelegramBot.bot_token`; если токен пуст — polling не стартует (в логе `[TelegramBot] Установите bot_token…`, сервер работает). После правки секции: `docker compose restart lampac`.
- **Токен только локально:** в git `config/init.conf` поле пустое (правило 10). Реальный токен живёт в `config/secrets.env`, а `docker-compose.yaml` монтирует `config/init.custom.conf` (собрать: `python3 scripts/config-secrets.py apply`). После `git pull` хук `post-merge` делает `apply` сам; если нет хуков — выполнить вручную. Второй экземпляр на том же токене даёт `Conflict: terminated by other getUpdates request`.
- Три подсистемы: трекер серий (Trakt→TMDB), трекер озвучек (параллельно Mirage/Collaps/VideoHub через `lampac_host`: `/lite/mirage`, `/lite/collaps`, `/lite/cdnvideohub`), клиентский плагин `tg-notify.js` (вкладка «TG Подписки», перехват 🔔). Полный разбор — в README.md.
- Данные подписок: `database/tgnotify/{users.json,subscriptions.json}` (том `./database/`, в .gitignore). Не «чинить» вручную — это живые данные бота.
- Диагностика: `GET /tg-notify.js` (200 без авторизации), `GET /api/tg/voices` (200), `/api/tg/*` остальной — требует `user_uid`. Озвучки могут не работать, если балансеры недоступны (проверить `GET /lite/mirage` — тело `disable`/ошибка, `/lite/collaps` — 503); тогда уведомления приходят только о выходе серий.
- `kp_api_key` опционален: без него VideoHub резолвит `kinopoisk_id` только через мост `/externalids` (покрытие ~40%), иначе — молча пропускается, Mirage/Collaps работают.
### 12. TelegramAuth / TelegramAuthBot (вход через Telegram)
- Не путать с Tg-notify.bot (уведомления): эта пара — **аутентификация** (вход в Lampa через Telegram вместо пароля), группа Community. Копии в `mods/TelegramAuth/` и `mods/TelegramAuthBot/`, манифесты `enable: true`, из `SkipModules` убраны (у нас `SkipModules: []`).
- **Сейчас: API-режим.** `TelegramAuth.enable: false` → `accsdb` не поднимается, гейт неактивен, доступ не режется; работают привязки, `/tg/auth/*`, админ-API. `TelegramAuthBot` — бот **`@GatusMonBot`**, long polling идёт (`Telegram API OK`).
- Гейт (`LampaWeb.telegramAuthGate`) активен только при `accsdb.enable: true` + `telegramAuthGate.enabled: true` + непустом `botUsername`, иначе тихий откат на `deny.js`. Включать **только вместе с `accsdb`**, и сначала связать свои устройства через бота — иначе текущие UID получат отказ.
- `mutations_api_secret` одинаковый в обоих модулях, ненулевой; `bot_token` — в `init.conf`, **не коммитить** (правило 10): в git оба поля пустые.
- `owner_telegram_ids: [143672]` → владелец создаётся как `admin` при старте. `auto_provision_users: true` + `auto_provision_activate_immediately: false` → новые привязки ждут одобрения (`/users` в боте, кнопки Принять/Отклонить).
- Данные: `database/tgauth/{users.json,admins.json,user_langs.json}` (том `lampac_database`, владелец `1000:1000`). Живые данные — не «чинить» руками.
- Проверка: `GET /tg/auth/status?uid=x` → 200; `GET /tg/auth/admin/users` → 401 без заголовка, 200 с `X-TelegramAuth-Mutations-Secret`.