# PZ Manager [![release](https://img.shields.io/gitea/v/release/mrleo1nid/pzmanager?gitea_url=https%3A%2F%2Fgitea.hsrv.site&logo=gitea&logoColor=white&label=релиз)](https://gitea.hsrv.site/mrleo1nid/pzmanager/releases/latest) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![Go](https://img.shields.io/badge/Go-1.24%2B-00ADD8?logo=go&logoColor=white)](https://go.dev/) [![Platform](https://img.shields.io/badge/platform-Linux%20%2F%20Ubuntu-EEEEEE?logo=ubuntu&logoColor=white&labelColor=555555)](https://ubuntu.com/) [![last commit](https://img.shields.io/gitea/last-commit/mrleo1nid/pzmanager?gitea_url=https%3A%2F%2Fgitea.hsrv.site&logo=gitea&logoColor=white)](https://gitea.hsrv.site/mrleo1nid/pzmanager/commits/branch/main) Веб-панель для управления выделенным сервером Project Zomboid на Ubuntu. Один бинарник без зависимостей: веб-интерфейс вшит внутрь, Node.js и Python на сервере не нужны. ## Что умеет - **Профили серверов** — несколько независимых сборок (свой мир, конфиг, песочница, моды и лимит памяти) с переключением одной кнопкой. - **Управление сервером** — запуск, остановка (через команду `quit`, мир сохраняется), перезапуск, автоподъём после падения, автозапуск вместе с менеджером. - **Живая консоль** — вывод сервера в реальном времени и отправка любых команд (`players`, `save`, `servermsg`, `kickuser`, …). - **Мониторинг** — состояние, аптайм, CPU и память процесса сервера, свободные ОЗУ и диск на хосте, список игроков онлайн. - **Редактор конфигов** — `servertest.ini` и `SandboxVars.lua` прямо в браузере: параметры разложены по разделам, у каждого видно описание из самого конфига, поиск идёт и по названию, и по описанию. - **Мод-менеджер** — моды списком с галочками и перетаскиванием порядка загрузки, добавление по ссылке и разворачивание коллекций Steam целиком. - **Бэкапы** — ручные и по расписанию, отдельно по каждому профилю, с ротацией, скачиванием и восстановлением (перед восстановлением автоматически снимается страховочная копия). - **Два языка** — русский и английский, с выбором в шапке и автоопределением по языку браузера. - **Установка и обновление** сервера через SteamCMD прямо из панели. - **Обновление самой панели** — кнопкой из интерфейса или одной командой в терминале. ## Как это устроено Менеджер запускает `start-server.sh` как дочерний процесс и держит его stdin, stdout и stderr. Отсюда следует всё остальное: команды консоли пишутся прямо в stdin (RCON не нужен), логи стримятся в браузер через SSE, а остановка идёт по цепочке `quit` → `SIGTERM` → `SIGKILL` для всей группы процессов. Сам менеджер живёт под systemd, который его и перезапускает. ``` systemd ──> pzmanager ──> start-server.sh ──> java (GameServer) │ ├── HTTP + SSE ──> браузер └── SteamCMD, бэкапы, конфиги ``` ## Быстрый старт На чистом Ubuntu/Debian всё ставится одной командой — она возьмёт последний релиз, проверит контрольные суммы и настроит systemd: ```bash curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh | sudo bash ``` По умолчанию панель слушает `127.0.0.1:8080` — только с самой машины. Чтобы она была доступна в локальной сети, укажите адрес сервера: ```bash curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh \ | sudo PZ_LISTEN=10.10.1.142:8080 bash ``` Переменные: `PZ_LISTEN` (адрес панели), `PZ_HOME` (каталог, по умолчанию `/opt/pzmanager`), `PZ_USER` (системный пользователь), `PZ_VERSION` (конкретный тег вместо последнего). Скрипт запускается от root, так что перед первым запуском его разумно прочитать: [deploy/get.sh](deploy/get.sh). Установщик поставит зависимости (SteamCMD, JRE, SDL2), заведёт пользователя `pzserver` без shell, положит бинарник в `/usr/local/bin` и создаст структуру: ``` /opt/pzmanager/ ├── config.yaml настройки менеджера ├── data/ учётки панели и скачанный SteamCMD ├── server/ серверные файлы Project Zomboid ├── Zomboid/ миры, конфиги и логи игры └── backups/ архивы ``` **Обновление** — та же команда или кнопка в панели (вкладка «Настройки» → «Версия панели»). Конфиг, миры и учётные записи не трогаются. Повторный запуск с той же версией службу не трогает вовсе: если бинарник не изменился, установщик не станет ради него ронять игровой сервер. Дальше: 1. Откройте панель в браузере и создайте администратора, введя код первичной настройки — установщик печатает его последним сообщением. Если панель слушает localhost, пробросьте порт: ```bash ssh -L 8080:127.0.0.1:8080 пользователь@сервер ``` Код одноразовый и перестаёт работать сразу после создания администратора. Если вывод установщика потерялся, код есть в журнале: ```bash journalctl -u pzmanager -n 30 --no-pager ``` 2. Во вкладке «Обзор» нажмите **Установить / обновить через SteamCMD** и дождитесь окончания — прогресс виден во вкладке «Консоль». 3. Запустите сервер. При первом запуске PZ создаст `Zomboid/Server/*.ini` — после этого станут доступны вкладки «Конфиг сервера», «Песочница» и «Моды». Порты, которые нужно открыть: ```bash sudo ufw allow 8080/tcp # веб-панель, если она слушает не localhost sudo ufw allow 16261/udp # игровой порт sudo ufw allow 16262/udp # второй игровой порт ``` ### Установка из исходников Если релизам предпочитаете свою сборку (нужен Go 1.24+, собирать можно на любой ОС, включая Windows): ```bash make build-linux ``` Скопируйте `pzmanager` и папку `deploy/` на сервер и запустите установщик: ```bash sudo PZ_LISTEN=10.10.1.142:8080 ./deploy/install.sh ``` ### Удаление Убрать панель, оставив миры и настройки: ```bash sudo /opt/pzmanager/install.sh --uninstall ``` Удалить всё вместе с мирами, бэкапами и учётными записями: ```bash sudo /opt/pzmanager/install.sh --uninstall --purge ``` `--purge` спрашивает подтверждение; в неинтерактивном запуске нужен ещё и `--yes`. Каталог сносится, только если похож на установку панели — иначе опечатка в `PZ_HOME` стоила бы чужих файлов. Установщик кладёт свою копию в `/opt/pzmanager/install.sh`, так что для удаления ничего скачивать не нужно. Если копии нет, те же аргументы принимает и установочная команда: ```bash curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh | sudo bash -s -- --uninstall --purge ``` ## Язык интерфейса Панель говорит по-русски и по-английски. Язык выбирается списком в шапке (и на экране входа, до того как вошли), а до первого выбора берётся из настроек браузера: русский — русским, всем остальным — английский. Выбор запоминается в браузере, поэтому у каждого, кто заходит в панель, он свой. Переводится всё, что показывает панель: интерфейс, подписи и описания всех параметров `servertest.ini` и `SandboxVars.lua`, ошибки и служебные сообщения менеджера в консоли. Вывод самого игрового сервера остаётся как есть — он приходит от Project Zomboid. ## Моды Project Zomboid хранит моды в двух строках конфига: `WorkshopItems` — что скачать из мастерской, `Mods` — что включить и в каком порядке. Панель показывает их одним списком, где каждая строка — мод: - **галочка** включает мод (добавляет его Mod ID в `Mods`); - **перетаскивание** (или стрелки) задаёт порядок загрузки — для модов, которые переопределяют одни и те же файлы, он решает, чья версия победит; - **«Убрать»** выбрасывает пакет из `WorkshopItems` вместе со всеми его модами. Добавить мод можно ссылкой на страницу мастерской или голым ID. Кнопка **«Добавить коллекцию»** разворачивает коллекцию в список входящих в неё модов — порядок берётся тот же, что на странице коллекции. Mod ID, который нужен серверу, лежит внутри самого мода (`mod.info`), поэтому панель читает уже скачанные моды с диска: ``` /steamapps/workshop/content/108600//mods/<мод>/mod.info ~/Zomboid/mods/<мод>/mod.info # моды, положенные вручную ``` Пока сервер не скачал мод, его строка помечена «ещё не скачан» и галочка недоступна: Mod ID неизвестен. Файлы загружает сам сервер при запуске по списку `WorkshopItems` — то есть достаточно добавить моды, сохранить и запустить сервер, после чего они станут доступны для включения. Один пакет мастерской может содержать несколько модов — в списке они идут отдельными строками, и включать их можно по отдельности. ## Редактор конфигов Вкладки «Конфиг сервера» и «Песочница» показывают параметры не сплошным списком, а разделами: «Основное», «PVP и урон», «Убежища», «Античит» и так далее. Разделы сворачиваются, а поиск сверху раскрывает те, где нашлось совпадение. Ключи, которых панель не знает (в новой версии игры их всегда добавляют), не теряются — они собираются в раздел «Прочее». Подписи и описания у панели свои, на языке интерфейса: игра пишет свои комментарии на языке сервера, а на Linux он почти всегда английский. Расшифровку конкретных значений (что означает 1, 2, 3) панель добавляет из самого конфига. Полный текст виден при наведении, начало — прямо под полем. Правка меняет ровно ту строку, которую поправили: комментарии, порядок и переводы строк файла сохраняются, а рядом остаётся копия прежней версии с расширением `.bak`. ## Профили серверов Профиль — это отдельный сервер: свой мир, свой `*.ini`, свои настройки песочницы, свой список модов и свой лимит памяти. Project Zomboid различает их по параметру `-servername`, поэтому профили не мешают друг другу: ``` ~/Zomboid/Server/vanilla.ini ~/Zomboid/Saves/Multiplayer/vanilla/ ~/Zomboid/Server/vanilla_SandboxVars.lua ~/Zomboid/Server/modded.ini ~/Zomboid/Saves/Multiplayer/modded/ ~/Zomboid/Server/modded_SandboxVars.lua ``` Работает всегда один профиль — активный: серверные файлы, игровые порты и файл запуска JVM общие. Переключить профиль можно на остановленном сервере, выбрав его в шапке панели или на вкладке «Профили»; после этого все вкладки — конфиг, песочница, моды, бэкапы — относятся уже к нему. При создании профиля можно скопировать `.ini` и настройки песочницы из существующего: удобно, когда нужен тот же набор правил, но чистый мир. ### Память JVM `java_memory` — это `-Xmx`, верхняя граница кучи. `java_memory_min` (`-Xms`) по умолчанию пуст, и это осознанно: равные значения избавляют от роста кучи на ходу, но заставляют JVM занять весь объём сразу при старте. На машине, где столько свободной памяти не набирается, сервер тогда не поднимется вовсе — `Failed to allocate initial Java heap`. Задавайте минимум, только если памяти заведомо хватает. Сервер, упавший сразу после запуска три раза подряд, панель перестаёт поднимать автоматически: обычно так проявляется нехватка памяти или битый конфиг, и перезапуск делу не помогает. Пока идёт отсчёт до автоподъёма, его можно отменить кнопкой «Отменить перезапуск». Удаление профиля убирает его только из панели — мир и конфиги остаются на диске. Чтобы вернуть профиль, создайте его с тем же идентификатором. Обновление с версии, где сервер был один, проходит само: старые поля `server_name`, `admin_password` и `java_memory` превращаются в первый профиль при первом запуске. ## Обновление Панель показывает свою версию и следит за релизами в репозитории: вкладка «Настройки» → «Версия панели». Если вышла новая версия, там появляется кнопка **«Обновить до …»**. Обновление из интерфейса устроено так. Сама панель работает от непривилегированного пользователя и не может ни заменить свой бинарник в `/usr/local/bin`, ни дёрнуть `systemctl`. Поэтому установщик кладёт рядом маленький root-скрипт `/usr/local/bin/pzmanager-admin` и разрешает сервисному пользователю ровно его одну команду: ``` pzserver ALL=(root) NOPASSWD: /usr/local/bin/pzmanager-admin ``` Скрипт не повторяет логику установки, а забирает из релиза `get.sh` (сверив его sha256) и отдаёт работу ему — поэтому кнопка обновляет всё сразу: бинарник, скрипты установки и systemd-юнит. Панель к этим файлам доступа не имеет, она может только попросить выполнить `update` или `restart`. Работа уходит в отдельный разовый юнит (`systemd-run --unit=pzmanager-update`). Иначе обновление не пережило бы само себя: панель вызывает скрипт через sudo, тот живёт в cgroup её юнита, и остановка службы убила бы его на середине. Побочный эффект — вывод идёт не в консоль панели, а в свой журнал: ```bash journalctl -u pzmanager-update -f ``` Если правило sudo не установлено (панель поставлена вручную или в системе нет `sudo`), кнопка не появится, а рядом будет показана команда для терминала: ```bash curl -fsSL https://gitea.hsrv.site/mrleo1nid/pzmanager/raw/branch/main/deploy/get.sh | sudo bash ``` Обновление и перезапуск панели останавливают игровой сервер — панель делает это сама, штатной командой `quit`, чтобы мир успел сохраниться. ## Доступ снаружи Панель управляет сервером, поэтому наружу её лучше не выставлять голой. Два разумных варианта: **SSH-туннель (по умолчанию)** — ничего настраивать не нужно, панель слушает только localhost. **Доверенная локальная сеть** — `PZ_LISTEN=:8080` при установке или `listen` в конфиге. Учтите, что HTTP не шифруется: пароль от панели идёт по сети открытым текстом, так что для доступа через интернет этот вариант не годится. **HTTPS через reverse-proxy** — если панель нужна с телефона. Настройте nginx с сертификатом (например, от Let's Encrypt): ```nginx server { listen 443 ssl; server_name pz.example.com; ssl_certificate /etc/letsencrypt/live/pz.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/pz.example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; # Живая консоль работает через Server-Sent Events. proxy_buffering off; proxy_read_timeout 24h; } } ``` И запустите менеджер с флагом `--secure-cookies`, чтобы cookie сессии передавалась только по HTTPS (добавьте флаг в `ExecStart` юнита). ## Команды ```bash pzmanager serve # запустить панель (команда по умолчанию) pzmanager useradd <логин> # создать пользователя панели pzmanager passwd <логин> # сменить пароль pzmanager users # список пользователей ``` Флаги: `--config <путь>`, `--listen <адрес>`, `--secure-cookies`. ## Конфигурация Файл `/opt/pzmanager/config.yaml` создаёт установщик (при запуске без него менеджер возьмёт `~/.config/pzmanager/config.yaml`). Всё, что в нём есть, правится и через вкладку «Настройки». ```yaml listen: 127.0.0.1:8080 # адрес веб-панели; 0.0.0.0 или IP — доступ по сети data_dir: /opt/pzmanager/data server_dir: /opt/pzmanager/server # куда SteamCMD ставит сервер zomboid_dir: /opt/pzmanager/Zomboid # миры, конфиги и логи игры steamcmd_path: "" # пусто — найти в PATH или скачать profiles: # серверы: у каждого свой мир и настройки - id: vanilla # vanilla -> vanilla.ini, Saves/Multiplayer/vanilla title: Ванильный мир # как профиль подписан в панели admin_password: "" # пароль админа игрового сервера java_memory: 4g # -Xmx: верхняя граница кучи JVM java_memory_min: "" # -Xms: пусто — JVM берёт память по мере надобности extra_args: [] # доп. аргументы start-server.sh - id: modded title: Сборка с модами java_memory: 8g active_profile: vanilla # какой профиль запускается и настраивается autostart: false # поднимать сервер вместе с менеджером autorestart: true # поднимать сервер после падения stop_timeout: 1m30s # сколько ждать после quit до SIGTERM log_buffer_lines: 5000 # строк консоли в памяти backup: dir: /opt/pzmanager/backups schedule: 6h # 0 — отключить автобэкапы keep: 20 # архивов на профиль stop_server: false # останавливать сервер на время бэкапа ``` Изменения `listen` применяются после `systemctl restart pzmanager`; остальные — при следующем запуске игрового сервера. ## Бэкапы В архив попадают мир активного профиля и его серверные конфиги. Имя файла — `pz-профиль-ГГГГММДД-ЧЧММСС[-пометка].tar.gz`, так что архивы разных сборок не путаются, а ротация (`backup.keep`) считается по каждому профилю отдельно. Архивы, снятые до появления профилей, панель тоже читает и восстанавливает. Бэкап на живом сервере технически корректен, но снимок может отставать от состояния мира в памяти. Если хочется гарантированной согласованности — включите `backup.stop_server` (игроков на время бэкапа выкинет) или отправляйте `save` в консоль перед ручным бэкапом. Восстановление требует остановленного сервера. Перед распаковкой менеджер сам делает страховочную копию с пометкой `pre-restore`. ## Безопасность - Пароли пользователей панели хранятся в виде bcrypt-хешей в `data_dir/users.json` (права 0600). - Сессии живут только в памяти: перезапуск менеджера разлогинивает всех. - Первого администратора можно создать только с одноразовым кодом из журнала. - Изменяющие запросы требуют собственный заголовок — это защита от CSRF. - Панель не выполняет произвольные команды хоста: всё, что она умеет, — это управление процессом сервера, SteamCMD и файлами в своих директориях. Тем не менее доступ к панели равносилен доступу к игровому серверу, так что пароль стоит выбрать не короче того, что вы поставили бы на SSH. ## Сборка и релизы Репозиторий собирается Gitea Actions ([`.gitea/workflows`](.gitea/workflows)): - **CI** — на каждый push и pull request в `main`: `gofmt`, `go vet`, тесты, проверка сборки под Linux и синтаксиса скриптов установки. - **Release** — на тег `v*`: собирает бинарники под `linux/amd64` и `linux/arm64`, считает контрольные суммы и публикует релиз с ними и скриптами установки. Выпуск новой версии: ```bash git tag -a v1.2.3 -m "Описание релиза" && git push origin v1.2.3 ``` Версия попадает в бинарник через `-ldflags -X main.version=` и видна в `pzmanager version` и в панели на вкладке «Настройки». Workflow не зависят от github.com и от Node внутри контейнера: код забирается через `git`, а релиз публикуется прямо в API Gitea. Токен Actions выдаёт сам; если в настройках инстанса его права урезаны, заведите секрет `RELEASE_TOKEN` с доступом на запись в репозиторий. Для работы Actions нужен зарегистрированный runner с меткой `ubuntu-latest`, умеющий запускать контейнеры (`docker`). ## Разработка ```bash make test # тесты make vet # go vet make run # локальный запуск с конфигом в ./dev ``` Веб-интерфейс — обычные HTML, CSS и JS в `web/static`, без сборщика; при `go build` они вшиваются в бинарник через `embed`. Переводы лежат рядом: строки интерфейса — в `web/static/i18n.js`, подписи параметров конфигов — в `web/static/hints-ru.json` и `hints-en.json` (загружается только нужный язык), сообщения самого менеджера — в `internal/i18n/catalog.go`, где ключом служит русская фраза из кода. Тесты в `web` и `internal/i18n` следят, чтобы словари не разъезжались. ## Лицензия [MIT](LICENSE) — делайте с кодом что угодно, но без каких-либо гарантий.