feat: add homepage background removal tool and fix whitelist multipart support

- Add HomePage.vue with background removal functionality
- Fix whitelist service to support multipart/form-data file uploads
- Remove unused imports
This commit is contained in:
jingrow 2025-12-20 21:17:06 +08:00
parent 1d20385e96
commit 6292b8b510
5 changed files with 1751 additions and 11 deletions

View File

@ -18,6 +18,12 @@ const router = createRouter({
},
{
path: '/',
name: 'HomePage',
component: () => import('../../views/HomePage.vue'),
meta: { requiresAuth: false }
},
{
path: '/app',
name: 'AppLayout',
component: () => import('../layouts/AppLayout.vue'),
meta: { requiresAuth: true },

View File

@ -17,7 +17,7 @@
"Pending Review": "待审核",
"Dashboard": "仪表板",
"Agents": "AI智能体",
"Settings": "设置",
"Settings": "系统设置",
"Agent Detail": "智能体详情",
"Flow Builder": "流程编排",
"Profile": "个人资料",

File diff suppressed because it is too large Load Diff

View File

@ -60,15 +60,6 @@ async def authenticate_request(request: Request, allow_guest: bool) -> bool:
async def _process_whitelist_call(request: Request, full_module_path: str):
"""通用处理:接收完整点分路径 '<package.module.function>' 并执行调用"""
try:
async def _get_request_data(req: Request) -> Dict[str, Any]:
"""GET 使用查询参数,其他方法使用 JSON body"""
if req.method == 'GET':
return dict(req.query_params)
try:
return await req.json()
except Exception:
return {}
# 确保 apps 目录在 sys.path 中(支持跨 app 导入)
ensure_apps_on_sys_path()
@ -96,8 +87,28 @@ async def _process_whitelist_call(request: Request, full_module_path: str):
if not await authenticate_request(request, whitelist_info['allow_guest']):
raise HTTPException(status_code=401, detail="Authentication required")
# 根据请求类型获取数据
if request.method == 'GET':
request_data = dict(request.query_params)
else:
# 检查 Content-Type 是否为 multipart/form-data
content_type = request.headers.get('content-type', '')
is_multipart = 'multipart/form-data' in content_type
if is_multipart:
# 处理文件上传
form = await request.form()
request_data = {}
for key, value in form.items():
request_data[key] = value
else:
# 处理 JSON body
try:
request_data = await request.json()
except Exception:
request_data = {}
# 调用函数(只有通过白名单验证的函数才能执行到这里)
request_data = await _get_request_data(request)
result = func(**request_data)
return JSONResponse(content={"success": True, "data": result})

View File

@ -15,6 +15,127 @@ from jingrow.utils.auth import get_jingrow_cloud_api_headers, get_jingrow_cloud_
logger = logging.getLogger(__name__)
@jingrow.whitelist(allow_guest=True)
def remove_background_from_file_free(file) -> Dict[str, Any]:
"""
免费图片去背景工具无需认证
参考 remove_background_from_file 实现调用 Jingrow Cloud API 实现图片背景移除
Args:
file: 上传的文件对象
Returns:
dict: 处理结果格式与 remove_background_from_file 一致
"""
try:
# 获取认证头(使用系统配置的 API Key
headers = get_jingrow_cloud_api_headers()
if not headers or not headers.get('Authorization'):
return {
"success": False,
"error": "API密钥未设置请联系管理员配置"
}
# 读取文件内容
# whitelist 服务传递的 file 是 FastAPI 的 UploadFile 对象
# UploadFile.file 是底层的 SpooledTemporaryFile可以同步读取
# 需要先重置文件指针到开头(因为可能已经被读取过)
file.file.seek(0)
file_content = file.file.read()
if not file_content or len(file_content) == 0:
return {
"success": False,
"error": "文件内容为空"
}
# 获取文件扩展名和内容类型
filename = getattr(file, 'filename', None) or 'image.png'
ext = filename.split('.')[-1].lower() if '.' in filename else 'png'
if ext == 'jpg':
ext = 'jpg'
content_type = 'image/jpeg'
elif ext == 'jpeg':
ext = 'jpg'
content_type = 'image/jpeg'
elif ext == 'png':
ext = 'png'
content_type = 'image/png'
elif ext == 'webp':
ext = 'webp'
content_type = 'image/webp'
else:
ext = 'png'
content_type = 'image/png'
# 处理EXIF orientation参考 remove_background_from_file
image = Image.open(io.BytesIO(file_content))
image = ImageOps.exif_transpose(image)
output = io.BytesIO()
save_format = image.format or content_type.split('/')[-1].upper()
image.save(output, format=save_format)
image_bytes = output.getvalue()
# 调用文件上传端点(与 remove_background_from_file 使用相同的接口)
api_url = f"{get_jingrow_cloud_api_url()}/rmbg/file/free"
# 移除 Content-Type让 requests 自动设置 multipart/form-data
upload_headers = {k: v for k, v in headers.items() if k.lower() != 'content-type'}
files = {
'file': (f'image.{ext}', image_bytes, content_type)
}
# 转发请求
response = requests.post(
api_url,
files=files,
headers=upload_headers,
timeout=180
)
if response.status_code == 200:
result_data = response.json()
# 使用 image_url 字段,返回格式与 remove_background_from_file 一致
if 'image_url' in result_data:
image_url = result_data.get('image_url', '')
return {
"success": True,
"data": [{
"success": True,
"image_url": image_url,
"index": 1,
"total": 1
}],
"total_processed": 1,
"total_success": 1
}
return {
"success": False,
"error": result_data.get('error', '未找到图片URL')
}
else:
try:
error_data = response.json()
error_message = error_data.get("message") or error_data.get("error") or f"API请求失败 (HTTP {response.status_code})"
except:
error_message = f"API请求失败 (HTTP {response.status_code})"
return {
"success": False,
"error": error_message
}
except Exception as e:
logger.error(f"调用图片去背景API异常{str(e)}", exc_info=True)
return {
"success": False,
"error": f"调用图片去背景API异常{str(e)}"
}
@jingrow.whitelist()
def remove_background_from_file(image_data: Union[str, list]) -> Dict[str, Any]:
"""