fix: proxy quoted media in dashboard messages

This commit is contained in:
liuwei
2026-04-07 17:40:10 +08:00
parent 867f00435c
commit e823c1049f
2 changed files with 65 additions and 7 deletions

View File

@@ -1,4 +1,5 @@
from flask import Blueprint, render_template, jsonify, request, current_app
import requests
from flask import Blueprint, render_template, jsonify, request, current_app, Response, stream_with_context
from .auth import login_required
from loguru import logger
import xml.etree.ElementTree as ET
@@ -17,6 +18,41 @@ def _is_emoji_message(msg: dict) -> bool:
return message_type in {'47', '1048625', '1090519089'} or any(marker in content for marker in xml_markers)
def _proxy_remote_media(target_url: str) -> Response:
if not target_url:
return Response("missing url", status=400)
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/123.0.0.0 Safari/537.36"
),
"Referer": "http://weixin.qq.com/"
}
range_header = request.headers.get("Range")
if range_header:
headers["Range"] = range_header
upstream = requests.get(target_url, headers=headers, stream=True, timeout=30)
response_headers = {}
for key in ("Content-Type", "Content-Length", "Content-Range", "Accept-Ranges", "Cache-Control", "ETag", "Last-Modified"):
value = upstream.headers.get(key)
if value:
response_headers[key] = value
if "Accept-Ranges" not in response_headers:
response_headers["Accept-Ranges"] = "bytes"
return Response(
stream_with_context(upstream.iter_content(chunk_size=64 * 1024)),
status=upstream.status_code,
headers=response_headers,
direct_passthrough=True
)
# 消息列表页面
@messages_bp.route('/messages')
@login_required
@@ -63,16 +99,16 @@ def get_messages():
continue
# 处理消息内容,格式化引用消息
if msg['message_type'] == "49" and msg['content']: # 应用消息类型
if str(msg.get('message_type')) == "49" and msg.get('content'): # 应用消息类型
try:
# 检查是否为引用消息
if '<refermsg>' in msg['content']:
# 使用格式化工具处理引用消息
quote_data = parse_quote_message(msg['content'])
msg['content'] = quote_data['formatted_message']
msg['quoted_type'] = quote_data['reference_type']
msg['quoted_preview_image'] = quote_data['preview_image']
msg['quoted_preview_video_thumb'] = quote_data['preview_video_thumb']
msg['content'] = quote_data.get('formatted_message') or format_quote_message(msg['content'])
msg['quoted_type'] = quote_data.get('reference_type', '')
msg['quoted_preview_image'] = quote_data.get('preview_image', '')
msg['quoted_preview_video_thumb'] = quote_data.get('preview_video_thumb', '')
else:
# 其他类型的应用消息,解析 XML 提取标题
root = ET.fromstring(msg['content'])
@@ -81,6 +117,10 @@ def get_messages():
msg['content'] = title_elem.text
except Exception as e:
logger.error(f"解析消息类型49出错: {e}")
try:
msg['content'] = format_quote_message(msg['content'])
except Exception:
pass
return jsonify(result)
except Exception as e:
@@ -143,3 +183,14 @@ def get_hourly_message_trend():
except Exception as e:
logger.error(f"获取按小时聊天趋势数据失败: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
@messages_bp.route('/api/messages/media_proxy', methods=['GET'])
@login_required
def message_media_proxy():
target_url = request.args.get('url', '').strip()
try:
return _proxy_remote_media(target_url)
except Exception as e:
logger.error(f"消息媒体代理失败: {e}")
return Response(f"proxy failed: {e}", status=502)

View File

@@ -96,7 +96,7 @@
{% raw %}{{ scope.row.content || `【消息类型: ${scope.row.message_type}】` }}{% endraw %}
<div v-if="scope.row.quoted_type === 'image' && scope.row.quoted_preview_image" class="quoted-media-preview">
<div class="message-media-label">【引用图片】</div>
<img :src="scope.row.quoted_preview_image" class="message-thumb" @click="showQuotedImage(scope.row.quoted_preview_image)">
<img :src="getMediaProxyUrl(scope.row.quoted_preview_image)" class="message-thumb" @click="showQuotedImage(scope.row.quoted_preview_image)">
</div>
<div v-else-if="scope.row.quoted_type === 'video' && scope.row.quoted_preview_video_thumb" class="quoted-media-preview">
<div class="message-media-label">【引用视频】</div>
@@ -361,6 +361,13 @@
}
return `/static/images/${fileName}`;
},
getMediaProxyUrl(url) {
if (!url) return '';
if (url.startsWith('/api/messages/media_proxy')) {
return url;
}
return `/api/messages/media_proxy?url=${encodeURIComponent(url)}`;
}
},
watch: {