garvitcpp commited on
Commit
75c68ee
·
verified ·
1 Parent(s): 2dfa62e

Upload 22 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+
3
+ WORKDIR /code
4
+
5
+ COPY ./requirements.txt /code/requirements.txt
6
+
7
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
+
9
+ COPY ./app /code/app
10
+ COPY ./app.py /code/app.py
11
+
12
+ CMD ["python", "app.py"]
app/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+
4
+ def create_app() -> FastAPI:
5
+ app = FastAPI(title="File Sharing API")
6
+
7
+ app.add_middleware(
8
+ CORSMiddleware,
9
+ allow_origins=[""],
10
+ allow_credentials=True,
11
+ allow_methods=[""],
12
+ allow_headers=["*"],
13
+ )
14
+
15
+ return app
app/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (756 Bytes). View file
 
app/__pycache__/config.cpython-311.pyc ADDED
Binary file (1.23 kB). View file
 
app/__pycache__/database.cpython-311.pyc ADDED
Binary file (736 Bytes). View file
 
app/__pycache__/main.cpython-311.pyc ADDED
Binary file (1.54 kB). View file
 
app/config.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic_settings import BaseSettings
2
+ from functools import lru_cache
3
+
4
+ class Settings(BaseSettings):
5
+ APPWRITE_ENDPOINT: str
6
+ APPWRITE_PROJECT_ID: str
7
+ APPWRITE_API_KEY: str
8
+ APPWRITE_BUCKET_ID: str
9
+
10
+ class Config:
11
+ env_file = ".env"
12
+
13
+ @lru_cache()
14
+ def get_settings() -> Settings:
15
+ return Settings()
16
+
17
+ settings = get_settings()
app/main.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from apscheduler.schedulers.asyncio import AsyncIOScheduler
3
+ from .routes import file_routes
4
+ from . import create_app
5
+ from .services.storage_service import StorageService
6
+
7
+ app = create_app()
8
+ storage_service = StorageService()
9
+
10
+ # Register routes
11
+ app.include_router(file_routes.router, prefix="/api")
12
+
13
+ # Set up scheduler for cleanup
14
+ scheduler = AsyncIOScheduler()
15
+ scheduler.add_job(storage_service.cleanup_expired_files, 'interval', minutes=1) # Run every 5 minutes
16
+ scheduler.start()
17
+
18
+ @app.get("/")
19
+ async def root():
20
+ return {"message": "File Sharing API with auto-cleanup"}
21
+
22
+ # Handle shutdown
23
+ @app.on_event("shutdown")
24
+ async def shutdown_event():
25
+ scheduler.shutdown()
app/routes/__init__.py ADDED
File without changes
app/routes/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (189 Bytes). View file
 
app/routes/__pycache__/file_routes.cpython-311.pyc ADDED
Binary file (1.65 kB). View file
 
app/routes/file_routes.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, File, UploadFile, Query
2
+ from fastapi.responses import JSONResponse
3
+ from enum import Enum
4
+ from ..services.storage_service import StorageService
5
+
6
+ router = APIRouter()
7
+ storage_service = StorageService()
8
+
9
+ class ExpirationTime(str, Enum):
10
+ THIRTY_SECONDS = "30s"
11
+ ONE_DAY = "24h"
12
+ THREE_DAYS = "3d"
13
+ FIVE_DAYS = "5d"
14
+ ONE_WEEK = "7d"
15
+
16
+ @router.post("/upload")
17
+ async def upload_file(
18
+ file: UploadFile = File(...),
19
+ expiration: ExpirationTime = Query(..., description="File expiration time")
20
+ ):
21
+ result = await storage_service.upload_file(file, expiration)
22
+ return JSONResponse(
23
+ content={
24
+ 'message': 'File uploaded successfully',
25
+ 'data': result
26
+ },
27
+ status_code=200
28
+ )
app/services/__init__.py ADDED
File without changes
app/services/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (191 Bytes). View file
 
app/services/__pycache__/appwrite_service.cpython-311.pyc ADDED
Binary file (3.02 kB). View file
 
app/services/__pycache__/storage_service.cpython-311.pyc ADDED
Binary file (7.13 kB). View file
 
