feat: add route to upload media and edit frontend component

This commit is contained in:
austnv
2026-06-16 15:53:27 +03:00
parent 0304b6fc2b
commit 86c739b696
6 changed files with 2164 additions and 22 deletions
@@ -7,6 +7,11 @@ from collections import defaultdict
from bot.utils.ttl_cache import AsyncTTLCache
import tempfile
import os
from aiogram.types import InputFile, Message
from aiogram.exceptions import TelegramBadRequest
BROADCAST_TARGET_ACTIVE_NEVER_CONNECTED = "active_never_connected"
BROADCAST_TARGETS = {
@@ -261,3 +266,67 @@ async def admin_broadcast_audience_counts_route(request: web.Request) -> web.Res
)
return _ok({"counts": counts})
async def admin_upload_media_route(request: web.Request) -> web.Response:
"""
Эндпоинт для загрузки медиафайла администратором.
Принимает multipart/form-data с полем 'file'.
Возвращает file_id, полученный от Telegram Bot.
"""
actor_id = _require_admin_user_id(request)
# Проверяем, что это multipart-запрос
if request.content_type and not request.content_type.startswith("multipart/form-data"):
return _error(400, "invalid_content_type")
reader = await request.multipart()
field = await reader.next()
if field is None or field.name != "file":
return _error(400, "file_required")
# Сохраняем файл во временный файл
with tempfile.NamedTemporaryFile(delete=False, suffix=".tmp") as tmp:
while True:
chunk = await field.read_chunk()
if not chunk:
break
tmp.write(chunk)
tmp_path = tmp.name
try:
# Получаем бота из app
bot = request.app.get("bot")
if bot is None:
return _error(503, "bot_unavailable")
# Отправляем файл в личный чат администратора, чтобы получить file_id
# Используем send_document, так как он подходит для любых файлов
with open(tmp_path, "rb") as f:
input_file = InputFile(f)
sent_msg: Message = await bot.send_document(
chat_id=actor_id,
document=input_file,
# Не отправляем лишний текст, чтобы не привлекать внимание
)
file_id = sent_msg.document.file_id
# Удаляем сообщение, чтобы не засорять чат
await bot.delete_message(chat_id=actor_id, message_id=sent_msg.message_id)
# Возвращаем file_id
return _ok({"file_id": file_id})
except TelegramBadRequest as e:
logger.warning("Failed to upload media to Telegram: %s", e)
return _error(400, f"telegram_error: {str(e)}")
except Exception as e:
logger.error("Unexpected error during media upload: %s", e, exc_info=True)
return _error(500, "upload_failed")
finally:
# Удаляем временный файл
try:
os.unlink(tmp_path)
except OSError:
pass
@@ -64,6 +64,8 @@ def setup_admin_routes(app: web.Application) -> None:
router.add_get("/api/admin/broadcast/audience-counts", admin_broadcast_audience_counts_route)
router.add_post("/api/admin/broadcast", admin_broadcast_route)
router.add_post("/api/admin/upload-media", admin_upload_media_route)
router.add_post("/api/admin/sync", admin_sync_route)
router.add_get("/api/admin/ads", admin_ads_list_route)