From 79568954bc0fb9b904d4bf30d24aa26e94f88762 Mon Sep 17 00:00:00 2001 From: "MH.Dmitrii" Date: Mon, 22 Sep 2025 14:22:46 +0300 Subject: [PATCH] db replica base --- server/backend/endpoints.py | 14 +++++++------- server/database/db.py | 12 ++++++------ server/database/db_balancer/master.yaml | 12 ++++++++++++ server/database/db_balancer/pgpool.conf | 20 ++++++++++++++++++++ server/database/db_balancer/replica.yaml | 11 +++++++++++ server/front/login/js.js | 16 +++++++++------- 6 files changed, 65 insertions(+), 20 deletions(-) create mode 100644 server/database/db_balancer/master.yaml create mode 100644 server/database/db_balancer/pgpool.conf create mode 100644 server/database/db_balancer/replica.yaml diff --git a/server/backend/endpoints.py b/server/backend/endpoints.py index 046f7ac..0faf14e 100644 --- a/server/backend/endpoints.py +++ b/server/backend/endpoints.py @@ -38,7 +38,7 @@ async def get_all_rows(current_user: str = Depends(JWT.current_user)): raise HTTPException(status_code=404, detail="The user isn't found") @api.get("/get_user_by_email/{email}", response_model=pydentic.CreateUser) async def GetUserbyEmail(email:str, current_user: str = Depends(JWT.current_user)): - user = await db.GetUserbyEmail(email) + user = await db.get_user_by_email(email) if user: return user else: @@ -46,18 +46,18 @@ async def GetUserbyEmail(email:str, current_user: str = Depends(JWT.current_user @api.post("/user_create", response_model=pydentic.CreateUser) async def create_user(row:pydentic.CreateUser): new_row = pydentic.CreateUser(email=row.email, description=row.description, activated = row.activated, password = row.password) - await db.CreateUser(new_row) + await db.create_user(new_row) return new_row @api.delete("/user_delete/{email}", response_model=pydentic.CreateUser) async def delete_user(email:str,current_user: str = Depends(JWT.current_user)): - user = await db.GetUserbyEmail(email) + user = await db.get_user_by_email(email) if not user: raise HTTPException(status_code=404, detail="The user isn't found") - await db.DeleteUser(email) + await db.delete_user(email) return user @api.put("/user_update/{email}", response_model=pydentic.CreateUser) async def update_user(email:str, updated_row: pydentic.UserUpdate, current_user: str = Depends(JWT.current_user)): - user = await db.GetUserbyEmail(email) + user = await db.get_user_by_email(email) if not user: raise HTTPException(status_code=404, detail="The user isn't found") changed = False @@ -74,14 +74,14 @@ async def update_user(email:str, updated_row: pydentic.UserUpdate, current_user: user.password = updated_row.password changed = True if changed: - await db.UpdateUser(user) + await db.update_user(user) else: pass return user @api.post("/login") async def login_user(form_data: OAuth2PasswordRequestForm = Depends()): creds = pydentic.UserLogin(email=form_data.username, password=form_data.password) - user = await db.LoginUser(creds) + user = await db.login_user(creds) if not user: raise HTTPException(status_code=401, detail="The user isn't found") diff --git a/server/database/db.py b/server/database/db.py index 8a8f1e0..cfd4cc7 100644 --- a/server/database/db.py +++ b/server/database/db.py @@ -11,7 +11,7 @@ db_folder.mkdir(parents=True, exist_ok=True) db_path = db_folder / "example.db" async_engine = create_async_engine(f"sqlite+aiosqlite:///{db_path}", echo=True) #sqlite+aiosqlite — тип БД + async-драйвер ///example.db — путь к файлу (три слэша, если путь относительный; четыре, если абсолютный - +#async_engine = create_async_engine( "postgresql+asyncpg://user:pass@host:5432/mydb", echo=True) #Можно указать Pgpool-II для psql или proxysql для mysql mariadb from passlib.context import CryptContext #Hash password pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") @@ -37,13 +37,13 @@ class User(Base): async def init_db(): async with async_engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) -async def CreateUser(user_info): +async def create_user(user_info): async with AsyncSessionLocal() as session: new_user = User(email=user_info.email, description=user_info.description, activated=user_info.activated, password=hash_password(user_info.password)) session.add(new_user) await session.commit() await session.refresh(new_user) -async def GetUserbyEmail(email): +async def get_user_by_email(email): async with AsyncSessionLocal() as session: result = await session.execute(select(User).where(User.email==email)) user = result.scalar_one_or_none() @@ -53,7 +53,7 @@ async def get_all_rows(): result = await session.execute(select(User)) users = result.scalars().all() return users -async def UpdateUser(user_info): +async def update_user(user_info): async with AsyncSessionLocal() as session: result = await session.execute(select(User).where(User.id==user_info.id)) user = result.scalar_one_or_none() @@ -63,14 +63,14 @@ async def UpdateUser(user_info): user.activated = user_info.activated user.password = hash_password(user_info.password) await session.commit() -async def DeleteUser(email): +async def delete_user(email): async with AsyncSessionLocal() as session: result = await session.execute(select(User).where(User.email==email)) user = result.scalar_one_or_none() if user: await session.delete(user) await session.commit() -async def LoginUser(user_info): +async def login_user(user_info): async with AsyncSessionLocal() as session: result = await session.execute(select(User).where(User.email == user_info.email)) user = result.scalar_one_or_none() diff --git a/server/database/db_balancer/master.yaml b/server/database/db_balancer/master.yaml new file mode 100644 index 0000000..a39c334 --- /dev/null +++ b/server/database/db_balancer/master.yaml @@ -0,0 +1,12 @@ +services: + postgres-master: + image: bitnami/postgresql:16 + environment: + - POSTGRESQL_REPLICATION_MODE=master + - POSTGRESQL_REPLICATION_USER=repl_user + - POSTGRESQL_REPLICATION_PASSWORD=repl_pass + - POSTGRESQL_USERNAME=admin + - POSTGRESQL_PASSWORD=admin + - POSTGRESQL_DATABASE=mydb + ports: + - '5432:5432' \ No newline at end of file diff --git a/server/database/db_balancer/pgpool.conf b/server/database/db_balancer/pgpool.conf new file mode 100644 index 0000000..130e124 --- /dev/null +++ b/server/database/db_balancer/pgpool.conf @@ -0,0 +1,20 @@ +#Это если вдруг мне когда нибудь приспичит сделать sql balancer с распределенными бд через pgpool-II + +backend_hostname0 = 'pg-master' +backend_port0 = 5432 +backend_weight0 = 1 +backend_flag0 = 'ALWAYS_PRIMARY' + +backend_hostname1 = 'pg-replica1' +backend_port1 = 5432 +backend_weight1 = 1 +backend_flag1 = 'ALLOW_TO_FAILOVER' + +backend_hostname2 = 'pg-replica2' +backend_port2 = 5432 +backend_weight2 = 1 #сколько запросов куда отправлять 1/1 например = 50/50 +backend_flag2 = 'ALLOW_TO_FAILOVER' + +load_balance_mode = on +replication_mode = off #не юзают, тк устарело и глючно +master_slave_mode = on \ No newline at end of file diff --git a/server/database/db_balancer/replica.yaml b/server/database/db_balancer/replica.yaml new file mode 100644 index 0000000..0d92cb7 --- /dev/null +++ b/server/database/db_balancer/replica.yaml @@ -0,0 +1,11 @@ +services: + postgres-replica: + image: bitnami/postgresql:16 + environment: + - POSTGRESQL_REPLICATION_MODE=slave + - POSTGRESQL_REPLICATION_USER=repl_user + - POSTGRESQL_REPLICATION_PASSWORD=repl_pass + - POSTGRESQL_MASTER_HOST=MASTER_IP_ИЛИ_DNS + - POSTGRESQL_PASSWORD=admin + ports: + - "5432:5432" \ No newline at end of file diff --git a/server/front/login/js.js b/server/front/login/js.js index 3ba13e2..a070266 100644 --- a/server/front/login/js.js +++ b/server/front/login/js.js @@ -19,14 +19,16 @@ document.getElementById('loginForm').addEventListener('submit', async function ( password }; try { - const response = await fetch('http://localhost:8000/login', { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' + const response = await fetch("http://localhost:8000/login", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", }, - body: JSON.stringify(userData) - }); + body: new URLSearchParams({ + username: userData.email, // ⚠️ имя поля должно быть username + password: userData.password + }), + }); const data = await response.json(); // читаем только один раз const remember_me = document.getElementById("remember").checked