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)}")