a7cfbb2e74
- Письмо в ARQA: организация просит подключить модуль неторговых поручений, выдать логин и пароль и разрешить подключение с нашего внешнего адреса — о bj и внутреннем устройстве ни слова. Внешний адрес bj узнаёт у сервера лицензий (GET /v1/ip, раз в час); пометка — убедиться, что адрес постоянный. - Под письмом — «Памятка администратору QUIK» (классы, логин InstrEu, справочник, перезапуск модуля) со ссылкой на подробную инструкцию. - Вместо свёрнутого «Автоматический цикл M2M — выключен · настроить» и справочника под раскрытием: после проверки подключения — «Как bj работает с QUIK»: «Только сверка» или «Автоматически» (+ торговый счёт), сохраняется сразу; справочник — строкой состояния с напоминанием о перезапуске. До проверки — одна строка. Тонкая настройка — в «Настройка → QUIK», мастер её не трогает. - Проверка МНП: ошибка сертификата (x509, «doesn't contain any IP SANs») — подсказка включить «Доверять сертификату сервера», «Дополнительно» раскрывается. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
75 lines
2.7 KiB
Go
75 lines
2.7 KiB
Go
package lkgateway
|
|
|
|
// Внешний адрес сервера bj (06.10.2026) — каким его видят снаружи. Нужен для письма в ARQA: поставщика просят
|
|
// разрешить подключение к модулю неторговых поручений с нашего адреса, и внутренний адрес (10.x, 192.168.x)
|
|
// ему ничего не даёт. Изнутри сети внешний адрес не узнать, поэтому спрашиваем наш же сервер лицензий
|
|
// (GET /v1/ip — он видит, откуда пришёл запрос); сторонние «узнать мой IP» для сервера финансовой
|
|
// организации не годятся. Раз в час, в фоне: письмо строится при показе страницы и сеть не ждёт.
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var publicIPState struct {
|
|
sync.Mutex
|
|
ip string
|
|
at time.Time
|
|
}
|
|
|
|
// publicIP — последний известный внешний адрес (пусто — сервер лицензий недоступен, например закрытый контур).
|
|
func publicIP() string {
|
|
publicIPState.Lock()
|
|
defer publicIPState.Unlock()
|
|
if time.Since(publicIPState.at) > 24*time.Hour {
|
|
return ""
|
|
}
|
|
return publicIPState.ip
|
|
}
|
|
|
|
// publicIPRefresh — спросить внешний адрес у сервера лицензий.
|
|
func publicIPRefresh(ctx context.Context) error {
|
|
cctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
|
defer cancel()
|
|
req, err := http.NewRequestWithContext(cctx, http.MethodGet, licenseServer+"/v1/ip", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp, err := licenseHTTP.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
b, _ := io.ReadAll(io.LimitReader(resp.Body, 64))
|
|
ip := net.ParseIP(strings.TrimSpace(string(b)))
|
|
if resp.StatusCode != http.StatusOK || ip == nil {
|
|
return &net.AddrError{Err: "сервер лицензий не сообщил внешний адрес", Addr: resp.Status}
|
|
}
|
|
publicIPState.Lock()
|
|
changed := publicIPState.ip != ip.String()
|
|
publicIPState.ip, publicIPState.at = ip.String(), time.Now()
|
|
publicIPState.Unlock()
|
|
if changed {
|
|
log.Printf("lk-gateway: внешний адрес сервера (по серверу лицензий): %s", ip)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// publicIPLoop — внешний адрес при запуске и раз в час (адрес у провайдера может смениться).
|
|
func publicIPLoop(ctx context.Context) {
|
|
for {
|
|
_ = publicIPRefresh(ctx)
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(time.Hour):
|
|
}
|
|
}
|
|
}
|