Files
Bridge-and-Join-s/internal/lkgateway/lkcabinet.go
T
zuevav 054222d154 Кабинет: страницы собирает ядро, на хостинге — ретранслятор (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
2026-09-18 11:15:29 +03:00

95 lines
3.6 KiB
Go

package lkgateway
// lkcabinet.go — кабинет клиента, собранный внутрь ядра.
//
// Страницы кабинета рисует ядро: у него все данные и шаблоны (пакет modules/lk — тот же,
// что собирается в самостоятельный bj-lk). На хостинге остаётся ретранслятор, который
// передаёт запросы браузера конвертами: в прямом режиме — POST /api/lk/render, в режиме
// ожидания — ядро само забирает конверты из очереди ретранслятора (lkagent.go).
//
// Данные встроенный кабинет берёт через тот же API /api/lk/*, вызывая обработчик внутри
// процесса: граница проверок одна и та же, где бы кабинет ни работал.
import (
"encoding/json"
"io"
"log"
"net/http"
"strings"
"sync"
"git.zetit.ru/zuevav/Bridge-and-Join-s/modules/lk"
)
// globalLKCabinet — встроенный кабинет; им пользуется и цикл режима ожидания.
var globalLKCabinet *lkCabinet
// lkCabinet — встроенный кабинет; пересобирается, если сменился заголовок.
type lkCabinet struct {
mu sync.Mutex
api http.Handler
rc *RuntimeConfig
srv *lk.Server
title string
}
func newLKCabinet(api http.Handler, rc *RuntimeConfig) *lkCabinet {
return &lkCabinet{api: api, rc: rc}
}
// server — кабинет с актуальным заголовком организации.
func (c *lkCabinet) server() (*lk.Server, error) {
s := c.rc.Snapshot()
title := strings.TrimSpace(orgName(s))
if title == "" {
title = "Личный кабинет"
} else {
title += " · личный кабинет"
}
c.mu.Lock()
defer c.mu.Unlock()
if c.srv != nil && c.title == title {
return c.srv, nil
}
srv, err := lk.New(lk.Config{Title: title, Local: c.api, LocalKey: func() string { return c.rc.Snapshot().LK.APIKey }})
if err != nil {
return nil, err
}
c.srv, c.title = srv, title
return srv, nil
}
// serve — выполнить конверт страницы.
func (c *lkCabinet) serve(r *http.Request, env lk.PageRequest) lk.PageResponse {
srv, err := c.server()
if err != nil {
log.Printf("кабинет: не собран: %v", err)
return lk.PageResponse{ID: env.ID, Status: http.StatusInternalServerError, Body: []byte("кабинет не собран")}
}
return srv.ServeEnvelope(r.Context(), env)
}
// registerRender — POST /api/lk/render: ретранслятор в прямом режиме присылает конверт
// запроса браузера, получает конверт ответа. Только по ключу модуля.
func (c *lkCabinet) registerRender(mux *http.ServeMux, auth func(http.ResponseWriter, *http.Request) bool) {
mux.HandleFunc("/api/lk/render", func(w http.ResponseWriter, r *http.Request) {
if !auth(w, r) {
return
}
if r.Method != http.MethodPost {
lkJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "нужен POST"})
return
}
var env lk.PageRequest
if err := json.NewDecoder(io.LimitReader(r.Body, 2<<20)).Decode(&env); err != nil {
lkJSON(w, http.StatusBadRequest, map[string]string{"error": "конверт не разобран"})
return
}
if env.Kind != "page" {
lkJSON(w, http.StatusBadRequest, map[string]string{"error": "через render идут только страницы"})
return
}
lkJSON(w, http.StatusOK, c.serve(r, env))
})
}