new web ract
This commit is contained in:
@@ -2,7 +2,21 @@ import os
|
||||
import httpx
|
||||
|
||||
API_URL = os.getenv("API_URL", "http://api:8000")
|
||||
_TIMEOUT = 15.0
|
||||
_TIMEOUT = httpx.Timeout(15.0, connect=5.0)
|
||||
|
||||
# Один клиент на весь процесс — TCP-соединения переиспользуются (keep-alive)
|
||||
_client: httpx.AsyncClient | None = None
|
||||
|
||||
|
||||
def _get() -> httpx.AsyncClient:
|
||||
global _client
|
||||
if _client is None or _client.is_closed:
|
||||
_client = httpx.AsyncClient(
|
||||
base_url=API_URL,
|
||||
timeout=_TIMEOUT,
|
||||
limits=httpx.Limits(max_keepalive_connections=5, max_connections=10),
|
||||
)
|
||||
return _client
|
||||
|
||||
|
||||
def _headers(token: str | None = None) -> dict:
|
||||
@@ -19,17 +33,22 @@ async def get_news(
|
||||
limit: int = 20,
|
||||
category: str | None = None,
|
||||
tag: str | None = None,
|
||||
date_from: str | None = None,
|
||||
date_to: str | None = None,
|
||||
) -> dict | None:
|
||||
params = {"offset": offset, "limit": limit}
|
||||
params: dict = {"offset": offset, "limit": limit}
|
||||
if category:
|
||||
params["category"] = category
|
||||
if tag:
|
||||
params["tag"] = tag
|
||||
if date_from:
|
||||
params["date_from"] = date_from
|
||||
if date_to:
|
||||
params["date_to"] = date_to
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.get(f"{API_URL}/news", params=params)
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().get("/news", params=params)
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
@@ -37,10 +56,9 @@ async def get_news(
|
||||
|
||||
async def get_article(slug: str) -> dict | None:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.get(f"{API_URL}/news/{slug}")
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().get(f"/news/{slug}")
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
@@ -48,10 +66,19 @@ async def get_article(slug: str) -> dict | None:
|
||||
|
||||
async def get_categories() -> list:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.get(f"{API_URL}/news/categories")
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().get("/news/categories")
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return []
|
||||
|
||||
|
||||
async def get_tags(limit: int = 30) -> list:
|
||||
try:
|
||||
r = await _get().get("/news/tags", params={"limit": limit})
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return []
|
||||
@@ -61,14 +88,13 @@ async def get_categories() -> list:
|
||||
|
||||
async def admin_login(username: str, password: str) -> str | None:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.post(
|
||||
f"{API_URL}/admin/login",
|
||||
json={"username": username, "password": password},
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return r.json().get("access_token")
|
||||
return None
|
||||
r = await _get().post(
|
||||
"/admin/login",
|
||||
json={"username": username, "password": password},
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return r.json().get("access_token")
|
||||
return None
|
||||
except httpx.RequestError:
|
||||
return None
|
||||
|
||||
@@ -76,14 +102,13 @@ async def admin_login(username: str, password: str) -> str | None:
|
||||
# ─── Admin: Articles ──────────────────────────────────────────────────────────
|
||||
|
||||
async def admin_list_articles(token: str, offset: int = 0, limit: int = 50, status: str | None = None) -> dict | None:
|
||||
params = {"offset": offset, "limit": limit}
|
||||
params: dict = {"offset": offset, "limit": limit}
|
||||
if status:
|
||||
params["status"] = status
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.get(f"{API_URL}/admin/articles", params=params, headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().get("/admin/articles", params=params, headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
@@ -91,10 +116,9 @@ async def admin_list_articles(token: str, offset: int = 0, limit: int = 50, stat
|
||||
|
||||
async def admin_get_article(token: str, article_id: str) -> dict | None:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.get(f"{API_URL}/admin/articles/{article_id}", headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().get(f"/admin/articles/{article_id}", headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
@@ -102,10 +126,9 @@ async def admin_get_article(token: str, article_id: str) -> dict | None:
|
||||
|
||||
async def admin_create_article(token: str, data: dict) -> dict | None:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.post(f"{API_URL}/admin/articles", json=data, headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().post("/admin/articles", json=data, headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
@@ -113,10 +136,9 @@ async def admin_create_article(token: str, data: dict) -> dict | None:
|
||||
|
||||
async def admin_update_article(token: str, article_id: str, data: dict) -> dict | None:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.put(f"{API_URL}/admin/articles/{article_id}", json=data, headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().put(f"/admin/articles/{article_id}", json=data, headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
@@ -124,18 +146,16 @@ async def admin_update_article(token: str, article_id: str, data: dict) -> dict
|
||||
|
||||
async def admin_delete_article(token: str, article_id: str) -> bool:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.delete(f"{API_URL}/admin/articles/{article_id}", headers=_headers(token))
|
||||
return r.status_code == 200
|
||||
r = await _get().delete(f"/admin/articles/{article_id}", headers=_headers(token))
|
||||
return r.status_code == 200
|
||||
except httpx.RequestError:
|
||||
return False
|
||||
|
||||
|
||||
async def admin_publish_article(token: str, article_id: str) -> bool:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.post(f"{API_URL}/admin/articles/{article_id}/publish", headers=_headers(token))
|
||||
return r.status_code == 200
|
||||
r = await _get().post(f"/admin/articles/{article_id}/publish", headers=_headers(token))
|
||||
return r.status_code == 200
|
||||
except httpx.RequestError:
|
||||
return False
|
||||
|
||||
@@ -144,10 +164,9 @@ async def admin_publish_article(token: str, article_id: str) -> bool:
|
||||
|
||||
async def admin_list_categories(token: str) -> list:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.get(f"{API_URL}/admin/categories", headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().get("/admin/categories", headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return []
|
||||
@@ -155,14 +174,13 @@ async def admin_list_categories(token: str) -> list:
|
||||
|
||||
async def admin_create_category(token: str, name: str, slug: str = "") -> dict | None:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.post(
|
||||
f"{API_URL}/admin/categories",
|
||||
json={"name": name, "slug": slug or None},
|
||||
headers=_headers(token),
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().post(
|
||||
"/admin/categories",
|
||||
json={"name": name, "slug": slug or None},
|
||||
headers=_headers(token),
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
@@ -170,14 +188,13 @@ async def admin_create_category(token: str, name: str, slug: str = "") -> dict |
|
||||
|
||||
async def admin_update_category(token: str, cat_id: str, name: str, slug: str = "") -> dict | None:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.put(
|
||||
f"{API_URL}/admin/categories/{cat_id}",
|
||||
json={"name": name, "slug": slug or None},
|
||||
headers=_headers(token),
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().put(
|
||||
f"/admin/categories/{cat_id}",
|
||||
json={"name": name, "slug": slug or None},
|
||||
headers=_headers(token),
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
@@ -185,9 +202,8 @@ async def admin_update_category(token: str, cat_id: str, name: str, slug: str =
|
||||
|
||||
async def admin_delete_category(token: str, cat_id: str) -> bool:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.delete(f"{API_URL}/admin/categories/{cat_id}", headers=_headers(token))
|
||||
return r.status_code == 200
|
||||
r = await _get().delete(f"/admin/categories/{cat_id}", headers=_headers(token))
|
||||
return r.status_code == 200
|
||||
except httpx.RequestError:
|
||||
return False
|
||||
|
||||
@@ -196,28 +212,26 @@ async def admin_delete_category(token: str, cat_id: str) -> bool:
|
||||
|
||||
async def admin_upload_media(token: str, file_bytes: bytes, filename: str, content_type: str) -> dict | None:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=60.0) as client:
|
||||
r = await client.post(
|
||||
f"{API_URL}/admin/media/upload",
|
||||
files={"file": (filename, file_bytes, content_type)},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().post(
|
||||
"/admin/media/upload",
|
||||
files={"file": (filename, file_bytes, content_type)},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
async def admin_list_media(token: str, offset: int = 0, limit: int = 50, media_type: str | None = None) -> list:
|
||||
params = {"offset": offset, "limit": limit}
|
||||
params: dict = {"offset": offset, "limit": limit}
|
||||
if media_type:
|
||||
params["media_type"] = media_type
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.get(f"{API_URL}/admin/media", params=params, headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
r = await _get().get("/admin/media", params=params, headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return []
|
||||
@@ -225,8 +239,88 @@ async def admin_list_media(token: str, offset: int = 0, limit: int = 50, media_t
|
||||
|
||||
async def admin_delete_media(token: str, media_id: str) -> bool:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
||||
r = await client.delete(f"{API_URL}/admin/media/{media_id}", headers=_headers(token))
|
||||
return r.status_code == 200
|
||||
r = await _get().delete(f"/admin/media/{media_id}", headers=_headers(token))
|
||||
return r.status_code == 200
|
||||
except httpx.RequestError:
|
||||
return False
|
||||
|
||||
|
||||
# ─── Admin: VK Sources ────────────────────────────────────────────────────────
|
||||
|
||||
async def admin_vk_status(token: str) -> dict:
|
||||
try:
|
||||
r = await _get().get("/admin/vk/status", headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return {"configured": False}
|
||||
|
||||
|
||||
async def admin_vk_list_sources(token: str) -> list:
|
||||
try:
|
||||
r = await _get().get("/admin/vk/sources", headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return []
|
||||
|
||||
|
||||
async def admin_vk_add_source(token: str, group_id: str, group_name: str, enabled: bool = True) -> dict | None:
|
||||
try:
|
||||
r = await _get().post(
|
||||
"/admin/vk/sources",
|
||||
json={"group_id": group_id, "group_name": group_name, "enabled": enabled},
|
||||
headers=_headers(token),
|
||||
)
|
||||
if r.status_code == 201:
|
||||
return r.json()
|
||||
return {"error": r.json().get("detail", "Ошибка")}
|
||||
except httpx.RequestError:
|
||||
return None
|
||||
|
||||
|
||||
async def admin_vk_update_source(token: str, source_id: str, group_id: str, group_name: str, enabled: bool) -> dict | None:
|
||||
try:
|
||||
r = await _get().patch(
|
||||
f"/admin/vk/sources/{source_id}",
|
||||
json={"group_id": group_id, "group_name": group_name, "enabled": enabled},
|
||||
headers=_headers(token),
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
async def admin_vk_delete_source(token: str, source_id: str) -> bool:
|
||||
try:
|
||||
r = await _get().delete(f"/admin/vk/sources/{source_id}", headers=_headers(token))
|
||||
return r.status_code == 204
|
||||
except httpx.RequestError:
|
||||
return False
|
||||
|
||||
|
||||
async def admin_vk_import(token: str) -> dict | None:
|
||||
try:
|
||||
r = await _get().post("/admin/vk/import", headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
async def admin_vk_import_history(token: str, source_id: str | None = None) -> dict | None:
|
||||
url = "/admin/vk/import/history"
|
||||
if source_id:
|
||||
url += f"/{source_id}"
|
||||
try:
|
||||
r = await _get().post(url, headers=_headers(token))
|
||||
if r.status_code == 200:
|
||||
return r.json()
|
||||
except httpx.RequestError:
|
||||
pass
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user