Add API support for get_document_by_id
This commit is contained in:
parent
c25252ceda
commit
378ae41b39
5
.gitignore
vendored
5
.gitignore
vendored
@ -1 +1,6 @@
|
|||||||
.venv/
|
.venv/
|
||||||
|
*__pycache__*
|
||||||
|
.vscode
|
||||||
|
/deploy/.*
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
@ -1,5 +1,5 @@
|
|||||||
# Dockerfile for Python Service
|
# Dockerfile for Python Service
|
||||||
FROM python:3.11-slim
|
FROM python:3.10-slim
|
||||||
|
|
||||||
# Set the working directory inside the container
|
# Set the working directory inside the container
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@ -16,4 +16,4 @@ EXPOSE 8005
|
|||||||
|
|
||||||
|
|
||||||
# Run the application using the start script
|
# Run the application using the start script
|
||||||
CMD ["uvicorn", "webapi.main:app", "--reload", "--port=8003", "--host=0.0.0.0"]
|
CMD ["uvicorn", "app.central_storage.webapi.main:app", "--reload", "--port=8005", "--host=0.0.0.0"]
|
||||||
|
|||||||
@ -92,6 +92,9 @@ class AzureBlobManager:
|
|||||||
app_settings.AZURE_STORAGE_DOCUMENT_API_ENDPOINT,
|
app_settings.AZURE_STORAGE_DOCUMENT_API_ENDPOINT,
|
||||||
credential=app_settings.AZURE_STORAGE_DOCUMENT_API_KEY,
|
credential=app_settings.AZURE_STORAGE_DOCUMENT_API_KEY,
|
||||||
) as blob_service_client:
|
) as blob_service_client:
|
||||||
|
# user_delegation_key = await blob_service_client.get_user_delegation_key(
|
||||||
|
# key_start_time=key_start_time, key_expiry_time=key_expiry_time
|
||||||
|
# )
|
||||||
blob_client = blob_service_client.get_blob_client(
|
blob_client = blob_service_client.get_blob_client(
|
||||||
container=container_name, blob=file_name
|
container=container_name, blob=file_name
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,3 +1,13 @@
|
|||||||
|
fastapi==0.114.0
|
||||||
|
fastapi-jwt==0.2.0
|
||||||
pika==1.3.2
|
pika==1.3.2
|
||||||
fastapi
|
pydantic==2.9.2
|
||||||
uvicorn
|
loguru==0.7.2
|
||||||
|
uvicorn==0.23.2
|
||||||
|
beanie==1.21.0
|
||||||
|
aio-pika
|
||||||
|
pydantic-settings
|
||||||
|
python-jose
|
||||||
|
azure-storage-blob==12.22.0
|
||||||
|
azure-identity
|
||||||
|
azure-core[aio]
|
||||||
@ -2,12 +2,12 @@ import logging
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.openapi.utils import get_openapi
|
from fastapi.openapi.utils import get_openapi
|
||||||
|
|
||||||
from webapi.providers import common
|
from app.central_storage.webapi.providers import common
|
||||||
from webapi.providers import logger
|
from app.central_storage.webapi.providers import logger
|
||||||
from webapi.providers import router
|
from app.central_storage.webapi.providers import router
|
||||||
from webapi.providers import database
|
from app.central_storage.webapi.providers import database
|
||||||
from webapi.providers import scheduler
|
from app.central_storage.webapi.providers import scheduler
|
||||||
from webapi.providers import exception_handler
|
from app.central_storage.webapi.providers import exception_handler
|
||||||
from .freeleaps_app import FreeleapsApp
|
from .freeleaps_app import FreeleapsApp
|
||||||
|
|
||||||
|
|
||||||
@ -49,11 +49,7 @@ def customize_openapi_security(app: FastAPI) -> None:
|
|||||||
|
|
||||||
# Add security scheme to components
|
# Add security scheme to components
|
||||||
openapi_schema["components"]["securitySchemes"] = {
|
openapi_schema["components"]["securitySchemes"] = {
|
||||||
"bearerAuth": {
|
"bearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"}
|
||||||
"type": "http",
|
|
||||||
"scheme": "bearer",
|
|
||||||
"bearerFormat": "JWT"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add security requirement globally
|
# Add security requirement globally
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
from webapi.bootstrap.application import create_app
|
from app.central_storage.webapi.bootstrap.application import create_app
|
||||||
from webapi.config.site_settings import site_settings
|
from app.central_storage.webapi.config.site_settings import site_settings
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from strawberry.fastapi import GraphQLRouter
|
|
||||||
from strawberry.fastapi.handlers import GraphQLTransportWSHandler, GraphQLWSHandler
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
|
|
||||||
@app.get("/", status_code=301)
|
@app.get("/", status_code=301)
|
||||||
async def root():
|
async def root():
|
||||||
"""
|
"""
|
||||||
@ -19,12 +18,16 @@ async def root():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
uvicorn.run(app="main:app", host=site_settings.SERVER_HOST, port=site_settings.SERVER_PORT)
|
uvicorn.run(
|
||||||
|
app="main:app", host=site_settings.SERVER_HOST, port=site_settings.SERVER_PORT
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_context() -> Any:
|
def get_context() -> Any:
|
||||||
# Define your context function. This is where you can set up authentication, database connections, etc.
|
# Define your context function. This is where you can set up authentication, database connections, etc.
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def get_root_value() -> Any:
|
def get_root_value() -> Any:
|
||||||
# Define your root value function. This can be used to customize the root value for GraphQL operations.
|
# Define your root value function. This can be used to customize the root value for GraphQL operations.
|
||||||
return {}
|
return {}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from webapi.config.site_settings import site_settings
|
from app.central_storage.webapi.config.site_settings import site_settings
|
||||||
|
|
||||||
|
|
||||||
def register(app):
|
def register(app):
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
from webapi.config.site_settings import site_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
|
||||||
|
|
||||||
|
|
||||||
def register(app):
|
def register(app):
|
||||||
app.debug = site_settings.DEBUG
|
app.debug = "mongo_debug"
|
||||||
app.title = site_settings.NAME
|
app.title = "mongo_name"
|
||||||
|
|
||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
async def start_database():
|
async def start_database():
|
||||||
@ -13,5 +14,12 @@ def register(app):
|
|||||||
|
|
||||||
|
|
||||||
async def initiate_database():
|
async def initiate_database():
|
||||||
#init your database here
|
client = AsyncIOMotorClient(
|
||||||
pass
|
app_settings.MONGODB_URI,
|
||||||
|
serverSelectionTimeoutMS=60000,
|
||||||
|
minPoolSize=5, # Minimum number of connections in the pool
|
||||||
|
maxPoolSize=20, # Maximum number of connections in the pool
|
||||||
|
)
|
||||||
|
await init_beanie(
|
||||||
|
database=client[app_settings.MONGODB_NAME], document_models=[DocumentDoc]
|
||||||
|
)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from common.config.log_settings import log_settings
|
from app.central_storage.common.config.log_settings import log_settings
|
||||||
|
|
||||||
|
|
||||||
def register(app=None):
|
def register(app=None):
|
||||||
@ -21,15 +21,8 @@ def register(app=None):
|
|||||||
logging.getLogger(name).propagate = True
|
logging.getLogger(name).propagate = True
|
||||||
|
|
||||||
# configure loguru
|
# configure loguru
|
||||||
logger.add(
|
logger.add(sink=sys.stdout)
|
||||||
sink=sys.stdout
|
logger.add(sink=file_path, level=level, retention=retention, rotation=rotation)
|
||||||
)
|
|
||||||
logger.add(
|
|
||||||
sink=file_path,
|
|
||||||
level=level,
|
|
||||||
retention=retention,
|
|
||||||
rotation=rotation
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.disable("pika.adapters")
|
logger.disable("pika.adapters")
|
||||||
logger.disable("pika.connection")
|
logger.disable("pika.connection")
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from webapi.routes import api_router
|
from app.central_storage.webapi.routes import api_router
|
||||||
|
|
||||||
from starlette import routing
|
from starlette import routing
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
from .get_document_by_id import router as doc_router
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
|
api_router.include_router(doc_router, tags=["attachment"])
|
||||||
|
|
||||||
websocket_router = APIRouter()
|
websocket_router = APIRouter()
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
from fastapi import APIRouter, Security, HTTPException
|
from fastapi import APIRouter, HTTPException, Request
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from fastapi_jwt import JwtAuthorizationCredentials
|
from infra.token.token_manager import TokenManager
|
||||||
from backend.infra.authentication.auth import access_security
|
|
||||||
from app.central_storage.backend.application.document_app import DocumentHub
|
from app.central_storage.backend.application.document_app import DocumentHub
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
token_manager = TokenManager()
|
||||||
|
|
||||||
|
|
||||||
# Web API
|
# Web API
|
||||||
@ -19,9 +19,32 @@ router = APIRouter()
|
|||||||
)
|
)
|
||||||
async def get_document_by_id(
|
async def get_document_by_id(
|
||||||
document_id: str,
|
document_id: str,
|
||||||
credentials: JwtAuthorizationCredentials = Security(access_security),
|
request: Request,
|
||||||
):
|
):
|
||||||
user_id = credentials["id"]
|
# Extract the Authorization header
|
||||||
|
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:
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid token payload")
|
||||||
|
|
||||||
# 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)
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
import os
|
|
||||||
from pydantic_settings import BaseSettings
|
from pydantic_settings import BaseSettings
|
||||||
|
|
||||||
class AppSettings():
|
|
||||||
NAME: str = "myapp"
|
class AppSettings(BaseSettings):
|
||||||
|
JWT_SECRET_KEY: str = ""
|
||||||
|
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
||||||
|
REFRESH_TOKEN_EXPIRE_DAYS: int = 30
|
||||||
|
MONGODB_NAME: str = "freeleaps2"
|
||||||
|
MONGODB_URI: str = (
|
||||||
|
"mongodb+srv://freeadmin:0eMV0bt8oyaknA0m@freeleaps2.zmsmpos.mongodb.net/?retryWrites=true&w=majority"
|
||||||
|
)
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
env_file = ".myapp.env"
|
env_file = ".myapp.env"
|
||||||
env_file_encoding = "utf-8"
|
env_file_encoding = "utf-8"
|
||||||
|
|
||||||
APPLICATION_ACTIVITY_LOG: str = "myapp-application-activity"
|
|
||||||
USER_ACTIVITY_LOG: str = "myapp-user-activity"
|
|
||||||
BUSINESS_METRIC_LOG: str = "myapp-business-metrics"
|
|
||||||
|
|
||||||
app_settings = AppSettings()
|
app_settings = AppSettings()
|
||||||
|
|||||||
59
infra/token/token_manager.py
Normal file
59
infra/token/token_manager.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# application/auth/token/token_manager.py
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from typing import Dict
|
||||||
|
from jose import jwt
|
||||||
|
from infra.config.app_settings import app_settings
|
||||||
|
|
||||||
|
|
||||||
|
class TokenManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.secret_key = app_settings.JWT_SECRET_KEY
|
||||||
|
self.algorithm = "HS256"
|
||||||
|
self.access_token_expire_minutes = app_settings.ACCESS_TOKEN_EXPIRE_MINUTES
|
||||||
|
self.refresh_token_expire_days = app_settings.REFRESH_TOKEN_EXPIRE_DAYS
|
||||||
|
|
||||||
|
def create_access_token(self, subject: Dict[str, str]) -> str:
|
||||||
|
"""
|
||||||
|
Generates an access token with a short expiration time.
|
||||||
|
Args:
|
||||||
|
subject (Dict[str, str]): A dictionary containing user information like 'id' and 'role'.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Encoded JWT access token.
|
||||||
|
"""
|
||||||
|
expire = datetime.now(timezone.utc) + timedelta(
|
||||||
|
minutes=self.access_token_expire_minutes
|
||||||
|
)
|
||||||
|
to_encode = subject.copy()
|
||||||
|
to_encode.update({"exp": expire})
|
||||||
|
return jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
|
||||||
|
|
||||||
|
def create_refresh_token(self, subject: Dict[str, str]) -> str:
|
||||||
|
"""
|
||||||
|
Generates a refresh token with a longer expiration time.
|
||||||
|
Args:
|
||||||
|
subject (Dict[str, str]): A dictionary containing user information like 'id' and 'role'.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Encoded JWT refresh token.
|
||||||
|
"""
|
||||||
|
expire = datetime.now(timezone.utc) + timedelta(
|
||||||
|
days=self.refresh_token_expire_days
|
||||||
|
)
|
||||||
|
to_encode = subject.copy()
|
||||||
|
to_encode.update({"exp": expire})
|
||||||
|
return jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
|
||||||
|
|
||||||
|
def decode_token(self, token: str) -> Dict:
|
||||||
|
"""
|
||||||
|
Decodes a JWT token and returns the payload.
|
||||||
|
Args:
|
||||||
|
token (str): Encoded JWT token.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: Decoded token payload.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
JWTError: If the token is invalid or expired.
|
||||||
|
"""
|
||||||
|
return jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
|
||||||
Loading…
Reference in New Issue
Block a user