Не хотите читать всё это?
Просто скиньте в любой ИИ-чат (Claude, ChatGPT и т.д.) ссылку на https://pastukhov.com/agents/portal/install.md или на https://pastukhov.com/llms.txt и попросите сгенерировать конфигурационные файлы и команды. Он прочитает документацию, задаст несколько вопросов о вашей настройке и выдаст готовую конфигурацию. Экономьте время — пусть модель читает за вас.
Вы также можете связаться со мной через Telegram — я всегда рад помочь. И это не просто вежливость — мне действительно нравится общаться с единомышленниками, особенно если вы так же увлечены программированием, как и я.
Портал-агент — программа, в которой живёт корпоративный портал: страницы, блоки, живые цифры из ваших систем и публичный сайт. Всё это разворачивается на вашем сервере одним Docker-контейнером. Служебные данные (база, лицензия, пометки о прочитанном) лежат в томе /apps/portal, а сам контент портала — отдельная папка /project/apps/portal, которую удобно держать в git. Отдельная база данных не нужна: встроенная SQLite работает из коробки, PostgreSQL подключается по желанию. Python тоже ставить не нужно — он уже внутри образа, поэтому динамические блоки работают сразу.
Минимальные требования
Достаточно любого Linux-сервера с Docker и Docker Compose (подойдёт недорогой VPS). Если удобнее работать на Windows, установите Docker Desktop — порядок действий описан на странице Windows Setup. Отдельно нужно позаботиться о двух вещах: постоянном томе под служебные данные и папке с контентом портала. Без тома пропадут база и лицензия при первом же пересоздании контейнера, а контент удобнее держать в папке репозитория под git — тогда он не зависит от контейнера и переносится простым копированием.
Продукт экономно расходует ресурсы сервера. В простое процессор не нагружается: пока с порталом никто не работает, приложение ничего не опрашивает по таймеру — обновления приходят сами, когда что-то меняется. Через 15 минут без активности освобождается занятая память: результаты расчётов, собранные страницы, результаты проверок и поисковый индекс страниц, — а при новом заходе всё поднимается само. Поэтому Портал-агент спокойно соседствует с другими сервисами на одном сервере.
Что уже встроено в образ и не требует установки:
- веб-сервер приложения (ASP.NET Core 9);
- база данных SQLite — работает без настройки;
- окружение Python для динамических блоков и скриптов — создаётся само при первом запуске;
- для чата с ИИ-агентом понадобится адрес и ключ вашего Pastukhov Agent — но это необязательно: без них всё работает, кроме самого чата.
Docker Compose
Создайте файл compose.yml и запустите docker compose up -d:
services:
portalagent-ru:
image: maxpastukhov/portal-agent:min-latest
container_name: portalagent-ru
ports:
- "3993:80"
volumes:
# /apps/portal — служебные данные: база, ключи, лицензия, окружение Python
- portalagent-data-ru:/apps/portal
# Контент портала в образ не вшит: по умолчанию он ищется в /project/apps/portal.
# Смонтируйте сюда папку с контентом под git; без этого портал поднимется пустым.
# - ./apps/portal:/project/apps/portal
# Файл настроек .env из корня папки проекта (об этом — в разделе «Файл .env» ниже):
# - ./.env:/project/.env
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://+:80
- PORTAL_APP_BRAND=pastukhov
- PORTAL_APP_DATA=/apps/portal
# PORTAL_APP_BASE_PATH=/portal # serve from a first-level subfolder (see ../subfolder-hosting.md)
restart: unless-stopped
sysctls:
fs.inotify.max_user_watches: "524288"
fs.inotify.max_user_instances: "512"
volumes:
portalagent-data-ru:
После запуска интерфейс будет доступен на порту 3993: http://<адрес-сервера>:3993.
Две строки sysctls повышают системные лимиты отслеживания изменений файлов — они нужны, чтобы правки страниц, сделанные агентом, появлялись на открытой странице сразу, без перезагрузки. Без них на большом портале часть изменений может не дойти до читателя.
Если портал должен жить не в корне домена, а в подпапке (например, /portal), раскомментируйте строку PORTAL_APP_BASE_PATH=/portal — приложение само приведёт все адреса к нужному виду.
Переменные окружения
Настройки приложения задаются переменными окружения — так их удобно прописать один раз в compose.yml и менять без правки кода. Все переменные, кроме AGENT_*, начинаются с PORTAL_APP_.
| Переменная | Что задаёт | По умолчанию |
PORTAL_APP_BRAND | бренд и язык интерфейса: pastukhov (русский) или xedant (английский) | xedant |
PORTAL_APP_DATA | папка служебных данных: база, ключи, лицензия, окружение Python, поисковый индекс | /apps/portal |
PORTAL_APP_DB | база данных: строка подключения PostgreSQL; пустое значение — встроенная SQLite | SQLite (/apps/portal/data.db) |
PORTAL_APP_PORTAL_PATH | папка с контентом портала — папка репозитория под git, отдельно от служебных данных | /project/apps/portal |
PORTAL_APP_HASH | SHA-256-хеш ключа, которым ИИ-агент управляет порталом (заголовок X-API-Key); без него управление выключено и отвечает 503 | — (управление выключено) |
PORTAL_APP_LOGIN | логин готового администратора; вместе с паролем создаёт пользователя при старте | — (саморегистрация) |
PORTAL_APP_PASSWORD | строчная SHA-256-сумма пароля, не сам пароль | — |
PORTAL_APP_BASE_PATH | размещение в подпапке первого уровня (например, /portal) | — (работа в корне домена) |
PORTAL_APP_SSL | свой самоподписанный HTTPS: auto — включить; рядом _SSL_PORT | — (только HTTP) |
PORTAL_APP_ERROR_REPORTING | 0 — выключить отправку сведений об ошибках разработчику | — (отправка включена) |
PORTAL_APP_ENV_FILE | 0 — не читать файл .env из корня папки проекта (см. ниже) | — (файл читается) |
AGENT_API_URL | адрес Pastukhov Agent, который ведёт портал: через него работает чат и правки словами | — (чат с агентом скрыт) |
AGENT_API_KEY | ключ доступа к вашему Pastukhov Agent | — |
AGENT_LICENSE | лицензионный ключ, чтобы не вводить его руками в интерфейсе | — (берётся файл лицензии из тома) |
Обратите внимание: PORTAL_APP_HASH содержит не сам ключ, а его сумму — «отпечаток», по которому ключ восстановить нельзя. Получить её можно командой printf '%s' 'ваш-ключ' | sha256sum и вставить в переменную. Так же устроен и пароль: в PORTAL_APP_PASSWORD попадает его строчная SHA-256-сумма (echo -n вашпароль | sha256sum), а при входе приложение считает сумму от введённого пароля и сравнивает с заданной.
Отдельно про сведения об ошибках. По умолчанию приложение отправляет разработчику отчёт о сбое — так о проблеме становится известно и её можно починить. Ничего настраивать для этого не нужно. Если отправка нежелательна, поставьте PORTAL_APP_ERROR_REPORTING=0 — приложение перестанет отправлять такие сведения.
Файл .env — настройки и секреты в одном месте
Тонкие настройки и секреты удобнее держать не в compose.yml, а отдельным файлом .env в корне папки проекта — там же, где лежит папка с контентом портала (по умолчанию /project/.env). Формат самый простой: в строке пишется имя и значение, а знак # начинает комментарий.
# отпечаток ключа, которым агент управляет порталом
PORTAL_APP_HASH=e3b0c44298fc1c149afbf4c8996fb924...
# подключение к вашему Pastukhov Agent
AGENT_API_URL=https://agent.ваш-домен
AGENT_API_KEY=ключ-агента
Что важно знать про этот файл:
- Читается при запуске. Значения из настоящего окружения — те, что заданы в
compose.ymlили флагом-e, — главнее: файл только дополняет то, чего в них нет. - Правки применяются на лету. Файл перечитывается сам, в течение секунды: ключ управления, размещение в подпапке, папка данных, вход через общий прокси — всё это можно менять без перезапуска. Настройки, которые читаются один раз при старте (база данных, адрес и ключ агента, лицензия, бренд), подхватятся после перезапуска контейнера.
- Значения видят скрипты. Динамические блоки запускаются в том же окружении, поэтому адреса и пароли к вашим системам удобно передавать скриптам именно так, а не хранить в контенте.
- Файл остаётся у вас. В нём обычно лежат ключи, поэтому в git он не попадает: репозиторий и история версий его не увидят.
- Можно отключить. Переменная
PORTAL_APP_ENV_FILE=0полностью выключает чтение файла — тогда работают только переменные окружения.
В контейнер файл нужно смонтировать, иначе ему неоткуда взяться: раскомментируйте в примере выше строку - ./.env:/project/.env или смонтируйте весь репозиторий как - .:/project. Если файл лежит в другой папке, укажите её переменной PORTAL_APP_PROJECT_FOLDER.
Где хранятся данные
Данные портала разделены на две части, и это удобно. Служебное состояние живёт в томе /apps/portal и переживает пересоздание контейнера:
/apps/portal/data.db— база данных SQLite (когда не используется PostgreSQL);/apps/portal/auth/— ключи защиты данных, которыми подписываются сессии входа;/apps/portal/.pastukhov/— лицензия (у брендаxedantпапка называется.xedant);/apps/portal/portal-data/— рабочая папка скриптов (временные файлы, результаты расчётов);/apps/portal/.venv— окружение Python, в котором выполняются динамические блоки;/apps/portal/portal-unread.txt— пометки «не прочитано» для страниц портала;/apps/portal/search-index/— поисковый индекс страниц: текст страниц лежит файлами на диске, поэтому поиск работает сразу после перезапуска и не требует заново обходить весь портал.
Сам контент лежит отдельно — в папке /project/apps/portal, и это не том, а ваша папка репозитория: внутри — папка на каждую страницу, тексты блоков и картинки. Том состояния обязателен: без него при первом же docker compose down вы потеряете базу и лицензию. А контент от контейнера не зависит: папку можно целиком держать в git, видеть историю правок и перенести на другой сервер простым копированием. В compose она монтируется в /project/apps/portal; если ничего не монтировать, портал поднимется пустым, и заведённые страницы не переживут пересоздание контейнера.
Первый вход
Откройте интерфейс в браузере. Пока в базе нет ни одного пользователя, форма регистрации открыта сама — первый зарегистрировавшийся становится администратором. Если логин и пароль заданы переменными PORTAL_APP_LOGIN и PORTAL_APP_PASSWORD, входите под ними (в переменной хранится сумма, при входе вводите сам пароль). Сессия входа живёт 90 дней. Если приложение развёрнуто вместе с другими продуктами, вход может идти через общий вход сервера — он настраивается переменной SSO_PROXY_TOKEN.
Что сделать после запуска
Когда интерфейс открылся, остаётся подключить управляющего агента и завести первые страницы:
- Подключите Pastukhov Agent — укажите
AGENT_API_URLиAGENT_API_KEY. После этого в интерфейсе появляется чат с ИИ-агентом и кнопки «Изменить с помощью агента» на странице и у блока. - Задайте ключ управления порталом — переменную
PORTAL_APP_HASHс хешем ключа. По этому адресу агент создаёт и правит страницы, файлы и скрипты. - Попросите агента создать первые страницы — просто опишите словами, что нужно: «сделай раздел Новости с лентой и страницу Контакты».
- Назначьте страницам доступ: у новой страницы по умолчанию «черновик», его видит только администратор.
- Отметьте страницы с доступом «публичная» — они появятся на внешнем сайте по адресу
/p/.
Подробнее о первых шагах — в разделе Начало работы.
Запуск без Compose
Можно запустить приложение и одной командой docker run:
docker run -d \
--name portalagent-ru \
-p 3993:80 \
-v portalagent-data-ru:/apps/portal \
-v ./apps/portal:/project/apps/portal \
-e PORTAL_APP_BRAND=pastukhov \
-e PORTAL_APP_DATA=/apps/portal \
--sysctl fs.inotify.max_user_watches=524288 \
--sysctl fs.inotify.max_user_instances=512 \
--restart unless-stopped \
maxpastukhov/portal-agent:min-latest
Остальные переменные окружения можно передать файлом через флаг --env-file .env — так удобнее, когда настроек много. Это файл самого Docker: он передаёт значения контейнеру один раз при запуске. Не путайте его с файлом .env из корня папки проекта: тот читает само приложение и перечитывает на лету, без перезапуска (см. раздел «Файл .env» выше).
Установка через Pastukhov MultiAgent
Если у вас несколько серверов и приложений, удобнее ставить продукты из Pastukhov MultiAgent: он сам поднимает контейнер, выдаёт постоянный адрес и HTTPS, подключает базу и общий том документов. Портал-агент есть в каталоге MultiAgent: в мастере установки выбирается существующий агент на этом сервере или создаётся новый, а связанному агенту сразу достаётся готовый навык управления порталом. Подробнее — на странице MultiAgent.
Лицензия
Лицензионный ключ активируется в диалоге лицензии в интерфейсе или передаётся переменной AGENT_LICENSE при запуске. Готовой лицензии в образе нет: свежий контейнер стартует без неё. Без лицензии работает всё — дерево и страницы портала, блоки, динамические блоки на Python, формы, публичный сайт и правки контента агентом. Лицензия нужна только для отправки сообщений в чат с ИИ-агентом. Подробнее — на странице Лицензирование.