diff --git a/frontends/desktop/static/app.js b/frontends/desktop/static/app.js index 75a993db..130e1b51 100644 --- a/frontends/desktop/static/app.js +++ b/frontends/desktop/static/app.js @@ -269,7 +269,7 @@ const I18N = { 'slash.unknown': '未知命令', 'upload.hint': '上传文件:选择 / 拖拽 / 粘贴', 'upload.button': '上传文件', - 'upload.tooLarge': '文件过大或数量超限', + 'upload.tooLarge': '文件过大或数量超限', 'upload.empty': '跳过空文件', 'upload.failed': '上传失败', 'upload.removeTitle': '移除', 'upload.dropHint': '松开以上传文件', @@ -362,7 +362,7 @@ const I18N = { 'slash.unknown': 'Unknown command', 'upload.hint': 'Upload file: pick / drag / paste', 'upload.button': 'Upload file', - 'upload.tooLarge': 'File too large or limit reached', + 'upload.tooLarge': 'File too large or limit reached', 'upload.empty': 'Skipped empty file', 'upload.failed': 'Upload failed', 'upload.removeTitle': 'Remove', 'upload.dropHint': 'Drop to upload files', @@ -1572,14 +1572,17 @@ async function addFiles(fileList) { const files = Array.from(fileList || []); if (files.length === 0) return; let skipped = false; + let emptyHit = false; const accepted = []; for (const f of files) { + if (!f || f.size === 0) { emptyHit = true; continue; } if (f.size > MAX_UPLOAD_BYTES) { skipped = true; continue; } if (state.pendingFiles.length + accepted.length >= MAX_UPLOAD_FILES) { skipped = true; break; } accepted.push(f); } + if (emptyHit) showChanToast(t('upload.empty'), '', 'err'); if (accepted.length === 0) { - if (skipped) showSystem(t('upload.tooLarge')); + if (skipped) showChanToast(t('upload.tooLarge'), '', 'err'); return; } for (const f of accepted) { @@ -1602,10 +1605,10 @@ async function addFiles(fileList) { insertPlaceholderInComposer(placeholderFor(entry)); renderThumbStrip(); } catch (e) { - showSystem(t('upload.failed') + ': ' + (e.message || e)); + showChanToast(t('upload.failed'), e.message || String(e), 'err'); } } - if (skipped) showSystem(t('upload.tooLarge')); + if (skipped) showChanToast(t('upload.tooLarge'), '', 'err'); } if (uploadBtn && imgInput) uploadBtn.addEventListener('click', (e) => { diff --git a/frontends/desktop_bridge.py b/frontends/desktop_bridge.py index 8599a2fc..ff02a6b6 100644 --- a/frontends/desktop_bridge.py +++ b/frontends/desktop_bridge.py @@ -1081,7 +1081,14 @@ async def upload_handler(request): Body: {name: "", dataUrl: "data:;base64,<...>"} Returns: {ok: true, path: ""} """ - data = await read_json(request) + try: + data = await request.json() + if not isinstance(data, dict): + data = {} + except web.HTTPRequestEntityTooLarge: + return json_ok({"ok": False, "error": "file too large for bridge body limit"}) + except Exception as e: + return json_ok({"ok": False, "error": f"invalid request: {e}"}) name = (data.get("name") or "file").strip().replace("/", "_").replace("\\", "_") data_url = data.get("dataUrl") or "" if "," in data_url: @@ -1092,6 +1099,8 @@ async def upload_handler(request): blob = base64.b64decode(b64) except Exception as e: return json_ok({"ok": False, "error": f"decode failed: {e}"}) + if not blob: + return json_ok({"ok": False, "error": "empty file"}) safe_name = name or "file" fpath = _WEB_UPLOAD_DIR / f"{uuid.uuid4().hex[:12]}__{safe_name}" fpath.write_bytes(blob) @@ -1216,7 +1225,7 @@ async def token_stats_handler(request): def create_app(): - app = web.Application(middlewares=[cors_middleware]) + app = web.Application(middlewares=[cors_middleware], client_max_size=1 * 1024 * 1024) app.router.add_get("/ws", ws_handler) app.router.add_get("/status", status_handler) app.router.add_get("/config", get_config_handler)