folder restructure

This commit is contained in:
johnpccd 2025-05-24 12:06:57 +02:00
parent d8a6cffcb4
commit e863a333bb
47 changed files with 159 additions and 159 deletions

View File

@ -9,8 +9,8 @@ from fastapi.openapi.utils import get_openapi
from src.api.v1 import teams, users, images, auth, search from src.api.v1 import teams, users, images, auth, search
# Import configuration and database # Import configuration and database
from src.core.config import settings from src.config.config import settings
from src.core.logging import setup_logging from src.utils.logging import setup_logging
from src.db import db from src.db import db
from src.db.providers.firestore_provider import firestore_db from src.db.providers.firestore_provider import firestore_db
from src.db.repositories import init_repositories from src.db.repositories import init_repositories

View File

@ -13,12 +13,12 @@ from src.db.repositories.user_repository import user_repository
from src.db.repositories.api_key_repository import api_key_repository from src.db.repositories.api_key_repository import api_key_repository
# Import models # Import models
from src.db.models.team import TeamModel from src.models.team import TeamModel
from src.db.models.user import UserModel from src.models.user import UserModel
from src.db.models.api_key import ApiKeyModel from src.models.api_key import ApiKeyModel
# Import security functions # Import security functions
from src.core.security import generate_api_key, calculate_expiry_date from src.auth.security import generate_api_key, calculate_expiry_date
async def create_admin(): async def create_admin():
# Create a new team # Create a new team

View File

@ -9,7 +9,7 @@ import string
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Get the API key secret from environment # Get the API key secret from environment
from src.core.config import settings from src.config.config import settings
def generate_api_key(team_id="dev-team", user_id="dev-admin"): def generate_api_key(team_id="dev-team", user_id="dev-admin"):
""" """

View File

