feat: implement blog project with CI pipeline
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"""Application package."""
|
||||
|
||||
Binary file not shown.
Binary file not shown.
1
app/api/__init__.py
Normal file
1
app/api/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""API module - HTTP routes and endpoints."""
|
||||
1
app/api/v1/__init__.py
Normal file
1
app/api/v1/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""API version 1 endpoints."""
|
||||
1
app/common/__init__.py
Normal file
1
app/common/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Common utilities and shared components."""
|
||||
48
app/common/error_handler.py
Normal file
48
app/common/error_handler.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from app.core.exceptions import AppException
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
status_code: int
|
||||
message: str
|
||||
details: dict[str, str] | None = None
|
||||
timestamp: str
|
||||
|
||||
|
||||
async def app_exception_handler(request: Request, exc: AppException) -> JSONResponse:
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content={
|
||||
"status_code": exc.status_code,
|
||||
"message": exc.message,
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content={
|
||||
"status_code": exc.status_code,
|
||||
"message": str(exc.detail),
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def register_exception_handlers(app: FastAPI) -> None:
|
||||
app.add_exception_handler(
|
||||
AppException,
|
||||
app_exception_handler, # type: ignore[arg-type]
|
||||
)
|
||||
app.add_exception_handler(
|
||||
HTTPException,
|
||||
http_exception_handler, # type: ignore[arg-type]
|
||||
)
|
||||
1
app/core/__init__.py
Normal file
1
app/core/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Core module - shared functionality and configuration."""
|
||||
15
app/core/config.py
Normal file
15
app/core/config.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
app_name: str = "Blog API"
|
||||
debug: bool = False
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
|
||||
database_url: str | None = None
|
||||
|
||||
model_config = SettingsConfigDict(env_file=".env")
|
||||
|
||||
|
||||
settings = Settings()
|
||||
25
app/core/exceptions.py
Normal file
25
app/core/exceptions.py
Normal file
@@ -0,0 +1,25 @@
|
||||
class AppException(Exception):
|
||||
def __init__(self, message: str, status_code: int = 500):
|
||||
self.message = message
|
||||
self.status_code = status_code
|
||||
super().__init__(self.message)
|
||||
|
||||
|
||||
class NotFoundError(AppException):
|
||||
def __init__(self, message: str = "Resource not found"):
|
||||
super().__init__(message, status_code=404)
|
||||
|
||||
|
||||
class ValidationError(AppException):
|
||||
def __init__(self, message: str = "Validation failed"):
|
||||
super().__init__(message, status_code=400)
|
||||
|
||||
|
||||
class UnauthorizedError(AppException):
|
||||
def __init__(self, message: str = "Unauthorized"):
|
||||
super().__init__(message, status_code=401)
|
||||
|
||||
|
||||
class ForbiddenError(AppException):
|
||||
def __init__(self, message: str = "Forbidden"):
|
||||
super().__init__(message, status_code=403)
|
||||
@@ -1,20 +1,21 @@
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncGenerator
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
yield
|
||||
|
||||
|
||||
def app_factory():
|
||||
def app_factory() -> FastAPI:
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
return app
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
uvicorn.run(app_factory, factory=True, host="0.0.0.0", port=8000)
|
||||
|
||||
|
||||
|
||||
1
app/modules/__init__.py
Normal file
1
app/modules/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Feature modules - business logic organized by domain."""
|
||||
Reference in New Issue
Block a user