Skip to content

Commit 3d4b19d

Browse files
authored
Merge pull request #1 from breba-apps/use-cloudflare-s3
use cloudflare instead of gcp
2 parents 539df26 + 08eab9b commit 3d4b19d

File tree

2 files changed

+32
-12
lines changed

2 files changed

+32
-12
lines changed

breba-proxy/main.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
11
import logging
22
import os
33

4+
import boto3
5+
from botocore.config import Config
6+
from botocore.exceptions import ClientError
47
from dotenv import load_dotenv
58
from fastapi import FastAPI, Request, HTTPException, Response
6-
from google.cloud import storage
79

810
logging.basicConfig(level=logging.INFO)
911
logger = logging.getLogger(__name__)
1012

1113
load_dotenv()
1214

1315
PUBLIC_BUCKET = os.getenv("PUBLIC_BUCKET")
16+
CLOUDFLARE_ENDPOINT = os.getenv("CLOUDFLARE_ENDPOINT")
17+
1418
if not PUBLIC_BUCKET:
1519
raise RuntimeError("Missing PUBLIC_BUCKET environment variable")
20+
if not CLOUDFLARE_ENDPOINT:
21+
raise RuntimeError("Missing CLOUDFLARE_ENDPOINT environment variable")
22+
23+
# R2 uses "auto" region; SigV4 works. Some setups prefer 's3v4' explicit signature.
24+
boto_cfg = Config(
25+
region_name="auto",
26+
retries={"max_attempts": 3, "mode": "standard"},
27+
s3={"addressing_style": "virtual"}
28+
)
29+
30+
session = boto3.session.Session()
31+
s3_client = session.client(
32+
service_name="s3",
33+
endpoint_url=CLOUDFLARE_ENDPOINT,
34+
config=boto_cfg,
35+
)
1636

17-
storage_client = storage.Client()
18-
bucket = storage_client.bucket(PUBLIC_BUCKET)
1937

2038
app = FastAPI()
2139

@@ -45,16 +63,18 @@ async def serve_file(request: Request, path: str = ""):
4563
if not subdomain:
4664
raise HTTPException(status_code=400, detail="Missing subdomain")
4765

48-
blob_path = f"{subdomain}/{path or 'index.html'}"
49-
blob = bucket.blob(blob_path)
50-
51-
if not blob.exists():
52-
raise HTTPException(status_code=404, detail="File not found")
66+
key = f"{subdomain}/{path or 'index.html'}"
5367

54-
blob.reload()
68+
try:
69+
obj = s3_client.get_object(Bucket=PUBLIC_BUCKET, Key=key)
70+
except ClientError as e:
71+
code = e.response.get("Error", {}).get("Code", "")
72+
if code in ("NoSuchKey", "NotFound", "404"):
73+
raise HTTPException(status_code=404, detail="File not found")
74+
raise HTTPException(status_code=502, detail="Storage backend error")
5575

56-
content_type = blob.content_type or "application/octet-stream"
57-
data = blob.download_as_bytes()
76+
data = obj["Body"].read()
77+
content_type = obj.get("ContentType") or "application/octet-stream"
5878

5979
return Response(content=data, media_type=content_type)
6080

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ license = { text = "MIT" }
1111
dependencies = [
1212
"fastapi",
1313
"uvicorn",
14-
"google-cloud-storage",
1514
"python-dotenv>=1.1.0",
15+
"boto3>=1.40.27",
1616
]
1717

1818
[tool.uv]

0 commit comments

Comments
 (0)