Spaces:
Running
Running
| from appwrite.client import Client | |
| from appwrite.services.storage import Storage | |
| from appwrite.input_file import InputFile | |
| from appwrite.exception import AppwriteException | |
| from fastapi import UploadFile, HTTPException | |
| from datetime import datetime, timedelta | |
| import uuid | |
| import os | |
| from typing import Dict, Any | |
| from ..config import settings | |
| from ..utils.validators import validate_file_extension, validate_file_size | |
| import logging | |
| class StorageService: | |
| def __init__(self): | |
| try: | |
| self.client = Client() | |
| self.client.set_endpoint(settings.APPWRITE_ENDPOINT) | |
| self.client.set_project(settings.APPWRITE_PROJECT_ID) | |
| self.client.set_key(settings.APPWRITE_API_KEY) | |
| self.storage = Storage(self.client) | |
| logging.info("StorageService initialized successfully") | |
| except Exception as e: | |
| logging.error(f"Failed to initialize storage service: {str(e)}") | |
| raise HTTPException(status_code=500, detail=f"Failed to initialize storage service: {str(e)}") | |
| def _get_expiration_seconds(self, expiration_time: str) -> int: | |
| expiration_map = { | |
| "30s": 30, | |
| "24h": 86400, | |
| "3d": 259200, | |
| "5d": 432000, | |
| "7d": 604800 | |
| } | |
| return expiration_map.get(expiration_time, 86400) | |
| async def upload_file(self, file: UploadFile, expiration_time: str) -> Dict[str, Any]: | |
| try: | |
| validate_file_extension(file.filename) | |
| contents = await file.read() | |
| validate_file_size(len(contents)) | |
| _, ext = os.path.splitext(file.filename) | |
| file_id = f"{uuid.uuid4().hex[:8]}{ext}" | |
| expiration_seconds = self._get_expiration_seconds(expiration_time) | |
| expiration = datetime.now() + timedelta(seconds=expiration_seconds) | |
| result = self.storage.create_file( | |
| bucket_id=settings.APPWRITE_BUCKET_ID, | |
| file_id=file_id, | |
| file=InputFile.from_bytes(contents, file.filename), | |
| permissions=['read("any")'] | |
| ) | |
| new_file_name = f"{file_id}__exp_{expiration.isoformat()}" | |
| self.storage.update_file( | |
| bucket_id=settings.APPWRITE_BUCKET_ID, | |
| file_id=file_id, | |
| name=new_file_name, | |
| permissions=['read("any")'] | |
| ) | |
| url = f"{settings.APPWRITE_ENDPOINT}/storage/buckets/{settings.APPWRITE_BUCKET_ID}/files/{file_id}/view?project={settings.APPWRITE_PROJECT_ID}" | |
| return { | |
| 'file_id': file_id, | |
| 'url': url, | |
| 'expiration': expiration.isoformat(), | |
| 'original_name': file.filename | |
| } | |
| except HTTPException as e: | |
| raise e | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}") | |
| finally: | |
| await file.seek(0) | |
| async def cleanup_expired_files(self): | |
| logging.info("Starting cleanup of expired files") | |
| try: | |
| files = self.storage.list_files(settings.APPWRITE_BUCKET_ID) | |
| logging.info(f"Found {len(files['files'])} files in the bucket") | |
| now = datetime.now() | |
| for file in files['files']: | |
| try: | |
| file_name = file['name'] | |
| file_id = file['$id'] | |
| logging.info(f"Processing file: {file_id} - {file_name}") | |
| if '__exp_' in file_name: | |
| _, exp_str = file_name.split('__exp_') | |
| expiration = datetime.fromisoformat(exp_str) | |
| logging.info(f"File expiration: {expiration}, Current time: {now}") | |
| if now > expiration: | |
| logging.info(f"Deleting expired file: {file_id}") | |
| self.storage.delete_file( | |
| bucket_id=settings.APPWRITE_BUCKET_ID, | |
| file_id=file_id | |
| ) | |
| logging.info(f"Successfully deleted expired file: {file_id}") | |
| else: | |
| logging.info(f"File {file_id} not yet expired") | |
| else: | |
| logging.warning(f"File {file_id} does not have expiration info in its name") | |
| except Exception as e: | |
| logging.error(f"Error processing file {file_id}: {str(e)}") | |
| continue | |
| logging.info("Cleanup process completed") | |
| except AppwriteException as e: | |
| logging.error(f"Appwrite error during cleanup: {str(e)}") | |
| except Exception as e: | |
| logging.error(f"Unexpected error during cleanup: {str(e)}") |