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, }) } }