fix: honor licensed user limits

This commit is contained in:
ShukeBta
2026-05-30 21:48:09 +08:00
parent e08b827861
commit de3f1f3bb4
8 changed files with 243 additions and 60 deletions
+40 -1
View File
@@ -2,6 +2,7 @@ package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"testing"
@@ -23,7 +24,7 @@ func newAuthTestServices(t *testing.T) (*repository.Container, *AuthService, *Pr
if err != nil {
t.Fatal(err)
}
if err := db.AutoMigrate(&model.User{}, &model.UserPermission{}, &model.RefreshToken{}, &model.TelegramBinding{}); err != nil {
if err := db.AutoMigrate(&model.User{}, &model.UserPermission{}, &model.RefreshToken{}, &model.TelegramBinding{}, &model.Setting{}); err != nil {
t.Fatal(err)
}
repos := repository.New(db)
@@ -57,6 +58,44 @@ func TestRegisterRejectsMoreThanTwentyUsers(t *testing.T) {
}
}
func TestRegisterUsesLicensedUserLimit(t *testing.T) {
ctx := context.Background()
repos, auth, _, _ := newAuthTestServices(t)
maxUsers := 25
state := LicenseActivationState{Valid: true, LicenseType: "plus", MaxUsers: &maxUsers}
raw, _ := json.Marshal(state)
if err := repos.Setting.Set(ctx, LicenseSettingActivation, string(raw)); err != nil {
t.Fatal(err)
}
for i := 0; i < OpenSourceUserLimit; i++ {
if err := repos.User.Create(ctx, &model.User{
Username: fmt.Sprintf("licensed-%02d", i),
PasswordHash: "hash",
Role: "user",
Tier: "free",
}); err != nil {
t.Fatal(err)
}
}
if _, _, err := auth.Register(ctx, "extra", "password"); err != nil {
t.Fatalf("licensed user limit should allow user 21: %v", err)
}
}
func TestLicensedMaxUsersCanBeUnlimited(t *testing.T) {
ctx := context.Background()
repos, _, _, _ := newAuthTestServices(t)
state := LicenseActivationState{Valid: true, LicenseType: "enterprise", UnlimitedUsers: true}
raw, _ := json.Marshal(state)
if err := repos.Setting.Set(ctx, LicenseSettingActivation, string(raw)); err != nil {
t.Fatal(err)
}
if got := LicensedMaxUsers(ctx, repos); got <= 1_000_000 {
t.Fatalf("unlimited license should return a very high limit, got %d", got)
}
}
func TestRegisterDefaultsAdultLibrariesHidden(t *testing.T) {
_, auth, _, _ := newAuthTestServices(t)
user, _, err := auth.Register(context.Background(), "viewer", "password")
+31 -13
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"encoding/json"
"math"
"time"
"github.com/ShukeBta/MediaStationGo/internal/repository"
@@ -16,19 +17,28 @@ const (
)
type LicenseActivationState struct {
Valid bool `json:"valid"`
LicenseType string `json:"license_type,omitempty"`
ExpiryDate string `json:"expiry_date,omitempty"`
MaxDevices int `json:"max_devices,omitempty"`
DaysRemaining *int `json:"days_remaining,omitempty"`
NextHeartbeat string `json:"next_heartbeat,omitempty"`
DeviceID string `json:"device_id,omitempty"`
DeviceName string `json:"device_name,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
Valid bool `json:"valid"`
LicenseType string `json:"license_type,omitempty"`
ExpiryDate string `json:"expiry_date,omitempty"`
MaxDevices int `json:"max_devices,omitempty"`
MaxUsers *int `json:"max_users,omitempty"`
UnlimitedUsers bool `json:"unlimited_users,omitempty"`
DaysRemaining *int `json:"days_remaining,omitempty"`
NextHeartbeat string `json:"next_heartbeat,omitempty"`
DeviceID string `json:"device_id,omitempty"`
DeviceName string `json:"device_name,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func LicensedMaxUsers(ctx context.Context, repos *repository.Container) int64 {
if LicenseActive(ctx, repos) {
state, ok := loadLicenseActivationState(ctx, repos)
if ok && state.Valid && !licenseExpired(state.ExpiryDate) {
if state.UnlimitedUsers {
return math.MaxInt64
}
if state.MaxUsers != nil && *state.MaxUsers > 0 {
return int64(*state.MaxUsers)
}
return LicensedUserLimit
}
return OpenSourceUserLimit
@@ -38,15 +48,23 @@ func LicenseActive(ctx context.Context, repos *repository.Container) bool {
if repos == nil || repos.Setting == nil {
return false
}
state, ok := loadLicenseActivationState(ctx, repos)
return ok && state.Valid && !licenseExpired(state.ExpiryDate)
}
func loadLicenseActivationState(ctx context.Context, repos *repository.Container) (LicenseActivationState, bool) {
if repos == nil || repos.Setting == nil {
return LicenseActivationState{}, false
}
raw, err := repos.Setting.Get(ctx, LicenseSettingActivation)
if err != nil || raw == "" {
return false
return LicenseActivationState{}, false
}
var state LicenseActivationState
if err := json.Unmarshal([]byte(raw), &state); err != nil {
return false
return LicenseActivationState{}, false
}
return state.Valid && !licenseExpired(state.ExpiryDate)
return state, true
}
func licenseExpired(expiry string) bool {