Кабинет: веб-установщик и управление пользователями в ядре
- установщик в самом кабинете: до настройки любая страница ведёт в /install; три шага — проверка сервера (каталог данных, https), связь с ядром (режим, адрес, ключ; связь проверяется до сохранения, в режиме ожидания страница ждёт подключения ядра), администратор кабинета (заводится в ядре через /api/lk/admin/bootstrap, только пока администраторов нет); настройки в data/lk.json 0600, окружение перекрывает файл; после установки установщик закрыт - настройки кабинета можно менять на ходу: Server за мьютексом, один mux на всё время - ключ модуля больше не кладётся в архив: его вводят в установщике; на странице настроек ключ показан целиком 10 минут после создания, потом маска - ядро: карточка «Пользователи кабинета» в настройках — список, заведение, смена пароля (гасит сессии), блокировка, права сотрудника, разрешение поручений, удаление; письма не отправляются - инструкция /admin/help/lk дополнена шагами установщика и разделом о подходящем хостинге - тесты: TestInstaller, TestNotConfigured, TestSetupPageLKUsersAndLink; установщик проверен вживую вторым экземпляром против боевого ядра Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+20
-3
@@ -19,6 +19,7 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -50,6 +51,15 @@ type agentQueue struct {
|
||||
waiting map[string]*agentTask
|
||||
signal chan struct{}
|
||||
seq uint64
|
||||
// lastFetch — когда ядро в последний раз забирало запросы: по этому установщик
|
||||
// понимает, что ядро дозвонилось до кабинета.
|
||||
lastFetch atomic.Int64
|
||||
}
|
||||
|
||||
// coreSeen — ядро подключалось за последнюю минуту.
|
||||
func (q *agentQueue) coreSeen() bool {
|
||||
t := q.lastFetch.Load()
|
||||
return t > 0 && time.Since(time.Unix(t, 0)) < time.Minute
|
||||
}
|
||||
|
||||
func newAgentQueue() *agentQueue {
|
||||
@@ -142,8 +152,13 @@ func idFromSeq(n uint64) string {
|
||||
|
||||
// registerAgent — точки, куда подключается ядро (только в режиме ожидания).
|
||||
func (s *Server) registerAgent(mux *http.ServeMux) {
|
||||
// В прямом режиме этих точек как бы нет: очередь не создана — отвечаем 404.
|
||||
keyOK := func(w http.ResponseWriter, r *http.Request) bool {
|
||||
if !secureEqual(r.Header.Get("X-BJ-Key"), s.cfg.BJKey) {
|
||||
if s.agentQueue() == nil {
|
||||
http.NotFound(w, r)
|
||||
return false
|
||||
}
|
||||
if !secureEqual(r.Header.Get("X-BJ-Key"), s.conf().BJKey) {
|
||||
http.Error(w, "неверный ключ модуля", http.StatusForbidden)
|
||||
return false
|
||||
}
|
||||
@@ -155,7 +170,9 @@ func (s *Server) registerAgent(mux *http.ServeMux) {
|
||||
return
|
||||
}
|
||||
wait := 25 * time.Second
|
||||
t := s.agent.next(r.Context(), wait)
|
||||
q := s.agentQueue()
|
||||
q.lastFetch.Store(time.Now().Unix())
|
||||
t := q.next(r.Context(), wait)
|
||||
if t == nil {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
@@ -182,7 +199,7 @@ func (s *Server) registerAgent(mux *http.ServeMux) {
|
||||
return
|
||||
}
|
||||
body, _ := base64.StdEncoding.DecodeString(in.Body)
|
||||
if !s.agent.reply(in.ID, agentResult{Status: in.Status, Body: body}) {
|
||||
if !s.agentQueue().reply(in.ID, agentResult{Status: in.Status, Body: body}) {
|
||||
w.WriteHeader(http.StatusGone) // запрос уже не ждут: клиент ушёл или истёк срок
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user