fix: serve admin media previews from telegram
This commit is contained in:
@@ -9,14 +9,18 @@ import re
|
|||||||
import secrets
|
import secrets
|
||||||
import time
|
import time
|
||||||
from datetime import date, datetime, timedelta, timezone
|
from datetime import date, datetime, timedelta, timezone
|
||||||
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from aiogram import Bot
|
||||||
|
from aiogram.client.session.aiohttp import AiohttpSession
|
||||||
|
from aiogram.client.telegram import TelegramAPIServer
|
||||||
from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile, status
|
from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile, status
|
||||||
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, RedirectResponse
|
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, RedirectResponse, Response
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -502,6 +506,40 @@ async def get_current_user(request: Request) -> dict | None:
|
|||||||
return dict(row) if row else None
|
return dict(row) if row else None
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/raw/media/{media_id}")
|
||||||
|
async def raw_media_preview(request: Request, media_id: int) -> Response:
|
||||||
|
if not await get_current_user(request):
|
||||||
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
||||||
|
pool = await get_pool()
|
||||||
|
media = await pool.fetchrow(
|
||||||
|
"SELECT media_type, tg_file_id FROM raw_post_media WHERE id=$1",
|
||||||
|
media_id,
|
||||||
|
)
|
||||||
|
if not media or media["media_type"] != "photo" or not media["tg_file_id"]:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
local_bot_api_url = str(await fetch_setting("local_bot_api_url", settings.local_bot_api_url) or "").strip()
|
||||||
|
session = (
|
||||||
|
AiohttpSession(api=TelegramAPIServer.from_base(local_bot_api_url.rstrip("/"), is_local=True))
|
||||||
|
if local_bot_api_url
|
||||||
|
else AiohttpSession()
|
||||||
|
)
|
||||||
|
bot = Bot(token=settings.tg_bot_token, session=session)
|
||||||
|
output = BytesIO()
|
||||||
|
try:
|
||||||
|
await bot.download(str(media["tg_file_id"]), destination=output)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("Telegram media preview failed for media {}: {}", media_id, exc)
|
||||||
|
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail="Media preview unavailable") from exc
|
||||||
|
finally:
|
||||||
|
await bot.session.close()
|
||||||
|
return Response(
|
||||||
|
content=output.getvalue(),
|
||||||
|
media_type="image/jpeg",
|
||||||
|
headers={"Cache-Control": "private, max-age=3600"},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def require_csrf(user: dict, csrf_token: str) -> None:
|
def require_csrf(user: dict, csrf_token: str) -> None:
|
||||||
if not user or csrf_token != user["csrf_token"]:
|
if not user or csrf_token != user["csrf_token"]:
|
||||||
raise PermissionError("bad csrf")
|
raise PermissionError("bad csrf")
|
||||||
@@ -2680,7 +2718,11 @@ async def raw_posts(
|
|||||||
jsonb_build_object(
|
jsonb_build_object(
|
||||||
'id', rpm.id,
|
'id', rpm.id,
|
||||||
'type', rpm.media_type,
|
'type', rpm.media_type,
|
||||||
'url', rpm.original_url,
|
'url', CASE
|
||||||
|
WHEN rpm.media_type='photo' AND rpm.tg_file_id IS NOT NULL
|
||||||
|
THEN '/raw/media/' || rpm.id::text
|
||||||
|
ELSE rpm.original_url
|
||||||
|
END,
|
||||||
'status', rpm.status,
|
'status', rpm.status,
|
||||||
'error', rpm.error,
|
'error', rpm.error,
|
||||||
'duration_sec', rpm.duration_sec
|
'duration_sec', rpm.duration_sec
|
||||||
@@ -2846,7 +2888,11 @@ async def fetch_single_editor_row(pool, post_id: int):
|
|||||||
jsonb_build_object(
|
jsonb_build_object(
|
||||||
'id', rpm.id,
|
'id', rpm.id,
|
||||||
'type', rpm.media_type,
|
'type', rpm.media_type,
|
||||||
'url', rpm.original_url,
|
'url', CASE
|
||||||
|
WHEN rpm.media_type='photo' AND rpm.tg_file_id IS NOT NULL
|
||||||
|
THEN '/raw/media/' || rpm.id::text
|
||||||
|
ELSE rpm.original_url
|
||||||
|
END,
|
||||||
'status', rpm.status,
|
'status', rpm.status,
|
||||||
'error', rpm.error,
|
'error', rpm.error,
|
||||||
'duration_sec', rpm.duration_sec
|
'duration_sec', rpm.duration_sec
|
||||||
@@ -2987,7 +3033,11 @@ async def editor_feed(
|
|||||||
jsonb_build_object(
|
jsonb_build_object(
|
||||||
'id', rpm.id,
|
'id', rpm.id,
|
||||||
'type', rpm.media_type,
|
'type', rpm.media_type,
|
||||||
'url', rpm.original_url,
|
'url', CASE
|
||||||
|
WHEN rpm.media_type='photo' AND rpm.tg_file_id IS NOT NULL
|
||||||
|
THEN '/raw/media/' || rpm.id::text
|
||||||
|
ELSE rpm.original_url
|
||||||
|
END,
|
||||||
'status', rpm.status,
|
'status', rpm.status,
|
||||||
'error', rpm.error,
|
'error', rpm.error,
|
||||||
'duration_sec', rpm.duration_sec
|
'duration_sec', rpm.duration_sec
|
||||||
@@ -3327,7 +3377,11 @@ async def raw_post_detail(request: Request, post_id: int):
|
|||||||
jsonb_build_object(
|
jsonb_build_object(
|
||||||
'id', rpm.id,
|
'id', rpm.id,
|
||||||
'type', rpm.media_type,
|
'type', rpm.media_type,
|
||||||
'url', rpm.original_url,
|
'url', CASE
|
||||||
|
WHEN rpm.media_type='photo' AND rpm.tg_file_id IS NOT NULL
|
||||||
|
THEN '/raw/media/' || rpm.id::text
|
||||||
|
ELSE rpm.original_url
|
||||||
|
END,
|
||||||
'status', rpm.status,
|
'status', rpm.status,
|
||||||
'error', rpm.error,
|
'error', rpm.error,
|
||||||
'duration_sec', rpm.duration_sec
|
'duration_sec', rpm.duration_sec
|
||||||
|
|||||||
Reference in New Issue
Block a user