feat: add avatars
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import lru_cache
|
||||
from typing import Any, List, Literal
|
||||
from typing import Any, List, Literal, Mapping
|
||||
from urllib.parse import quote
|
||||
|
||||
from dotenv import find_dotenv, load_dotenv
|
||||
@@ -39,6 +39,7 @@ class Settings(BaseSettings):
|
||||
VAULT_DOCS_SECRET_PATH: str = 'docs'
|
||||
VAULT_JWT_KID_PATH: str = 'jwt/kid'
|
||||
VAULT_JWT_KIDS_PREFIX: str = 'jwt/kids'
|
||||
VAULT_S3_SECRET_PATH: str = 's3/avatars'
|
||||
|
||||
DATABASE_URL_DIRECT: str | None = Field(default=None, validation_alias='DATABASE_URL')
|
||||
DATABASE_HOST: str = ''
|
||||
@@ -89,6 +90,15 @@ class Settings(BaseSettings):
|
||||
RABBIT_CONNECT_TIMEOUT: int = 5
|
||||
RABBIT_EMAIL_CODE_QUEUE: str = 'email.verification_code'
|
||||
|
||||
S3_BUCKET: str = ''
|
||||
S3_REGION: str = 'us-east-1'
|
||||
S3_ACCESS_KEY_ID: str = ''
|
||||
S3_SECRET_ACCESS_KEY: str = ''
|
||||
S3_ENDPOINT_URL: str = ''
|
||||
S3_PUBLIC_BASE_URL: str = ''
|
||||
S3_REGRU_PUBLIC_WEBSITE_HOST: bool = True
|
||||
S3_AVATAR_KEY_PREFIX: str = 'avatars'
|
||||
|
||||
LOG_LEVEL: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] = 'INFO'
|
||||
LOG_FORMAT: Literal['JSON', 'TEXT'] = 'JSON'
|
||||
|
||||
@@ -99,7 +109,78 @@ class Settings(BaseSettings):
|
||||
return str(value)
|
||||
return ''
|
||||
|
||||
def _reset_s3_config(self) -> None:
|
||||
object.__setattr__(self, 'S3_BUCKET', '')
|
||||
object.__setattr__(self, 'S3_ACCESS_KEY_ID', '')
|
||||
object.__setattr__(self, 'S3_SECRET_ACCESS_KEY', '')
|
||||
object.__setattr__(self, 'S3_ENDPOINT_URL', '')
|
||||
object.__setattr__(self, 'S3_PUBLIC_BASE_URL', '')
|
||||
object.__setattr__(self, 'S3_REGION', 'us-east-1')
|
||||
object.__setattr__(self, 'S3_REGRU_PUBLIC_WEBSITE_HOST', True)
|
||||
object.__setattr__(self, 'S3_AVATAR_KEY_PREFIX', 'avatars')
|
||||
|
||||
@staticmethod
|
||||
def _vault_kv(mapping: Mapping[str, Any], *keys: str) -> Any:
|
||||
for k in keys:
|
||||
if k in mapping and mapping[k] is not None:
|
||||
return mapping[k]
|
||||
return None
|
||||
|
||||
def _apply_s3_from_vault_secret(self, s3: dict[str, Any]) -> None:
|
||||
bucket_name = (
|
||||
self._vault_kv(s3, 'bucket_name', 'BUCKET_NAME', 'bucket')
|
||||
or self._vault_kv(s3, 'S3_BUCKET', 'bucketName')
|
||||
)
|
||||
endpoint_url = (
|
||||
self._vault_kv(s3, 's3_endpoint_url', 'S3_ENDPOINT_URL', 'endpoint_url', 'ENDPOINT_URL')
|
||||
or self._vault_kv(s3, 'endpoint')
|
||||
)
|
||||
ak = (
|
||||
self._vault_kv(s3, 's3_access_key_id', 'S3_ACCESS_KEY_ID', 'ACCESS_KEY_ID', 'access_key_id')
|
||||
or self._vault_kv(s3, 'AWS_ACCESS_KEY_ID')
|
||||
)
|
||||
sk = (
|
||||
self._vault_kv(s3, 's3_secret_access_key', 'S3_SECRET_ACCESS_KEY', 'SECRET_ACCESS_KEY')
|
||||
or self._vault_kv(s3, 'AWS_SECRET_ACCESS_KEY')
|
||||
)
|
||||
if bucket_name is None or str(bucket_name).strip() == '':
|
||||
raise ValueError('Vault S3 secret must contain bucket_name')
|
||||
if endpoint_url is None or str(endpoint_url).strip() == '':
|
||||
raise ValueError('Vault S3 secret must contain s3_endpoint_url')
|
||||
if ak is None or str(ak).strip() == '':
|
||||
raise ValueError('Vault S3 secret must contain s3_access_key_id')
|
||||
if sk is None or str(sk).strip() == '':
|
||||
raise ValueError('Vault S3 secret must contain s3_secret_access_key')
|
||||
object.__setattr__(self, 'S3_BUCKET', str(bucket_name).strip())
|
||||
object.__setattr__(self, 'S3_ENDPOINT_URL', str(endpoint_url).strip())
|
||||
object.__setattr__(self, 'S3_ACCESS_KEY_ID', str(ak).strip())
|
||||
object.__setattr__(self, 'S3_SECRET_ACCESS_KEY', str(sk).strip())
|
||||
region = (
|
||||
self._vault_kv(s3, 's3_region', 'S3_REGION', 'region')
|
||||
)
|
||||
if region is not None and str(region).strip() != '':
|
||||
object.__setattr__(self, 'S3_REGION', str(region).strip())
|
||||
public_base = (
|
||||
self._vault_kv(s3, 's3_public_base_url', 'S3_PUBLIC_BASE_URL', 'public_base_url')
|
||||
or self._vault_kv(s3, 'public_url')
|
||||
)
|
||||
if public_base is not None and str(public_base).strip() != '':
|
||||
object.__setattr__(self, 'S3_PUBLIC_BASE_URL', str(public_base).strip())
|
||||
prefix = self._vault_kv(s3, 'avatar_key_prefix', 'S3_AVATAR_KEY_PREFIX', 's3_avatar_key_prefix')
|
||||
if prefix is not None and str(prefix).strip() != '':
|
||||
object.__setattr__(self, 'S3_AVATAR_KEY_PREFIX', str(prefix).strip())
|
||||
rf = (
|
||||
self._vault_kv(s3, 's3_reg_ru_public_website_host', 'S3_REGRU_PUBLIC_WEBSITE_HOST')
|
||||
)
|
||||
if rf is not None:
|
||||
v = str(rf).strip().lower()
|
||||
if v in {'1', 'true', 'yes', 'on'}:
|
||||
object.__setattr__(self, 'S3_REGRU_PUBLIC_WEBSITE_HOST', True)
|
||||
elif v in {'0', 'false', 'no', 'off'}:
|
||||
object.__setattr__(self, 'S3_REGRU_PUBLIC_WEBSITE_HOST', False)
|
||||
|
||||
def model_post_init(self, __context: Any) -> None:
|
||||
self._reset_s3_config()
|
||||
if not self.VAULT_ROLE_ID.strip() or not self.VAULT_SECRET_ID.strip():
|
||||
if not self.DATABASE_URL:
|
||||
raise ValueError(
|
||||
@@ -164,6 +245,16 @@ class Settings(BaseSettings):
|
||||
if p is not None:
|
||||
object.__setattr__(self, 'DOCS_PASSWORD', str(p))
|
||||
|
||||
s3_rel_path = self.VAULT_S3_SECRET_PATH.strip()
|
||||
if s3_rel_path:
|
||||
try:
|
||||
s3_secret_data = client.read_secret(s3_rel_path)
|
||||
except Exception as exc:
|
||||
raise ValueError(
|
||||
f'Vault S3 secret not readable at mount {self.VAULT_MOUNT_POINT}/{s3_rel_path}: {exc!r}'
|
||||
) from exc
|
||||
self._apply_s3_from_vault_secret(s3_secret_data)
|
||||
|
||||
if not self.DATABASE_URL:
|
||||
raise ValueError('Database URL could not be built from Vault database secret')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user