Commit inicial
This commit is contained in:
commit
5385af660d
10 changed files with 173 additions and 0 deletions
33
app/core/configs.py
Normal file
33
app/core/configs.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
from pathlib import Path
|
||||
|
||||
from pydantic import SecretStr
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
# Raiz do projeto (api-podium/), pra achar o .env independente de onde a API for iniciada
|
||||
RAIZ_PROJETO = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=RAIZ_PROJETO / '.env',
|
||||
env_file_encoding='utf-8',
|
||||
env_ignore_empty=True, # variável vazia no .env conta como não configurada
|
||||
extra='ignore', # outras variáveis no .env não geram erro
|
||||
)
|
||||
|
||||
# SQL Server
|
||||
db_host: str
|
||||
db_porta: int = 1433
|
||||
db_database: str
|
||||
db_login: str
|
||||
db_senha: SecretStr # SecretStr evita que a senha apareça em print/log
|
||||
db_driver: str = 'ODBC Driver 17 for SQL Server'
|
||||
|
||||
# Pool de conexões
|
||||
db_pool_size: int = 5
|
||||
db_pool_max_overflow: int = 10
|
||||
|
||||
# Configs da api
|
||||
port: int
|
||||
|
||||
settings = Settings()
|
||||
47
app/core/database.py
Normal file
47
app/core/database.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
from collections.abc import Iterator
|
||||
from typing import Annotated
|
||||
|
||||
import pyodbc
|
||||
from fastapi import Depends
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.engine import URL
|
||||
from sqlalchemy.pool import PoolProxiedConnection
|
||||
|
||||
from app.core.configs import settings
|
||||
|
||||
# O pool do SQLAlchemy substitui o pool interno do ODBC. Com os dois ligados,
|
||||
# conexões que o SQLAlchemy descarta continuam abertas no driver.
|
||||
pyodbc.pooling = False
|
||||
|
||||
|
||||
def _escapar(valor: str) -> str:
|
||||
# Envolve o valor em chaves pra que ; ou } na senha não quebrem a connection string
|
||||
return '{' + valor.replace('}', '}}') + '}'
|
||||
|
||||
|
||||
connection_string = (
|
||||
f"DRIVER={_escapar(settings.db_driver)};"
|
||||
f"SERVER={settings.db_host},{settings.db_porta};"
|
||||
f"DATABASE={_escapar(settings.db_database)};"
|
||||
f"UID={_escapar(settings.db_login)};"
|
||||
f"PWD={_escapar(settings.db_senha.get_secret_value())};"
|
||||
)
|
||||
|
||||
engine = create_engine(
|
||||
URL.create('mssql+pyodbc', query={'odbc_connect': connection_string}),
|
||||
pool_size=settings.db_pool_size, # conexões mantidas abertas
|
||||
max_overflow=settings.db_pool_max_overflow, # extras permitidas em pico de acesso
|
||||
pool_pre_ping=True, # testa a conexão antes de entregar (o servidor pode ter derrubado)
|
||||
pool_recycle=1800, # renova conexões com mais de 30 min
|
||||
)
|
||||
|
||||
|
||||
def get_conexao() -> Iterator[PoolProxiedConnection]:
|
||||
"""Pega uma conexão do pool e devolve ao final da requisição."""
|
||||
conexao = engine.raw_connection()
|
||||
try:
|
||||
yield conexao
|
||||
finally:
|
||||
conexao.close() # devolve ao pool, não fecha de verdade
|
||||
|
||||
Conexao = Annotated[PoolProxiedConnection, Depends(get_conexao)]
|
||||
Loading…
Add table
Add a link
Reference in a new issue