feat: update project structure and docs

This commit is contained in:
2026-04-25 16:26:33 +03:00
parent 9c3b44b561
commit 9772c3c908
42 changed files with 1342 additions and 6 deletions

View File

@@ -0,0 +1 @@
"""Application package."""

Binary file not shown.

1
app/api/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""API module - HTTP routes and endpoints."""

1
app/api/v1/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""API version 1 endpoints."""

1
app/common/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Common utilities and shared components."""

View File

@@ -0,0 +1,55 @@
"""Common error response schema and exception handlers."""
from datetime import datetime
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):
"""Standard error response format."""
status_code: int
message: str
details: dict | None = None
timestamp: str
async def app_exception_handler(request: Request, exc: AppException) -> JSONResponse:
"""Handle application exceptions with standard response."""
return JSONResponse(
status_code=exc.status_code,
content={
"status_code": exc.status_code,
"message": exc.message,
"timestamp": datetime.utcnow().isoformat(),
},
)
async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
"""Handle HTTP exceptions with standard response."""
return JSONResponse(
status_code=exc.status_code,
content={
"status_code": exc.status_code,
"message": str(exc.detail),
"timestamp": datetime.utcnow().isoformat(),
},
)
def register_exception_handlers(app: FastAPI):
"""Register all exception handlers with FastAPI app."""
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
View File

@@ -0,0 +1 @@
"""Core module - shared functionality and configuration."""

20
app/core/config.py Normal file
View File

@@ -0,0 +1,20 @@
"""Application configuration and settings."""
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""Application settings from environment variables."""
app_name: str = "Blog API"
debug: bool = False
host: str = "0.0.0.0"
port: int = 8000
# Database (when added)
database_url: str | None = None
model_config = SettingsConfigDict(env_file=".env")
settings = Settings()

64
app/core/exceptions.py Normal file
View File

@@ -0,0 +1,64 @@
"""Custom application exceptions."""
class AppException(Exception):
"""Base application exception."""
def __init__(self, message: str, status_code: int = 500):
"""Initialize application exception.
Args:
message: Error message.
status_code: HTTP status code.
"""
self.message = message
self.status_code = status_code
super().__init__(self.message)
class NotFoundError(AppException):
"""Resource not found error."""
def __init__(self, message: str = "Resource not found"):
"""Initialize not found error.
Args:
message: Error message.
"""
super().__init__(message, status_code=404)
class ValidationError(AppException):
"""Validation error."""
def __init__(self, message: str = "Validation failed"):
"""Initialize validation error.
Args:
message: Error message.
"""
super().__init__(message, status_code=400)
class UnauthorizedError(AppException):
"""Authentication required."""
def __init__(self, message: str = "Unauthorized"):
"""Initialize unauthorized error.
Args:
message: Error message.
"""
super().__init__(message, status_code=401)
class ForbiddenError(AppException):
"""Permission denied."""
def __init__(self, message: str = "Forbidden"):
"""Initialize forbidden error.
Args:
message: Error message.
"""
super().__init__(message, status_code=403)

View File

@@ -1,21 +1,43 @@
"""FastAPI application factory and entry point."""
from contextlib import asynccontextmanager
import uvicorn
from fastapi import FastAPI
from app.common.error_handler import register_exception_handlers
from app.core.config import settings
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan manager for startup/shutdown events."""
# Startup: initialize DB connections, cache, etc.
yield
# Shutdown: cleanup resources
def app_factory():
app = FastAPI(lifespan=lifespan)
def app_factory() -> FastAPI:
"""Create and configure FastAPI application instance.
Returns:
Configured FastAPI application.
"""
app = FastAPI(title=settings.app_name, debug=settings.debug, lifespan=lifespan)
# Register exception handlers
register_exception_handlers(app)
# Register routers (when added)
# from app.api.v1.router import api_router
# app.include_router(api_router, prefix="/api/v1")
return app
def main():
uvicorn.run(app_factory, factory=True, host="0.0.0.0", port=8000)
"""Run the application with uvicorn server."""
uvicorn.run(app_factory, factory=True, host=settings.host, port=settings.port)
if __name__ == "__main__":

1
app/modules/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Feature modules - business logic organized by domain."""