1
0
mirror of https://github.com/ijaric/voice_assistant.git synced 2025-05-24 14:33:26 +00:00

Merge pull request #11 from ijaric/tasks/#10_fastapi_template_fix_settings

FIX [#10] Fastapi Template: Настройки
This commit is contained in:
Artem Litvinov 2023-09-23 18:39:11 +01:00 committed by GitHub
commit 852242efd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 193 additions and 55 deletions

View File

@ -1,10 +1,10 @@
DB_HOST=db POSTGRES_HOST=db
DB_PORT=5432 POSTGRES_PORT=5432
DB_USER=user POSTGRES_USER=user
DB_PASSWORD=Qwe123 POSTGRES_PASSWORD=Qwe123
DB_NAME=api_db POSTGRES_NAME=api_db
SERVER_HOST=0.0.0.0 API_HOST=0.0.0.0
SERVER_PORT=8000 API_PORT=8000
JWT_SECRET_KEY=v9LctjUWwol4XbvczPiLFMDtZ8aal7mm JWT_SECRET_KEY=v9LctjUWwol4XbvczPiLFMDtZ8aal7mm

View File

@ -3,14 +3,14 @@ import logging
import uvicorn import uvicorn
import lib.app.app as app_module import lib.app.app as app_module
from lib.app import settings as app_settings import lib.app.settings as app_settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
app_instance = app_module.Application() app_instance = app_module.Application()
app = app_instance.create_app() app = app_instance.create_app()
settings = app_settings.get_settings() settings = app_settings.settings
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -4,13 +4,13 @@ services:
db: db:
image: postgres:15.2 image: postgres:15.2
environment: environment:
POSTGRES_USER: ${DB_USER} POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${DB_NAME} POSTGRES_DB: ${POSTGRES_NAME}
env_file: env_file:
- .env - .env
ports: ports:
- "${DB_PORT}:${DB_PORT}" - "${POSTGRES_PORT}:${POSTGRES_PORT}"
volumes: volumes:
- postgres_data:/var/lib/postgresql/data/ - postgres_data:/var/lib/postgresql/data/
restart: always restart: always
@ -25,7 +25,7 @@ services:
restart: always restart: always
entrypoint: ["/opt/app/entrypoint.sh"] entrypoint: ["/opt/app/entrypoint.sh"]
ports: ports:
- "${SERVER_PORT}:${SERVER_PORT}" - "${API_PORT}:${API_PORT}"
depends_on: depends_on:
- db - db
env_file: env_file:

View File

@ -4,13 +4,13 @@ services:
db: db:
image: postgres:15.2 image: postgres:15.2
environment: environment:
POSTGRES_USER: ${DB_USER} POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${DB_NAME} POSTGRES_DB: ${POSTGRES_NAME}
env_file: env_file:
- .env - .env
ports: ports:
- "127.0.0.1:${DB_PORT}:${DB_PORT}" - "127.0.0.1:${API_PORT}:${API_PORT}"
volumes: volumes:
- postgres_data:/var/lib/postgresql/data/ - postgres_data:/var/lib/postgresql/data/
restart: always restart: always

View File

@ -2,11 +2,11 @@ import fastapi
from jose import JWTError, jwt from jose import JWTError, jwt
from pydantic import ValidationError from pydantic import ValidationError
import lib.app.settings as app_settings
from lib.api.v1 import schemas as app_schemas from lib.api.v1 import schemas as app_schemas
from lib.app import settings as app_settings
app = fastapi.FastAPI() app = fastapi.FastAPI()
settings = app_settings.get_settings() settings = app_settings.settings
security = fastapi.security.HTTPBearer() security = fastapi.security.HTTPBearer()
@ -16,7 +16,7 @@ def get_token_data(
) -> app_schemas.entity.Token: ) -> app_schemas.entity.Token:
token = authorization.credentials token = authorization.credentials
try: try:
secret_key = settings.jwt_secret_key secret_key = settings.project.jwt_secret_key
payload = jwt.decode(token, secret_key, algorithms=["HS256"]) payload = jwt.decode(token, secret_key, algorithms=["HS256"])
return app_schemas.entity.Token(**payload) return app_schemas.entity.Token(**payload)
except (JWTError, ValidationError): except (JWTError, ValidationError):

