feat(install): консольный установщик bj и первичная настройка по одноразовому коду
- cmd/bj-installer (вместо веб-мастера): проверка Debian 13 / Astra, пакеты, пользователь и каталоги, активация лицензии ключом продукта, PostgreSQL и миграции m2m_core, компоненты по лицензии с проверкой подписи, root-скрипты и службы, одноразовый код и итоговый экран с адресом http://<сервер>:8080/first-run; проверка в песочнице (--steps, --root) - bj: /first-run — пока нет пользователей и установщик оставил код, по коду создаётся первый администратор (прежние установки без файла работают как раньше) - internal/release/install.go — общая установка компонентов (bj-component-install и установщик); bj-component-install стал компонентом ядра - открытые ключи вынесены в internal/license и internal/release; миграции и файлы развёртывания встроены (migrations/embed.go, deploy/embed.go); unit bj-server исправлен - scripts/build-release.sh: сборка с версиями, подпись, публикация на сервер обновлений Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -490,7 +490,7 @@ type AuthSettings struct {
|
||||
|
||||
// Перепроверка групп доменных пользователей (adrecheck.go): поиск от имени
|
||||
// сервисной учётной записи только на чтение. Пусто — роль меняется только при входе.
|
||||
ADBindUser string `json:"ad_bind_user"` // svc-bj@corp.example.ru, CORP\svc-bj или DN
|
||||
ADBindUser string `json:"ad_bind_user"` // svc-bj@corp.example.ru, CORP\svc-bj или DN
|
||||
ADBindPassword string `json:"ad_bind_password"`
|
||||
ADRecheckMinutes int `json:"ad_recheck_minutes"` // 0 = 15
|
||||
}
|
||||
@@ -805,7 +805,7 @@ func currentUser(w http.ResponseWriter) *User {
|
||||
// authExempt — пути без входа: API модулей (свой ключ/токен), health, статика.
|
||||
func authExempt(p string) bool {
|
||||
switch {
|
||||
case p == "/login" || p == "/logout" || p == "/healthz":
|
||||
case p == "/login" || p == "/logout" || p == "/healthz" || p == "/first-run":
|
||||
return true
|
||||
case strings.HasPrefix(p, "/api/"):
|
||||
return true
|
||||
@@ -857,6 +857,14 @@ func authMiddleware(rc *RuntimeConfig, next http.Handler) http.Handler {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
if firstRunPending() { // свежая установка: сначала администратор по одноразовому коду
|
||||
if isAjax(r) {
|
||||
http.Error(w, "требуется первичная настройка", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/first-run", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
if authOpen() {
|
||||
next.ServeHTTP(&authRW{ResponseWriter: w, user: nil}, r)
|
||||
return
|
||||
@@ -924,9 +932,14 @@ type loginData struct {
|
||||
var loginTpl = template.Must(template.New("login").Parse(loginHTML))
|
||||
|
||||
func registerAuth(mux *http.ServeMux, rc *RuntimeConfig) {
|
||||
registerFirstRun(mux)
|
||||
mux.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
|
||||
a := rc.Snapshot().Auth
|
||||
next := safeReturnPath(r.FormValue("next"))
|
||||
if firstRunPending() {
|
||||
http.Redirect(w, r, "/first-run", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
if authOpen() {
|
||||
http.Redirect(w, r, "/admin/setup#users", http.StatusSeeOther)
|
||||
return
|
||||
@@ -978,10 +991,10 @@ func registerAuth(mux *http.ServeMux, rc *RuntimeConfig) {
|
||||
// ---------- обработчики настроек ----------
|
||||
|
||||
type UsersView struct {
|
||||
Users []*User
|
||||
Roles []roleDef // доступные при текущей лицензии
|
||||
Hidden []struct{ Title, Missing string } // роли, недоступные без модулей
|
||||
Modules []struct{ Key, Title string } // подключённые модули
|
||||
Users []*User
|
||||
Roles []roleDef // доступные при текущей лицензии
|
||||
Hidden []struct{ Title, Missing string } // роли, недоступные без модулей
|
||||
Modules []struct{ Key, Title string } // подключённые модули
|
||||
Sessions []*Session
|
||||
Open bool
|
||||
Me *User
|
||||
|
||||
Reference in New Issue
Block a user