feat: живой цикл M2M с НРД + мастер установки ключа на флешку
Инфраструктура M2M (живой обмен с НРД через ИШ): - обработка M2MTransferResponse: ERROR(M2Mxx) → заявка Отклонена, сохранение ответа; INFO → ждём Decision; идемпотентность поллера - fallback-корреляция ответов с нулевым GUID (M2M14/M2M17) по FIFO - сырой XML ответа НРД в карточке заявки (для пересылки в ТП) - тестовый пакет роботу приведён к эталону m2m_robot_samples (CostInfo=Yes, 4 бумаги, IsolationStatus, DocumentSeries=сценарий); override паспорта - редирект из теста сразу в карточку заявки Мастер установки ключа Валидаты на флешку (admin/setup/keywizard): - пошаговый: загрузка .7z+пароль → выбор флешки → запись → справочник сертификатов (CRL) → перезапуск+проверка ИШ → готово - привилегированный воркер (bj-keymedia) в host-namespace через файл-обмен, bj-server остаётся в песочнице - сохранение структуры профиля архива (spr<N>), перечисление съёмных USB Прочее: - пакет-доказательство для ТП НРД + форма регистрации участника M2M - эталонные образцы робота (DOC/m2m_robot_samples) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
// Command bj-license-server — онлайн-сервис учёта и отзыва лицензий.
|
||||
//
|
||||
// Базовая модель лицензирования офлайновая: bj-server проверяет подпись и
|
||||
// срок сам. Этот сервер нужен для:
|
||||
// - реестра выданных лицензий (учёт);
|
||||
// - ОТЗЫВА (revocation) до окончания срока;
|
||||
// - проверки клиентом «не отозвана ли» (опциональный online-чек).
|
||||
//
|
||||
// Хранилище — JSON-файл со списком отозванных ID (для каркаса; в проде —
|
||||
// PostgreSQL). API:
|
||||
//
|
||||
// GET /v1/check?id=<license-id> → {"revoked":bool}
|
||||
// GET /healthz
|
||||
//
|
||||
// Управление отзывом — правкой файла revoked.json (или будущим admin API).
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
mu sync.RWMutex
|
||||
path string
|
||||
revoked map[string]bool
|
||||
}
|
||||
|
||||
func newStore(path string) *store {
|
||||
s := &store{path: path, revoked: map[string]bool{}}
|
||||
s.load()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *store) load() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
b, err := os.ReadFile(s.path)
|
||||
if err != nil {
|
||||
return // файла нет — пустой список
|
||||
}
|
||||
var ids []string
|
||||
if err := json.Unmarshal(b, &ids); err != nil {
|
||||
log.Printf("license-server: разбор %s: %v", s.path, err)
|
||||
return
|
||||
}
|
||||
s.revoked = map[string]bool{}
|
||||
for _, id := range ids {
|
||||
s.revoked[id] = true
|
||||
}
|
||||
log.Printf("license-server: загружено отозванных лицензий: %d", len(s.revoked))
|
||||
}
|
||||
|
||||
func (s *store) isRevoked(id string) bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.revoked[id]
|
||||
}
|
||||
|
||||
func main() {
|
||||
addr := flag.String("addr", ":8091", "адрес прослушивания")
|
||||
file := flag.String("revoked", "./revoked.json", "JSON-файл со списком отозванных license ID")
|
||||
flag.Parse()
|
||||
|
||||
st := newStore(*file)
|
||||
|
||||
// Перечитываем файл отзывов раз в минуту (горячее применение).
|
||||
go func() {
|
||||
t := time.NewTicker(time.Minute)
|
||||
defer t.Stop()
|
||||
for range t.C {
|
||||
st.load()
|
||||
}
|
||||
}()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("ok")) })
|
||||
mux.HandleFunc("/v1/check", func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.URL.Query().Get("id")
|
||||
if id == "" {
|
||||
http.Error(w, "id required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]bool{"revoked": st.isRevoked(id)})
|
||||
})
|
||||
|
||||
log.Printf("license-server: слушаю %s, отзывы из %s", *addr, *file)
|
||||
srv := &http.Server{Addr: *addr, Handler: mux, ReadHeaderTimeout: 10 * time.Second}
|
||||
log.Fatal(srv.ListenAndServe())
|
||||
}
|
||||
Reference in New Issue
Block a user