Files
FTCWebsite/object_storage.py
abhiramtx f4d32ff9ee Ensure uploaded images are permanently stored in object storage
Integrates Replit Object Storage for persistent file uploads, replacing ephemeral local storage and adding `google-cloud-storage` dependency.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: cd9a7d26-a4e5-4215-975c-c59f4ed1f06d
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 093bebfc-3b06-4716-8c6a-2dea6a89816d
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/d0a1d46d-d203-4308-bc6a-312ac7c0243b/cd9a7d26-a4e5-4215-975c-c59f4ed1f06d/D3TcT39
2025-11-13 05:36:58 +00:00

94 lines
3.1 KiB
Python

"""
Object Storage Service for FTC Team Website
Handles persistent file uploads using Google Cloud Storage (Replit Object Storage)
"""
from google.cloud import storage
import os
from uuid import uuid4
from werkzeug.utils import secure_filename
REPLIT_SIDECAR_ENDPOINT = "http://127.0.0.1:1106"
class ObjectStorageService:
def __init__(self):
"""Initialize the object storage client with Replit credentials"""
self.client = storage.Client(
credentials={
"audience": "replit",
"subject_token_type": "access_token",
"token_url": f"{REPLIT_SIDECAR_ENDPOINT}/token",
"type": "external_account",
"credential_source": {
"url": f"{REPLIT_SIDECAR_ENDPOINT}/credential",
"format": {
"type": "json",
"subject_token_field_name": "access_token",
},
},
"universe_domain": "googleapis.com",
},
project="",
)
# Get bucket name from environment variable
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."
)
self.bucket = self.client.bucket(self.bucket_name)
def upload_file(self, file, folder='uploads'):
"""
Upload a file to object storage
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
# Generate unique filename
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}"
# Upload to bucket
blob = self.bucket.blob(object_name)
blob.upload_from_file(
file.stream,
content_type=file.content_type or 'application/octet-stream'
)
# Make the blob publicly accessible
blob.make_public()
# Return the public URL
return blob.public_url
def get_blob(self, url):
"""Get a blob object from a URL"""
if not url or not url.startswith('https://storage.googleapis.com/'):
return None
# Extract blob name from URL
parts = url.replace(f'https://storage.googleapis.com/{self.bucket_name}/', '')
return self.bucket.blob(parts)
def delete_file(self, url):
"""Delete a file from object storage"""
blob = self.get_blob(url)
if blob and blob.exists():
blob.delete()
return True
return False