package api import ( "crypto/ed25519" "net/http" "time" "github.com/rcourtman/pulse-go-rewrite/internal/models" agentsdocker "github.com/rcourtman/pulse-go-rewrite/pkg/agents/docker" agentshost "github.com/rcourtman/pulse-go-rewrite/pkg/agents/host" agentsk8s "github.com/rcourtman/pulse-go-rewrite/pkg/agents/kubernetes" pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing" "github.com/rcourtman/pulse-go-rewrite/pkg/licensing/metering" ) // Sentinel errors for user-friendly activation error mapping. var ( errMalformedLicenseSentinel = pkglicensing.ErrMalformedLicense errInvalidLicenseSentinel = pkglicensing.ErrInvalidLicense errSignatureInvalidSentinel = pkglicensing.ErrSignatureInvalid errExpiredLicenseSentinel = pkglicensing.ErrExpiredLicense errNoPublicKeySentinel = pkglicensing.ErrNoPublicKey ) type licenseServerErrorModel = pkglicensing.LicenseServerError type licenseService = pkglicensing.Service type licenseModel = pkglicensing.License type licenseStatus = pkglicensing.LicenseStatus type licensePersistence = pkglicensing.Persistence type licenseEvaluator = pkglicensing.Evaluator type licenseFeatureServiceResolver = pkglicensing.FeatureServiceResolver type licenseFeatureChecker = pkglicensing.FeatureChecker type licenseFeaturesResponse = pkglicensing.LicenseFeaturesResponse type activateLicenseRequestModel = pkglicensing.ActivateLicenseRequest type activateLicenseResponseModel = pkglicensing.ActivateLicenseResponse type trialStartDecisionModel = pkglicensing.TrialStartDecision type trialStartDenialReasonModel = pkglicensing.TrialStartDenialReason type billingStoreModel = pkglicensing.BillingStore type billingState = pkglicensing.BillingState type subscriptionState = pkglicensing.SubscriptionState type entitlementPayloadModel = pkglicensing.EntitlementPayload type limitStatusModel = pkglicensing.LimitStatus type upgradeReasonModel = pkglicensing.UpgradeReason type entitlementUsageSnapshotModel = pkglicensing.EntitlementUsageSnapshot type legacyConnectionCountsModel = pkglicensing.LegacyConnectionCounts type commercialMigrationStatusModel = pkglicensing.CommercialMigrationStatus type conversionRecorder = pkglicensing.Recorder type conversionPipelineHealth = pkglicensing.PipelineHealth type conversionCollectionConfig = pkglicensing.CollectionConfig type conversionStore = pkglicensing.ConversionStore type conversionEvent = pkglicensing.ConversionEvent type conversionHealthStatus = pkglicensing.HealthStatus type conversionCollectionConfigSnapshot = pkglicensing.CollectionConfigSnapshot type trialActivationClaimsModel = pkglicensing.TrialActivationClaims type entitlementLeaseClaimsModel = pkglicensing.EntitlementLeaseClaims type licenseTier = pkglicensing.Tier const ( featureMultiTenantKey = pkglicensing.FeatureMultiTenant featureAgentProfilesValue = pkglicensing.FeatureAgentProfiles featureAIPatrolValue = pkglicensing.FeatureAIPatrol featureAIAutoFixValue = pkglicensing.FeatureAIAutoFix featureAuditLoggingValue = pkglicensing.FeatureAuditLogging featureRBACValue = pkglicensing.FeatureRBAC featureAdvancedReportingValue = pkglicensing.FeatureAdvancedReporting featureLongTermMetricsValue = pkglicensing.FeatureLongTermMetrics maxMonitoredSystemsLicenseGateKey = pkglicensing.MaxMonitoredSystemsLicenseGateKey maxUsersLicenseGateKey = pkglicensing.MaxUsersLicenseGateKey subscriptionStateActiveValue = pkglicensing.SubStateActive subscriptionStateExpiredValue = pkglicensing.SubStateExpired subscriptionStateGraceValue = pkglicensing.SubStateGrace subscriptionStateCanceledValue = pkglicensing.SubStateCanceled activationKeyPrefixValue = pkglicensing.ActivationKeyPrefix // Conversion event type constants for backend-emitted events. conversionEventTrialStarted = pkglicensing.EventTrialStarted conversionEventLicenseActivated = pkglicensing.EventLicenseActivated conversionEventLicenseActivationFailed = pkglicensing.EventLicenseActivationFailed conversionEventCheckoutStarted = pkglicensing.EventCheckoutStarted conversionEventCheckoutCompleted = pkglicensing.EventCheckoutCompleted conversionEventLimitBlocked = pkglicensing.EventLimitBlocked ) func newLicenseService() *licenseService { return pkglicensing.NewService() } func hasMultiTenantLicense(service *licenseService) bool { return service != nil && service.HasFeature(featureMultiTenantKey) } func upgradeURLForFeatureFromLicensing(feature string) string { return pkglicensing.UpgradeURLForFeature(feature) } func proTrialSignupURLFromLicensing(override string) string { return pkglicensing.ResolveProTrialSignupURL(override) } func defaultBillingStateFromLicensing() *billingState { return pkglicensing.DefaultBillingState() } func normalizeBillingStateFromLicensing(state *billingState) *billingState { return pkglicensing.NormalizeBillingState(state) } func cloneCommercialMigrationStatusFromLicensing(state *commercialMigrationStatusModel) *commercialMigrationStatusModel { return pkglicensing.CloneCommercialMigrationStatus(state) } func classifyLegacyExchangeErrorFromLicensing(err error) *commercialMigrationStatusModel { return pkglicensing.ClassifyLegacyExchangeError(err) } func isValidBillingSubscriptionStateFromLicensing(state subscriptionState) bool { return pkglicensing.IsValidBillingSubscriptionState(state) } func cloudCapabilitiesFromLicensing() []string { return pkglicensing.DeriveCapabilitiesFromTier(pkglicensing.TierCloud, nil) } func defaultTrialDurationFromLicensing() time.Duration { return pkglicensing.DefaultTrialDuration } func buildTrialBillingStateWithPlanFromLicensing(now time.Time, capabilities []string, planVersion string, duration time.Duration) *billingState { return pkglicensing.BuildTrialBillingStateWithPlan(now, capabilities, planVersion, duration) } func evaluateTrialStartEligibilityFromLicensing(hasActiveLicense bool, existing *billingState) trialStartDecisionModel { return pkglicensing.EvaluateTrialStartEligibility(hasActiveLicense, existing) } func trialStartErrorFromLicensing(reason trialStartDenialReasonModel) (code, message string, includeOrgID bool) { return pkglicensing.TrialStartError(reason) } func newLicensePersistenceFromLicensing(configDir string) (*licensePersistence, error) { return pkglicensing.NewPersistence(configDir) } func newLicenseServerClientFromLicensing(baseURL string) *pkglicensing.LicenseServerClient { return pkglicensing.NewLicenseServerClient(baseURL) } func isLicenseValidationDevModeFromLicensing() bool { return pkglicensing.IsLicenseValidationDevMode() } func newLicenseEvaluatorForBillingStoreFromLicensing(store billingStoreModel, orgID string, cacheTTL time.Duration, expectedInstanceHost string) *licenseEvaluator { return pkglicensing.NewEvaluator( pkglicensing.NewDatabaseSource(store, orgID, cacheTTL).WithExpectedInstanceHost(expectedInstanceHost), ) } func maxUsersLimitFromLicensing(lic *licenseModel) int { return pkglicensing.MaxUsersLimitFromLicense(lic) } func exceedsUserLimitFromLicensing(current, additions, limit int) bool { return pkglicensing.ExceedsUserLimit(current, additions, limit) } func userLimitExceededMessageFromLicensing(current, limit int) string { return pkglicensing.UserLimitExceededMessage(current, limit) } func monitoredSystemLimitExceededMessageFromLicensing(current, limit int) string { return pkglicensing.MonitoredSystemLimitExceededMessage(current, limit) } func hostReportTargetsExistingHostFromLicensing(snapshot models.StateSnapshot, report agentshost.Report, tokenID string) bool { return pkglicensing.HostReportTargetsExistingHost(snapshot, report, tokenID) } func dockerReportTargetsExistingHostFromLicensing(snapshot models.StateSnapshot, report agentsdocker.Report, tokenID string) bool { return pkglicensing.DockerReportTargetsExistingHost(snapshot, report, tokenID) } func kubernetesReportTargetsExistingClusterFromLicensing(snapshot models.StateSnapshot, report agentsk8s.Report, tokenID string) bool { return pkglicensing.KubernetesReportTargetsExistingCluster(snapshot, report, tokenID) } func kubernetesReportIdentifierFromLicensing(report agentsk8s.Report) string { return pkglicensing.KubernetesReportIdentifier(report) } func hostReportTargetsExistingHostsFromLicensing(hosts []models.Host, report agentshost.Report, tokenID string) bool { return pkglicensing.HostReportTargetsExistingHosts(hosts, report, tokenID) } func mapStripeSubscriptionStatusToStateFromLicensing(status string) subscriptionState { return pkglicensing.MapStripeSubscriptionStatusToState(status) } func shouldGrantPaidCapabilitiesFromLicensing(state subscriptionState) bool { return pkglicensing.ShouldGrantPaidCapabilities(state) } func deriveStripePlanVersionFromLicensing(metadata map[string]string, priceID string) string { return pkglicensing.DeriveStripePlanVersion(metadata, priceID) } func limitsForCloudPlanFromLicensing(planVersion string) (map[string]int64, bool) { return pkglicensing.LimitsForCloudPlan(planVersion) } func buildEntitlementPayloadFromLicensing(status *licenseStatus, subscriptionState string) entitlementPayloadModel { return pkglicensing.BuildEntitlementPayload(status, subscriptionState) } func buildFeatureMapFromLicensing(service *licenseService) map[string]bool { return pkglicensing.BuildFeatureMap(service, nil) } func buildEntitlementPayloadWithUsageFromLicensing( status *licenseStatus, subscriptionState string, usage entitlementUsageSnapshotModel, trialEndsAtUnix *int64, ) entitlementPayloadModel { return pkglicensing.BuildEntitlementPayloadWithUsage(status, subscriptionState, usage, trialEndsAtUnix) } func limitStateFromLicensing(current, limit int64) string { return pkglicensing.LimitState(current, limit) } func newCollectionConfigFromLicensing() *conversionCollectionConfig { return pkglicensing.NewCollectionConfig() } func newConversionRecorderFromLicensing(store *conversionStore) *conversionRecorder { return pkglicensing.NewRecorderFromWindowedAggregator(metering.NewWindowedAggregator(), store) } func newConversionPipelineHealthFromLicensing() *conversionPipelineHealth { return pkglicensing.NewPipelineHealth() } func conversionValidationReasonFromLicensing(err error) string { return pkglicensing.ConversionValidationReason(err) } func parseOptionalTimeParamFromLicensing(raw string, defaultValue time.Time) (time.Time, error) { return pkglicensing.ParseOptionalTimeParam(raw, defaultValue) } func trialActivationPublicKeyFromLicensing() (ed25519.PublicKey, error) { return pkglicensing.TrialActivationPublicKey() } func verifyTrialActivationTokenFromLicensing(token string, key ed25519.PublicKey, expectedInstanceHost string, now time.Time) (*trialActivationClaimsModel, error) { return pkglicensing.VerifyTrialActivationToken(token, key, expectedInstanceHost, now) } func verifyEntitlementLeaseTokenFromLicensing(token string, key ed25519.PublicKey, expectedInstanceHost string, now time.Time) (*entitlementLeaseClaimsModel, error) { return pkglicensing.VerifyEntitlementLeaseToken(token, key, expectedInstanceHost, now) } func parseEntitlementLeaseTokenFromLicensing(token string, key ed25519.PublicKey, expectedInstanceHost string) (*entitlementLeaseClaimsModel, error) { return pkglicensing.ParseEntitlementLeaseToken(token, key, expectedInstanceHost) } func writePaymentRequiredFromLicensing(w http.ResponseWriter, payload map[string]interface{}) { pkglicensing.WritePaymentRequired(w, payload) } func writeLicenseRequiredFromLicensing(w http.ResponseWriter, feature, message string) { pkglicensing.WriteLicenseRequired(w, feature, message, pkglicensing.UpgradeURLForFeature) } func recordConversionInvalidMetric(reason string) { pkglicensing.GetConversionMetrics().RecordInvalid(reason) } func recordConversionSkippedMetric(reason string) { pkglicensing.GetConversionMetrics().RecordSkipped(reason) } func recordConversionEventMetric(eventType, surface string) { pkglicensing.GetConversionMetrics().RecordEvent(eventType, surface) } // licenseTierFreeValue is the canonical free-tier constant for use outside the bridge. const licenseTierFreeValue = pkglicensing.TierFree // quickstartCreditsTotalFromLicensing returns the total quickstart credits granted per workspace. const quickstartCreditsTotalFromLicensing = pkglicensing.QuickstartCreditsTotal // overflowBonusFromLicensing returns the number of bonus host slots granted by the onboarding overflow. func overflowBonusFromLicensing(tier licenseTier, overflowGrantedAt *int64, now time.Time) int { return pkglicensing.OverflowBonus(tier, overflowGrantedAt, now) } // overflowDaysRemainingFromLicensing returns the number of days remaining in the overflow window. func overflowDaysRemainingFromLicensing(tier licenseTier, overflowGrantedAt *int64, now time.Time) int { return pkglicensing.OverflowDaysRemaining(tier, overflowGrantedAt, now) } // freeHistoryDaysDefault is the fallback history days when no license service is available. var freeHistoryDaysDefault = pkglicensing.TierHistoryDays[pkglicensing.TierFree] func tierHistoryDaysFromLicensing(tier pkglicensing.Tier) int { days := pkglicensing.TierHistoryDays[tier] if days == 0 { days = freeHistoryDaysDefault } return days }