1212from fastapi import FastAPI
1313
1414from config import AppSettings
15+ from infrastructure .cache .entitlement_cache import EntitlementCache
1516from infrastructure .cache .feature_flag_cache import FeatureFlagCache
1617from infrastructure .cache .meta_fetch_cache import MetaFetchCache
1718from infrastructure .cache .onboarding_cache import OnboardingCache
3738from repositories .blocked_url_repository import BlockedUrlRepository
3839from repositories .click_repository import ClickRepository
3940from repositories .custom_domain_repository import CustomDomainRepository
41+ from repositories .entitlement_event_repository import EntitlementEventRepository
42+ from repositories .entitlement_override_repository import (
43+ EntitlementOverrideRepository ,
44+ )
4045from repositories .feature_flag_repository import FeatureFlagRepository
4146from repositories .feed_domain_repository import FeedDomainRepository
4247from repositories .legacy .emoji_url_repository import EmojiUrlRepository
4752 ReportSubmissionRepository ,
4853)
4954from repositories .scheduled_task_repository import ScheduledTaskRepository
55+ from repositories .subscription_repository import SubscriptionRepository
5056from repositories .tag_repository import TagRepository
5157from repositories .token_repository import TokenRepository
5258from repositories .url_repository import UrlRepository
7985from services .custom_domain_service import CustomDomainService
8086from services .domain_intel_service import DomainIntelService
8187from services .edge_cache .og_writethrough import OgEdgeWritethrough
88+ from services .entitlements import EntitlementService
8289from services .events .sinks import (
8390 InlineDomainEventSink ,
8491 NullDomainEventSink ,
8794from services .export .formatters import default_formatters
8895from services .export .service import ExportService
8996from services .feature_flag_service import FeatureFlagService
97+ from services .features .catalog import validate_override
9098from services .meta_tags .sinks import NullMetaImageSink , RedisStreamMetaImageSink
9199from services .mock_dcv_backend import MockDcvBackend
92100from services .oauth_service import OAuthService
@@ -258,6 +266,49 @@ def build_posthog_eraser(settings: AppSettings, http_client) -> PostHogEraser:
258266 )
259267
260268
269+ def build_entitlement_store (
270+ db , redis_client
271+ ) -> tuple [
272+ EntitlementCache ,
273+ EntitlementEventRepository ,
274+ SubscriptionRepository ,
275+ EntitlementOverrideRepository ,
276+ ]:
277+ """The three entitlement repositories sharing one cache, so every write
278+ to subscriptions or overrides invalidates the same ``ent:{id}`` key."""
279+ cache = EntitlementCache (redis_client )
280+ events = EntitlementEventRepository (db ["entitlement_events" ])
281+ subscriptions = SubscriptionRepository (db ["subscriptions" ], events , cache )
282+ overrides = EntitlementOverrideRepository (
283+ db ["entitlement_overrides" ], events , cache , check = validate_override
284+ )
285+ return cache , events , subscriptions , overrides
286+
287+
288+ def build_entitlement_service (
289+ db , settings : AppSettings , redis_client , * , store = None
290+ ) -> EntitlementService :
291+ cache , events , subscriptions , overrides = store or build_entitlement_store (
292+ db , redis_client
293+ )
294+ return EntitlementService (
295+ subscriptions ,
296+ overrides ,
297+ events ,
298+ cache ,
299+ selfhost = settings .billing .selfhost ,
300+ usage = {
301+ "custom_domains_max" : CustomDomainRepository (
302+ db ["custom_domains" ]
303+ ).count_by_owner ,
304+ "webhook_endpoints_max" : WebhookEndpointRepository (
305+ db ["webhook-endpoints" ]
306+ ).count_by_user ,
307+ "api_keys_max" : ApiKeyRepository (db ["api-keys" ]).count_by_user ,
308+ },
309+ )
310+
311+
261312def build_account_erasure_service (
262313 db ,
263314 settings : AppSettings ,
@@ -355,6 +406,9 @@ def build_account_erasure_service(
355406 redis_client = redis_client ,
356407 url_service = url_service ,
357408 )
409+ _ , ent_events , subscription_repo , override_repo = build_entitlement_store (
410+ db , redis_client
411+ )
358412
359413 return AccountErasureService (
360414 user_repo = user_repo ,
@@ -372,6 +426,9 @@ def build_account_erasure_service(
372426 report_repo = ReportRepository (db ["reports" ]),
373427 report_submission_repo = ReportSubmissionRepository (db ["report_submissions" ]),
374428 feature_flag_repo = FeatureFlagRepository (db ["feature_flags" ]),
429+ subscription_repo = subscription_repo ,
430+ override_repo = override_repo ,
431+ entitlement_event_repo = ent_events ,
375432 r2_storage = r2_storage ,
376433 posthog = build_posthog_eraser (settings , http_client ),
377434 mailer = build_erasure_mailer (settings , http_client ),
@@ -403,6 +460,11 @@ def wire_services(app: FastAPI, settings: AppSettings, redis_client) -> None:
403460 blocked_url_repo = BlockedUrlRepository (db ["blocked-urls" ])
404461 app_grant_repo = AppGrantRepository (db ["app-grants" ])
405462 feature_flag_repo = FeatureFlagRepository (db ["feature_flags" ])
463+ ent_store = build_entitlement_store (db , redis_client )
464+ _ , ent_events , subscription_repo , override_repo = ent_store
465+ app .state .entitlement_service = build_entitlement_service (
466+ db , settings , redis_client , store = ent_store
467+ )
406468
407469 # ── Infrastructure ───────────────────────────────────────────────────
408470 url_cache = UrlCache (redis_client , ttl_seconds = settings .redis .redis_ttl_seconds )
@@ -773,7 +835,9 @@ def wire_services(app: FastAPI, settings: AppSettings, redis_client) -> None:
773835 max_active_keys = settings .max_active_api_keys ,
774836 )
775837 app .state .page_layout_service = PageLayoutService (page_layout_repo )
776- token_factory = TokenFactory (settings .jwt )
838+ token_factory = TokenFactory (
839+ settings .jwt , plan_of = app .state .entitlement_service .plan_hint_for
840+ )
777841 otp_service = OtpService (token_repo )
778842
779843 app .state .user_repo = user_repo
@@ -1012,6 +1076,9 @@ def wire_services(app: FastAPI, settings: AppSettings, redis_client) -> None:
10121076 report_repo = report_repo ,
10131077 report_submission_repo = report_submission_repo ,
10141078 feature_flag_repo = feature_flag_repo ,
1079+ subscription_repo = subscription_repo ,
1080+ override_repo = override_repo ,
1081+ entitlement_event_repo = ent_events ,
10151082 r2_storage = r2_storage ,
10161083 posthog = build_posthog_eraser (settings , http_client ),
10171084 mailer = erasure_mailer ,
0 commit comments