From 78b81b9e3cde583f370cbc8e47b2c98d6651deb3 Mon Sep 17 00:00:00 2001 From: leo <260626284+cameledev@users.noreply.github.com> Date: Tue, 25 Aug 2026 16:55:25 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(backend)=20factorize=20s3=20?= =?UTF-8?q?client=20creation=20in=20utils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factorize s3 client creation in utils for code simplification. --- src/backend/core/utils.py | 67 ++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/src/backend/core/utils.py b/src/backend/core/utils.py index 058fa328..8e2b2bef 100644 --- a/src/backend/core/utils.py +++ b/src/backend/core/utils.py @@ -381,6 +381,31 @@ def detect_mimetype(file_buffer: bytes, filename: str | None = None) -> str: return mimetype_from_content or "application/octet-stream" +def _get_s3_client(*, override_domain: bool = True): + """Return an S3 client, honoring the AWS_S3_DOMAIN_REPLACE endpoint override. + + AWS_S3_DOMAIN_REPLACE is used when the backend and frontend reach object + storage under different domains (this is the case in the docker compose stack + used in development: the frontend connects to the object storage on localhost + while the backend uses the object storage service name declared in the stack). + The domain name is used to compute the signature, so it can't be changed + dynamically by the frontend; we build a dedicated boto3 client pointed at that + endpoint. Otherwise we reuse the default storage client. + """ + if settings.AWS_S3_DOMAIN_REPLACE and override_domain: + return boto3.client( + "s3", + aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID, + aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY, + endpoint_url=settings.AWS_S3_DOMAIN_REPLACE, + config=botocore.client.Config( + region_name=settings.AWS_S3_REGION_NAME, + signature_version=settings.AWS_S3_SIGNATURE_VERSION, + ), + ) + return default_storage.connection.meta.client + + def generate_upload_policy(file): """ Generate a S3 upload policy for a given file. @@ -391,26 +416,7 @@ def generate_upload_policy(file): key = file.temporary_file_key - # This settings should be used if the backend application and the frontend application - # can't connect to the object storage with the same domain. This is the case in the - # docker compose stack used in development. The frontend application will use localhost - # to connect to the object storage while the backend application will use the object storage - # service name declared in the docker compose stack. - # This is needed because the domain name is used to compute the signature. So it can't be - # changed dynamically by the frontend application. - if settings.AWS_S3_DOMAIN_REPLACE: - s3_client = boto3.client( - "s3", - aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID, - aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY, - endpoint_url=settings.AWS_S3_DOMAIN_REPLACE, - config=botocore.client.Config( - region_name=settings.AWS_S3_REGION_NAME, - signature_version=settings.AWS_S3_SIGNATURE_VERSION, - ), - ) - else: - s3_client = default_storage.connection.meta.client + s3_client = _get_s3_client() # Generate the policy policy = s3_client.generate_presigned_url( @@ -431,26 +437,7 @@ def generate_download_s3_url( if not key: raise ValueError("key cannot be empty") - # This setting should be used if the backend application and the frontend application - # can't connect to the object storage with the same domain. This is the case in the - # docker compose stack used in development. The frontend application will use localhost - # to connect to the object storage while the backend application will use the object storage - # service name declared in the docker compose stack. - # This is needed because the domain name is used to compute the signature. So it can't be - # changed dynamically by the frontend application. - if settings.AWS_S3_DOMAIN_REPLACE and override_domain: - s3_client = boto3.client( - "s3", - aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID, - aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY, - endpoint_url=settings.AWS_S3_DOMAIN_REPLACE, - config=botocore.client.Config( - region_name=settings.AWS_S3_REGION_NAME, - signature_version=settings.AWS_S3_SIGNATURE_VERSION, - ), - ) - else: - s3_client = default_storage.connection.meta.client + s3_client = _get_s3_client(override_domain=override_domain) return s3_client.generate_presigned_url( ClientMethod="get_object",