Код настройки печатает установщик, появилось удаление панели
CI / test (push) Successful in 21s
Release / release (push) Successful in 29s

Менеджер кладёт код первичной настройки в data/setup-token, установщик
дожидается файла и печатает код последним сообщением — за ним больше не
нужно идти в journalctl. Файл удаляется, как только создан администратор,
и не появляется на уже настроенной панели.

Установщик научился снимать панель с машины: --uninstall убирает службу,
бинарники и правило sudo, --purge вдобавок сносит каталог с мирами,
бэкапами и учётками. Данные удаляются только по явному подтверждению, а
каталог сносится, лишь если похож на установку панели: опечатка в PZ_HOME
не должна стоить чужих файлов. Копия установщика теперь лежит в
/opt/pzmanager, так что удалять можно без похода в репозиторий.

get.sh пробрасывает аргументы установщику и в режиме удаления не качает
бинарник. Заодно исправлена подстановка пустого массива аргументов:
"${ARGS[@]:-}" разворачивается в один пустой аргумент, из-за чего обычная
установка спотыкалась бы о него.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 20:03:13 +03:00
co-authored by Claude Opus 5
parent a8d86e0c48
commit e25294db16
6 changed files with 250 additions and 52 deletions
+21 -11
View File
@@ -131,24 +131,34 @@ func serve(cfg *config.Config, users *auth.Store, secureCookies bool) error {
// Пока учёток нет, создать первого администратора можно только с этим
// кодом — иначе панель захватит любой, кто откроет её первым.
setupToken := ""
setupTokenFile := filepath.Join(cfg.DataDir, "setup-token")
if users.IsEmpty() {
setupToken = randomToken()
// Код кладём и в файл: установщик читает его оттуда и показывает сразу
// после установки, не заставляя идти в журнал.
if err := os.WriteFile(setupTokenFile, []byte(setupToken+"\n"), 0o600); err != nil {
fmt.Fprintf(os.Stderr, "не удалось записать %s: %v\n", setupTokenFile, err)
}
fmt.Printf("\n Панель ещё не настроена.\n"+
" Откройте её в браузере и введите код первичной настройки:\n\n %s\n\n", setupToken)
} else {
// Панель уже настроена: файл мог остаться от прошлой установки.
os.Remove(setupTokenFile)
}
apiServer := api.New(api.Options{
Version: version,
Config: cfg,
Users: users,
Supervisor: supervisor,
Jobs: jobs,
Backups: backups,
Steam: steam,
Static: static,
SecureCookies: secureCookies,
SetupToken: setupToken,
Repo: repoURL(),
Version: version,
Config: cfg,
Users: users,
Supervisor: supervisor,
Jobs: jobs,
Backups: backups,
Steam: steam,
Static: static,
SecureCookies: secureCookies,
SetupToken: setupToken,
SetupTokenFile: setupTokenFile,
Repo: repoURL(),
})
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)