|
1 | 1 | """ |
2 | | -Service and repository dependency providers. |
| 2 | +Service dependency providers. |
3 | 3 |
|
4 | | -Each function assembles the repositories and infrastructure a service needs, |
5 | | -then returns a fully-wired service instance for the current request. |
| 4 | +Each function is a thin lookup that returns the singleton service instance |
| 5 | +built during application startup in the lifespan (app.py). No per-request |
| 6 | +object construction — services are stateless and shared across requests. |
6 | 7 | """ |
7 | 8 |
|
8 | 9 | from __future__ import annotations |
9 | 10 |
|
10 | | -from fastapi import Depends, Request |
11 | | - |
12 | | -from config import AppSettings |
13 | | -from dependencies.infra import ( |
14 | | - get_db, |
15 | | - get_email_provider, |
16 | | - get_geoip_service, |
17 | | - get_settings, |
18 | | - get_url_cache, |
19 | | -) |
20 | | -from infrastructure.cache.url_cache import UrlCache |
21 | | -from infrastructure.captcha.hcaptcha import HCaptchaProvider |
22 | | -from infrastructure.geoip import GeoIPService |
23 | | -from infrastructure.webhook.discord import DiscordWebhookProvider |
24 | | -from repositories.api_key_repository import ApiKeyRepository |
25 | | -from repositories.blocked_url_repository import BlockedUrlRepository |
26 | | -from repositories.click_repository import ClickRepository |
27 | | -from repositories.legacy.emoji_url_repository import EmojiUrlRepository |
28 | | -from repositories.legacy.legacy_url_repository import LegacyUrlRepository |
29 | | -from repositories.token_repository import TokenRepository |
30 | | -from repositories.url_repository import UrlRepository |
31 | | -from repositories.user_repository import UserRepository |
| 11 | +from fastapi import Request |
| 12 | + |
32 | 13 | from services.api_key_service import ApiKeyService |
33 | 14 | from services.auth_service import AuthService |
34 | | -from services.click import ClickService, LegacyClickHandler, V2ClickHandler |
| 15 | +from services.click import ClickService |
35 | 16 | from services.contact_service import ContactService |
36 | | -from services.export.formatters import default_formatters |
37 | 17 | from services.export.service import ExportService |
38 | 18 | from services.oauth_service import OAuthService |
39 | 19 | from services.profile_picture_service import ProfilePictureService |
40 | 20 | from services.stats_service import StatsService |
41 | 21 | from services.url_service import UrlService |
42 | 22 |
|
43 | 23 |
|
44 | | -async def get_url_service( |
45 | | - db=Depends(get_db), |
46 | | - url_cache: UrlCache = Depends(get_url_cache), |
47 | | - settings: AppSettings = Depends(get_settings), |
48 | | -) -> UrlService: |
49 | | - url_repo = UrlRepository(db["urlsV2"]) |
50 | | - legacy_repo = LegacyUrlRepository(db["urls"]) |
51 | | - emoji_repo = EmojiUrlRepository(db["emojis"]) |
52 | | - blocked_url_repo = BlockedUrlRepository(db["blocked-urls"]) |
53 | | - blocked_self_domains = [settings.app_url] if settings.app_url else [] |
54 | | - return UrlService( |
55 | | - url_repo, |
56 | | - legacy_repo, |
57 | | - emoji_repo, |
58 | | - blocked_url_repo, |
59 | | - url_cache, |
60 | | - blocked_self_domains, |
61 | | - ) |
62 | | - |
63 | | - |
64 | | -async def get_stats_service(db=Depends(get_db)) -> StatsService: |
65 | | - click_repo = ClickRepository(db["clicks"]) |
66 | | - url_repo = UrlRepository(db["urlsV2"]) |
67 | | - return StatsService(click_repo, url_repo) |
68 | | - |
69 | | - |
70 | | -async def get_export_service( |
71 | | - stats: StatsService = Depends(get_stats_service), |
72 | | -) -> ExportService: |
73 | | - return ExportService(stats, default_formatters()) |
74 | | - |
75 | | - |
76 | | -async def get_api_key_service(db=Depends(get_db)) -> ApiKeyService: |
77 | | - api_key_repo = ApiKeyRepository(db["api-keys"]) |
78 | | - return ApiKeyService(api_key_repo) |
79 | | - |
80 | | - |
81 | | -async def get_auth_service( |
82 | | - db=Depends(get_db), |
83 | | - settings: AppSettings = Depends(get_settings), |
84 | | - email=Depends(get_email_provider), |
85 | | -) -> AuthService: |
86 | | - """Build and return an AuthService for the current request.""" |
87 | | - user_repo = UserRepository(db["users"]) |
88 | | - token_repo = TokenRepository(db["verification-tokens"]) |
89 | | - return AuthService(user_repo, token_repo, email, settings.jwt) |
90 | | - |
91 | | - |
92 | | -async def get_oauth_service( |
93 | | - db=Depends(get_db), |
94 | | - auth_service: AuthService = Depends(get_auth_service), |
95 | | - email=Depends(get_email_provider), |
96 | | -) -> OAuthService: |
97 | | - """Build and return an OAuthService for the current request.""" |
98 | | - user_repo = UserRepository(db["users"]) |
99 | | - return OAuthService(user_repo, auth_service, email) |
100 | | - |
101 | | - |
102 | | -async def get_profile_picture_service(db=Depends(get_db)) -> ProfilePictureService: |
103 | | - return ProfilePictureService(UserRepository(db["users"])) |
104 | | - |
105 | | - |
106 | | -async def get_contact_service( |
107 | | - request: Request, |
108 | | - settings: AppSettings = Depends(get_settings), |
109 | | -) -> ContactService: |
110 | | - http_client = request.app.state.http_client |
111 | | - captcha = HCaptchaProvider(settings.hcaptcha_secret, http_client) |
112 | | - contact_webhook = DiscordWebhookProvider(settings.contact_webhook, http_client) |
113 | | - report_webhook = DiscordWebhookProvider(settings.url_report_webhook, http_client) |
114 | | - return ContactService(contact_webhook, report_webhook, captcha) |
115 | | - |
116 | | - |
117 | | -async def get_click_service( |
118 | | - db=Depends(get_db), |
119 | | - url_cache: UrlCache = Depends(get_url_cache), |
120 | | - geoip: GeoIPService = Depends(get_geoip_service), |
121 | | -) -> ClickService: |
122 | | - """Build and return a ClickService with V2 and legacy handlers.""" |
123 | | - url_repo = UrlRepository(db["urlsV2"]) |
124 | | - legacy_repo = LegacyUrlRepository(db["urls"]) |
125 | | - emoji_repo = EmojiUrlRepository(db["emojis"]) |
126 | | - click_repo = ClickRepository(db["clicks"]) |
127 | | - v2_handler = V2ClickHandler(click_repo, url_repo, geoip, url_cache) |
128 | | - v1_handler = LegacyClickHandler(legacy_repo, emoji_repo, geoip) |
129 | | - return ClickService({"v2": v2_handler, "v1": v1_handler}) |
| 24 | +def get_url_service(request: Request) -> UrlService: |
| 25 | + return request.app.state.url_service |
| 26 | + |
| 27 | + |
| 28 | +def get_stats_service(request: Request) -> StatsService: |
| 29 | + return request.app.state.stats_service |
| 30 | + |
| 31 | + |
| 32 | +def get_export_service(request: Request) -> ExportService: |
| 33 | + return request.app.state.export_service |
| 34 | + |
| 35 | + |
| 36 | +def get_api_key_service(request: Request) -> ApiKeyService: |
| 37 | + return request.app.state.api_key_service |
| 38 | + |
| 39 | + |
| 40 | +def get_auth_service(request: Request) -> AuthService: |
| 41 | + return request.app.state.auth_service |
| 42 | + |
| 43 | + |
| 44 | +def get_oauth_service(request: Request) -> OAuthService: |
| 45 | + return request.app.state.oauth_service |
| 46 | + |
| 47 | + |
| 48 | +def get_profile_picture_service(request: Request) -> ProfilePictureService: |
| 49 | + return request.app.state.profile_picture_service |
| 50 | + |
| 51 | + |
| 52 | +def get_contact_service(request: Request) -> ContactService: |
| 53 | + return request.app.state.contact_service |
| 54 | + |
| 55 | + |
| 56 | +def get_click_service(request: Request) -> ClickService: |
| 57 | + return request.app.state.click_service |
0 commit comments