Право manage для изменений, read — для чтения.
Планировщик
Список — GET /servers/{id}/schedules
{"ok": true, "schedules": [{"id": 1, "action": "reboot", "action_title": "Перезагрузка", "mode": "daily", "hour": 4, "minute": 30, "weekday": null, "run_at": null, "tz_offset": 180, "enabled": true, "last_run": 1789750200, "last_result": "ok", "next_run": 1789836600, "created_at": 1789733705}], "actions": {"reboot": "Перезагрузка", "shutdown": "Выключение", "power_on": "Включение"}, "max": 10}
Создать — POST /servers/{id}/schedules
| Поле | Значение |
|---|---|
action | reboot, shutdown, power_on |
mode | daily, weekly, once |
hour, minute | время (для daily и weekly) |
weekday | 0–6, понедельник = 0 (для weekly) |
run_at | timestamp (для once, не раньше чем через минуту и не позже года) |
tz_offset | смещение часового пояса в минутах: Москва 180, Екатеринбург 300 |
{"action": "reboot", "mode": "weekly", "weekday": 6, "hour": 5, "minute": 0, "tz_offset": 180}
Ответ — обновлённый список и created (id). До 10 расписаний на сервер (max). Если время уже прошло — 400 validation.
Изменить — PATCH /servers/{id}/schedules/{sid}
Любые поля из создания, чаще всего {"enabled": false} — приостановить без удаления.
Удалить — DELETE /servers/{id}/schedules/{sid}
Ответ — оставшийся список. Результат каждого срабатывания виден в last_run / last_result (ok, failed: …) и в журнале сервера (категория schedule).
Firewall (ufw, только Linux)
Состояние — GET /servers/{id}/firewall
Отдаёт состояние из кэша мгновенно. stale: true — данные могли устареть (например, вы меняли правила по SSH сами) — отправьте действие refresh.
{"ok": true, "supported": true, "stale": false, "client_ip": "203.0.113.5", "state": {"active": true, "default_in": "deny", "ssh_port": 22, "ssh_open": false, "ssh_whitelist": ["203.0.113.0/24"], "rules": [{"num": 1, "port": "22", "proto": "tcp", "action": "allow", "direction": "in", "src": "203.0.113.0/24", "comment": "SSH whitelist"}, {"num": 2, "port": "443", "proto": "tcp", "action": "allow", "direction": "in", "src": "any", "comment": ""}]}, "job": null, "presets": [{"id": "basic", "title": "Базовый", "hint": "SSH, HTTP и HTTPS", "ports": ["80/tcp", "443/tcp"]}, {"id": "vpn", "title": "VPN-панели", "ports": ["80/tcp", "443/tcp", "2053/tcp", "2083/tcp", "2087/tcp", "2096/tcp", "8443/tcp", "51820/udp", "1194/udp"]}, {"id": "strict", "title": "Только SSH", "ports": []}]}
| Поле | Описание |
|---|---|
supported | false — Windows |
state | null, пока состояние ни разу не читали (отправьте refresh) |
state.active | firewall включён |
state.ssh_port | порт SSH определяется автоматически и защищён от закрытия |
state.ssh_open / ssh_whitelist | SSH открыт всем или только списку адресов |
state.rules[] | num — номер правила ufw (нужен для delete), src: "any" — с любого адреса |
job | текущая фоновая задача или null |
client_ip | адрес, с которого сделан запрос — удобно для белого списка |
Действие — POST /servers/{id}/firewall
Выполняется в фоне: ответ содержит job, опрашивайте GET до job.status = done (обычно 3–10 с).
{"ok": true, "job": {"action": "allow", "status": "running", "phase": "connect", "error": "", "started": 1789736023}}
job.status: running → done | failed (текст в error). phase: connect, apply, read.
action | Дополнительные поля | Что делает |
|---|---|---|
preset | preset: basic / vpn / strict | сбросить правила и применить набор; SSH остаётся открытым |
allow | port (1–65535), proto: tcp / udp / both, from (IP или подсеть, необязательно), comment | открыть порт |
deny | port, proto | закрыть порт (кроме SSH) |
delete | num — номер правила из state.rules | удалить правило |
whitelist | ips: список 1–20 адресов/подсетей | SSH только с этих адресов |
open_ssh | — | снова открыть SSH всем |
enable, disable | — | включить / выключить firewall |
reset | — | сбросить всё, оставить только SSH |
refresh | — | перечитать состояние без изменений |
install | — | установить ufw, если его нет |
Порт SSH и адрес кабинета закрыть нельзя — вернётся 400 validation. Docker публикует порты в обход ufw — учитывайте это в контейнерах.
Пример: открыть 8443/tcp только для своей подсети и дождаться результата.
curl -s -X POST https://panel.dobry.host/api/servers/1/firewall \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"action": "allow", "port": 8443, "proto": "tcp", "from": "203.0.113.0/24", "comment": "panel"}'
sleep 5
curl -s https://panel.dobry.host/api/servers/1/firewall -H "Authorization: Bearer $TOKEN" | jq '.job, .state.rules'