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
94 lines
3.1 KiB
Python
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
|