View File

@ -1,18 +1,15 @@
import logging import logging
import logging.config as logging_config
import fastapi import fastapi
from .logger import LOGGING import lib.app.settings as app_settings
from .settings import get_settings
logging_config.dictConfig(LOGGING)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Application: class Application:
def __init__(self) -> None: def __init__(self) -> None:
self.settings = get_settings() self.settings = app_settings
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.producer = None self.producer = None

View File

@ -1,35 +1,26 @@
import functools import logging.config as logging_config
import pydantic import pydantic
import pydantic_settings import pydantic_settings
import lib.app.split_settings as app_split_settings
class DbSettings(pydantic_settings.BaseSettings):
host: str = pydantic.Field("127.0.0.1", validation_alias="db_host")
port: int = pydantic.Field(5432, validation_alias="db_port")
user: str = pydantic.Field(..., validation_alias="db_user")
password: str = pydantic.Field(..., validation_alias="db_password")
name: str = pydantic.Field("db_name", validation_alias="db_name")
class ApiSettings(pydantic_settings.BaseSettings):
host: str = pydantic.Field("0.0.0.0", validation_alias="server_host")
port: int = pydantic.Field(8000, validation_alias="server_port")
class Settings(pydantic_settings.BaseSettings): class Settings(pydantic_settings.BaseSettings):
debug: str = pydantic.Field("false", validation_alias="debug") api: app_split_settings.ApiSettings = pydantic.Field(default_factory=lambda: app_split_settings.ApiSettings())
db: DbSettings = pydantic.Field(default_factory=lambda: DbSettings()) postgres: app_split_settings.PostgresSettings = pydantic.Field(
api: ApiSettings = pydantic.Field(default_factory=lambda: ApiSettings()) default_factory=lambda: app_split_settings.PostgresSettings()
)
jwt_secret_key: str = pydantic.Field(..., validation_alias="jwt_secret_key") logger: app_split_settings.LoggingSettings = pydantic.Field(
default_factory=lambda: app_split_settings.LoggingSettings()
@pydantic.field_validator("debug") )
@classmethod project: app_split_settings.ProjectSettings = pydantic.Field(
def validate_debug(cls, v: str) -> bool: default_factory=lambda: app_split_settings.ProjectSettings()
return v.lower() == "true" )
@functools.lru_cache settings = Settings() # todo Вынести в инициализацию
def get_settings() -> Settings:
return Settings() logging_config.dictConfig( # todo Вынести в инициализацию
app_split_settings.get_logging_config(**settings.logger.model_dump())
)

View File

@ -0,0 +1,12 @@
from .api import *
from .logger import *
from .postgres import *
from .project import *
__all__ = [
"ApiSettings",
"LoggingSettings",
"get_logging_config",
"PostgresSettings",
"ProjectSettings",
]

View File

@ -0,0 +1,15 @@
import pydantic_settings
import lib.app.split_settings.utils as app_split_settings_utils
class ApiSettings(pydantic_settings.BaseSettings):
model_config = pydantic_settings.SettingsConfigDict(
env_file=app_split_settings_utils.ENV_PATH,
env_prefix="API_",
env_file_encoding="utf-8",
extra="ignore",
)
host: str = "0.0.0.0"
port: int = 8000

View File

