197 lines
5.8 KiB
Go
197 lines
5.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"aijianzhan/platform/internal/authx"
|
|
"aijianzhan/platform/internal/license"
|
|
"aijianzhan/platform/internal/svc"
|
|
|
|
"github.com/zeromicro/go-zero/rest/httpx"
|
|
)
|
|
|
|
func requireLicenseSecret(svcCtx *svc.ServiceContext, r *http.Request) error {
|
|
want := strings.TrimSpace(svcCtx.Config.License.ControlSecret)
|
|
if want == "" {
|
|
want = strings.TrimSpace(svcCtx.Config.Auth.IssueSecret)
|
|
}
|
|
if want == "" {
|
|
return errMsg("未配置 License.ControlSecret")
|
|
}
|
|
got := strings.TrimSpace(r.Header.Get("X-License-Secret"))
|
|
if got == "" {
|
|
got = strings.TrimSpace(r.URL.Query().Get("secret"))
|
|
}
|
|
if subtle.ConstantTimeCompare([]byte(got), []byte(want)) != 1 {
|
|
return errMsg("invalid license control secret")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type errMsg string
|
|
|
|
func (e errMsg) Error() string { return string(e) }
|
|
|
|
func licenseStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if svcCtx.License == nil || !svcCtx.License.Enabled() {
|
|
httpx.OkJson(w, map[string]any{"enabled": false, "message": "本实例未启用授权租约"})
|
|
return
|
|
}
|
|
httpx.OkJson(w, svcCtx.License.Status(time.Now()))
|
|
}
|
|
}
|
|
|
|
func licenseRenewHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if err := requireLicenseSecret(svcCtx, r); err != nil {
|
|
authx.WriteError(w, http.StatusUnauthorized, err.Error())
|
|
return
|
|
}
|
|
if svcCtx.License == nil || !svcCtx.License.Enabled() {
|
|
authx.WriteError(w, http.StatusBadRequest, "license 未启用")
|
|
return
|
|
}
|
|
var body struct {
|
|
NotAfter string `json:"not_after"`
|
|
Note string `json:"note"`
|
|
By string `json:"by"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
authx.WriteError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
na, err := license.ParseNotAfter(body.NotAfter)
|
|
if err != nil || na.IsZero() {
|
|
authx.WriteError(w, http.StatusBadRequest, "not_after 无效")
|
|
return
|
|
}
|
|
by := strings.TrimSpace(body.By)
|
|
if by == "" {
|
|
by = "remote"
|
|
}
|
|
lease, err := svcCtx.License.Renew(na, by, body.Note)
|
|
if err != nil {
|
|
authx.WriteError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
httpx.OkJson(w, map[string]any{"ok": true, "lease": lease, "status": svcCtx.License.Status(time.Now())})
|
|
}
|
|
}
|
|
|
|
func licenseExtendHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if err := requireLicenseSecret(svcCtx, r); err != nil {
|
|
authx.WriteError(w, http.StatusUnauthorized, err.Error())
|
|
return
|
|
}
|
|
if svcCtx.License == nil || !svcCtx.License.Enabled() {
|
|
authx.WriteError(w, http.StatusBadRequest, "license 未启用")
|
|
return
|
|
}
|
|
var body struct {
|
|
Days int `json:"days"`
|
|
Note string `json:"note"`
|
|
By string `json:"by"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
authx.WriteError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
if body.Days <= 0 {
|
|
body.Days = license.DefaultExtensionMaxDays
|
|
}
|
|
by := strings.TrimSpace(body.By)
|
|
if by == "" {
|
|
by = "remote"
|
|
}
|
|
lease, err := svcCtx.License.Extend(body.Days, by, body.Note)
|
|
if err != nil {
|
|
authx.WriteError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
httpx.OkJson(w, map[string]any{"ok": true, "lease": lease, "status": svcCtx.License.Status(time.Now())})
|
|
}
|
|
}
|
|
|
|
func licensePutLeaseHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if err := requireLicenseSecret(svcCtx, r); err != nil {
|
|
authx.WriteError(w, http.StatusUnauthorized, err.Error())
|
|
return
|
|
}
|
|
if svcCtx.License == nil || !svcCtx.License.Enabled() {
|
|
authx.WriteError(w, http.StatusBadRequest, "license 未启用")
|
|
return
|
|
}
|
|
var body struct {
|
|
Customer string `json:"customer"`
|
|
NotAfter string `json:"not_after"`
|
|
ExtensionsUsed int `json:"extensions_used"`
|
|
ExtensionsMax int `json:"extensions_max"`
|
|
ExtensionMaxDays int `json:"extension_max_days"`
|
|
Note string `json:"note"`
|
|
By string `json:"by"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
authx.WriteError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
na, err := license.ParseNotAfter(body.NotAfter)
|
|
if err != nil || na.IsZero() {
|
|
authx.WriteError(w, http.StatusBadRequest, "not_after 无效")
|
|
return
|
|
}
|
|
by := strings.TrimSpace(body.By)
|
|
if by == "" {
|
|
by = "watchdog"
|
|
}
|
|
lease, err := svcCtx.License.PutLease(&license.Lease{
|
|
Customer: body.Customer,
|
|
NotAfter: na,
|
|
ExtensionsUsed: body.ExtensionsUsed,
|
|
ExtensionsMax: body.ExtensionsMax,
|
|
ExtensionMaxDays: body.ExtensionMaxDays,
|
|
Note: body.Note,
|
|
}, by)
|
|
if err != nil {
|
|
authx.WriteError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
httpx.OkJson(w, map[string]any{"ok": true, "lease": lease, "status": svcCtx.License.Status(time.Now())})
|
|
}
|
|
}
|
|
|
|
func licenseImportHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if err := requireLicenseSecret(svcCtx, r); err != nil {
|
|
authx.WriteError(w, http.StatusUnauthorized, err.Error())
|
|
return
|
|
}
|
|
if svcCtx.License == nil || !svcCtx.License.Enabled() {
|
|
authx.WriteError(w, http.StatusBadRequest, "license 未启用")
|
|
return
|
|
}
|
|
raw, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
authx.WriteError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
by := strings.TrimSpace(r.URL.Query().Get("by"))
|
|
if by == "" {
|
|
by = "offline"
|
|
}
|
|
lease, err := svcCtx.License.ImportSigned(raw, by)
|
|
if err != nil {
|
|
authx.WriteError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
httpx.OkJson(w, map[string]any{"ok": true, "lease": lease, "status": svcCtx.License.Status(time.Now())})
|
|
}
|
|
}
|