Files
FTCWebsite/object_storage.py
abhiramtx 4a0166b80e Update file uploads to use signed URLs from Replit's sidecar service
Rewrite object_storage.py to utilize the Replit sidecar endpoint for generating signed URLs for file uploads, replacing direct Google Cloud Storage client interactions.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: cd9a7d26-a4e5-4215-975c-c59f4ed1f06d
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 3bc03de1-efba-4e8b-802f-990f440ce8da
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/d0a1d46d-d203-4308-bc6a-312ac7c0243b/cd9a7d26-a4e5-4215-975c-c59f4ed1f06d/05bPjFc
2025-12-13 21:37:17 +00:00

90 lines
2.9 KiB
Python

"""
Object Storage Service for FTC Team Website
Handles persistent file uploads using Replit Object Storage (via sidecar signed URLs)
"""
import os
import requests
from uuid import uuid4
from werkzeug.utils import secure_filename
from datetime import datetime, timedelta
REPLIT_SIDECAR_ENDPOINT = "http://127.0.0.1:1106"
def get_signed_upload_url(bucket_name, object_name, ttl_sec=900):
"""Get a signed URL for uploading to object storage"""
request_data = {
"bucket_name": bucket_name,
"object_name": object_name,
"method": "PUT",
"expires_at": (datetime.utcnow() + timedelta(seconds=ttl_sec)).isoformat() + "Z"
}
response = requests.post(
f"{REPLIT_SIDECAR_ENDPOINT}/object-storage/signed-object-url",
json=request_data,
headers={"Content-Type": "application/json"}
)
if not response.ok:
raise Exception(f"Failed to get signed URL: {response.status_code} - {response.text}")
data = response.json()
return data.get("signed_url")
def get_public_url(bucket_name, object_name):
"""Get the public URL for an object"""
return f"https://storage.googleapis.com/{bucket_name}/{object_name}"
class ObjectStorageService:
def __init__(self):
"""Initialize the object storage service"""
self.bucket_name = os.environ.get('OBJECT_STORAGE_BUCKET')
if not self.bucket_name:
raise ValueError(
"OBJECT_STORAGE_BUCKET environment variable not set. "
"Please create a bucket in the Object Storage pane and set this variable."
)
def upload_file(self, file, folder='uploads'):
"""
Upload a file to object storage using signed URLs
Args:
file: Werkzeug FileStorage object
folder: Folder name in the bucket (default: 'uploads')
Returns:
str: Public URL of the uploaded file
"""
if not file or not file.filename:
return None
original_filename = secure_filename(file.filename)
file_extension = os.path.splitext(original_filename)[1]
unique_filename = f"{uuid4()}{file_extension}"
object_name = f"{folder}/{unique_filename}"
signed_url = get_signed_upload_url(self.bucket_name, object_name)
file_content = file.read()
content_type = file.content_type or 'application/octet-stream'
upload_response = requests.put(
signed_url,
data=file_content,
headers={"Content-Type": content_type}
)
if not upload_response.ok:
raise Exception(f"Upload failed: {upload_response.status_code} - {upload_response.text}")
return get_public_url(self.bucket_name, object_name)
def delete_file(self, url):
"""Delete a file from object storage (placeholder - not implemented)"""
return False