@ -22,16 +22,16 @@ load_dotenv()
# Add the parent directory to the path so we can import from src # Add the parent directory to the path so we can import from src
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from src.db.models.team import TeamModel from src.models.team import TeamModel
from src.db.models.user import UserModel from src.models.user import UserModel
from src.db.models.api_key import ApiKeyModel from src.models.api_key import ApiKeyModel
from src.db.models.image import ImageModel from src.models.image import ImageModel
from src.db.providers.firestore_provider import firestore_db from src.db.providers.firestore_provider import firestore_db
from src.db.repositories.firestore_team_repository import firestore_team_repository from src.db.repositories.firestore_team_repository import firestore_team_repository
from src.db.repositories.firestore_user_repository import firestore_user_repository from src.db.repositories.firestore_user_repository import firestore_user_repository
from src.db.repositories.firestore_api_key_repository import firestore_api_key_repository from src.db.repositories.firestore_api_key_repository import firestore_api_key_repository
from src.db.repositories.firestore_image_repository import firestore_image_repository from src.db.repositories.firestore_image_repository import firestore_image_repository
from src.core.security import hash_api_key as app_hash_api_key from src.auth.security import hash_api_key as app_hash_api_key
# Configure logging # Configure logging
logging.basicConfig( logging.basicConfig(

View File

@ -21,7 +21,7 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from src.db.providers.firestore_provider import firestore_db from src.db.providers.firestore_provider import firestore_db
from src.db.repositories.firestore_api_key_repository import firestore_api_key_repository from src.db.repositories.firestore_api_key_repository import firestore_api_key_repository
from src.db.repositories.firestore_user_repository import firestore_user_repository from src.db.repositories.firestore_user_repository import firestore_user_repository
from src.core.security import hash_api_key, verify_api_key from src.auth.security import hash_api_key, verify_api_key
# Configure logging # Configure logging
logging.basicConfig( logging.basicConfig(

View File

@ -8,9 +8,9 @@ from src.db.repositories.api_key_repository import api_key_repository
from src.db.repositories.user_repository import user_repository from src.db.repositories.user_repository import user_repository
from src.db.repositories.team_repository import team_repository from src.db.repositories.team_repository import team_repository
from src.schemas.api_key import ApiKeyCreate, ApiKeyResponse, ApiKeyWithValueResponse, ApiKeyListResponse from src.schemas.api_key import ApiKeyCreate, ApiKeyResponse, ApiKeyWithValueResponse, ApiKeyListResponse
from src.core.security import generate_api_key, verify_api_key, calculate_expiry_date, is_expired, hash_api_key from src.auth.security import generate_api_key, verify_api_key, calculate_expiry_date, is_expired, hash_api_key
from src.db.models.api_key import ApiKeyModel from src.models.api_key import ApiKeyModel
from src.core.logging import log_request from src.utils.logging import log_request
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -4,9 +4,9 @@ from bson import ObjectId
from src.db.repositories.team_repository import team_repository from src.db.repositories.team_repository import team_repository
from src.schemas.team import TeamCreate, TeamUpdate, TeamResponse, TeamListResponse from src.schemas.team import TeamCreate, TeamUpdate, TeamResponse, TeamListResponse
from src.db.models.team import TeamModel from src.models.team import TeamModel
from src.api.v1.auth import get_current_user from src.api.v1.auth import get_current_user
from src.core.logging import log_request from src.utils.logging import log_request
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -6,7 +6,7 @@ import hashlib
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Optional, Tuple from typing import Optional, Tuple
from src.core.config import settings from src.config.config import settings
def generate_api_key(team_id: str, user_id: str) -> Tuple[str, str]: def generate_api_key(team_id: str, user_id: str) -> Tuple[str, str]:
""" """

0
src/config/__init__.py Normal file
View File

View File

@ -1,100 +1,100 @@
import logging import logging
import os import os
import json import json
from google.cloud import firestore from google.cloud import firestore
from google.oauth2 import service_account from google.oauth2 import service_account
from src.core.config import settings from src.config.config import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Database: class Database:
client = None client = None
database_name = None database_name = None
def connect_to_database(self): def connect_to_database(self):
"""Create database connection.""" """Create database connection."""
try: try:
# Store database name # Store database name
self.database_name = settings.FIRESTORE_DATABASE_NAME self.database_name = settings.FIRESTORE_DATABASE_NAME
# Print project ID and database name for debugging # Print project ID and database name for debugging
logger.info(f"Attempting to connect to Firestore with project ID: {settings.FIRESTORE_PROJECT_ID}, database: {self.database_name}") logger.info(f"Attempting to connect to Firestore with project ID: {settings.FIRESTORE_PROJECT_ID}, database: {self.database_name}")
# First try using Application Default Credentials (for Cloud environments) # First try using Application Default Credentials (for Cloud environments)
try: try:
logger.info("Attempting to connect using Application Default Credentials") logger.info("Attempting to connect using Application Default Credentials")
self.client = firestore.Client( self.client = firestore.Client(
project=settings.FIRESTORE_PROJECT_ID, project=settings.FIRESTORE_PROJECT_ID,
database=self.database_name database=self.database_name
) )
# Test connection by trying to access a collection # Test connection by trying to access a collection
self.client.collection('test').limit(1).get() self.client.collection('test').limit(1).get()
logger.info(f"Connected to Firestore project using Application Default Credentials: {settings.FIRESTORE_PROJECT_ID}, database: {self.database_name}") logger.info(f"Connected to Firestore project using Application Default Credentials: {settings.FIRESTORE_PROJECT_ID}, database: {self.database_name}")
return return
except Exception as adc_error: except Exception as adc_error:
logger.error(f"Application Default Credentials failed: {adc_error}", exc_info=True) logger.error(f"Application Default Credentials failed: {adc_error}", exc_info=True)
# Fall back to service account file # Fall back to service account file
credentials_path = settings.FIRESTORE_CREDENTIALS_FILE credentials_path = settings.FIRESTORE_CREDENTIALS_FILE
if not os.path.exists(credentials_path): if not os.path.exists(credentials_path):
logger.error(f"Firestore credentials file not found: {credentials_path}") logger.error(f"Firestore credentials file not found: {credentials_path}")
raise FileNotFoundError(f"Credentials file not found: {credentials_path}") raise FileNotFoundError(f"Credentials file not found: {credentials_path}")
# Print key file contents (without sensitive parts) for debugging # Print key file contents (without sensitive parts) for debugging
try: try:
with open(credentials_path, 'r') as f: with open(credentials_path, 'r') as f:
key_data = json.load(f) key_data = json.load(f)
# Log non-sensitive parts of the key # Log non-sensitive parts of the key
logger.info(f"Using credentials file with project_id: {key_data.get('project_id')}") logger.info(f"Using credentials file with project_id: {key_data.get('project_id')}")
logger.info(f"Client email: {key_data.get('client_email')}") logger.info(f"Client email: {key_data.get('client_email')}")
logger.info(f"Key file type: {key_data.get('type')}") logger.info(f"Key file type: {key_data.get('type')}")
except Exception as e: except Exception as e:
logger.error(f"Error reading key file: {e}") logger.error(f"Error reading key file: {e}")
# Load credentials # Load credentials
credentials = service_account.Credentials.from_service_account_file(credentials_path) credentials = service_account.Credentials.from_service_account_file(credentials_path)
# Initialize Firestore client # Initialize Firestore client
self.client = firestore.Client( self.client = firestore.Client(
project=settings.FIRESTORE_PROJECT_ID, project=settings.FIRESTORE_PROJECT_ID,
database=self.database_name, database=self.database_name,
credentials=credentials credentials=credentials
) )
# Test connection by trying to access a collection # Test connection by trying to access a collection
self.client.collection('test').limit(1).get() self.client.collection('test').limit(1).get()
logger.info(f"Connected to Firestore project using credentials file: {settings.FIRESTORE_PROJECT_ID}, database: {self.database_name}") logger.info(f"Connected to Firestore project using credentials file: {settings.FIRESTORE_PROJECT_ID}, database: {self.database_name}")
except Exception as e: except Exception as e:
logger.error(f"Failed to connect to Firestore: {e}", exc_info=True) logger.error(f"Failed to connect to Firestore: {e}", exc_info=True)
raise raise
def close_database_connection(self): def close_database_connection(self):
"""Close database connection.""" """Close database connection."""
try: try:
# No explicit close method needed for Firestore client # No explicit close method needed for Firestore client
self.client = None self.client = None
logger.info("Closed Firestore connection") logger.info("Closed Firestore connection")
except Exception as e: except Exception as e:
logger.error(f"Failed to close Firestore connection: {e}") logger.error(f"Failed to close Firestore connection: {e}")
def get_database(self): def get_database(self):
"""Get the database instance.""" """Get the database instance."""
if self.client is None: if self.client is None:
logger.warning("Database client is None. Attempting to reconnect...") logger.warning("Database client is None. Attempting to reconnect...")
try: try:
self.connect_to_database() self.connect_to_database()
except Exception as e: except Exception as e:
logger.error(f"Failed to reconnect to database: {e}") logger.error(f"Failed to reconnect to database: {e}")
# Return None to avoid further errors but log this issue # Return None to avoid further errors but log this issue
return None return None
# Verify that client is properly initialized # Verify that client is properly initialized
if self.client is None: if self.client is None:
logger.error("Database client is still None after reconnect attempt") logger.error("Database client is still None after reconnect attempt")
return None return None
return self.client return self.client
# Create a singleton database instance # Create a singleton database instance
db = Database() db = Database()

View File

@ -4,11 +4,11 @@ import os
from google.cloud import firestore from google.cloud import firestore
from pydantic import BaseModel from pydantic import BaseModel
from src.core.config import settings from src.config.config import settings
from src.db.models.team import TeamModel from src.models.team import TeamModel
from src.db.models.user import UserModel from src.models.user import UserModel
from src.db.models.api_key import ApiKeyModel from src.models.api_key import ApiKeyModel
from src.db.models.image import ImageModel from src.models.image import ImageModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,5 +1,5 @@
import logging import logging
from src.core.config import settings from src.config.config import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -4,7 +4,7 @@ from typing import List, Optional
from bson import ObjectId from bson import ObjectId
from src.db import db from src.db import db
from src.db.models.api_key import ApiKeyModel from src.models.api_key import ApiKeyModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,6 +1,6 @@
import logging import logging
from src.db.repositories.firestore_repository import FirestoreRepository from src.db.repositories.firestore_repository import FirestoreRepository
from src.db.models.api_key import ApiKeyModel from src.models.api_key import ApiKeyModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,6 +1,6 @@
import logging import logging
from src.db.repositories.firestore_repository import FirestoreRepository from src.db.repositories.firestore_repository import FirestoreRepository
from src.db.models.image import ImageModel from src.models.image import ImageModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -3,7 +3,7 @@ from typing import List, Optional
from bson import ObjectId from bson import ObjectId
from src.db.repositories.firestore_repository import FirestoreRepository from src.db.repositories.firestore_repository import FirestoreRepository
from src.db.models.team import TeamModel from src.models.team import TeamModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,6 +1,6 @@
import logging import logging
from src.db.repositories.firestore_repository import FirestoreRepository from src.db.repositories.firestore_repository import FirestoreRepository
from src.db.models.user import UserModel from src.models.user import UserModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -4,7 +4,7 @@ from typing import List, Optional, Dict, Any
from bson import ObjectId from bson import ObjectId
from src.db import db from src.db import db
from src.db.models.image import ImageModel from src.models.image import ImageModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -4,7 +4,7 @@ from typing import List, Optional
from bson import ObjectId from bson import ObjectId
from src.db import db from src.db import db
from src.db.models.team import TeamModel from src.models.team import TeamModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -4,7 +4,7 @@ from typing import List, Optional
from bson import ObjectId from bson import ObjectId
from src.db import db from src.db import db
from src.db.models.user import UserModel from src.models.user import UserModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

0
src/models/__init__.py Normal file
View File

View File

@ -3,7 +3,7 @@ from typing import Optional, ClassVar
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from bson import ObjectId from bson import ObjectId
from src.db.models.team import PyObjectId from src.models.team import PyObjectId
class ApiKeyModel(BaseModel): class ApiKeyModel(BaseModel):
"""Database model for an API key""" """Database model for an API key"""

View File

@ -3,7 +3,7 @@ from typing import Optional, List, Dict, Any, ClassVar
from pydantic import BaseModel, Field, HttpUrl from pydantic import BaseModel, Field, HttpUrl
from bson import ObjectId from bson import ObjectId
from src.db.models.team import PyObjectId from src.models.team import PyObjectId
class ImageModel(BaseModel): class ImageModel(BaseModel):
"""Database model for an image""" """Database model for an image"""

View File

@ -3,7 +3,7 @@ from typing import Optional, List, ClassVar
from pydantic import BaseModel, Field, EmailStr from pydantic import BaseModel, Field, EmailStr
from bson import ObjectId from bson import ObjectId
from src.db.models.team import PyObjectId from src.models.team import PyObjectId
class UserModel(BaseModel): class UserModel(BaseModel):
"""Database model for a user""" """Database model for a user"""

View File

@ -5,7 +5,7 @@ from typing import List, Dict, Any, Union, Optional
import numpy as np import numpy as np
from PIL import Image from PIL import Image
from src.core.config import settings from src.config.config import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -10,7 +10,7 @@ from google.cloud import storage
from google.oauth2 import service_account from google.oauth2 import service_account
from PIL import Image from PIL import Image
from src.core.config import settings from src.config.config import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -3,7 +3,7 @@ from typing import List, Dict, Any, Optional, Tuple
import pinecone import pinecone
from bson import ObjectId from bson import ObjectId
from src.core.config import settings from src.config.config import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

0
src/utils/__init__.py Normal file
View File

View File

@ -2,7 +2,7 @@ import logging
import sys import sys
from typing import Dict, Any from typing import Dict, Any
from src.core.config import settings from src.config.config import settings
def setup_logging(): def setup_logging():
"""Configure logging settings for the application""" """Configure logging settings for the application"""

View File

@ -6,11 +6,11 @@ from bson import ObjectId
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from src.db.models.team import TeamModel from src.models.team import TeamModel
from src.db.models.user import UserModel from src.models.user import UserModel
from src.db.models.api_key import ApiKeyModel from src.models.api_key import ApiKeyModel
from src.db.models.image import ImageModel from src.models.image import ImageModel
from src.core.security import generate_api_key from src.auth.security import generate_api_key
from src.db.repositories.team_repository import team_repository from src.db.repositories.team_repository import team_repository
from src.db.repositories.user_repository import user_repository from src.db.repositories.user_repository import user_repository
from src.db.repositories.api_key_repository import api_key_repository from src.db.repositories.api_key_repository import api_key_repository

View File

@ -3,9 +3,9 @@ from fastapi.testclient import TestClient
from datetime import datetime, timedelta from datetime import datetime, timedelta
from bson import ObjectId from bson import ObjectId
from src.db.models.api_key import ApiKeyModel from src.models.api_key import ApiKeyModel
from src.db.repositories.api_key_repository import api_key_repository from src.db.repositories.api_key_repository import api_key_repository
from src.core.security import generate_api_key from src.auth.security import generate_api_key
@pytest.mark.asyncio @pytest.mark.asyncio

View File

@ -5,7 +5,7 @@ from fastapi.testclient import TestClient
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
from src.db.repositories.image_repository import ImageRepository, image_repository from src.db.repositories.image_repository import ImageRepository, image_repository
from src.db.models.image import ImageModel from src.models.image import ImageModel
from main import app from main import app
# Hardcoded API key as requested # Hardcoded API key as requested

View File

@ -3,7 +3,7 @@ from fastapi.testclient import TestClient
from datetime import datetime from datetime import datetime
from bson import ObjectId from bson import ObjectId
from src.db.models.image import ImageModel from src.models.image import ImageModel
from src.db.repositories.image_repository import image_repository # Assuming this exists from src.db.repositories.image_repository import image_repository # Assuming this exists

View File

@ -3,7 +3,7 @@ from fastapi.testclient import TestClient
from datetime import datetime from datetime import datetime
from bson import ObjectId from bson import ObjectId
from src.db.models.image import ImageModel from src.models.image import ImageModel
from src.db.repositories.image_repository import image_repository # Assuming this exists from src.db.repositories.image_repository import image_repository # Assuming this exists

View File

@ -1,7 +1,7 @@
import pytest import pytest
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from datetime import datetime, timedelta from datetime import datetime, timedelta
from src.core.security import generate_api_key, hash_api_key, verify_api_key, calculate_expiry_date, is_expired from src.auth.security import generate_api_key, hash_api_key, verify_api_key, calculate_expiry_date, is_expired
def test_api_key_generation(): def test_api_key_generation():

View File

@ -2,7 +2,7 @@ import pytest
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from bson import ObjectId from bson import ObjectId
from src.db.models.team import TeamModel from src.models.team import TeamModel
from src.db.repositories.team_repository import team_repository from src.db.repositories.team_repository import team_repository

0
tests/auth/__init__.py Normal file
View File

View File

@ -2,7 +2,7 @@ import pytest
from datetime import datetime, timedelta from datetime import datetime, timedelta
from bson import ObjectId from bson import ObjectId
from pydantic import ValidationError from pydantic import ValidationError
from src.db.models.api_key import ApiKeyModel from src.models.api_key import ApiKeyModel
class TestApiKeyModel: class TestApiKeyModel:
def test_create_api_key(self): def test_create_api_key(self):

View File

@ -2,7 +2,7 @@ import pytest
from datetime import datetime from datetime import datetime
from bson import ObjectId from bson import ObjectId
from pydantic import ValidationError, HttpUrl from pydantic import ValidationError, HttpUrl
from src.db.models.image import ImageModel from src.models.image import ImageModel
class TestImageModel: class TestImageModel:
def test_create_image(self): def test_create_image(self):

View File

@ -1,7 +1,7 @@
import pytest import pytest
from bson import ObjectId from bson import ObjectId
from pydantic import ValidationError, BaseModel, Field from pydantic import ValidationError, BaseModel, Field
from src.db.models.team import PyObjectId from src.models.team import PyObjectId
class TestPyObjectId: class TestPyObjectId:
def test_valid_object_id(self): def test_valid_object_id(self):

View File

@ -1,7 +1,7 @@
import pytest import pytest
from datetime import datetime from datetime import datetime
from bson import ObjectId from bson import ObjectId
from src.db.models.team import TeamModel, PyObjectId from src.models.team import TeamModel, PyObjectId
class TestTeamModel: class TestTeamModel:
def test_create_team(self): def test_create_team(self):

View File

@ -2,8 +2,8 @@ import pytest
from datetime import datetime from datetime import datetime
from bson import ObjectId from bson import ObjectId
from pydantic import ValidationError from pydantic import ValidationError
from src.db.models.user import UserModel from src.models.user import UserModel
from src.db.models.team import PyObjectId from src.models.team import PyObjectId
class TestUserModel: class TestUserModel:
def test_create_user(self): def test_create_user(self):

0
tests/models/__init__.py Normal file
View File

View File

@ -7,7 +7,7 @@ from io import BytesIO
from src.services.storage import StorageService from src.services.storage import StorageService
from src.db.repositories.image_repository import ImageRepository, image_repository from src.db.repositories.image_repository import ImageRepository, image_repository
from src.db.models.image import ImageModel from src.models.image import ImageModel
# Hardcoded API key as requested # Hardcoded API key as requested
API_KEY = "Wwg4eJjJ.d03970d43cf3a454ad4168b3226b423f" API_KEY = "Wwg4eJjJ.d03970d43cf3a454ad4168b3226b423f"