app/services/storage_service.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from appwrite.client import Client
2
+ from appwrite.services.storage import Storage
3
+ from appwrite.input_file import InputFile
4
+ from appwrite.exception import AppwriteException
5
+ from fastapi import UploadFile, HTTPException
6
+ from datetime import datetime, timedelta
7
+ import uuid
8
+ import os
9
+ from typing import Dict, Any
10
+ from ..config import settings
11
+ from ..utils.validators import validate_file_extension, validate_file_size
12
+
13
+ class StorageService:
14
+ def __init__(self):
15
+ try:
16
+ self.client = Client()
17
+ self.client.set_endpoint(settings.APPWRITE_ENDPOINT)
18
+ self.client.set_project(settings.APPWRITE_PROJECT_ID)
19
+ self.client.set_key(settings.APPWRITE_API_KEY)
20
+ self.storage = Storage(self.client)
21
+ except Exception as e:
22
+ raise HTTPException(
23
+ status_code=500,
24
+ detail=f"Failed to initialize storage service: {str(e)}"
25
+ )
26
+
27
+ def _get_expiration_seconds(self, expiration_time: str) -> int:
28
+ expiration_map = {
29
+ "30s": 30,
30
+ "24h": 86400,
31
+ "3d": 259200,
32
+ "5d": 432000,
33
+ "7d": 604800
34
+ }
35
+ return expiration_map.get(expiration_time, 86400)
36
+
37
+ async def upload_file(self, file: UploadFile, expiration_time: str) -> Dict[str, Any]:
38
+ try:
39
+ validate_file_extension(file.filename)
40
+ contents = await file.read()
41
+ validate_file_size(len(contents))
42
+
43
+ _, ext = os.path.splitext(file.filename)
44
+ file_id = f"{uuid.uuid4().hex[:8]}{ext}"
45
+
46
+ expiration_seconds = self._get_expiration_seconds(expiration_time)
47
+ expiration = datetime.now() + timedelta(seconds=expiration_seconds)
48
+
49
+ result = self.storage.create_file(
50
+ bucket_id=settings.APPWRITE_BUCKET_ID,
51
+ file_id=file_id,
52
+ file=InputFile.from_bytes(contents, file.filename),
53
+ permissions=['read("any")']
54
+ )
55
+
56
+ new_file_name = f"{file_id}__exp_{expiration.isoformat()}"
57
+ self.storage.update_file(
58
+ bucket_id=settings.APPWRITE_BUCKET_ID,
59
+ file_id=file_id,
60
+ name=new_file_name,
61
+ permissions=['read("any")']
62
+ )
63
+
64
+ url = f"{settings.APPWRITE_ENDPOINT}/storage/buckets/{settings.APPWRITE_BUCKET_ID}/files/{file_id}/view?project={settings.APPWRITE_PROJECT_ID}"
65
+
66
+ return {
67
+ 'file_id': file_id,
68
+ 'url': url,
69
+ 'expiration': expiration.isoformat(),
70
+ 'original_name': file.filename
71
+ }
72
+
73
+ except HTTPException as e:
74
+ raise e
75
+ except Exception as e:
76
+ raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}")
77
+ finally:
78
+ await file.seek(0)
79
+
80
+ async def cleanup_expired_files(self):
81
+ try:
82
+ files = self.storage.list_files(settings.APPWRITE_BUCKET_ID)
83
+ now = datetime.now()
84
+
85
+ for file in files['files']:
86
+ try:
87
+ file_name = file['name']
88
+ if '__exp_' in file_name:
89
+ _, exp_str = file_name.split('__exp_')
90
+ expiration = datetime.fromisoformat(exp_str)
91
+ if now > expiration:
92
+ self.storage.delete_file(
93
+ bucket_id=settings.APPWRITE_BUCKET_ID,
94
+ file_id=file['$id']
95
+ )
96
+ print(f"Deleted expired file: {file['$id']}")
97
+ except Exception as e:
98
+ print(f"Error processing file {file['$id']}: {str(e)}")
99
+ continue
100
+
101
+ except Exception as e:
102
+ print(f"Error during cleanup: {str(e)}")
103
+
104
+ async def get_file_info(self, file_id: str) -> Dict[str, Any]:
105
+ try:
106
+ file = self.storage.get_file(
107
+ bucket_id=settings.APPWRITE_BUCKET_ID,
108
+ file_id=file_id
109
+ )
110
+ return file
111
+ except AppwriteException as e:
112
+ raise HTTPException(status_code=404, detail="File not found")
app/utils/__init__.py ADDED
File without changes
app/utils/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (188 Bytes). View file
 
app/utils/__pycache__/url_shortener.cpython-311.pyc ADDED
Binary file (2.3 kB). View file
 
app/utils/__pycache__/validators.cpython-311.pyc ADDED
Binary file (1.51 kB). View file
 
app/utils/validators.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import HTTPException
2
+ from typing import List
3
+
4
+ ALLOWED_EXTENSIONS = {'.pdf', '.doc', '.docx', '.txt', '.jpg', '.jpeg', '.png', '.gif', '.mp3', '.mp4', '.mov', '.zip', '.rar'}
5
+ MAX_FILE_SIZE = 50 * 1024 * 1024 # 10MB
6
+
7
+ def validate_file_extension(filename: str) -> bool:
8
+ import os
9
+ ext = os.path.splitext(filename)[1].lower()
10
+ if ext not in ALLOWED_EXTENSIONS:
11
+ raise HTTPException(
12
+ status_code=400,
13
+ detail=f"File extension not allowed. Allowed extensions: {', '.join(ALLOWED_EXTENSIONS)}"
14
+ )
15
+ return True
16
+
17
+ def validate_file_size(file_size: int) -> bool:
18
+ if file_size > MAX_FILE_SIZE:
19
+ raise HTTPException(
20
+ status_code=400,
21
+ detail=f"File too large. Maximum size allowed: {MAX_FILE_SIZE/1024/1024}MB"
22
+ )
23
+ return True