package lexicomai import ( "encoding/json" "fmt" "strings" "time" ) // License — содержимое license.json продукта. Формат (подтверждён на прод-боксе): // // { "service": "cls", "expire": "2045-01-01", "licenses": 0, "key": "50e1...9cf" } // // Key — подписанная последовательность (service+expire+licenses+uuid), её // генерация — на стороне вендора; модуль только валидирует и применяет. type License struct { Service string `json:"service"` Expire string `json:"expire"` Licenses int `json:"licenses"` Key string `json:"key"` } // ParseLicense разбирает license.json. Отсутствующий файл — доменная ошибка // (из неё инструмент делает понятный errorResult). func ParseLicense(data []byte) (*License, error) { var l License if err := json.Unmarshal(data, &l); err != nil { return nil, fmt.Errorf("parse license: %w", err) } if strings.TrimSpace(l.Service) == "" { return nil, fmt.Errorf("license: service is empty") } if strings.TrimSpace(l.Expire) == "" { return nil, fmt.Errorf("license: expire is empty") } return &l, nil } // VerifyFields проверяет поля лицензии без обращения к сервису: привязана ли // она к нужному продукту и не истекла ли. Возвращает список замечаний (пустой — // поля корректны). func (l *License) VerifyFields(product string) []string { var notes []string if strings.TrimSpace(l.Service) != product { notes = append(notes, fmt.Sprintf("license bound to %q, wanted %q", l.Service, product)) } if exp, err := time.Parse("2006-01-02", l.Expire); err == nil { if time.Now().After(exp) { notes = append(notes, fmt.Sprintf("license expired on %s", l.Expire)) } } else { notes = append(notes, fmt.Sprintf("license expire %q is not YYYY-MM-DD", l.Expire)) } if l.Licenses == 0 { notes = append(notes, "license has 0 seats") } return notes } // KeyMasked возвращает ключ лицензии с маской — полный key не выводим // (может быть чувствительным). Короткие ключи скрываются целиком. func (l *License) KeyMasked() string { k := strings.TrimSpace(l.Key) if k == "" { return "" } if len(k) <= 6 { return "***" } return k[:3] + "…" + k[len(k)-3:] }