infinity serch
This commit is contained in:
115
api/route/admin_system.py
Normal file
115
api/route/admin_system.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import os
|
||||
import time
|
||||
import asyncio
|
||||
from urllib.parse import urlparse
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from bd.database import get_db, get_redis, DATABASE_URL, REDIS_URL
|
||||
from route.deps import require_admin
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
MINIO_ENDPOINT = os.getenv("MINIO_ENDPOINT", "minio:9000")
|
||||
MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY", "minioadmin")
|
||||
MINIO_SECRET_KEY = os.getenv("MINIO_SECRET_KEY", "minioadmin")
|
||||
MINIO_BUCKET = os.getenv("MINIO_BUCKET", "news-media")
|
||||
MINIO_PUBLIC_URL = os.getenv("MINIO_PUBLIC_URL", "http://localhost:9000")
|
||||
|
||||
|
||||
def _parse_db_url(url: str) -> dict:
|
||||
"""Вытащить host/port/db/user из DATABASE_URL без пароля."""
|
||||
try:
|
||||
# postgresql+asyncpg://user:pass@host:port/db
|
||||
p = urlparse(url.replace("+asyncpg", ""))
|
||||
return {
|
||||
"host": p.hostname or "?",
|
||||
"port": p.port or 5432,
|
||||
"database": (p.path or "/").lstrip("/") or "?",
|
||||
"user": p.username or "?",
|
||||
}
|
||||
except Exception:
|
||||
return {"host": "?", "port": 5432, "database": "?", "user": "?"}
|
||||
|
||||
|
||||
def _parse_redis_url(url: str) -> dict:
|
||||
"""Вытащить host/port из REDIS_URL без пароля."""
|
||||
try:
|
||||
p = urlparse(url)
|
||||
return {
|
||||
"host": p.hostname or "?",
|
||||
"port": p.port or 6379,
|
||||
"db": (p.path or "/0").lstrip("/") or "0",
|
||||
}
|
||||
except Exception:
|
||||
return {"host": "?", "port": 6379, "db": "0"}
|
||||
|
||||
|
||||
async def _check_postgres(db: AsyncSession) -> dict:
|
||||
info = _parse_db_url(DATABASE_URL)
|
||||
t0 = time.monotonic()
|
||||
try:
|
||||
result = await db.execute(text("SELECT version()"))
|
||||
version = result.scalar() or ""
|
||||
latency_ms = round((time.monotonic() - t0) * 1000, 1)
|
||||
# Извлекаем короткую версию: "PostgreSQL 15.3 ..."
|
||||
short_ver = version.split(",")[0] if version else "?"
|
||||
return {"ok": True, "latency_ms": latency_ms, "version": short_ver, **info}
|
||||
except Exception as e:
|
||||
return {"ok": False, "error": str(e), "latency_ms": None, **info}
|
||||
|
||||
|
||||
async def _check_redis() -> dict:
|
||||
info = _parse_redis_url(REDIS_URL)
|
||||
t0 = time.monotonic()
|
||||
try:
|
||||
redis = await get_redis()
|
||||
pong = await redis.ping()
|
||||
latency_ms = round((time.monotonic() - t0) * 1000, 1)
|
||||
server_info = await redis.info("server")
|
||||
version = server_info.get("redis_version", "?")
|
||||
return {"ok": bool(pong), "latency_ms": latency_ms, "version": f"Redis {version}", **info}
|
||||
except Exception as e:
|
||||
return {"ok": False, "error": str(e), "latency_ms": None, **info}
|
||||
|
||||
|
||||
async def _check_minio() -> dict:
|
||||
t0 = time.monotonic()
|
||||
try:
|
||||
from minio import Minio
|
||||
loop = asyncio.get_event_loop()
|
||||
client = Minio(MINIO_ENDPOINT, access_key=MINIO_ACCESS_KEY, secret_key=MINIO_SECRET_KEY, secure=False)
|
||||
exists = await loop.run_in_executor(None, client.bucket_exists, MINIO_BUCKET)
|
||||
latency_ms = round((time.monotonic() - t0) * 1000, 1)
|
||||
return {
|
||||
"ok": True,
|
||||
"latency_ms": latency_ms,
|
||||
"endpoint": MINIO_ENDPOINT,
|
||||
"bucket": MINIO_BUCKET,
|
||||
"public_url": MINIO_PUBLIC_URL,
|
||||
"bucket_exists": exists,
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"ok": False,
|
||||
"error": str(e),
|
||||
"latency_ms": None,
|
||||
"endpoint": MINIO_ENDPOINT,
|
||||
"bucket": MINIO_BUCKET,
|
||||
"public_url": MINIO_PUBLIC_URL,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/status")
|
||||
async def system_status(db: AsyncSession = Depends(get_db), _: str = Depends(require_admin)):
|
||||
pg, redis, minio = await asyncio.gather(
|
||||
_check_postgres(db),
|
||||
_check_redis(),
|
||||
_check_minio(),
|
||||
)
|
||||
return {
|
||||
"postgres": pg,
|
||||
"redis": redis,
|
||||
"minio": minio,
|
||||
}
|
||||
@@ -15,16 +15,18 @@ class SourceIn(BaseModel):
|
||||
group_id: str
|
||||
group_name: str
|
||||
enabled: bool = True
|
||||
strict_filter: bool = True
|
||||
|
||||
|
||||
def _source_dict(s: VkSource) -> dict:
|
||||
return {
|
||||
"id": str(s.id),
|
||||
"group_id": s.group_id,
|
||||
"group_name": s.group_name,
|
||||
"enabled": s.enabled,
|
||||
"last_run": s.last_run.isoformat() if s.last_run else None,
|
||||
"created_at": s.created_at.isoformat(),
|
||||
"id": str(s.id),
|
||||
"group_id": s.group_id,
|
||||
"group_name": s.group_name,
|
||||
"enabled": s.enabled,
|
||||
"strict_filter": s.strict_filter,
|
||||
"last_run": s.last_run.isoformat() if s.last_run else None,
|
||||
"created_at": s.created_at.isoformat(),
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +58,7 @@ async def add_source(data: SourceIn, db: AsyncSession = Depends(get_db), _: str
|
||||
exists = (await db.execute(select(VkSource).where(VkSource.group_id == group_id))).scalar_one_or_none()
|
||||
if exists:
|
||||
raise HTTPException(status_code=409, detail="Источник с таким group_id уже есть")
|
||||
source = VkSource(group_id=group_id, group_name=data.group_name, enabled=data.enabled)
|
||||
source = VkSource(group_id=group_id, group_name=data.group_name, enabled=data.enabled, strict_filter=data.strict_filter)
|
||||
db.add(source)
|
||||
await db.commit()
|
||||
await db.refresh(source)
|
||||
@@ -68,9 +70,10 @@ async def update_source(source_id: str, data: SourceIn, db: AsyncSession = Depen
|
||||
source = (await db.execute(select(VkSource).where(VkSource.id == source_id))).scalar_one_or_none()
|
||||
if not source:
|
||||
raise HTTPException(status_code=404, detail="Источник не найден")
|
||||
source.group_id = _clean_group_id(data.group_id)
|
||||
source.group_name = data.group_name
|
||||
source.enabled = data.enabled
|
||||
source.group_id = _clean_group_id(data.group_id)
|
||||
source.group_name = data.group_name
|
||||
source.enabled = data.enabled
|
||||
source.strict_filter = data.strict_filter
|
||||
await db.commit()
|
||||
await db.refresh(source)
|
||||
return _source_dict(source)
|
||||
@@ -94,20 +97,25 @@ async def vk_manual_import(_: str = Depends(require_admin)):
|
||||
|
||||
|
||||
@router.post("/import/history")
|
||||
async def vk_history_import_all(background_tasks: BackgroundTasks, _: str = Depends(require_admin)):
|
||||
background_tasks.add_task(run_history_import)
|
||||
return {"ok": True, "message": "Исторический импорт всех групп запущен в фоне (до 5 мин)"}
|
||||
async def vk_history_import_all(
|
||||
background_tasks: BackgroundTasks,
|
||||
since_days: int = 365,
|
||||
_: str = Depends(require_admin),
|
||||
):
|
||||
background_tasks.add_task(run_history_import, since_days=since_days)
|
||||
return {"ok": True, "message": f"Исторический импорт всех групп запущен в фоне (глубина {since_days} дн.)"}
|
||||
|
||||
|
||||
@router.post("/import/history/{source_id}")
|
||||
async def vk_history_import_one(
|
||||
source_id: str,
|
||||
background_tasks: BackgroundTasks,
|
||||
since_days: int = 365,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_: str = Depends(require_admin),
|
||||
):
|
||||
source = (await db.execute(select(VkSource).where(VkSource.id == source_id))).scalar_one_or_none()
|
||||
if not source:
|
||||
raise HTTPException(status_code=404, detail="Источник не найден")
|
||||
background_tasks.add_task(run_history_import, group_id=source.group_id)
|
||||
background_tasks.add_task(run_history_import, group_id=source.group_id, since_days=since_days)
|
||||
return {"ok": True, "message": f"Исторический импорт группы «{source.group_name}» запущен в фоне"}
|
||||
|
||||
Reference in New Issue
Block a user