Кабинет: страницы собирает ядро, на хостинге — ретранслятор (Go и PHP)
- modules/lk: конверт запроса/ответа (PageRequest/PageResponse), ServeEnvelope — тот же обработчик страниц внутри ядра; клиент к API внутри процесса (Config.Local/LocalKey); режим ретранслятора (Config.Relay, BJ_RENDER=core, выбор в установщике и lk.json) - ядро: встроенный кабинет (lkcabinet.go), POST /api/lk/render для прямого режима, цикл режима ожидания выполняет конверты страниц; API через ретранслятор недостижимо - PHP-ретранслятор для обычного хостинга (modules/lk/relay-php: index.php, .htaccess, README): установщик в браузере, прямой режим через curl, режим ожидания через очередь в SQLite (BEGIN IMMEDIATE, busy_timeout), конфиг в bj-lk-data рядом с сайтом или в data под .htaccess; архив со страницы настроек (/admin/setup/lk/relay-php.zip) - настройки: карточка НПР/УДС перенесена из раздела «Лицензия» в «Экспорт QUIK»; кнопка «Обновить с сервера лицензий» видна при любом ключе - инструкция /admin/help/lk: два варианта размещения, шаги для PHP-хостинга - тесты: TestLKRelayRendersInCore (оба режима), TestLKPHPRelayAgentMode (настоящий PHP против цикла ядра, PHP_BIN), проверка безопасности через PHP-ретранслятор 34/34 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
This commit is contained in:
@@ -15,7 +15,6 @@ package lkgateway
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -25,6 +24,8 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"git.zetit.ru/zuevav/Bridge-and-Join-s/modules/lk"
|
||||
)
|
||||
|
||||
// lkAgentState — состояние канала для страницы настроек.
|
||||
@@ -125,8 +126,8 @@ func (s *Server) lkAgentWorker(ctx context.Context) {
|
||||
if task == nil {
|
||||
continue // запросов не было, кабинет отпустил соединение — подключаемся заново
|
||||
}
|
||||
status, body := s.lkAgentServe(ctx, task)
|
||||
if err := lkAgentReply(ctx, client, cabinet, cfg.APIKey, task.ID, status, body); err != nil {
|
||||
res := s.lkAgentServe(ctx, task)
|
||||
if err := lkAgentReply(ctx, client, cabinet, cfg.APIKey, res); err != nil {
|
||||
log.Printf("кабинет: ответ на запрос %s не доставлен: %v", task.ID, err)
|
||||
continue
|
||||
}
|
||||
@@ -134,17 +135,8 @@ func (s *Server) lkAgentWorker(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// lkAgentTask — запрос, ожидающий в кабинете.
|
||||
type lkAgentTask struct {
|
||||
ID string `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Path string `json:"path"`
|
||||
Session string `json:"session"`
|
||||
Body string `json:"body"` // base64
|
||||
// Адрес и браузер посетителя кабинета — для привязки сессии и лимита попыток входа.
|
||||
ClientIP string `json:"client_ip"`
|
||||
ClientAgent string `json:"client_agent"`
|
||||
}
|
||||
// lkAgentTask — запрос, ожидающий в кабинете: вызов API или страница в конверте.
|
||||
type lkAgentTask = lk.PageRequest
|
||||
|
||||
// lkAgentFetch — забрать очередной запрос. nil без ошибки — запросов не было.
|
||||
func lkAgentFetch(ctx context.Context, c *http.Client, cabinet, key string) (*lkAgentTask, error) {
|
||||
@@ -166,7 +158,7 @@ func lkAgentFetch(ctx context.Context, c *http.Client, cabinet, key string) (*lk
|
||||
if err := json.NewDecoder(io.LimitReader(resp.Body, 8<<20)).Decode(&t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.ID == "" || !strings.HasPrefix(t.Path, "/api/lk/") {
|
||||
if t.ID == "" || (t.Kind != "page" && !strings.HasPrefix(t.Path, "/api/lk/")) {
|
||||
return nil, fmt.Errorf("кабинет прислал неизвестный запрос")
|
||||
}
|
||||
return &t, nil
|
||||
@@ -177,14 +169,21 @@ func lkAgentFetch(ctx context.Context, c *http.Client, cabinet, key string) (*lk
|
||||
}
|
||||
}
|
||||
|
||||
// lkAgentServe — выполнить запрос кабинета у себя: тот же обработчик /api/lk/*.
|
||||
func (s *Server) lkAgentServe(ctx context.Context, t *lkAgentTask) (int, []byte) {
|
||||
body, _ := base64.StdEncoding.DecodeString(t.Body)
|
||||
// lkAgentServe — выполнить запрос кабинета у себя: страницу — встроенным кабинетом,
|
||||
// вызов API — тем же обработчиком /api/lk/*, что и по сети.
|
||||
func (s *Server) lkAgentServe(ctx context.Context, t *lkAgentTask) lk.PageResponse {
|
||||
if t.Kind == "page" {
|
||||
if globalLKCabinet == nil {
|
||||
return lk.PageResponse{ID: t.ID, Status: http.StatusServiceUnavailable, Body: []byte("кабинет в ядре не запущен")}
|
||||
}
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/", nil)
|
||||
return globalLKCabinet.serve(req, *t)
|
||||
}
|
||||
cctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(cctx, t.Method, t.Path, bytes.NewReader(body))
|
||||
req, err := http.NewRequestWithContext(cctx, t.Method, t.Path, bytes.NewReader(t.Body))
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, []byte(`{"error":"некорректный запрос кабинета"}`)
|
||||
return lk.PageResponse{ID: t.ID, Status: http.StatusBadRequest, Body: []byte(`{"error":"некорректный запрос кабинета"}`)}
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-BJ-Key", s.rc.Snapshot().LK.APIKey) // запрос пришёл по ключу, он же идёт дальше
|
||||
@@ -200,7 +199,7 @@ func (s *Server) lkAgentServe(ctx context.Context, t *lkAgentTask) (int, []byte)
|
||||
req.RemoteAddr = "127.0.0.1:0" // обращение пришло по нашему исходящему каналу
|
||||
rec := &lkAgentRecorder{status: http.StatusOK}
|
||||
s.mux.ServeHTTP(rec, req)
|
||||
return rec.status, rec.buf.Bytes()
|
||||
return lk.PageResponse{ID: t.ID, Status: rec.status, Body: rec.buf.Bytes()}
|
||||
}
|
||||
|
||||
// lkAgentRecorder — приёмник ответа обработчика: пишем в память, а не в сеть.
|
||||
@@ -220,9 +219,8 @@ func (w *lkAgentRecorder) Write(b []byte) (int, error) { return w.buf.Write(b) }
|
||||
func (w *lkAgentRecorder) WriteHeader(code int) { w.status = code }
|
||||
|
||||
// lkAgentReply — вернуть кабинету ответ на его запрос.
|
||||
func lkAgentReply(ctx context.Context, c *http.Client, cabinet, key, id string, status int, body []byte) error {
|
||||
payload, _ := json.Marshal(map[string]any{"id": id, "status": status,
|
||||
"body": base64.StdEncoding.EncodeToString(body)})
|
||||
func lkAgentReply(ctx context.Context, c *http.Client, cabinet, key string, res lk.PageResponse) error {
|
||||
payload, _ := json.Marshal(res)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, cabinet+"/agent/result", bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user