Additional check in - try-sign-in and upload-document works fine with the production creds. Need further tuning on remaining parts.

This commit is contained in:
jetli 2024-10-22 05:08:21 +00:00
parent 26eedb1b89
commit 193188dbb7
22 changed files with 235 additions and 135 deletions

View File

@ -6,8 +6,7 @@ from app.authentication.backend.models.user.constants import (
UserLoginAction, UserLoginAction,
NewUserMethod, NewUserMethod,
) )
from infra.exception.exceptions import InvalidAuthCodeException from app.authentication.backend.models.user.constants import UserLoginAction
from app.authentication.backend.models.constants import UserLoginAction
from app.authentication.backend.services.user.user_management_service import ( from app.authentication.backend.services.user.user_management_service import (
UserManagementService, UserManagementService,
) )

View File

@ -8,10 +8,10 @@ from app.authentication.backend.infra.code_management.depot_handler import (
) )
from app.authentication.backend.models.constants import ( from app.authentication.backend.models.user.constants import (
AuthType, AuthType,
) )
from app.authentication.backend.models.models import ( from app.authentication.backend.models.user.models import (
AuthCodeDoc, AuthCodeDoc,
UserEmailDoc, UserEmailDoc,
UserMobileDoc, UserMobileDoc,

View File

@ -0,0 +1,8 @@
from .gitea import code_models
from .user import user_models
from .user_profile import profile_models
backend_models = []
backend_models.extend(code_models)
backend_models.extend(user_models)
backend_models.extend(profile_models)

View File

@ -1,43 +0,0 @@
from enum import IntEnum
class NewUserMethod(IntEnum):
EMAIL = 1
MOBILE = 2
class UserAccountProperty(IntEnum):
EMAIL_VERIFIED = 1
MOBILE_VERIFIED = 2
PAYMENT_SETUP = 4
ACCEPT_REQUEST = 8
READY_PROVIDER = 16
MANAGE_PROJECT = 32
class UserLoginAction(IntEnum):
VERIFY_EMAIL_WITH_AUTH_CODE = 0
EXISTING_USER_PASSWORD_REQUIRED = 1
NEW_USER_SET_PASSWORD = 2
EMAIL_NOT_ASSOCIATED_WITH_USER = 3
REVIEW_AND_REVISE_FLID = 4
USER_SIGNED_IN = 100
class AuthType(IntEnum):
MOBILE = 0
EMAIL = 1
PASSWORD = 2
class DepotStatus(IntEnum):
TO_BE_CREATED = 0
CREATED = 1
DELETED = 2
class UserAccountStatus(IntEnum):
TO_BE_CREATED = 0
CREATED = 1
DELETED = 2
DEACTIVATED = 3

View File

@ -0,0 +1,3 @@
from .models import CodeDepotDoc
code_models = [CodeDepotDoc]

View File

@ -1,37 +0,0 @@
from datetime import datetime
from beanie import Document
from .constants import AuthType
class UserPasswordDoc(Document):
user_id: str
password: str
class Settings:
name = "user_password"
class UserEmailDoc(Document):
user_id: str
email: str
class Settings:
name = "user_email"
class UserMobileDoc(Document):
user_id: str
mobile: str
class Settings:
name = "user_mobile"
class AuthCodeDoc(Document):
auth_code: str
method: str
method_type: AuthType
expiry: datetime
class Settings:
name = "user_auth_code"

View File

@ -0,0 +1,15 @@
from .models import (
UserAccountDoc,
UserPasswordDoc,
UserEmailDoc,
UserMobileDoc,
AuthCodeDoc,
)
user_models = [
UserAccountDoc,
UserPasswordDoc,
UserEmailDoc,
UserMobileDoc,
AuthCodeDoc,
]

View File

@ -35,3 +35,45 @@ UserRegionToCurrency = {
UserRegion.ZH_CN: Currency.CNY.name, UserRegion.ZH_CN: Currency.CNY.name,
UserRegion.OTHER: Currency.USD.name, UserRegion.OTHER: Currency.USD.name,
} }
class NewUserMethod(IntEnum):
EMAIL = 1
MOBILE = 2
class UserAccountProperty(IntEnum):
EMAIL_VERIFIED = 1
MOBILE_VERIFIED = 2
PAYMENT_SETUP = 4
ACCEPT_REQUEST = 8
READY_PROVIDER = 16
MANAGE_PROJECT = 32
class UserLoginAction(IntEnum):
VERIFY_EMAIL_WITH_AUTH_CODE = 0
EXISTING_USER_PASSWORD_REQUIRED = 1
NEW_USER_SET_PASSWORD = 2
EMAIL_NOT_ASSOCIATED_WITH_USER = 3
REVIEW_AND_REVISE_FLID = 4
USER_SIGNED_IN = 100
class AuthType(IntEnum):
MOBILE = 0
EMAIL = 1
PASSWORD = 2
class DepotStatus(IntEnum):
TO_BE_CREATED = 0
CREATED = 1
DELETED = 2
class UserAccountStatus(IntEnum):
TO_BE_CREATED = 0
CREATED = 1
DELETED = 2
DEACTIVATED = 3

View File

@ -7,7 +7,9 @@ from app.authentication.backend.models.permission.constants import (
AdministrativeRole, AdministrativeRole,
Capability, Capability,
) )
from datetime import datetime
from infra.models.constants import UserRegion from infra.models.constants import UserRegion
from .constants import AuthType
class UserAccountDoc(Document): class UserAccountDoc(Document):
@ -21,3 +23,37 @@ class UserAccountDoc(Document):
class Settings: class Settings:
name = "user_account" name = "user_account"
class UserPasswordDoc(Document):
user_id: str
password: str
class Settings:
name = "user_password"
class UserEmailDoc(Document):
user_id: str
email: str
class Settings:
name = "user_email"
class UserMobileDoc(Document):
user_id: str
mobile: str
class Settings:
name = "user_mobile"
class AuthCodeDoc(Document):
auth_code: str
method: str
method_type: AuthType
expiry: datetime
class Settings:
name = "user_auth_code"

