File size: 4,622 Bytes
b36cb8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
120
121
122
123
124
125
"""
Image Similarity Search API with FastAPI and Qdrant - Fixed Access
This application provides endpoints for uploading images and searching for similar images
using vector embeddings from the CLIP model. Implemented using OOP principles.
"""

import uvicorn # type: ignore
from fastapi import FastAPI # type: ignore
from contextlib import asynccontextmanager
import os
import ssl
from fastapi.middleware.cors import CORSMiddleware # type: ignore

from config import Config
from services.embedding_service import ImageEmbeddingModel
from services.vector_db_service import VectorDatabaseClient
from api.routes import register_routes


@asynccontextmanager
async def lifespan(app: FastAPI):
    """Lifespan context manager for FastAPI application startup and shutdown events"""
    # This runs before the application starts
    vector_db = app.state.vector_db
    vector_db.ensure_collection_exists()
    
    yield  # This yields control back to FastAPI
    
    # This runs when the application is shutting down
    # Cleanup code can go here if needed


class ImageSimilarityAPI:
    """Main application class that orchestrates all components"""
    
    def __init__(self):
        # Initialize config
        self.config = Config()
        
        # Initialize components
        self.embedding_model = ImageEmbeddingModel(self.config.model_name)
        self.vector_db = VectorDatabaseClient(
            self.config.qdrant_url, 
            self.config.qdrant_api_key,
            self.config.collection_name,
            self.config.embedding_size
        )
        
        # Initialize FastAPI app with lifespan handler
        self.app = FastAPI(
            title="Image Similarity Search API",
            description="API for uploading images and searching for similar images using CLIP embeddings",
            version="1.0.0",
            lifespan=lifespan
        )
        
         # ✅ Enable CORS to allow mobile access
        self.app.add_middleware(
            CORSMiddleware,
            allow_origins=["*"],  # Or set to ["http://192.168.1.42"] for better security
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )
        # Store vector_db in app state for use in lifespan
        self.app.state.vector_db = self.vector_db
        
        # Register routes
        register_routes(self.app, self.embedding_model, self.vector_db)
    
    def run(self, use_https=False, cert_file="./certs/cert.pem", key_file="./certs/key.pem"):
        """Run the FastAPI application with optional HTTPS support
        
        Args:
            use_https: Whether to use HTTPS or plain HTTP   
            cert_file: Path to SSL certificate file
            key_file: Path to SSL private key file
        """
        host = "0.0.0.0"  # Use localhost instead of 0.0.0.0 for better access
        port = 8000 if not use_https else 8443
        
        ssl_context = None
        if use_https:
            # Check if certificate files exist
            if not os.path.exists(cert_file) or not os.path.exists(key_file):
                print(f"ERROR: SSL certificate files not found at {cert_file} and/or {key_file}")
                print("Falling back to HTTP. To use HTTPS, please provide valid certificate files.")
                use_https = False
            else:
                # Create SSL context for HTTPS
                ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
                ssl_context.load_cert_chain(cert_file, key_file)
        
        # Print access URLs for convenience
        protocol = "https" if use_https else "http"
        print(f"\n{'='*50}")
        print(f"Access the API at: {protocol}://{host}:{port}")
        print(f"Swagger UI available at: {protocol}://{host}:{port}/docs")
        print(f"ReDoc UI available at: {protocol}://{host}:{port}/redoc")
        print(f"{'='*50}\n")
        
        uvicorn.run(
            self.app, 
            host=host,
            port=port,
            reload=self.config.environment == "development",
            ssl_certfile=cert_file if use_https else None,
            ssl_keyfile=key_file if use_https else None
        )


def create_app() -> FastAPI:
    """Create and return the FastAPI application"""
    api = ImageSimilarityAPI()
    return api.app


if __name__ == "__main__":
    api = ImageSimilarityAPI()
    # Set to False for now until certificates are properly set up
    api.run(
        use_https=False,  # Change to True when certificates are ready
        cert_file="./certs/cert.pem",
        key_file="./certs/key.pem"
    )