From 306ae271716c9406d3238227bb81937ea7c97f71 Mon Sep 17 00:00:00 2001 From: ksieuk Date: Wed, 4 Oct 2023 23:38:15 +0300 Subject: [PATCH 1/7] feat: [#38] add proxy settings --- src/assistant/.env.example | 6 +++ src/assistant/lib/app/settings.py | 1 + .../lib/app/split_settings/__init__.py | 2 + src/assistant/lib/app/split_settings/proxy.py | 40 +++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 src/assistant/lib/app/split_settings/proxy.py diff --git a/src/assistant/.env.example b/src/assistant/.env.example index 23e920f..faeea2a 100644 --- a/src/assistant/.env.example +++ b/src/assistant/.env.example @@ -5,6 +5,12 @@ POSTGRES_USER=user POSTGRES_PASSWORD=Qwe123 POSTGRES_DB_NAME=api_db +PROXY_HOST=255.255.255.255 +PROXY_PORT=8888 +PROXY_USER=YOUR_USER +PROXY_PASSWORD=YOUR_PASSWORD +PROXY_ENABLE=False + NGINX_PORT=80 API_HOST=0.0.0.0 API_PORT=8000 diff --git a/src/assistant/lib/app/settings.py b/src/assistant/lib/app/settings.py index 95e727e..0becd43 100644 --- a/src/assistant/lib/app/settings.py +++ b/src/assistant/lib/app/settings.py @@ -16,3 +16,4 @@ class Settings(pydantic_settings.BaseSettings): project: app_split_settings.ProjectSettings = pydantic.Field( default_factory=lambda: app_split_settings.ProjectSettings() ) + proxy: app_split_settings.ProxySettings = pydantic.Field(default_factory=lambda: app_split_settings.ProxySettings()) diff --git a/src/assistant/lib/app/split_settings/__init__.py b/src/assistant/lib/app/split_settings/__init__.py index 793ebe0..f9ddd1f 100644 --- a/src/assistant/lib/app/split_settings/__init__.py +++ b/src/assistant/lib/app/split_settings/__init__.py @@ -3,6 +3,7 @@ from .app import * from .logger import * from .postgres import * from .project import * +from .proxy import * __all__ = [ "ApiSettings", @@ -10,5 +11,6 @@ __all__ = [ "LoggingSettings", "PostgresSettings", "ProjectSettings", + "ProxySettings", "get_logging_config", ] diff --git a/src/assistant/lib/app/split_settings/proxy.py b/src/assistant/lib/app/split_settings/proxy.py new file mode 100644 index 0000000..a79a746 --- /dev/null +++ b/src/assistant/lib/app/split_settings/proxy.py @@ -0,0 +1,40 @@ +import pydantic +import pydantic_settings + +import lib.app.split_settings.utils as app_split_settings_utils + + +class ProxySettings(pydantic_settings.BaseSettings): + model_config = pydantic_settings.SettingsConfigDict( + env_file=app_split_settings_utils.ENV_PATH, + env_prefix="PROXY_", + env_file_encoding="utf-8", + extra="ignore", + ) + user: str | None = None + password: pydantic.SecretStr | None = None + host: str | None = None + port: int | None = None + enable: bool = False + + @property + def dsn(self) -> str: + if self.user and self.password: + password = self.password.get_secret_value() + return f"http://{self.user}:{password}@{self.host}:{self.port}" + return f"http://{self.host}:{self.port}" + + @pydantic.computed_field + @property + def dsn_as_safe_url(self) -> str: + if self.user and self.password: + return f"http://{self.user}:{self.password}@{self.host}:{self.port}" + return f"http://{self.host}:{self.port}" + + @pydantic.model_validator(mode="after") + def check_proxy(self): + if not self.enable: + return self + if self.host and self.port: + return self + raise ValueError("Proxy settings must be set if use_proxy is True") From 4f29e4de73a2f93c750231042e275fcd85c3cb39 Mon Sep 17 00:00:00 2001 From: ksieuk Date: Wed, 4 Oct 2023 23:38:52 +0300 Subject: [PATCH 2/7] feat: [#38] add http client --- src/assistant/lib/app/app.py | 11 ++++++++ src/assistant/lib/clients/__init__.py | 6 +++- src/assistant/lib/clients/http_client.py | 36 ++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/assistant/lib/clients/http_client.py diff --git a/src/assistant/lib/app/app.py b/src/assistant/lib/app/app.py index 6c18806..7a67a1b 100644 --- a/src/assistant/lib/app/app.py +++ b/src/assistant/lib/app/app.py @@ -57,6 +57,17 @@ class Application: logger.info("Initializing clients") + http_yandex_tts_client = clients.AsyncHttpClient( + base_url="yandex", # todo add yandex api url from settings + proxy_settings=settings.proxy, + ) + disposable_resources.append( + DisposableResource( + name="http_client yandex", + dispose_callback=http_yandex_tts_client.close(), + ) + ) + # Repositories logger.info("Initializing repositories") diff --git a/src/assistant/lib/clients/__init__.py b/src/assistant/lib/clients/__init__.py index 1fbe64c..0050424 100644 --- a/src/assistant/lib/clients/__init__.py +++ b/src/assistant/lib/clients/__init__.py @@ -1,3 +1,7 @@ +from .http_client import AsyncHttpClient from .postgres import AsyncPostgresClient -__all__ = ["AsyncPostgresClient"] +__all__ = [ + "AsyncHttpClient", + "AsyncPostgresClient", +] diff --git a/src/assistant/lib/clients/http_client.py b/src/assistant/lib/clients/http_client.py new file mode 100644 index 0000000..1382acb --- /dev/null +++ b/src/assistant/lib/clients/http_client.py @@ -0,0 +1,36 @@ +import httpx + +import lib.app.split_settings as app_split_settings + + +class AsyncHttpClient: + def __init__( + self, + proxy_settings: app_split_settings.ProxySettings, + base_url: str | None = None, + **client_params: dict[str, str], + ) -> None: + self.base_url = base_url if base_url else "" + self.proxy_settings = proxy_settings + self.proxies = self._get_proxies_from_settings() + self.client_params = client_params + + self.client = self._get_client() + + def _get_proxies_from_settings(self) -> dict[str, str] | None: + if not self.proxy_settings.enable: + return None + proxies = {"all://": self.proxy_settings.dsn} + return proxies + + def _get_client(self): + return httpx.AsyncClient( + base_url=self.base_url, + proxies=self.proxies, # type: ignore + **self.client_params, + ) + + async def close(self): + if not self.client: + return + await self.client.aclose() From 3c8ef48952dd7ca57e2a0ff48d418277057ba338 Mon Sep 17 00:00:00 2001 From: ksieuk Date: Wed, 4 Oct 2023 23:53:16 +0300 Subject: [PATCH 3/7] fix(typing): [#38] **client_params to typing.Any --- src/assistant/lib/clients/http_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/assistant/lib/clients/http_client.py b/src/assistant/lib/clients/http_client.py index 1382acb..5de287d 100644 --- a/src/assistant/lib/clients/http_client.py +++ b/src/assistant/lib/clients/http_client.py @@ -1,3 +1,5 @@ +import typing + import httpx import lib.app.split_settings as app_split_settings @@ -8,7 +10,7 @@ class AsyncHttpClient: self, proxy_settings: app_split_settings.ProxySettings, base_url: str | None = None, - **client_params: dict[str, str], + **client_params: dict[typing.Any, typing.Any], ) -> None: self.base_url = base_url if base_url else "" self.proxy_settings = proxy_settings From ef3e74606262e8d611209a411f8c9345c4556cba Mon Sep 17 00:00:00 2001 From: ksieuk Date: Wed, 4 Oct 2023 23:59:01 +0300 Subject: [PATCH 4/7] fix(typing): [#38] **client_params to typing.Any --- src/assistant/lib/clients/http_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assistant/lib/clients/http_client.py b/src/assistant/lib/clients/http_client.py index 5de287d..1da4a66 100644 --- a/src/assistant/lib/clients/http_client.py +++ b/src/assistant/lib/clients/http_client.py @@ -10,7 +10,7 @@ class AsyncHttpClient: self, proxy_settings: app_split_settings.ProxySettings, base_url: str | None = None, - **client_params: dict[typing.Any, typing.Any], + **client_params: typing.Any, ) -> None: self.base_url = base_url if base_url else "" self.proxy_settings = proxy_settings From 468a73d380ab46f90fbd8575a561f02ca6806eef Mon Sep 17 00:00:00 2001 From: ksieuk Date: Sat, 7 Oct 2023 01:43:21 +0300 Subject: [PATCH 5/7] fix: [#38] method name to private --- src/assistant/lib/clients/http_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assistant/lib/clients/http_client.py b/src/assistant/lib/clients/http_client.py index 1da4a66..3a0b855 100644 --- a/src/assistant/lib/clients/http_client.py +++ b/src/assistant/lib/clients/http_client.py @@ -14,12 +14,12 @@ class AsyncHttpClient: ) -> None: self.base_url = base_url if base_url else "" self.proxy_settings = proxy_settings - self.proxies = self._get_proxies_from_settings() + self.proxies = self.__get_proxies_from_settings() self.client_params = client_params self.client = self._get_client() - def _get_proxies_from_settings(self) -> dict[str, str] | None: + def __get_proxies_from_settings(self) -> dict[str, str] | None: if not self.proxy_settings.enable: return None proxies = {"all://": self.proxy_settings.dsn} From fa601a3eca6a17ba4c6c6f184acaa98e955e351b Mon Sep 17 00:00:00 2001 From: ksieuk Date: Sat, 7 Oct 2023 01:54:50 +0300 Subject: [PATCH 6/7] fix: [#38] socks5 is possible --- src/assistant/lib/app/split_settings/proxy.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/assistant/lib/app/split_settings/proxy.py b/src/assistant/lib/app/split_settings/proxy.py index a79a746..336c748 100644 --- a/src/assistant/lib/app/split_settings/proxy.py +++ b/src/assistant/lib/app/split_settings/proxy.py @@ -1,3 +1,5 @@ +import typing + import pydantic import pydantic_settings @@ -11,6 +13,7 @@ class ProxySettings(pydantic_settings.BaseSettings): env_file_encoding="utf-8", extra="ignore", ) + protocol: typing.Literal["http", "socks5"] = "http" user: str | None = None password: pydantic.SecretStr | None = None host: str | None = None @@ -21,15 +24,15 @@ class ProxySettings(pydantic_settings.BaseSettings): def dsn(self) -> str: if self.user and self.password: password = self.password.get_secret_value() - return f"http://{self.user}:{password}@{self.host}:{self.port}" - return f"http://{self.host}:{self.port}" + return f"{self.protocol}://{self.user}:{password}@{self.host}:{self.port}" + return f"{self.protocol}://{self.host}:{self.port}" @pydantic.computed_field @property def dsn_as_safe_url(self) -> str: if self.user and self.password: - return f"http://{self.user}:{self.password}@{self.host}:{self.port}" - return f"http://{self.host}:{self.port}" + return f"{self.protocol}://{self.user}:{self.password}@{self.host}:{self.port}" + return f"{self.protocol}://{self.host}:{self.port}" @pydantic.model_validator(mode="after") def check_proxy(self): From c44c8d2503f7081673f7aade3939ea45d8307f58 Mon Sep 17 00:00:00 2001 From: ksieuk Date: Sat, 7 Oct 2023 01:56:46 +0300 Subject: [PATCH 7/7] feat: [#38] class inherits all methods of httpx.Asynclient --- src/assistant/lib/clients/http_client.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/assistant/lib/clients/http_client.py b/src/assistant/lib/clients/http_client.py index 3a0b855..efc6a53 100644 --- a/src/assistant/lib/clients/http_client.py +++ b/src/assistant/lib/clients/http_client.py @@ -5,7 +5,7 @@ import httpx import lib.app.split_settings as app_split_settings -class AsyncHttpClient: +class AsyncHttpClient(httpx.AsyncClient): def __init__( self, proxy_settings: app_split_settings.ProxySettings, @@ -17,7 +17,7 @@ class AsyncHttpClient: self.proxies = self.__get_proxies_from_settings() self.client_params = client_params - self.client = self._get_client() + super().__init__(base_url=self.base_url, proxies=self.proxies, **client_params) def __get_proxies_from_settings(self) -> dict[str, str] | None: if not self.proxy_settings.enable: @@ -25,14 +25,5 @@ class AsyncHttpClient: proxies = {"all://": self.proxy_settings.dsn} return proxies - def _get_client(self): - return httpx.AsyncClient( - base_url=self.base_url, - proxies=self.proxies, # type: ignore - **self.client_params, - ) - - async def close(self): - if not self.client: - return - await self.client.aclose() + async def close(self) -> None: + await self.aclose()