View File

@ -0,0 +1,3 @@
from .models import BasicProfileDoc, ProviderProfileDoc
profile_models = [BasicProfileDoc, ProviderProfileDoc]

View File

@ -1,7 +1,7 @@
from infra.config.app_settings import app_settings from infra.config.app_settings import app_settings
from beanie import init_beanie from beanie import init_beanie
from motor.motor_asyncio import AsyncIOMotorClient from motor.motor_asyncio import AsyncIOMotorClient
from app.central_storage.backend.models.models import DocumentDoc from app.authentication.backend.models import backend_models
def register(app): def register(app):
@ -21,5 +21,5 @@ async def initiate_database():
maxPoolSize=20, # Maximum number of connections in the pool maxPoolSize=20, # Maximum number of connections in the pool
) )
await init_beanie( await init_beanie(
database=client[app_settings.MONGODB_NAME], document_models=[DocumentDoc] database=client[app_settings.MONGODB_NAME], document_models=backend_models
) )

View File

@ -1,15 +0,0 @@
from app.central_storage.backend.business.document_manager import (
DocumentBusinessManager,
)
class DocumentHub:
def __init__(self, user_id: str):
self.user_id = user_id
self.document_business_manager = DocumentBusinessManager(self.user_id)
return
async def get_document_by_id(self, document_id: str):
return await self.document_business_manager.get_document_details_by_id(
document_id
)

View File

@ -0,0 +1,25 @@
from app.central_storage.backend.business.document_manager import (
DocumentManager,
)
class DocumentHub:
def __init__(self, user_id: str):
self.user_id = user_id
self.document_manager = DocumentManager(self.user_id)
return
async def get_document_by_id(self, document_id: str):
return await self.document_manager.get_document_details_by_id(document_id)
async def upload_document_for_object(
self, object_id: str, file_name: str, file_data: bytes
) -> bool:
"""Upload a file
Args:
file_name: the name of the file
file (bytes): the file to be uploaded
"""
return await self.document_manager.upload_file_for_object(
object_id, file_name, file_data
)

View File

