""" Object Storage Service for FTC Team Website Handles persistent file uploads using Google Cloud Storage (Replit Object Storage) """ from google.cloud import storage from google.auth import identity_pool import os import json from uuid import uuid4 from werkzeug.utils import secure_filename REPLIT_SIDECAR_ENDPOINT = "http://127.0.0.1:1106" def get_replit_credentials(): """Create proper credentials for Replit Object Storage""" credentials_config = { "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", } credentials = identity_pool.Credentials.from_info(credentials_config) return credentials class ObjectStorageService: def __init__(self): """Initialize the object storage client with Replit credentials""" credentials = get_replit_credentials() self.client = storage.Client( credentials=credentials, project="", ) 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 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}" blob = self.bucket.blob(object_name) blob.upload_from_file( file.stream, content_type=file.content_type or 'application/octet-stream' ) blob.make_public() 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 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