@ -0,0 +1,79 @@
import pydantic_settings
import lib.app.split_settings.utils as app_split_settings_utils
class LoggingSettings(pydantic_settings.BaseSettings):
model_config = pydantic_settings.SettingsConfigDict(
env_file=app_split_settings_utils.ENV_PATH, env_file_encoding="utf-8", extra="ignore"
)
log_format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
log_default_handlers: list[str] = [
"console",
]
log_level_handlers: str = "INFO"
log_level_loggers: str = "INFO"
log_level_root: str = "INFO"
def get_logging_config(
log_format: str,
log_default_handlers: list[str],
log_level_handlers: str,
log_level_loggers: str,
log_level_root: str,
):
return {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {"format": log_format},
"default": {
"()": "uvicorn.logging.DefaultFormatter",
"fmt": "%(levelprefix)s %(message)s",
"use_colors": None,
},
"access": {
"()": "uvicorn.logging.AccessFormatter",
"fmt": "%(levelprefix)s %(client_addr)s - '%(request_line)s' %(status_code)s",
},
},
"handlers": {
"console": {
"level": log_level_handlers,
"class": "logging.StreamHandler",
"formatter": "verbose",
},
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
},
"access": {
"formatter": "access",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
},
},
"loggers": {
"": {
"handlers": log_default_handlers,
"level": log_level_loggers,
},
"uvicorn.error": {
"level": log_level_loggers,
},
"uvicorn.access": {
"handlers": ["access"],
"level": log_level_loggers,
"propagate": False,
},
},
"root": {
"level": log_level_root,
"formatter": "verbose",
"handlers": log_default_handlers,
},
}

View File

@ -0,0 +1,21 @@
import pydantic
import pydantic_settings
import lib.app.split_settings.utils as app_split_settings_utils
class PostgresSettings(pydantic_settings.BaseSettings):
model_config = pydantic_settings.SettingsConfigDict(
env_file=app_split_settings_utils.ENV_PATH,
env_prefix="POSTGRES_",
env_file_encoding="utf-8",
extra="ignore",
)
name: str = "database_name"
host: str = "localhost"
port: int = 5432
user: str = "app"
password: pydantic.SecretStr = pydantic.Field(
default=..., validation_alias=pydantic.AliasChoices("password", "postgres_password")
)

View File

@ -0,0 +1,19 @@
import pydantic
import pydantic_settings
import lib.app.split_settings.utils as app_split_settings_utils
class ProjectSettings(pydantic_settings.BaseSettings):
model_config = pydantic_settings.SettingsConfigDict(
env_file=app_split_settings_utils.ENV_PATH,
env_file_encoding="utf-8",
extra="ignore",
)
debug: str = "false"
jwt_secret_key: pydantic.SecretStr = pydantic.Field(default=..., validation_alias="jwt_secret_key")
@pydantic.field_validator("debug")
def validate_debug(cls, v: str) -> bool:
return v.lower() == "true"

View File

@ -0,0 +1,4 @@
import pathlib
BASE_PATH = pathlib.Path(__file__).parent.parent.parent.parent.resolve()
ENV_PATH = BASE_PATH / ".env"

View File

@ -3,9 +3,9 @@ import typing
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase from sqlalchemy.orm import DeclarativeBase
from lib.app import settings as app_settings import lib.app.settings as app_settings
settings = app_settings.get_settings() settings = app_settings.settings
# Создаём базовый класс для будущих моделей # Создаём базовый класс для будущих моделей
@ -22,7 +22,7 @@ class AsyncDB:
f"postgresql+asyncpg://{settings.db.user}:{settings.db.password}" f"postgresql+asyncpg://{settings.db.user}:{settings.db.password}"
f"@{settings.db.host}:{settings.db.port}/{settings.db.name}" f"@{settings.db.host}:{settings.db.port}/{settings.db.name}"
) )
self.engine = create_async_engine(self.database_dsn, echo=settings.debug, future=True) self.engine = create_async_engine(self.database_dsn, echo=settings.project.debug, future=True)
self.async_session = async_sessionmaker(self.engine, class_=AsyncSession, expire_on_commit=False) self.async_session = async_sessionmaker(self.engine, class_=AsyncSession, expire_on_commit=False)