This commit is contained in:
2026-05-05 16:52:40 +02:00
commit bdb523d4b8
58 changed files with 8880 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
import uuid
import boto3
from botocore.client import Config
from botocore.exceptions import ClientError
from app.core.config import settings
def _client():
return boto3.client(
"s3",
endpoint_url=settings.S3_ENDPOINT_URL,
aws_access_key_id=settings.S3_ACCESS_KEY,
aws_secret_access_key=settings.S3_SECRET_KEY,
region_name=settings.S3_REGION,
config=Config(signature_version="s3v4"),
)
def ensure_bucket() -> None:
s3 = _client()
try:
s3.head_bucket(Bucket=settings.S3_BUCKET)
except ClientError:
s3.create_bucket(Bucket=settings.S3_BUCKET)
# Make objects publicly readable so the frontend can <img src=...> directly.
policy = (
'{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},'
'"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::'
+ settings.S3_BUCKET
+ '/*"]}]}'
)
try:
s3.put_bucket_policy(Bucket=settings.S3_BUCKET, Policy=policy)
except ClientError:
pass
def upload_image(file_bytes: bytes, filename: str, content_type: str) -> tuple[str, str]:
"""Upload bytes to S3, return (object_key, public_url)."""
ext = ""
if "." in filename:
ext = "." + filename.rsplit(".", 1)[-1].lower()
key = f"characters/{uuid.uuid4().hex}{ext}"
s3 = _client()
s3.put_object(
Bucket=settings.S3_BUCKET,
Key=key,
Body=file_bytes,
ContentType=content_type or "application/octet-stream",
)
public_url = f"{settings.S3_PUBLIC_URL.rstrip('/')}/{settings.S3_BUCKET}/{key}"
return key, public_url
def delete_object_by_url(url: str) -> None:
prefix = f"{settings.S3_PUBLIC_URL.rstrip('/')}/{settings.S3_BUCKET}/"
if not url.startswith(prefix):
return
key = url[len(prefix):]
try:
_client().delete_object(Bucket=settings.S3_BUCKET, Key=key)
except ClientError:
pass