Канал ИШ: необязательные параметры — NULL (иначе ИШ молча выкидывал канал); мастер: шаг «Учётные записи», HTTPS сразу, DEPO без процедур — не блокирует

- bj-igate-admin: пустые строки в числовых/интервальных параметрах канала ИШ разбирал с ошибкой и не
  загружал канал («There are no active channels», API 500 «more than one Channel…»); теперь NULL, как пишет
  интерфейс ИШ; старые каналы чинятся при «Создать канал»; папка архивов 1042S — по имени
- проверка «Шлюз НРД»: канал контура действительно загружен и включён (api/admin/channels/wsl)
- «Учёт DEPO»: нет процедур bj2depo — жёлтое «не забудьте доустановить», не блокирует
- HTTPS: перенаправление с http:// включено по умолчанию, только когда HTTPS реально слушает порт
- мастер: шаг «Учётные записи» (роли, создание, «Придумать пароль») перед проверкой; письма с проверки убраны;
  форма «создать» не меняет существующую запись с тем же логином

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-06 23:19:01 +03:00
parent b3f3454e4a
commit 44f00a006a
12 changed files with 264 additions and 47 deletions
+23 -7
View File
@@ -25,16 +25,25 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
)
// HTTPSSettings — HTTPS веб-интерфейса.
type HTTPSSettings struct {
Port int `json:"port,omitempty"` // 0 — 8443
Redirect bool `json:"redirect,omitempty"` // браузер с HTTP — на HTTPS
Custom bool `json:"custom,omitempty"` // загружен сертификат организации (не самоподписанный)
Port int `json:"port,omitempty"` // 0 — 8443
// RedirectOff — не перенаправлять браузер с HTTP на HTTPS. С 06.10.2026 перенаправление включено сразу
// (самоподписанный сертификат bj создаёт сам): пароли не должны идти по сети открытым текстом.
RedirectOff bool `json:"redirect_off,omitempty"`
Custom bool `json:"custom,omitempty"` // загружен сертификат организации (не самоподписанный)
}
// RedirectOn — браузер с HTTP перенаправляется на HTTPS.
func (h HTTPSSettings) RedirectOn() bool { return !h.RedirectOff }
// httpsUp — HTTPS действительно слушает порт: иначе перенаправление оставило бы без входа в bj.
var httpsUp atomic.Bool
func (h HTTPSSettings) port() int {
if h.Port > 0 && h.Port < 65536 {
return h.Port
@@ -148,7 +157,7 @@ func tlsCertInfo() (subject string, notAfter time.Time, err error) {
func httpsRedirect(rc *RuntimeConfig, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h := rc.Snapshot().HTTPS
if h.Redirect && r.TLS == nil && !strings.HasPrefix(r.URL.Path, "/api/") && r.URL.Path != "/healthz" {
if h.RedirectOn() && httpsUp.Load() && r.TLS == nil && !strings.HasPrefix(r.URL.Path, "/api/") && r.URL.Path != "/healthz" {
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
host = r.RemoteAddr
@@ -216,9 +225,16 @@ func startHTTPS(rc *RuntimeConfig, handler http.Handler) *http.Server {
ReadHeaderTimeout: 5 * time.Second,
TLSConfig: &tls.Config{GetCertificate: loadTLSCert, MinVersion: tls.VersionTLS12},
}
ln, err := net.Listen("tcp", srv.Addr)
if err != nil {
log.Printf("https: %v — HTTPS выключен, вход по HTTP без перенаправления", err)
return nil
}
httpsUp.Store(true)
go func() {
log.Printf("lk-gateway: listen https %s", srv.Addr)
if err := srv.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
if err := srv.ServeTLS(ln, "", ""); err != nil && err != http.ErrServerClosed {
httpsUp.Store(false)
log.Printf("https: %v", err)
}
}()
@@ -232,13 +248,13 @@ func registerHTTPS(mux *http.ServeMux, rc *RuntimeConfig) {
return
}
h := rc.Snapshot().HTTPS
h.Redirect = r.FormValue("redirect") != ""
h.RedirectOff = r.FormValue("redirect") == ""
if err := rc.UpdateHTTPS(h); err != nil {
setupFlash(w, r, "HTTPS: "+err.Error())
return
}
msg := "HTTPS: перенаправление с HTTP выключено"
if h.Redirect {
if h.RedirectOn() {
msg = "HTTPS: браузеры теперь перенаправляются на https://…:" + strconv.Itoa(h.port())
}
setupFlash(w, r, msg)