157 lines
6.2 KiB
Python
157 lines
6.2 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
|
|
from app.api.admin.deps import get_admin_claims
|
|
from app.core.request_id import get_or_create_request_id
|
|
from app.db.session import with_sqlite_busy_retry
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _worker_last_seen_from_value_json(value_json: str | None) -> str | None:
|
|
if value_json is None:
|
|
return None
|
|
raw = str(value_json or "").strip()
|
|
if not raw:
|
|
return None
|
|
try:
|
|
data = json.loads(raw)
|
|
except Exception:
|
|
return None
|
|
if isinstance(data, dict):
|
|
at = data.get("at")
|
|
return str(at) if isinstance(at, str) and at.strip() else None
|
|
if isinstance(data, str):
|
|
return data.strip() or None
|
|
return None
|
|
|
|
|
|
@router.get("/summary")
|
|
async def get_summary(
|
|
request: Request,
|
|
_claims: dict[str, Any] = Depends(get_admin_claims),
|
|
) -> dict[str, Any]:
|
|
_ = _claims
|
|
rid = get_or_create_request_id(request)
|
|
|
|
engine = request.app.state.engine
|
|
|
|
async def _op() -> dict[str, Any]:
|
|
async with engine.connect() as conn:
|
|
images_total = int((await conn.exec_driver_sql("SELECT COUNT(*) FROM images;")).scalar_one())
|
|
images_enabled = int(
|
|
(await conn.exec_driver_sql("SELECT COUNT(*) FROM images WHERE status=1;")).scalar_one()
|
|
)
|
|
|
|
missing_tags = int(
|
|
(
|
|
await conn.exec_driver_sql(
|
|
"""
|
|
SELECT COUNT(*)
|
|
FROM images
|
|
WHERE status=1
|
|
AND id NOT IN (SELECT DISTINCT image_id FROM image_tags);
|
|
""".strip()
|
|
)
|
|
).scalar_one()
|
|
)
|
|
missing_geometry = int(
|
|
(
|
|
await conn.exec_driver_sql(
|
|
"SELECT COUNT(*) FROM images WHERE status=1 AND (width IS NULL OR height IS NULL);"
|
|
)
|
|
).scalar_one()
|
|
)
|
|
missing_r18 = int(
|
|
(await conn.exec_driver_sql("SELECT COUNT(*) FROM images WHERE status=1 AND x_restrict IS NULL;")).scalar_one()
|
|
)
|
|
missing_ai = int(
|
|
(await conn.exec_driver_sql("SELECT COUNT(*) FROM images WHERE status=1 AND ai_type IS NULL;")).scalar_one()
|
|
)
|
|
missing_illust_type = int(
|
|
(await conn.exec_driver_sql("SELECT COUNT(*) FROM images WHERE status=1 AND illust_type IS NULL;")).scalar_one()
|
|
)
|
|
missing_user = int(
|
|
(await conn.exec_driver_sql("SELECT COUNT(*) FROM images WHERE status=1 AND user_id IS NULL;")).scalar_one()
|
|
)
|
|
missing_title = int(
|
|
(
|
|
await conn.exec_driver_sql(
|
|
"SELECT COUNT(*) FROM images WHERE status=1 AND (title IS NULL OR TRIM(title)='');"
|
|
)
|
|
).scalar_one()
|
|
)
|
|
missing_created_at = int(
|
|
(
|
|
await conn.exec_driver_sql(
|
|
"SELECT COUNT(*) FROM images WHERE status=1 AND (created_at_pixiv IS NULL OR TRIM(created_at_pixiv)='');"
|
|
)
|
|
).scalar_one()
|
|
)
|
|
missing_popularity = int(
|
|
(
|
|
await conn.exec_driver_sql(
|
|
"SELECT COUNT(*) FROM images "
|
|
"WHERE status=1 AND (bookmark_count IS NULL OR view_count IS NULL OR comment_count IS NULL);"
|
|
)
|
|
).scalar_one()
|
|
)
|
|
|
|
tokens_total = int((await conn.exec_driver_sql("SELECT COUNT(*) FROM pixiv_tokens;")).scalar_one())
|
|
tokens_enabled = int(
|
|
(await conn.exec_driver_sql("SELECT COUNT(*) FROM pixiv_tokens WHERE enabled=1;")).scalar_one()
|
|
)
|
|
|
|
proxies_total = int((await conn.exec_driver_sql("SELECT COUNT(*) FROM proxy_endpoints;")).scalar_one())
|
|
proxies_enabled = int(
|
|
(await conn.exec_driver_sql("SELECT COUNT(*) FROM proxy_endpoints WHERE enabled=1;")).scalar_one()
|
|
)
|
|
|
|
pools_total = int((await conn.exec_driver_sql("SELECT COUNT(*) FROM proxy_pools;")).scalar_one())
|
|
pools_enabled = int(
|
|
(await conn.exec_driver_sql("SELECT COUNT(*) FROM proxy_pools WHERE enabled=1;")).scalar_one()
|
|
)
|
|
|
|
bindings_total = int((await conn.exec_driver_sql("SELECT COUNT(*) FROM token_proxy_bindings;")).scalar_one())
|
|
|
|
jobs_counts: dict[str, int] = {}
|
|
rows = (await conn.exec_driver_sql("SELECT status, COUNT(*) AS c FROM jobs GROUP BY status;")).fetchall()
|
|
for status, count in rows:
|
|
jobs_counts[str(status)] = int(count)
|
|
|
|
worker_last_seen_json = (
|
|
await conn.exec_driver_sql("SELECT value_json FROM runtime_settings WHERE key = ?;", ("worker.last_seen_at",))
|
|
).scalar_one_or_none()
|
|
worker_last_seen_at = _worker_last_seen_from_value_json(str(worker_last_seen_json) if worker_last_seen_json is not None else None)
|
|
|
|
return {
|
|
"images": {"total": images_total, "enabled": images_enabled},
|
|
"hydration": {
|
|
"enabled_images_total": images_enabled,
|
|
"missing": {
|
|
"tags": missing_tags,
|
|
"geometry": missing_geometry,
|
|
"r18": missing_r18,
|
|
"ai": missing_ai,
|
|
"illust_type": missing_illust_type,
|
|
"user": missing_user,
|
|
"title": missing_title,
|
|
"created_at": missing_created_at,
|
|
"popularity": missing_popularity,
|
|
},
|
|
},
|
|
"tokens": {"total": tokens_total, "enabled": tokens_enabled},
|
|
"proxies": {"endpoints_total": proxies_total, "endpoints_enabled": proxies_enabled},
|
|
"proxy_pools": {"total": pools_total, "enabled": pools_enabled},
|
|
"bindings": {"total": bindings_total},
|
|
"jobs": {"counts": jobs_counts},
|
|
"worker": {"last_seen_at": worker_last_seen_at},
|
|
}
|
|
|
|
counts = await with_sqlite_busy_retry(_op)
|
|
return {"ok": True, "counts": counts, "request_id": rid}
|