license-server/license/service.go

148 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package license
import (
"encoding/base64"
"encoding/json"
"license-server/storage"
"time"
"github.com/gofiber/fiber/v2"
)
func GenerateLicenseHandler(db storage.Database) fiber.Handler {
return func(c *fiber.Ctx) error {
var req LicenseRequest
if err := c.BodyParser(&req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
}
if req.MachineID == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing machine ID")
}
// ✅ 检查是否已经生成过
if db.HasGenerated(req.MachineID) {
return fiber.NewError(fiber.StatusForbidden, "License already generated for this machine")
}
// ❌ 不再接收客户端传的 Expiry由后续激活阶段设置
req.Expiry = ""
payloadBytes, err := json.Marshal(req)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to encode payload")
}
signature, err := SignPayload(payloadBytes)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Signing failed")
}
licenseFile := LicenseFile{
Payload: base64.StdEncoding.EncodeToString(payloadBytes),
Signature: signature,
}
// ✅ 保存生成记录(避免重复)
db.SaveGenerated(req.MachineID, licenseFile.Payload+"."+licenseFile.Signature)
return c.JSON(licenseFile)
}
}
func ActivateLicenseHandler(db storage.Database) fiber.Handler {
return func(c *fiber.Ctx) error {
var lf LicenseFile
if err := c.BodyParser(&lf); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid license format")
}
payloadBytes, err := base64.StdEncoding.DecodeString(lf.Payload)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid base64 payload")
}
if !VerifySignature(GetPublicKey(), payloadBytes, lf.Signature) {
return fiber.NewError(fiber.StatusUnauthorized, "Invalid license signature")
}
var req LicenseRequest
if err := json.Unmarshal(payloadBytes, &req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Malformed payload")
}
if req.MachineID == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing machine ID")
}
if db.HasActivated(req.MachineID) {
return fiber.NewError(fiber.StatusForbidden, "This machine is already activated")
}
// ✅ 设置新的 Expiry 字段(激活日起算 365 天)
req.Expiry = time.Now().AddDate(1, 0, 0).Format("2006-01-02")
// ✅ 重新生成 payload 和签名
newPayload, err := json.Marshal(req)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to re-encode payload")
}
newSignature, err := SignPayload(newPayload)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "Signing failed")
}
db.SaveActivation(req.MachineID, base64.StdEncoding.EncodeToString(newPayload)+"."+newSignature)
return c.JSON(fiber.Map{
"status": "success",
"message": "License activated successfully",
"expiry": req.Expiry,
"payload": base64.StdEncoding.EncodeToString(newPayload),
"signature": newSignature,
})
}
}
func ValidateLicenseHandler(db storage.Database) fiber.Handler {
return func(c *fiber.Ctx) error {
var lf LicenseFile
if err := c.BodyParser(&lf); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid license format")
}
payloadBytes, err := base64.StdEncoding.DecodeString(lf.Payload)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid base64 payload")
}
if !VerifySignature(GetPublicKey(), payloadBytes, lf.Signature) {
return fiber.NewError(fiber.StatusUnauthorized, "Invalid license signature")
}
var req LicenseRequest
if err := json.Unmarshal(payloadBytes, &req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Malformed payload")
}
expiry, err := time.Parse("2006-01-02", req.Expiry)
if err != nil || time.Now().After(expiry) {
return fiber.NewError(fiber.StatusForbidden, "License expired")
}
clientMachineID := c.Get("X-Machine-ID")
if clientMachineID == "" {
return fiber.NewError(fiber.StatusBadRequest, "Missing machine ID in header")
}
if clientMachineID != req.MachineID {
return fiber.NewError(fiber.StatusForbidden, "Machine ID mismatch")
}
return c.JSON(fiber.Map{
"valid": true,
"features": req.Features,
"expiry": req.Expiry,
})
}
}