отрефакторил и вроде работает
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
Details
This commit is contained in:
parent
aaa88b7442
commit
d6b36ad456
|
@ -1,3 +1,7 @@
|
||||||
BLOCKING_IP__DB__URL=postgresql+asyncpg://username:password@localhost:5432/dbname
|
BLOCKING_IP__DB__drivername=postgresql+asyncpg
|
||||||
|
BLOCKING_IP__DB__username=username
|
||||||
|
BLOCKING_IP__DB__password=password
|
||||||
|
BLOCKING_IP__DB__host=localhost
|
||||||
|
BLOCKING_IP__DB__port=5432
|
||||||
|
BLOCKING_IP__DB__database=dbname
|
||||||
BLOCKING_IP__DB__ECHO=0
|
BLOCKING_IP__DB__ECHO=0
|
||||||
ALEMBIC_CONFIG=alembic/alembic.ini
|
|
|
@ -20,7 +20,7 @@ if config.config_file_name is not None:
|
||||||
# for 'autogenerate' support
|
# for 'autogenerate' support
|
||||||
# from myapp import mymodel
|
# from myapp import mymodel
|
||||||
# target_metadata = mymodel.Base.metadata
|
# target_metadata = mymodel.Base.metadata
|
||||||
from core.models import Base
|
from core.models.base import Base
|
||||||
|
|
||||||
target_metadata = Base.metadata
|
target_metadata = Base.metadata
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
from api.swagger import router as swagger_router
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from core.config import settings
|
from core.config import settings
|
||||||
|
|
||||||
from api.ip import router as ip_router
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
router.include_router(
|
router.include_router(
|
||||||
ip_router,
|
swagger_router,
|
||||||
prefix=settings.api.ip,
|
prefix=settings.prefix.swagger,
|
||||||
)
|
)
|
||||||
|
|
17
api/ip.py
17
api/ip.py
|
@ -1,17 +0,0 @@
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
|
||||||
|
|
||||||
from core.models import db_helper
|
|
||||||
from core.schemas.ip import IpRead
|
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter(
|
|
||||||
tags=["Ip"],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=list[IpRead])
|
|
||||||
async def get_ip(session: AsyncSession = Depends(db_helper.session_dependency)):
|
|
||||||
pass
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from fastapi.openapi.docs import get_swagger_ui_html
|
||||||
|
|
||||||
|
from core.config import settings
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", include_in_schema=False)
|
||||||
|
async def custom_swagger_ui_html():
|
||||||
|
return get_swagger_ui_html(
|
||||||
|
openapi_url=settings.swagger.openapi_url,
|
||||||
|
title=settings.swagger.title,
|
||||||
|
oauth2_redirect_url=settings.swagger.oauth2_redirect_url,
|
||||||
|
swagger_js_url=settings.swagger.swagger_js_url,
|
||||||
|
swagger_css_url=settings.swagger.swagger_css_url,
|
||||||
|
swagger_favicon_url=settings.swagger.swagger_favicon_url,
|
||||||
|
)
|
|
@ -5,6 +5,8 @@ from pydantic import BaseModel, PostgresDsn
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).parent.parent
|
BASE_DIR = Path(__file__).parent.parent
|
||||||
|
TEMPLATES_DIR = BASE_DIR / "templates"
|
||||||
|
STATIC_DIR = TEMPLATES_DIR / "static"
|
||||||
|
|
||||||
|
|
||||||
class RunConfig(BaseModel):
|
class RunConfig(BaseModel):
|
||||||
|
@ -14,19 +16,39 @@ class RunConfig(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class DatabaseConfig(BaseModel):
|
class DatabaseConfig(BaseModel):
|
||||||
url: str
|
drivername: str = "postgresql+asyncpg"
|
||||||
|
username: str = "username"
|
||||||
|
password: str = "password"
|
||||||
|
host: str = "localhost"
|
||||||
|
port: int = 5432
|
||||||
|
database: str = "dbname"
|
||||||
|
url: PostgresDsn = f"{drivername}://{username}:{password}@{host}:{port}/{database}"
|
||||||
echo: bool = False
|
echo: bool = False
|
||||||
echo_pool: bool = False
|
echo_pool: bool = False
|
||||||
pool_size: int = 50
|
pool_size: int = 50
|
||||||
max_overflow: int = 10
|
max_overflow: int = 10
|
||||||
|
naming_convention: dict[str, str] = {
|
||||||
|
"ix": "ix_%(column_0_label)s",
|
||||||
|
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
||||||
|
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
||||||
|
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
||||||
|
"pk": "pk_%(table_name)s",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ApiPrefix(BaseModel):
|
class PrefixConfig(BaseModel):
|
||||||
|
api: str = "/api"
|
||||||
ip: str = "/api/ip"
|
ip: str = "/api/ip"
|
||||||
|
swagger: str = "/docs"
|
||||||
|
|
||||||
|
|
||||||
class WebPrefix(BaseModel):
|
class SwaggerConfig(BaseModel):
|
||||||
pass
|
openapi_url: str = "/openapi.json"
|
||||||
|
title: str = "Blocked IP API"
|
||||||
|
oauth2_redirect_url: str = "/docs/oauth2-redirect"
|
||||||
|
swagger_js_url: str = "/static/swagger/swagger-ui-bundle.js"
|
||||||
|
swagger_css_url: str = "/static/swagger/swagger-ui.css"
|
||||||
|
swagger_favicon_url: str = "/static/swagger/favicon.png"
|
||||||
|
|
||||||
|
|
||||||
class LogConfig(BaseModel):
|
class LogConfig(BaseModel):
|
||||||
|
@ -45,13 +67,21 @@ class Settings(BaseSettings):
|
||||||
env_prefix="BLOCKING_IP__",
|
env_prefix="BLOCKING_IP__",
|
||||||
)
|
)
|
||||||
run: RunConfig = RunConfig()
|
run: RunConfig = RunConfig()
|
||||||
api: ApiPrefix = ApiPrefix()
|
prefix: PrefixConfig = PrefixConfig()
|
||||||
web: WebPrefix = WebPrefix()
|
|
||||||
log: LogConfig = LogConfig()
|
log: LogConfig = LogConfig()
|
||||||
|
swagger: SwaggerConfig = SwaggerConfig()
|
||||||
db: DatabaseConfig
|
db: DatabaseConfig
|
||||||
|
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
settings.db.url = (
|
||||||
|
f"{settings.db.drivername}://"
|
||||||
|
f"{settings.db.username}:"
|
||||||
|
f"{settings.db.password}@"
|
||||||
|
f"{settings.db.host}:"
|
||||||
|
f"{settings.db.port}/"
|
||||||
|
f"{settings.db.database}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def config_logging(level: str) -> None:
|
def config_logging(level: str) -> None:
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
__all__ = (
|
from core.models.base import Base
|
||||||
|
from core.models.db_helper import db_helper
|
||||||
|
from core.models.ip import Ip
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"db_helper",
|
||||||
"Base",
|
"Base",
|
||||||
"Ip",
|
"Ip",
|
||||||
"db_helper",
|
]
|
||||||
)
|
|
||||||
|
|
||||||
from .base import Base
|
|
||||||
from .ip import Ip
|
|
||||||
from .db_helper import DatabaseHelper, db_helper
|
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
|
from sqlalchemy import MetaData
|
||||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr
|
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr
|
||||||
|
|
||||||
|
from utils import camel_case_to_snake_case
|
||||||
|
from core.config import settings
|
||||||
|
|
||||||
|
|
||||||
class Base(DeclarativeBase):
|
class Base(DeclarativeBase):
|
||||||
"""
|
__abstract__ = True
|
||||||
Базовый класс для описания моделей.
|
metadata = MetaData(
|
||||||
"""
|
naming_convention=settings.db.naming_convention,
|
||||||
|
)
|
||||||
__abstract__ = True # Что бы эта таблица не создавалась в базе данных
|
|
||||||
|
|
||||||
@declared_attr.directive
|
@declared_attr.directive
|
||||||
def __tablename__(cls) -> str:
|
def __tablename__(cls) -> str:
|
||||||
return f"{cls.__name__.lower()}s"
|
return f"{camel_case_to_snake_case(cls.__name__)}s"
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
|
|
@ -1,43 +1,57 @@
|
||||||
from asyncio import current_task
|
from typing import AsyncGenerator
|
||||||
|
|
||||||
from sqlalchemy.ext.asyncio import (
|
from sqlalchemy.ext.asyncio import (
|
||||||
AsyncSession,
|
AsyncSession,
|
||||||
create_async_engine,
|
create_async_engine,
|
||||||
async_sessionmaker,
|
async_sessionmaker,
|
||||||
async_scoped_session,
|
AsyncEngine,
|
||||||
)
|
)
|
||||||
|
|
||||||
from core.config import settings
|
from core.config import settings
|
||||||
|
|
||||||
|
|
||||||
class DatabaseHelper:
|
class DatabaseHelper:
|
||||||
def __init__(self, url: str, echo: bool = False):
|
def __init__(
|
||||||
self.engine = create_async_engine(
|
self,
|
||||||
|
url: str,
|
||||||
|
echo: bool,
|
||||||
|
echo_pool: bool,
|
||||||
|
pool_size: int,
|
||||||
|
max_overflow: int,
|
||||||
|
) -> None:
|
||||||
|
self.engine: AsyncEngine = create_async_engine(
|
||||||
url=url,
|
url=url,
|
||||||
echo=echo,
|
echo=echo,
|
||||||
|
echo_pool=echo_pool,
|
||||||
|
pool_size=pool_size,
|
||||||
|
max_overflow=max_overflow,
|
||||||
)
|
)
|
||||||
self.session_factory = async_sessionmaker(
|
self.session_factory: async_sessionmaker[AsyncSession] = async_sessionmaker(
|
||||||
bind=self.engine,
|
bind=self.engine,
|
||||||
autoflush=False,
|
autoflush=False,
|
||||||
autocommit=False,
|
autocommit=False,
|
||||||
expire_on_commit=False,
|
expire_on_commit=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_scoped_session(self):
|
# def get_scoped_session(self):
|
||||||
session = async_scoped_session(
|
# session = async_scoped_session(
|
||||||
session_factory=self.session_factory,
|
# session_factory=self.session_factory,
|
||||||
scopefunc=current_task,
|
# scopefunc=current_task,
|
||||||
)
|
# )
|
||||||
return session
|
# return session
|
||||||
|
|
||||||
async def session_dependency(self) -> AsyncSession:
|
async def dispose(self) -> None:
|
||||||
|
await self.engine.dispose()
|
||||||
|
|
||||||
|
async def session_getter(self) -> AsyncGenerator[AsyncSession, None]:
|
||||||
async with self.session_factory() as session:
|
async with self.session_factory() as session:
|
||||||
yield session
|
yield session
|
||||||
await session.close()
|
|
||||||
|
|
||||||
|
|
||||||
db_helper = DatabaseHelper(
|
db_helper = DatabaseHelper(
|
||||||
url=settings.db.url,
|
url=str(settings.db.url),
|
||||||
echo=settings.db.echo,
|
echo=settings.db.echo,
|
||||||
|
echo_pool=settings.db.echo_pool,
|
||||||
|
pool_size=settings.db.pool_size,
|
||||||
|
max_overflow=settings.db.max_overflow,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from sqlalchemy.orm import Mapped
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
from sqlalchemy.orm import mapped_column
|
|
||||||
from sqlalchemy.orm import relationship
|
from core.models.base import Base
|
||||||
from .base import Base
|
|
||||||
|
|
||||||
|
|
||||||
class Ip(Base):
|
class Ip(Base):
|
||||||
|
|
|
@ -16,5 +16,17 @@ class IpBase(BaseModel):
|
||||||
domain: str
|
domain: str
|
||||||
|
|
||||||
|
|
||||||
|
class IpCreate(IpBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IpRead(IpBase):
|
class IpRead(IpBase):
|
||||||
id: int
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class IpUpdate(IpBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class IpDelete(IpBase):
|
||||||
|
pass
|
||||||
|
|
27
main.py
27
main.py
|
@ -3,13 +3,13 @@ from contextlib import asynccontextmanager
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.openapi.docs import get_swagger_ui_html
|
|
||||||
from fastapi.responses import ORJSONResponse
|
from fastapi.responses import ORJSONResponse
|
||||||
from starlette.staticfiles import StaticFiles
|
from starlette.staticfiles import StaticFiles
|
||||||
from api import router as api_router
|
|
||||||
|
|
||||||
from core.config import BASE_DIR, settings
|
from core.config import STATIC_DIR, settings
|
||||||
from core.models import db_helper
|
from core.models.db_helper import db_helper
|
||||||
|
|
||||||
|
from api import router as swagger_router
|
||||||
|
|
||||||
log = logging.getLogger()
|
log = logging.getLogger()
|
||||||
|
|
||||||
|
@ -26,24 +26,9 @@ main_app = FastAPI(
|
||||||
docs_url=None,
|
docs_url=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
main_app.include_router(swagger_router)
|
||||||
|
|
||||||
main_app.mount(
|
main_app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
||||||
"/static", StaticFiles(directory=BASE_DIR / "web" / "static"), name="static"
|
|
||||||
)
|
|
||||||
main_app.include_router(api_router)
|
|
||||||
|
|
||||||
|
|
||||||
@main_app.get("/docs", include_in_schema=False)
|
|
||||||
async def custom_swagger_ui_html():
|
|
||||||
return get_swagger_ui_html(
|
|
||||||
openapi_url=main_app.openapi_url,
|
|
||||||
title="Blocked IP API",
|
|
||||||
oauth2_redirect_url=main_app.swagger_ui_oauth2_redirect_url,
|
|
||||||
swagger_js_url="/static/swagger/swagger-ui-bundle.js",
|
|
||||||
swagger_css_url="/static/swagger/swagger-ui.css",
|
|
||||||
swagger_favicon_url="/static/swagger/favicon.png",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
|
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
from utils.case_converter import camel_case_to_snake_case
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"camel_case_to_snake_case",
|
||||||
|
]
|
|
@ -0,0 +1,29 @@
|
||||||
|
"""
|
||||||
|
Taken from
|
||||||
|
https://github.com/mahenzon/ri-sdk-python-wrapper/blob/master/ri_sdk_codegen/utils/case_converter.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def camel_case_to_snake_case(input_str: str) -> str:
|
||||||
|
"""
|
||||||
|
>>> camel_case_to_snake_case("SomeSDK")
|
||||||
|
'some_sdk'
|
||||||
|
>>> camel_case_to_snake_case("RServoDrive")
|
||||||
|
'r_servo_drive'
|
||||||
|
>>> camel_case_to_snake_case("SDKDemo")
|
||||||
|
'sdk_demo'
|
||||||
|
"""
|
||||||
|
chars = []
|
||||||
|
for c_idx, char in enumerate(input_str):
|
||||||
|
if c_idx and char.isupper():
|
||||||
|
nxt_idx = c_idx + 1
|
||||||
|
# idea of the flag is to separate abbreviations
|
||||||
|
# as new words, show them in lower case
|
||||||
|
flag = nxt_idx >= len(input_str) or input_str[nxt_idx].isupper()
|
||||||
|
prev_char = input_str[c_idx - 1]
|
||||||
|
if prev_char.isupper() and flag:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
chars.append("_")
|
||||||
|
chars.append(char.lower())
|
||||||
|
return "".join(chars)
|
Loading…
Reference in New Issue