From 6f66254577bd5d55105af385b89368c1b838681e Mon Sep 17 00:00:00 2001 From: Giovanni Date: Mon, 5 Oct 2026 21:29:48 +0200 Subject: [PATCH] implement S3 garage on the backend --- .gitignore | 6 +++--- apps/backend/app/.env.example | 3 +++ apps/backend/app/main.py | 1 + apps/backend/app/routers/media.py | 23 +++++++++++++++++++++++ apps/backend/app/s3.py | 11 +++++++++++ apps/backend/requirements.txt | 25 ++++++++++++++++++++++--- 6 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 apps/backend/app/.env.example create mode 100644 apps/backend/app/routers/media.py create mode 100644 apps/backend/app/s3.py diff --git a/.gitignore b/.gitignore index 5aa044e..8c9fb30 100644 --- a/.gitignore +++ b/.gitignore @@ -27,9 +27,9 @@ ENV/ *.sqlite3 # Environnement & Secrets -.env -.env.* -!.env.example +apps/backend/app/.env +apps/backend/app/.env.example +!apps/backend/app/.env.example # Éditeurs & Système .idea/ diff --git a/apps/backend/app/.env.example b/apps/backend/app/.env.example new file mode 100644 index 0000000..2c8d032 --- /dev/null +++ b/apps/backend/app/.env.example @@ -0,0 +1,3 @@ +GARAGE_ENDPOINT_URL= +GARAGE_ACCESS_KEY= +GARAGE_SECRET_KEY= \ No newline at end of file diff --git a/apps/backend/app/main.py b/apps/backend/app/main.py index 9473e4d..fc2e215 100644 --- a/apps/backend/app/main.py +++ b/apps/backend/app/main.py @@ -11,3 +11,4 @@ app = FastAPI() app.include_router(projects.router, prefix="/api") app.include_router(experiences.router, prefix="/api") app.include_router(skills.router, prefix="/api") +app.include_router(media.router, prefix="/api") diff --git a/apps/backend/app/routers/media.py b/apps/backend/app/routers/media.py new file mode 100644 index 0000000..12c3190 --- /dev/null +++ b/apps/backend/app/routers/media.py @@ -0,0 +1,23 @@ +from botocore.exceptions import ClientError +from fastapi import APIRoute, APIRouter, HTTPException +from fastapi.responses import StreamingResponse + +from app.s3 import BUCKET_NAME, s3_client + +router = APIRouter(prefix="/media", tags=["media"]) + + +@router.get("/{file_path:path}") +async def get_media(file_path: str): + try: + s3_object = s3_client.get_object(Bucket=BUCKET_NAME, Key=file_path) + + return StreamingResponse( + content=s3_object["Body"].iter_chunks(), + media_type=s3_object.get("ContentType", "application/octet-stream"), + headers={"Cache-Control": "public, max-age=86400"}, + ) + except ClientError as e: + if e.response["Error"]["Code"] in ["NoSuchKey", "404"]: + raise HTTPException(status_code=404, detail="Fichier introuvable") + raise HTTPException(status_code=500, detail="Erreur serveur S3") \ No newline at end of file diff --git a/apps/backend/app/s3.py b/apps/backend/app/s3.py new file mode 100644 index 0000000..1cdcade --- /dev/null +++ b/apps/backend/app/s3.py @@ -0,0 +1,11 @@ +import os +import boto3 + +s3_client = boto3.client( + "s3", + endpoint_url=os.getenv("GARAGE_ENDPOINT_URL", "http://127.0.0.1:3900"), + aws_access_key_id=os.getenv("GARAGE_ACCESS_KEY"), + aws_secret_access_key=os.getenv("GARAGE_SECRET_KEY"), +) + +BUCKET_NAME = os.getenv("GARAGE_BUCKET_NAME", "nom-de-ton-bucket") \ No newline at end of file diff --git a/apps/backend/requirements.txt b/apps/backend/requirements.txt index 984613f..00d27cc 100644 --- a/apps/backend/requirements.txt +++ b/apps/backend/requirements.txt @@ -1,3 +1,22 @@ -fastapi -uvicorn[standard] -sqlalchemy \ No newline at end of file +annotated-doc==0.0.5 +annotated-types==0.8.0 +anyio==4.15.1 +boto3==1.43.108 +botocore==1.43.108 +click==8.5.0 +fastapi==0.142.2 +h11==0.16.0 +idna==3.20 +jmespath==1.1.0 +opentelemetry-api==1.45.0 +pydantic==2.13.5 +pydantic_core==2.46.5 +python-dateutil==2.9.0.post0 +s3transfer==0.19.2 +six==1.17.0 +SQLAlchemy==2.1.3 +starlette==1.7.0 +typing-inspection==0.4.4 +typing_extensions==4.16.0 +urllib3==2.8.0 +uvicorn==0.54.0