Spaces:
Running
Running
File size: 4,962 Bytes
29116ca 5b35049 29116ca | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 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)}") |