chore: initial commit of ai site platform
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
196
platform/internal/handler/license.go
Normal file
196
platform/internal/handler/license.go
Normal file
@@ -0,0 +1,196 @@
|
||||
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())})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user