99 lines
10 KiB
Markdown
99 lines
10 KiB
Markdown
# AdminPanel
|
||
|
||
Веб-**админка** Lampac: статический UI (**`auth.html`**, **`index.html`**) и JSON API для просмотра и правки **`init.conf`**, снимка **`current.conf`** и пользователей **`users.json`** (режим **accsdb**). Доступ к закрытым маршрутам — после ввода **root-пароля** из файла **`passwd`** (cookie **`accspasswd`**; см. ниже); при отказе **`[Authorization]`** перенаправляет на **`/adminpanel/auth`**.
|
||
|
||
## Быстрый старт
|
||
|
||
1. Включите модуль в **`manifest.json`** (`"enable": true`). По умолчанию в репозитории стоит **`false`**.
|
||
2. Соберите и задеплойте сборку так же, как остальные модули (динамический модуль; список файлов — поле **`tree`** в **`manifest.json`**).
|
||
3. Откройте **`http(s)://<хост>:<порт>/adminpanel/auth`** и войдите **root-паролем из файла `passwd`** в рабочей директории сервера (см. [Вход](#вход)).
|
||
4. Рабочая панель: **`/adminpanel`**.
|
||
|
||
Удобный порт в dev-сборке через Docker часто задаёт compose (например **9118**); точный адрес зависит от **`listen`** в конфигурации Lampac.
|
||
|
||
## Вход и сессия
|
||
|
||
Страница **`/adminpanel/auth`** сохраняет пароль в cookie **`accspasswd`** (срок **180 дней**, **`path=/`**, **`SameSite=Lax`**, в HTTPS добавляется **`Secure`**).
|
||
|
||
Панель дергает API с **`credentials: 'same-origin'`**, поэтому cookie должна быть установлена для того же origin, что и сервер Lampac.
|
||
|
||
### Файл `passwd` (root-пароль)
|
||
|
||
Вход в админку **не** берётся из **`users.json`** и не совпадает с **`accsdb.shared_passwd`** в **`init.conf`**. Для маршрутов с **`[Authorization]`** ядро сравнивает cookie **`accspasswd`** с **`CoreInit.rootPasswd`**, который читается из файла **`passwd`** в **рабочей директории** процесса (рядом с **`init.conf`**):
|
||
|
||
- если **`passwd`** есть при старте — содержимое файла загружается в память, пробелы и переводы строк снимаются;
|
||
- если файла нет при старте — генерируется случайная строка и записывается в **`passwd`** (имеет смысл сразу задать свой пароль и при необходимости перезапустить процесс).
|
||
|
||
В форме входа нужно указывать **именно эту** root-строку.
|
||
|
||
Дополнительно ядро ограничивает число **разных** попыток пароля в cookie с одного IP за сутки; после порога ответы к защищённым маршрутам выглядят как **404** (см. **`Core/Middlewares/Accsdb.cs`**).
|
||
|
||
Тот же **`rootPasswd`** используется для внутренних вызовов с HTTP-заголовком **`lcrqpasswd`** (это не форма входа в браузере).
|
||
|
||
Остальная политика (локальный доступ без проверки, **accsdb** для клиентов Lampa, WAF и т.д.) накладывается **поверх** этой проверки — см. конфигурацию ядра. Не выкладывайте **`passwd`** в Git и не публикуйте админку в открытый интернет без осознанной защиты. В Docker/Kubernetes **`passwd`** обычно монтируют вместе с **`init.conf`** (см. комментарии в **`docker-compose.yaml`**, чарт **`charts/lampac`**).
|
||
|
||
## Где лежат файлы
|
||
|
||
Пути **`passwd`**, **`init.conf`**, **`current.conf`**, **`users.json`** задаются относительно **текущей рабочей директории процесса** Lampac (не от каталога модуля).
|
||
|
||
- Если **`current.conf`** на диске отсутствует или повреждён как JSON, при чтении подставляется снимок из **`CoreInit.CurrentConf`** (если доступен хостом).
|
||
- HTML читаются из **`ModInit.modpath`** (каталог загруженного модуля): **`auth.html`**, **`index.html`**.
|
||
|
||
В Docker проверьте, что том с конфигом смонтирован туда же, откуда процесс видит те же имена файлов, что вы правите через UI.
|
||
|
||
### Запись на диск
|
||
|
||
Сохранение **`init.conf`** и **`users.json`** делается атомарно: сначала запись во временный файл, затем **`Move`** с перезаписью. Если на bind mount **`Move`** даёт ошибку занятости цели (**EBUSY**), выполняется прямая запись в целевой файл — это осознанный fallback для некоторых сценариев Docker.
|
||
|
||
Ответ успешной записи: **`{"ok":true}`**. Ошибки: **`400`/`500`** с телом **`{"error":"...", "detail":"..."}`** (поле **`detail`** опционально).
|
||
|
||
## Различие «init» и «current» в UI
|
||
|
||
| Файл | Роль |
|
||
|------|------|
|
||
| **`init.conf`** | То, что можно **сохранять** через API: целиком (**`POST /adminpanel/api/init`**) или по корневым ключам (**`POST .../init/section/{key}`**). |
|
||
| **`current.conf`** | **Только чтение** в админке (**`GET /adminpanel/api/current`**): актуальный снимок/мердж конфигурации для просмотра и группировки секций в интерфейсе. Прямой POST для **`current`** в контроллере не предусмотрен. |
|
||
|
||
Если нужно изменить один корневой ключ в **`init.conf`**, тело **`POST /adminpanel/api/init/section/{key}`** — валидный JSON **значения** секции (объект, массив, примитив — как в корне конфига). Ключ секции не должен содержать **`/`** или **`\`**.
|
||
|
||
## HTTP API
|
||
|
||
Подробности тел запросов и валидации — в **`AdminPanelController.cs`**.
|
||
|
||
| Метод и маршрут | Описание |
|
||
|-----------------|----------|
|
||
| **`GET /adminpanel/auth`** | Страница входа (**`AllowAnonymous`**). |
|
||
| **`GET /adminpanel`** | Главная панель (**`index.html`**). |
|
||
| **`GET /adminpanel/api/groups`** | Группы секций по ключам, **присутствующим** в **`current.conf`** (или в памяти, если файл не прочитан), плюс блок **«Прочее»** для ключей без каталога. |
|
||
| **`GET /adminpanel/api/groups/catalog`** | Полный **каталог** групп из **`ConfigSectionGroups.Catalog`**; если в актуальном конфиге есть ключи вне каталога — добавляется **«Прочее»** для них (подписи ориентированы на ключи из current). |
|
||
| **`GET /adminpanel/api/init`** | Содержимое **`init.conf`** (пустой объект, если файла нет); JSON приводится к отформатированному виду когда возможно. |
|
||
| **`POST /adminpanel/api/init`** | Тело — один JSON **объект** (корень **`init.conf`**). Целиком перезаписывает файл. |
|
||
| **`POST /adminpanel/api/init/section/{key}`** | Обновить один корневой ключ в **`init.conf`**. |
|
||
| **`GET /adminpanel/api/current`** | **`current.conf`** или fallback из **`CoreInit.CurrentConf`**. |
|
||
| **`GET /adminpanel/api/users-json`** | Массив пользователей; если файла нет — **`[]`**. |
|
||
| **`POST /adminpanel/api/users-json`** | Тело — JSON **массив** объектов пользователей (схема **accsdb**); перезаписывает **`users.json`**. |
|
||
|
||
## Группы в редакторе
|
||
|
||
Класс **`ConfigSectionGroups`** задаёт человекочитаемые группы и подсказки для бокового каталога: какие корневые ключи конфига к какой группе относятся (рантайм, listen, модули, источники и т.д.).
|
||
|
||
При добавлении нового модуля с секцией в корне JSON имеет смысл дописать её ключ в **`Catalog`** нужной группы или в блок **«modules»**, чтобы ключ не попадал только в **«Прочее»**.
|
||
|
||
Публичный **`GroupDto`** (см. низ **`ConfigSectionGroups.cs`**): **`id`**, **`title`**, **`hint`**, массив **`keys`**.
|
||
|
||
## Конфигурация модуля
|
||
|
||
Отдельной секции настроек в **`ModInit`** нет — только **`manifest.json`** (**`enable`**, **`dynamic`**, **`tree`**).
|
||
|
||
Статические ассеты панели лежат рядом с кодом модуля (**`index.html`**, **`auth.html`**); при изменении их в репозитории нужна перезагрузка/пересборка в соответствии с тем, как хост подхватывает динамические модули.
|
||
|
||
## Файлы модуля
|
||
|
||
| Файл | Роль |
|
||
|------|------|
|
||
| **`AdminPanelController.cs`** | Маршруты UI и API, работа с файлами конфигурации. |
|
||
| **`ConfigSectionGroups.cs`** | Каталог групп и билдер **`GroupDto`**. |
|
||
| **`ModInit.cs`** | **`IModuleLoaded`**: сохраняет **`modpath`** для раздачи HTML. |
|
||
| **`manifest.json`** | Включение модуля и список подгружаемых **`.cs`**. |
|
||
| **`auth.html`**, **`index.html`** | Интерфейс входа и панели. |
|