Язык выбирается в шапке панели, а до первого выбора берётся из настроек браузера. Переведены интерфейс, подписи и описания всех параметров конфигов, ошибки API и служебные сообщения менеджера в консоли. Подписи параметров вынесены из app.js в hints-<язык>.json: их больше четырёхсот на язык, и грузить чужой язык незачем. Сообщения менеджера переводятся на выходе из API по словарю, где ключом служит русская фраза из кода, — вывод самого игрового сервера через него проходит без изменений. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.1 KiB
Go
82 lines
3.1 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/mrleo1nid/pzmanager/internal/pzserver"
|
|
"github.com/mrleo1nid/pzmanager/internal/updater"
|
|
)
|
|
|
|
// handleCheckUpdate спрашивает у репозитория последнюю версию.
|
|
func (s *Server) handleCheckUpdate(w http.ResponseWriter, r *http.Request) {
|
|
writeJSON(w, http.StatusOK, s.updater.Check(r.Context()))
|
|
}
|
|
|
|
// handleApplyUpdate качает новую версию и перезапускает панель. Ответ уходит
|
|
// раньше, чем работа заканчивается: обновление убивает текущий процесс.
|
|
func (s *Server) handleApplyUpdate(w http.ResponseWriter, r *http.Request) {
|
|
var body struct {
|
|
Version string `json:"version"`
|
|
}
|
|
if r.ContentLength > 0 && !decodeJSON(w, r, &body) {
|
|
return
|
|
}
|
|
|
|
if !s.updater.CanApply(r.Context()) {
|
|
writeError(w, r, http.StatusPreconditionFailed, updater.ErrNoAdmin.Error())
|
|
return
|
|
}
|
|
|
|
name := "Обновление панели"
|
|
err := s.jobs.Start(name, func(ctx context.Context, log func(string)) error {
|
|
// Игровой сервер останавливаем сами: иначе это сделает systemd уже в
|
|
// момент перезапуска, и ожидание растянется на таймаут остановки.
|
|
s.stopServerBefore(log, "обновлением")
|
|
log("Запускаю обновление — оно идёт отдельной службой и переживёт перезапуск панели")
|
|
log("Подробности: journalctl -u pzmanager-update -f")
|
|
return s.updater.Apply(ctx, log, body.Version)
|
|
})
|
|
if err != nil {
|
|
writeOpError(w, r, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusAccepted, map[string]any{"task": name})
|
|
}
|
|
|
|
// handleRestartManager перезапускает саму панель.
|
|
func (s *Server) handleRestartManager(w http.ResponseWriter, r *http.Request) {
|
|
if !s.updater.CanApply(r.Context()) {
|
|
writeError(w, r, http.StatusPreconditionFailed, updater.ErrNoAdmin.Error())
|
|
return
|
|
}
|
|
|
|
name := "Перезапуск панели"
|
|
err := s.jobs.Start(name, func(ctx context.Context, log func(string)) error {
|
|
s.stopServerBefore(log, "перезапуском")
|
|
log("Перезапускаю панель")
|
|
return s.updater.Restart(ctx, log)
|
|
})
|
|
if err != nil {
|
|
writeOpError(w, r, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusAccepted, map[string]any{"task": name})
|
|
}
|
|
|
|
// stopServerBefore корректно гасит игровой сервер перед тем, как панель уйдёт
|
|
// в перезапуск: мир сохраняется, а systemd не ждёт таймаут остановки.
|
|
func (s *Server) stopServerBefore(log func(string), what string) {
|
|
if s.sup.State() == pzserver.StateStopped {
|
|
return
|
|
}
|
|
log("Останавливаю игровой сервер перед " + what)
|
|
if err := s.sup.Stop(); err != nil && !errors.Is(err, pzserver.ErrNotRunning) {
|
|
log("Остановить сервер не удалось: " + err.Error())
|
|
return
|
|
}
|
|
s.sup.WaitStopped(30 * time.Second)
|
|
}
|