package model import ( "database/sql" "encoding/json" "time" ) type Product struct { ID int64 `json:"id"` Code string `json:"code"` Name string `json:"name"` KeyPrefix string `json:"key_prefix"` DefaultLimits json.RawMessage `json:"default_limits"` AvailableFeatures json.RawMessage `json:"available_features"` Active bool `json:"active"` CreatedAt time.Time `json:"created_at"` } type License struct { ID int64 `json:"id"` ProductID int64 `json:"product_id"` LicenseKey string `json:"license_key"` LicenseType string `json:"license_type"` CustomerName string `json:"customer_name"` CustomerPIB string `json:"customer_pib"` CustomerEmail string `json:"customer_email"` Limits json.RawMessage `json:"limits"` Features json.RawMessage `json:"features"` IssuedAt time.Time `json:"issued_at"` ExpiresAt sql.NullTime `json:"expires_at"` GraceDays int `json:"grace_days"` Active bool `json:"active"` Revoked bool `json:"revoked"` RevokedAt sql.NullTime `json:"revoked_at"` RevokedReason string `json:"revoked_reason"` Notes string `json:"notes"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` // Joined fields ProductCode string `json:"product_code,omitempty"` ProductName string `json:"product_name,omitempty"` ProductPrefix string `json:"-"` } func (l *License) IsExpired() bool { if !l.ExpiresAt.Valid { return false // PERPETUAL } return time.Now().After(l.ExpiresAt.Time) } func (l *License) IsInGrace() bool { if !l.ExpiresAt.Valid { return false } if !l.IsExpired() { return false } graceEnd := l.ExpiresAt.Time.AddDate(0, 0, l.GraceDays) return time.Now().Before(graceEnd) } func (l *License) IsGraceExpired() bool { if !l.ExpiresAt.Valid { return false } graceEnd := l.ExpiresAt.Time.AddDate(0, 0, l.GraceDays) return time.Now().After(graceEnd) } func (l *License) MaskedKey() string { if len(l.LicenseKey) < 10 { return l.LicenseKey } return l.LicenseKey[:len(l.ProductPrefix)+4] + "-****-****-" + l.LicenseKey[len(l.LicenseKey)-4:] } func (l *License) StatusText() string { if l.Revoked { return "Opozvana" } if !l.Active { return "Neaktivna" } if l.IsGraceExpired() { return "Istekla (grace)" } if l.IsInGrace() { return "Grace period" } if l.IsExpired() { return "Istekla" } if l.LicenseType == "TRIAL" { return "Trial" } return "Aktivna" } func (l *License) StatusClass() string { switch l.StatusText() { case "Aktivna": return "status-active" case "Trial": return "status-trial" case "Grace period": return "status-grace" case "Istekla", "Istekla (grace)": return "status-expired" case "Opozvana": return "status-revoked" default: return "" } } func (l *License) ExpiresAtFormatted() string { if !l.ExpiresAt.Valid { return "Neograniceno" } return l.ExpiresAt.Time.Format("02.01.2006") } type LicenseWithActivation struct { License ActiveActivations int `json:"active_activations"` }