fix: единый singleton engine в bd, убраны 17 локальных make_engine()
- bd/__init__.py: добавлены get_engine() и get_session() — один SQLAlchemy engine на весь процесс - make_engine(): pool_size=5, max_overflow=5, pool_timeout=30, pool_recycle=1800 - Все 17 route-файлов: убраны локальные get_session()/make_engine(), импорт из bd - transfer_crud.py: _get_session() → get_session() из bd - init_data_base.py: get_engine() вместо make_engine() - Результат: 78 idle соединений → 1
This commit is contained in:
@@ -47,7 +47,11 @@ def make_engine():
|
|||||||
s.DATABASE_URL_syncpg,
|
s.DATABASE_URL_syncpg,
|
||||||
connect_args=s.PG8000_CONNECT_ARGS,
|
connect_args=s.PG8000_CONNECT_ARGS,
|
||||||
future=True,
|
future=True,
|
||||||
pool_pre_ping=True, # проверяет соединение перед каждым запросом
|
pool_pre_ping=True,
|
||||||
|
pool_size=5,
|
||||||
|
max_overflow=5,
|
||||||
|
pool_timeout=30,
|
||||||
|
pool_recycle=1800,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -70,3 +74,22 @@ def wait_for_db(retries: int = 15, delay: float = 2.0) -> None:
|
|||||||
if attempt < retries:
|
if attempt < retries:
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
raise RuntimeError("PostgreSQL did not become ready in time.")
|
raise RuntimeError("PostgreSQL did not become ready in time.")
|
||||||
|
|
||||||
|
# ── Singleton engine + session factory (один движок на весь процесс) ───────
|
||||||
|
_engine = None
|
||||||
|
_SessionFactory = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_engine():
|
||||||
|
global _engine
|
||||||
|
if _engine is None:
|
||||||
|
_engine = make_engine()
|
||||||
|
return _engine
|
||||||
|
|
||||||
|
|
||||||
|
def get_session():
|
||||||
|
global _SessionFactory
|
||||||
|
if _SessionFactory is None:
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
_SessionFactory = sessionmaker(bind=get_engine(), future=True)
|
||||||
|
return _SessionFactory
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from typing import Optional
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
from bd.tables.users import User
|
from bd.tables.users import User
|
||||||
from route.auth_utils import hash_password, verify_password, create_access_token, SECRET_KEY, ALGORITHM, oauth2_scheme
|
from route.auth_utils import hash_password, verify_password, create_access_token, SECRET_KEY, ALGORITHM, oauth2_scheme
|
||||||
@@ -15,9 +15,6 @@ from redis_db import blacklist_token
|
|||||||
router = APIRouter(tags=["auth"], prefix="/auth")
|
router = APIRouter(tags=["auth"], prefix="/auth")
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
class RegisterIn(BaseModel):
|
class RegisterIn(BaseModel):
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from fastapi.security import OAuth2PasswordBearer
|
|||||||
from jose import JWTError, jwt
|
from jose import JWTError, jwt
|
||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
SECRET_KEY: str = os.getenv("SECRET_KEY", "")
|
SECRET_KEY: str = os.getenv("SECRET_KEY", "")
|
||||||
if not SECRET_KEY:
|
if not SECRET_KEY:
|
||||||
@@ -45,9 +45,6 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -
|
|||||||
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_user(token: str = Depends(oauth2_scheme)):
|
def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from pydantic import BaseModel
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
from bd.tables.choice import Choice
|
from bd.tables.choice import Choice
|
||||||
from bd.tables.question import Question
|
from bd.tables.question import Question
|
||||||
@@ -23,9 +23,6 @@ class ChoiceUpdate(BaseModel):
|
|||||||
position: Optional[int]
|
position: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/", status_code=201, dependencies=[Depends(require_admin_key)])
|
@router.post("/", status_code=201, dependencies=[Depends(require_admin_key)])
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
from bd.tables.question import Question
|
from bd.tables.question import Question
|
||||||
from bd.tables.choice import Choice
|
from bd.tables.choice import Choice
|
||||||
@@ -372,9 +372,6 @@ CHOICE_SCORES: dict[int, float] = {
|
|||||||
CHOICE_LABELS: list[str] = ["++", "+", "0", "-", "--"]
|
CHOICE_LABELS: list[str] = ["++", "+", "0", "-", "--"]
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/glomshtok", status_code=201)
|
@router.post("/glomshtok", status_code=201)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from pydantic import BaseModel
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
from bd.tables.group import Group
|
from bd.tables.group import Group
|
||||||
from route.auth_utils import require_admin_key
|
from route.auth_utils import require_admin_key
|
||||||
@@ -19,9 +19,6 @@ class GroupUpdate(BaseModel):
|
|||||||
name_group: Optional[str] = None
|
name_group: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
def _group_dict(grp: Group) -> dict:
|
def _group_dict(grp: Group) -> dict:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from fastapi import APIRouter, HTTPException
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
from bd.tables.question import Question
|
from bd.tables.question import Question
|
||||||
@@ -141,9 +141,6 @@ DIMENSIONS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/holland", status_code=201)
|
@router.post("/holland", status_code=201)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import importlib
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from bd import Settings, make_engine
|
from bd import Settings, get_engine
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -48,7 +48,7 @@ async def create_tables():
|
|||||||
|
|
||||||
Endpoint вызывается по нажатию кнопки в UI (POST).
|
Endpoint вызывается по нажатию кнопки в UI (POST).
|
||||||
"""
|
"""
|
||||||
engine = make_engine()
|
engine = get_engine()
|
||||||
|
|
||||||
metadatas = collect_metadatas()
|
metadatas = collect_metadatas()
|
||||||
if not metadatas:
|
if not metadatas:
|
||||||
@@ -73,7 +73,7 @@ async def create_tables():
|
|||||||
@router.post("/db/migrate-cascade-user", dependencies=[Depends(require_admin_key)])
|
@router.post("/db/migrate-cascade-user", dependencies=[Depends(require_admin_key)])
|
||||||
async def migrate_cascade_user():
|
async def migrate_cascade_user():
|
||||||
"""Добавляет FK responses.user_id → users.id ON DELETE CASCADE, если ещё не существует."""
|
"""Добавляет FK responses.user_id → users.id ON DELETE CASCADE, если ещё не существует."""
|
||||||
engine = make_engine()
|
engine = get_engine()
|
||||||
try:
|
try:
|
||||||
with engine.connect() as conn:
|
with engine.connect() as conn:
|
||||||
conn.execute(text("""
|
conn.execute(text("""
|
||||||
@@ -97,7 +97,7 @@ async def migrate_cascade_user():
|
|||||||
@router.post("/db/migrate-users", dependencies=[Depends(require_admin_key)])
|
@router.post("/db/migrate-users", dependencies=[Depends(require_admin_key)])
|
||||||
async def migrate_users():
|
async def migrate_users():
|
||||||
"""Добавляет колонки username и hashed_password в таблицу users, если они ещё не существуют."""
|
"""Добавляет колонки username и hashed_password в таблицу users, если они ещё не существуют."""
|
||||||
engine = make_engine()
|
engine = get_engine()
|
||||||
try:
|
try:
|
||||||
with engine.connect() as conn:
|
with engine.connect() as conn:
|
||||||
conn.execute(text("""
|
conn.execute(text("""
|
||||||
@@ -118,7 +118,7 @@ class ClearDBIn(BaseModel):
|
|||||||
@router.post("/db/migrate", dependencies=[Depends(require_admin_key)])
|
@router.post("/db/migrate", dependencies=[Depends(require_admin_key)])
|
||||||
async def migrate_tables():
|
async def migrate_tables():
|
||||||
"""Приводит схему БД в соответствие с моделями: убирает устаревшие колонки, добавляет новые."""
|
"""Приводит схему БД в соответствие с моделями: убирает устаревшие колонки, добавляет новые."""
|
||||||
engine = make_engine()
|
engine = get_engine()
|
||||||
migrations = [
|
migrations = [
|
||||||
# Убираем старый FK и колонку group.user_id (если остался от прежней схемы)
|
# Убираем старый FK и колонку group.user_id (если остался от прежней схемы)
|
||||||
'ALTER TABLE "group" DROP COLUMN IF EXISTS user_id',
|
'ALTER TABLE "group" DROP COLUMN IF EXISTS user_id',
|
||||||
@@ -150,7 +150,7 @@ async def clear_tables(payload: ClearDBIn):
|
|||||||
if not payload.confirm:
|
if not payload.confirm:
|
||||||
raise HTTPException(status_code=400, detail="Confirmation required")
|
raise HTTPException(status_code=400, detail="Confirmation required")
|
||||||
|
|
||||||
engine = make_engine()
|
engine = get_engine()
|
||||||
|
|
||||||
metadatas = collect_metadatas()
|
metadatas = collect_metadatas()
|
||||||
if not metadatas:
|
if not metadatas:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from fastapi import APIRouter, HTTPException
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
from bd.tables.question import Question
|
from bd.tables.question import Question
|
||||||
from bd.tables.choice import Choice
|
from bd.tables.choice import Choice
|
||||||
@@ -159,9 +159,6 @@ DIMENSIONS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/klimov", status_code=201)
|
@router.post("/klimov", status_code=201)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from typing import Optional
|
|||||||
import uuid
|
import uuid
|
||||||
import io
|
import io
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
from bd.tables.organization import Organization
|
from bd.tables.organization import Organization
|
||||||
from route.auth_utils import require_admin_key
|
from route.auth_utils import require_admin_key
|
||||||
@@ -20,9 +20,6 @@ class OrganizationUpdate(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
def _org_dict(org: Organization) -> dict:
|
def _org_dict(org: Organization) -> dict:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from pydantic import BaseModel
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
from bd.tables.question import Question
|
from bd.tables.question import Question
|
||||||
@@ -31,9 +31,6 @@ class PollIn(BaseModel):
|
|||||||
questions: Optional[List[QuestionIn]]
|
questions: Optional[List[QuestionIn]]
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/", status_code=201, dependencies=[Depends(require_admin_key)])
|
@router.post("/", status_code=201, dependencies=[Depends(require_admin_key)])
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from pydantic import BaseModel
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
from bd.tables.question import Question
|
from bd.tables.question import Question
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
@@ -25,9 +25,6 @@ class QuestionUpdate(BaseModel):
|
|||||||
position: Optional[int]
|
position: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/", status_code=201, dependencies=[Depends(require_admin_key)])
|
@router.post("/", status_code=201, dependencies=[Depends(require_admin_key)])
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from fastapi.responses import FileResponse
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlalchemy.orm import Session as SASession
|
from sqlalchemy.orm import Session as SASession
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
from bd.tables.response import Response, Answer
|
from bd.tables.response import Response, Answer
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
from bd.tables.scale import ScaleDimension, ChoiceScore
|
from bd.tables.scale import ScaleDimension, ChoiceScore
|
||||||
@@ -32,9 +32,6 @@ from route.radar_svg_gen import save_radar_svg
|
|||||||
router = APIRouter(tags=["radar"])
|
router = APIRouter(tags=["radar"])
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from pydantic import BaseModel
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
from bd.tables.question import Question
|
from bd.tables.question import Question
|
||||||
@@ -26,9 +26,6 @@ class ResponseIn(BaseModel):
|
|||||||
answers: List[AnswerIn]
|
answers: List[AnswerIn]
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{poll_id}/responses", status_code=201)
|
@router.post("/{poll_id}/responses", status_code=201)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from typing import List, Optional
|
|||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
from bd.tables.choice import Choice
|
from bd.tables.choice import Choice
|
||||||
from bd.tables.scale import ScaleDimension, ChoiceScore
|
from bd.tables.scale import ScaleDimension, ChoiceScore
|
||||||
@@ -27,9 +27,6 @@ from bd.tables.scale import ScaleDimension, ChoiceScore
|
|||||||
router = APIRouter(tags=["scale"])
|
router = APIRouter(tags=["scale"])
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from sqlalchemy import func
|
|||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
from bd.tables.response import Response
|
from bd.tables.response import Response
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
from bd.tables.group import Group
|
from bd.tables.group import Group
|
||||||
@@ -26,9 +26,6 @@ def _cache_key(*parts) -> str:
|
|||||||
return hashlib.md5(raw.encode()).hexdigest()
|
return hashlib.md5(raw.encode()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/summary")
|
@router.get("/summary")
|
||||||
|
|||||||
@@ -24,9 +24,7 @@ from pathlib import Path
|
|||||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
||||||
from sqlalchemy.orm import sessionmaker
|
from bd import get_session
|
||||||
|
|
||||||
from bd import make_engine
|
|
||||||
from bd.tables.organization import Organization
|
from bd.tables.organization import Organization
|
||||||
from bd.tables.group import Group
|
from bd.tables.group import Group
|
||||||
from bd.tables.poll import Poll
|
from bd.tables.poll import Poll
|
||||||
@@ -61,8 +59,6 @@ _TABLES: list[tuple[str, object]] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def _get_session():
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
def _row_to_dict(row) -> dict:
|
def _row_to_dict(row) -> dict:
|
||||||
@@ -89,7 +85,7 @@ def _row_to_dict(row) -> dict:
|
|||||||
dependencies=[Depends(require_admin_key)],
|
dependencies=[Depends(require_admin_key)],
|
||||||
)
|
)
|
||||||
def export_data():
|
def export_data():
|
||||||
Session = _get_session()
|
Session = get_session()
|
||||||
buf = io.BytesIO()
|
buf = io.BytesIO()
|
||||||
counts = {}
|
counts = {}
|
||||||
|
|
||||||
@@ -147,7 +143,7 @@ def import_data(file: UploadFile = File(...)):
|
|||||||
if fmt not in (EXPORT_FORMAT_VERSION, "1.0"):
|
if fmt not in (EXPORT_FORMAT_VERSION, "1.0"):
|
||||||
raise HTTPException(status_code=400, detail=f"Неподдерживаемая версия формата: {fmt}")
|
raise HTTPException(status_code=400, detail=f"Неподдерживаемая версия формата: {fmt}")
|
||||||
|
|
||||||
Session = _get_session()
|
Session = get_session()
|
||||||
result = {"inserted": {}, "skipped": {}}
|
result = {"inserted": {}, "skipped": {}}
|
||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
@@ -218,7 +214,7 @@ backup_router = APIRouter(prefix="/backup", tags=["backup"])
|
|||||||
)
|
)
|
||||||
def backup_export():
|
def backup_export():
|
||||||
"""Идентично /transfer/export, но в имени файла указан префикс backup_."""
|
"""Идентично /transfer/export, но в имени файла указан префикс backup_."""
|
||||||
Session = _get_session()
|
Session = get_session()
|
||||||
buf = io.BytesIO()
|
buf = io.BytesIO()
|
||||||
counts = {}
|
counts = {}
|
||||||
|
|
||||||
@@ -279,7 +275,7 @@ def backup_restore(file: UploadFile = File(...)):
|
|||||||
if fmt not in (EXPORT_FORMAT_VERSION, "1.0"):
|
if fmt not in (EXPORT_FORMAT_VERSION, "1.0"):
|
||||||
raise HTTPException(status_code=400, detail=f"Неподдерживаемая версия формата: {fmt}")
|
raise HTTPException(status_code=400, detail=f"Неподдерживаемая версия формата: {fmt}")
|
||||||
|
|
||||||
Session = _get_session()
|
Session = get_session()
|
||||||
deleted_counts: dict[str, int] = {}
|
deleted_counts: dict[str, int] = {}
|
||||||
inserted_counts: dict[str, int] = {}
|
inserted_counts: dict[str, int] = {}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from pydantic import BaseModel, field_validator
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from bd import make_engine
|
from bd import get_session
|
||||||
|
|
||||||
from bd.tables.users import User
|
from bd.tables.users import User
|
||||||
from route.auth_utils import get_current_user, require_admin_key, hash_password
|
from route.auth_utils import get_current_user, require_admin_key, hash_password
|
||||||
@@ -42,9 +42,6 @@ class UserUpdate(BaseModel):
|
|||||||
organization_id: Optional[str] = None
|
organization_id: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
return sessionmaker(bind=make_engine(), future=True)
|
|
||||||
|
|
||||||
|
|
||||||
def _user_dict(user: User) -> dict:
|
def _user_dict(user: User) -> dict:
|
||||||
|
|||||||
Reference in New Issue
Block a user