@ -1,7 +1,8 @@
from app.central_storage.backend.services.document_service import DocumentService from app.central_storage.backend.services.document_service import DocumentService
from app.central_storage.backend.models.models import MediaType, DataFormat
class DocumentBusinessManager: class DocumentManager:
def __init__(self, user_id) -> None: def __init__(self, user_id) -> None:
self.user_id = user_id self.user_id = user_id
self.document_service = DocumentService() self.document_service = DocumentService()
@ -17,3 +18,20 @@ class DocumentBusinessManager:
"file_name": file_name, "file_name": file_name,
"file_download_url": download_link, "file_download_url": download_link,
} }
async def upload_file_for_object(
self, object_id: str, file_name: str, file_data: bytes
) -> bool:
await self.document_service.new_document(
file_name=file_name,
# This 'UNKNOWN' will make the document manager decide the media type from file name
media_type=MediaType.UNKNOWN,
data_format=DataFormat.RAW,
created_by=object_id,
)
return await self.document_service.save_document_file(file_data) or None
# TODO: This should go to Freeleaps App
# add_doc = await self.attachment_service.add_document_to_request(
# request_id, document_id
# )
# await self.proposal_store.update_latest_proposal_with_documents(request_id)

View File

@ -16,7 +16,7 @@ from azure.storage.blob import (
from azure.core.exceptions import ResourceExistsError from azure.core.exceptions import ResourceExistsError
class AzureBlobManager: class AzureBlobHandler:
def __init__(self) -> None: def __init__(self) -> None:
pass pass

View File

@ -0,0 +1,4 @@
from .models import DocumentDoc
backend_models = []
backend_models.extend([DocumentDoc])

View File

@ -3,8 +3,8 @@ from app.central_storage.backend.models.models import DocumentDoc
from app.central_storage.backend.models.constants import MediaType, DataFormat from app.central_storage.backend.models.constants import MediaType, DataFormat
from infra.exception.exceptions import DoesNotExistError from infra.exception.exceptions import DoesNotExistError
from app.central_storage.backend.infra.azure_storage.blob_manager import ( from app.central_storage.backend.infra.azure_storage.blob_handler import (
AzureBlobManager, AzureBlobHandler,
) )
import base64 import base64
import os import os
@ -14,7 +14,7 @@ from re import match
class DocumentService: class DocumentService:
def __init__(self) -> None: def __init__(self) -> None:
self.__document_doc = None self.__document_doc = None
self.blob_manager = AzureBlobManager() self.blob_manager = AzureBlobHandler()
return return
def __normalize_file_name__(file_name: str) -> str: def __normalize_file_name__(file_name: str) -> str:
@ -86,14 +86,14 @@ class DocumentService:
document = DocumentDoc( document = DocumentDoc(
document_id=None, document_id=None,
location=None, location=None,
file_name=DocumentManager.__normalize_file_name__(file_name), file_name=DocumentService.__normalize_file_name__(file_name),
created_by=created_by, created_by=created_by,
create_time=dt.now(tz.utc), create_time=dt.now(tz.utc),
updated_by=created_by, updated_by=created_by,
update_time=dt.now(tz.utc), update_time=dt.now(tz.utc),
version_number=1, version_number=1,
media_type=( media_type=(
DocumentManager.__retrieve_media_type_from_file_name__(file_name) DocumentService.__retrieve_media_type_from_file_name__(file_name)
if media_type == MediaType.UNKNOWN if media_type == MediaType.UNKNOWN
else media_type else media_type
), ),

View File

@ -5,8 +5,10 @@ pydantic==2.9.2
loguru==0.7.2 loguru==0.7.2
uvicorn==0.23.2 uvicorn==0.23.2
beanie==1.21.0 beanie==1.21.0
httpx
aio-pika aio-pika
pydantic-settings pydantic-settings
python-multipart
python-jose python-jose
azure-storage-blob==12.22.0 azure-storage-blob==12.22.0
azure-identity azure-identity

View File

@ -1,7 +1,7 @@
from infra.config.app_settings import app_settings from infra.config.app_settings import app_settings
from beanie import init_beanie from beanie import init_beanie
from motor.motor_asyncio import AsyncIOMotorClient from motor.motor_asyncio import AsyncIOMotorClient
from app.central_storage.backend.models.models import DocumentDoc from app.central_storage.backend.models import backend_models
def register(app): def register(app):
@ -21,5 +21,5 @@ async def initiate_database():
maxPoolSize=20, # Maximum number of connections in the pool maxPoolSize=20, # Maximum number of connections in the pool
) )
await init_beanie( await init_beanie(
database=client[app_settings.MONGODB_NAME], document_models=[DocumentDoc] database=client[app_settings.MONGODB_NAME], document_models=backend_models
) )

View File

@ -1,7 +1,9 @@
from fastapi import APIRouter from fastapi import APIRouter
from .get_document_by_id import router as doc_router from .get_document_by_id import router as doc_router
from .upload_document import router as ud_router
api_router = APIRouter() api_router = APIRouter()
api_router.include_router(doc_router, tags=["attachment"]) api_router.include_router(doc_router, tags=["attachment"])
api_router.include_router(ud_router, tags=["attachment"])
websocket_router = APIRouter() websocket_router = APIRouter()

View File

@ -1,8 +1,14 @@
from fastapi import APIRouter, HTTPException, Request from fastapi import APIRouter
from infra.token.token_manager import TokenManager
from fastapi import APIRouter, Depends
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from fastapi import Depends, HTTPException
from starlette.status import HTTP_401_UNAUTHORIZED
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from infra.token.token_manager import TokenManager from infra.token.token_manager import TokenManager
from app.central_storage.backend.application.document_app import DocumentHub from app.central_storage.backend.application.document_hub import DocumentHub
router = APIRouter() router = APIRouter()
token_manager = TokenManager() token_manager = TokenManager()
@ -19,32 +25,14 @@ token_manager = TokenManager()
) )
async def get_document_by_id( async def get_document_by_id(
document_id: str, document_id: str,
request: Request, current_user: dict = Depends(token_manager.get_current_user),
): ):
# Extract the Authorization header user_id = current_user.get("id")
auth_header = request.headers.get("Authorization")
if not auth_header:
raise HTTPException(status_code=401, detail="Authorization header missing")
# Ensure the header starts with 'Bearer'
if not auth_header.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Invalid authorization header")
token = auth_header.split(" ")[1]
try:
# Decode the token using the TokenManager
credentials = token_manager.decode_token(token)
except Exception as e:
raise HTTPException(
status_code=401, detail=f"Invalid or expired token: {str(e)}"
)
# Get the user_id from the decoded token
user_id = credentials.get("id")
if not user_id: if not user_id:
raise HTTPException(status_code=401, detail="Invalid token payload") raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED, detail="Could not validate credentials"
)
# Fetch the document using DocumentHub # Fetch the document using DocumentHub
document = await DocumentHub(user_id).get_document_by_id(document_id) document = await DocumentHub(user_id).get_document_by_id(document_id)

