rename users

This commit is contained in:
2026-03-31 18:10:12 +05:00
parent 59b3bcd0f4
commit 4e9b89fbe5
22 changed files with 1262 additions and 163 deletions

233
web/api_client.py Normal file
View File

@@ -0,0 +1,233 @@
"""
api_client.py — единое место для всех HTTP-запросов к бэкенду.
Не зависит от Flet. Все функции async, возвращают данные или None/ошибку.
"""
import os
from typing import Any
import httpx
API_URL = os.getenv("API_URL", "http://api:8000")
_TIMEOUT = 10
# ---------------------------------------------------------------------------
# Auth
# ---------------------------------------------------------------------------
async def login(username: str, password: str) -> tuple[str | None, str | None]:
"""
POST /auth/login
Возвращает (token, None) при успехе или (None, error_message) при ошибке.
"""
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.post(
"/auth/login",
data={"username": username, "password": password},
)
if resp.status_code == 200:
return resp.json()["access_token"], None
elif resp.status_code == 401:
return None, "Неверный логин или пароль"
return None, f"Ошибка сервера: {resp.status_code}"
except httpx.ConnectError:
return None, "Не удалось подключиться к серверу"
except Exception as e:
return None, f"Ошибка: {e}"
async def get_me(token: str) -> dict | None:
"""GET /users/me — данные текущего пользователя."""
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.get(
"/users/me",
headers={"Authorization": f"Bearer {token}"},
)
if resp.status_code == 200:
return resp.json()
except Exception:
pass
return None
async def register(
username: str,
password: str,
first_name: str,
last_name: str,
group_id: str | None = None,
organization_id: str | None = None,
) -> str | None:
"""
POST /auth/register
Возвращает None при успехе или строку с ошибкой.
"""
payload: dict[str, Any] = {
"username": username,
"password": password,
"first_name": first_name,
"last_name": last_name,
}
if group_id:
payload["group_id"] = group_id
if organization_id:
payload["organization_id"] = organization_id
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.post("/auth/register", json=payload)
if resp.status_code == 201:
return None
elif resp.status_code == 400:
return resp.json().get("detail", "Ошибка регистрации")
return f"Ошибка сервера: {resp.status_code}"
except httpx.ConnectError:
return "Не удалось подключиться к серверу"
except Exception as e:
return f"Ошибка: {e}"
# ---------------------------------------------------------------------------
# Organizations & Groups
# ---------------------------------------------------------------------------
async def get_organizations() -> list:
"""GET /organizations/"""
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.get("/organizations/")
if resp.status_code == 200:
return resp.json()
except Exception:
pass
return []
async def get_groups() -> list:
"""GET /groups/"""
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.get("/groups/")
if resp.status_code == 200:
return resp.json()
except Exception:
pass
return []
# ---------------------------------------------------------------------------
# Polls
# ---------------------------------------------------------------------------
async def get_polls() -> list:
"""GET /polls/ — список всех опросов."""
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.get("/polls/")
if resp.status_code == 200:
return resp.json()
except Exception:
pass
return []
async def get_poll(poll_id: str) -> dict | None:
"""GET /polls/{poll_id} — опрос с вопросами и вариантами ответов."""
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.get(f"/polls/{poll_id}")
if resp.status_code == 200:
return resp.json()
except Exception:
pass
return None
async def get_response(response_id: str) -> dict | None:
"""GET /polls/responses/{response_id} — детальный ответ с answers."""
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.get(f"/polls/responses/{response_id}")
if resp.status_code == 200:
return resp.json()
except Exception:
pass
return None
async def submit_response(
poll_id: str,
answers: list[dict],
user_id: str | None = None,
group_id: str | None = None,
organization_id: str | None = None,
) -> tuple[str | None, str | None]:
"""
POST /polls/{poll_id}/responses
answers: [{"question_id": ..., "choice_id": ..., "text": ...}, ...]
Возвращает (response_id, None) или (None, error_message).
"""
payload: dict[str, Any] = {"answers": answers}
if user_id:
payload["user_id"] = user_id
if group_id:
payload["group_id"] = group_id
if organization_id:
payload["organization_id"] = organization_id
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.post(f"/polls/{poll_id}/responses", json=payload)
if resp.status_code == 201:
return resp.json()["id"], None
return None, f"Ошибка сервера: {resp.status_code}"
except httpx.ConnectError:
return None, "Не удалось подключиться к серверу"
except Exception as e:
return None, f"Ошибка: {e}"
async def get_user_responses(user_id: str) -> list:
"""GET /polls/users/{user_id}/responses — ответы конкретного пользователя."""
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.get(f"/polls/users/{user_id}/responses")
if resp.status_code == 200:
return resp.json()
except Exception:
pass
return []
async def update_user(
user_id: str,
token: str,
first_name: str | None = None,
last_name: str | None = None,
group_id: str | None = None,
organization_id: str | None = None,
) -> tuple[dict | None, str | None]:
"""PUT /users/{user_id} — обновить данные пользователя."""
payload: dict[str, Any] = {}
if first_name is not None:
payload["first_name"] = first_name
if last_name is not None:
payload["last_name"] = last_name
if group_id is not None:
payload["group_id"] = group_id
if organization_id is not None:
payload["organization_id"] = organization_id
try:
async with httpx.AsyncClient(base_url=API_URL, timeout=_TIMEOUT) as client:
resp = await client.put(
f"/users/{user_id}",
json=payload,
headers={"Authorization": f"Bearer {token}"},
)
if resp.status_code == 200:
return resp.json(), None
return None, f"Ошибка сервера: {resp.status_code}"
except httpx.ConnectError:
return None, "Не удалось подключиться к серверу"
except Exception as e:
return None, f"Ошибка: {e}"