package services // Pro 授权:离线许可码(HMAC-SHA256 签名,零外部依赖)。 // 发行:`lokistack license --plan=pro --edition=team ...`(需发行密钥,见 licenseKeyOverride); // 激活:设置页输入许可码 → 验签落库;无有效许可 = 免费版(查询/部署/用户/LDAP 全可用)。 import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "strings" "time" "lokistack/models" "lokistack/repository" ) // licenseIssueKey 发行密钥(正式发行用 -ldflags 覆盖;默认值为开发/试用签名) var licenseIssueKey = "lokistack-issue-key-override-in-release-build" // Pro 功能清单(免费版可用功能见 router 的 RequirePro 挂载点) var proFeatures = []string{"log_analysis", "log_alerts", "ai_ops", "audit_log"} type licensePayload struct { Plan string `json:"plan"` Edition string `json:"edition"` Seats int `json:"seats"` Instances int `json:"instances"` Customer string `json:"customer"` ExpiresAt int64 `json:"expiresAt"` IssuedAt int64 `json:"issuedAt"` } // GenerateLicenseKey 签发许可码(发行方工具;days=0 表示永久) func GenerateLicenseKey(plan, edition, customer string, seats, instances, days int) (string, error) { if plan != "pro" { return "", fmt.Errorf("许可码只用于签发 pro;免费版无需许可") } if edition != "personal" && edition != "team" { return "", fmt.Errorf("edition 仅支持 personal / team") } p := licensePayload{ Plan: plan, Edition: edition, Seats: seats, Instances: instances, Customer: customer, IssuedAt: time.Now().Unix(), } if days > 0 { p.ExpiresAt = time.Now().AddDate(0, 0, days).Unix() } body, _ := json.Marshal(p) mac := hmac.New(sha256.New, []byte(licenseIssueKey)) mac.Write(body) sig := mac.Sum(nil) return base64.RawURLEncoding.EncodeToString(append(sig[:16], body...)), nil } // VerifyLicenseKey 校验许可码,返回许可内容;伪造/篡改/过期均报错 func VerifyLicenseKey(key string) (*models.License, error) { raw, err := base64.RawURLEncoding.DecodeString(strings.TrimSpace(key)) if err != nil || len(raw) <= 16 { return nil, fmt.Errorf("许可码格式无效") } sig, body := raw[:16], raw[16:] mac := hmac.New(sha256.New, []byte(licenseIssueKey)) mac.Write(body) if !hmac.Equal(sig, mac.Sum(nil)[:16]) { return nil, fmt.Errorf("许可码签名校验失败(伪造或被篡改)") } var p licensePayload if err := json.Unmarshal(body, &p); err != nil { return nil, fmt.Errorf("许可码内容无效") } if p.Plan != "pro" { return nil, fmt.Errorf("许可码不是 Pro 版") } if p.ExpiresAt > 0 && p.ExpiresAt < time.Now().Unix() { return nil, fmt.Errorf("许可码已过期") } return &models.License{ Plan: p.Plan, Edition: p.Edition, Seats: p.Seats, Instances: p.Instances, Customer: p.Customer, ExpiresAt: p.ExpiresAt, IssuedAt: time.Unix(p.IssuedAt, 0), ActivatedAt: time.Now(), }, nil } // ActivateLicense 激活(验签后落库,覆盖旧许可) func ActivateLicense(key string) (*models.License, error) { lic, err := VerifyLicenseKey(key) if err != nil { return nil, err } db := repository.GetDB() db.Lock() db.License = lic db.RequestSave() db.Unlock() return lic, nil } // CurrentLicense 当前有效许可(nil = 免费版) func CurrentLicense() *models.License { db := repository.GetDB() if db == nil || db.License == nil { return nil } if db.License.Plan != "pro" { return nil } if db.License.ExpiresAt > 0 && db.License.ExpiresAt < time.Now().Unix() { return nil // 过期回落免费版 } return db.License } // IsPro 是否为有效 Pro func IsPro() bool { return CurrentLicense() != nil } // ProFeatureList Pro 功能标识(给前端展示) func ProFeatureList() []string { return proFeatures }