Add persistent file storage and improve error handling

Refactor object storage initialization to use a dedicated function and add error handling for uploads, including a fallback to local storage.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: cd9a7d26-a4e5-4215-975c-c59f4ed1f06d
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 127466ff-4d39-4e2e-bc28-80d552851c25
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/d0a1d46d-d203-4308-bc6a-312ac7c0243b/cd9a7d26-a4e5-4215-975c-c59f4ed1f06d/05bPjFc
This commit is contained in:
abhiramtx
2025-12-13 21:34:03 +00:00
parent 34a7a59899
commit 6a8eb826a6
3 changed files with 40 additions and 27 deletions

View File

@@ -12,10 +12,6 @@ deploymentTarget = "cloudrun"
localPort = 5000 localPort = 5000
externalPort = 80 externalPort = 80
[[ports]]
localPort = 37833
externalPort = 3000
[agent] [agent]
expertMode = true expertMode = true

18
app.py
View File

@@ -17,11 +17,16 @@ ADMIN_PASSWORD = 'techturb123'
try: try:
storage_service = ObjectStorageService() storage_service = ObjectStorageService()
USING_OBJECT_STORAGE = True USING_OBJECT_STORAGE = True
print(f"Object Storage initialized successfully - using bucket: {storage_service.bucket_name}")
except ValueError as e: except ValueError as e:
print(f"Warning: Object Storage not configured - {e}") print(f"Warning: Object Storage not configured - {e}")
print("Files will be saved locally (ephemeral storage)") print("Files will be saved locally (ephemeral storage)")
storage_service = None storage_service = None
USING_OBJECT_STORAGE = False USING_OBJECT_STORAGE = False
except Exception as e:
print(f"Error initializing Object Storage: {e}")
storage_service = None
USING_OBJECT_STORAGE = False
def get_db_connection(): def get_db_connection():
conn = psycopg2.connect(os.environ['DATABASE_URL']) conn = psycopg2.connect(os.environ['DATABASE_URL'])
@@ -36,10 +41,17 @@ def upload_file_to_storage(file, folder='uploads'):
return None return None
if USING_OBJECT_STORAGE and storage_service: if USING_OBJECT_STORAGE and storage_service:
# Upload to persistent object storage try:
return storage_service.upload_file(file, folder) result = storage_service.upload_file(file, folder)
print(f"File uploaded to object storage: {result}")
return result
except Exception as e:
print(f"Error uploading to object storage: {e}")
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
return f'images/{filename}'
else: else:
# Fallback to local storage (ephemeral)
filename = secure_filename(file.filename) filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath) file.save(filepath)

View File

@@ -4,35 +4,45 @@ Handles persistent file uploads using Google Cloud Storage (Replit Object Storag
""" """
from google.cloud import storage from google.cloud import storage
from google.auth import identity_pool
import os import os
import json
from uuid import uuid4 from uuid import uuid4
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
REPLIT_SIDECAR_ENDPOINT = "http://127.0.0.1:1106" 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: class ObjectStorageService:
def __init__(self): def __init__(self):
"""Initialize the object storage client with Replit credentials""" """Initialize the object storage client with Replit credentials"""
credentials = get_replit_credentials()
self.client = storage.Client( self.client = storage.Client(
credentials={ credentials=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="", project="",
) )
# Get bucket name from environment variable
self.bucket_name = os.environ.get('OBJECT_STORAGE_BUCKET') self.bucket_name = os.environ.get('OBJECT_STORAGE_BUCKET')
if not self.bucket_name: if not self.bucket_name:
raise ValueError( raise ValueError(
@@ -56,23 +66,19 @@ class ObjectStorageService:
if not file or not file.filename: if not file or not file.filename:
return None return None
# Generate unique filename
original_filename = secure_filename(file.filename) original_filename = secure_filename(file.filename)
file_extension = os.path.splitext(original_filename)[1] file_extension = os.path.splitext(original_filename)[1]
unique_filename = f"{uuid4()}{file_extension}" unique_filename = f"{uuid4()}{file_extension}"
object_name = f"{folder}/{unique_filename}" object_name = f"{folder}/{unique_filename}"
# Upload to bucket
blob = self.bucket.blob(object_name) blob = self.bucket.blob(object_name)
blob.upload_from_file( blob.upload_from_file(
file.stream, file.stream,
content_type=file.content_type or 'application/octet-stream' content_type=file.content_type or 'application/octet-stream'
) )
# Make the blob publicly accessible
blob.make_public() blob.make_public()
# Return the public URL
return blob.public_url return blob.public_url
def get_blob(self, url): def get_blob(self, url):
@@ -80,7 +86,6 @@ class ObjectStorageService:
if not url or not url.startswith('https://storage.googleapis.com/'): if not url or not url.startswith('https://storage.googleapis.com/'):
return None return None
# Extract blob name from URL
parts = url.replace(f'https://storage.googleapis.com/{self.bucket_name}/', '') parts = url.replace(f'https://storage.googleapis.com/{self.bucket_name}/', '')
return self.bucket.blob(parts) return self.bucket.blob(parts)