From 866b75f64490e3386d19608491e13c7ffc0aecf7 Mon Sep 17 00:00:00 2001 From: ShukeBta <272197458+ShukeBta@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:19:42 +0800 Subject: [PATCH] fix: refresh license status from server edits --- internal/handler/license.go | 40 +++++++++++++++++---------- internal/handler/license_test.go | 47 ++++++++++++++++++++++++++++++++ web/src/pages/LicensePage.tsx | 2 ++ 3 files changed, 74 insertions(+), 15 deletions(-) diff --git a/internal/handler/license.go b/internal/handler/license.go index 0e9434f..76b8005 100644 --- a/internal/handler/license.go +++ b/internal/handler/license.go @@ -51,6 +51,7 @@ type licenseServerStatusResp struct { Valid bool `json:"valid"` LicenseType *string `json:"license_type"` ExpiryDate *string `json:"expiry_date"` + MaxDevices int `json:"max_devices"` MaxUsers *int `json:"max_users"` UnlimitedUsers bool `json:"unlimited_users"` DaysRemaining *int `json:"days_remaining"` @@ -114,21 +115,7 @@ func licenseStatusHandler(svc *service.Container) gin.HandlerFunc { if idErr == nil { var upstream licenseServerStatusResp if getErr := client.get(c.Request.Context(), "/api/v1/status/"+url.PathEscape(deviceID), &upstream); getErr == nil && upstream.Valid { - state.Valid = upstream.Valid - if upstream.LicenseType != nil { - state.LicenseType = *upstream.LicenseType - } - if upstream.ExpiryDate != nil { - state.ExpiryDate = *upstream.ExpiryDate - } - state.MaxUsers = upstream.MaxUsers - state.UnlimitedUsers = upstream.UnlimitedUsers - state.DaysRemaining = upstream.DaysRemaining - if upstream.DeviceName != "" { - state.DeviceName = upstream.DeviceName - } - state.DeviceID = deviceID - state.UpdatedAt = time.Now().Format(time.RFC3339) + applyLicenseStatus(&state, upstream, deviceID) _ = persistLicenseState(c.Request.Context(), svc, state) } else if getErr == nil && !upstream.Valid { state.Valid = false @@ -380,6 +367,29 @@ func licenseStatusMaxUsers(state service.LicenseActivationState) any { return service.OpenSourceUserLimit } +func applyLicenseStatus(state *service.LicenseActivationState, upstream licenseServerStatusResp, deviceID string) { + state.Valid = upstream.Valid + if upstream.LicenseType != nil { + state.LicenseType = *upstream.LicenseType + } + if upstream.ExpiryDate != nil { + state.ExpiryDate = *upstream.ExpiryDate + } else { + state.ExpiryDate = "" + } + if upstream.MaxDevices > 0 { + state.MaxDevices = upstream.MaxDevices + } + state.MaxUsers = upstream.MaxUsers + state.UnlimitedUsers = upstream.UnlimitedUsers + state.DaysRemaining = upstream.DaysRemaining + if upstream.DeviceName != "" { + state.DeviceName = upstream.DeviceName + } + state.DeviceID = deviceID + state.UpdatedAt = time.Now().Format(time.RFC3339) +} + func persistLicenseState(ctx context.Context, svc *service.Container, state service.LicenseActivationState) error { data, err := json.Marshal(state) if err != nil { diff --git a/internal/handler/license_test.go b/internal/handler/license_test.go index 408e77e..c20a699 100644 --- a/internal/handler/license_test.go +++ b/internal/handler/license_test.go @@ -30,3 +30,50 @@ func TestLicenseStatusMaxUsersFallsBackToOpenSourceLimit(t *testing.T) { t.Fatalf("expected open-source max users %d, got %#v", service.OpenSourceUserLimit, got) } } + +func TestApplyLicenseStatusReflectsEditedLimitAndClearsExpiry(t *testing.T) { + maxUsers := 60 + licenseType := "subscription" + state := service.LicenseActivationState{ + Valid: true, + LicenseType: "enterprise", + ExpiryDate: "2026-01-01", + MaxDevices: 2, + UnlimitedUsers: true, + } + + applyLicenseStatus(&state, licenseServerStatusResp{ + Valid: true, + LicenseType: &licenseType, + ExpiryDate: nil, + MaxDevices: 5, + MaxUsers: &maxUsers, + UnlimitedUsers: false, + DeviceName: "Edited Device", + }, "device-1") + + if !state.Valid || state.LicenseType != "subscription" || state.ExpiryDate != "" || state.MaxDevices != 5 { + t.Fatalf("status fields were not fully refreshed: %+v", state) + } + if state.MaxUsers == nil || *state.MaxUsers != 60 || state.UnlimitedUsers { + t.Fatalf("user limit was not refreshed from status: %+v", state) + } + if state.DeviceID != "device-1" || state.DeviceName != "Edited Device" { + t.Fatalf("device fields were not refreshed: %+v", state) + } +} + +func TestApplyLicenseStatusReflectsUnlimitedUsers(t *testing.T) { + maxUsers := 30 + state := service.LicenseActivationState{Valid: true, MaxUsers: &maxUsers} + + applyLicenseStatus(&state, licenseServerStatusResp{ + Valid: true, + MaxUsers: nil, + UnlimitedUsers: true, + }, "device-1") + + if state.MaxUsers != nil || !state.UnlimitedUsers { + t.Fatalf("unlimited status should clear previous finite user limit: %+v", state) + } +} diff --git a/web/src/pages/LicensePage.tsx b/web/src/pages/LicensePage.tsx index 8f2cb91..e08e256 100644 --- a/web/src/pages/LicensePage.tsx +++ b/web/src/pages/LicensePage.tsx @@ -72,6 +72,8 @@ export function LicensePage() { setStatus({ active: true, activation, + max_users: activation.max_users, + unlimited_users: activation.unlimited_users, message: '已激活', }) } catch (err: unknown) {