View File

@ -0,0 +1,50 @@
from fastapi import APIRouter, UploadFile, File, Form, HTTPException
from fastapi import APIRouter, Depends
from infra.token.token_manager import TokenManager
from starlette.status import HTTP_401_UNAUTHORIZED
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from app.central_storage.backend.application.document_hub import DocumentHub
router = APIRouter()
token_manager = TokenManager()
@router.post(
"/upload-document",
summary="upload a document for a given object.",
description="upload a document. If success, returning the document id",
)
async def attach_document_for_request(
object_id: str = Form(...),
file: UploadFile = File(None),
current_user: dict = Depends(token_manager.get_current_user),
):
print("current user", current_user)
user_id = current_user.get("id")
print("current user id", user_id)
if not user_id:
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED, detail="Could not validate credentials"
)
document_hub = DocumentHub(user_id)
# File processing
try:
file_data = await file.read() # You can use async chunking for larger files
document_id = await document_hub.upload_document_for_object(
object_id, file.filename, file_data
)
if document_id:
result = {"document_id": str(document_id), "file_name": file.filename}
return JSONResponse(content=jsonable_encoder(result))
else:
return JSONResponse(
status_code=500, content={"error": "File upload failed"}
)
except Exception as e:
print("this is exception", e)
return JSONResponse(status_code=500, content={"error": "Internal server error"})