From cadc021bfa673ac9ff2bb29ea3fd3acc7dda5872 Mon Sep 17 00:00:00 2001 From: hjlarry Date: Sun, 12 Apr 2026 20:35:23 +0800 Subject: [PATCH] fix: sign workflow online user avatars in app list API --- api/controllers/console/app/workflow.py | 17 +++++++++++++++-- .../controllers/console/app/test_workflow.py | 8 ++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/api/controllers/console/app/workflow.py b/api/controllers/console/app/workflow.py index f3c024a6b8..88dcc8bb83 100644 --- a/api/controllers/console/app/workflow.py +++ b/api/controllers/console/app/workflow.py @@ -6,7 +6,7 @@ from typing import Any from flask import abort, request from flask_restx import Resource, fields, marshal_with from graphon.enums import NodeType -from graphon.file import File +from graphon.file import File, helpers as file_helpers from graphon.graph_engine.manager import GraphEngineManager from graphon.model_runtime.utils.encoders import jsonable_encoder from pydantic import BaseModel, Field, ValidationError, field_validator @@ -1414,9 +1414,22 @@ class WorkflowOnlineUsersApi(Resource): users = [] for _, user_info_json in users_json.items(): try: - users.append(json.loads(user_info_json)) + user_info = json.loads(user_info_json) except Exception: continue + + if not isinstance(user_info, dict): + continue + + avatar = user_info.get("avatar") + if isinstance(avatar, str) and avatar and not avatar.startswith(("http://", "https://")): + try: + user_info["avatar"] = file_helpers.get_signed_file_url(avatar) + except Exception: + # keep original avatar value when signing fails + pass + + users.append(user_info) results.append({"app_id": app_id, "users": users}) return {"data": results} diff --git a/api/tests/unit_tests/controllers/console/app/test_workflow.py b/api/tests/unit_tests/controllers/console/app/test_workflow.py index a1f3ec9307..3bfda0d1a8 100644 --- a/api/tests/unit_tests/controllers/console/app/test_workflow.py +++ b/api/tests/unit_tests/controllers/console/app/test_workflow.py @@ -298,12 +298,15 @@ def test_workflow_online_users_filters_inaccessible_workflow( ) -> None: app_id_1 = "11111111-1111-1111-1111-111111111111" app_id_2 = "22222222-2222-2222-2222-222222222222" + signed_avatar_url = "https://files.example.com/signed/avatar-1" + sign_avatar = Mock(return_value=signed_avatar_url) monkeypatch.setattr(workflow_module, "current_account_with_tenant", lambda: (SimpleNamespace(), "tenant-1")) monkeypatch.setattr( workflow_module, "WorkflowService", lambda: SimpleNamespace(get_accessible_app_ids=lambda app_ids, tenant_id: {app_id_1}), ) + monkeypatch.setattr(workflow_module.file_helpers, "get_signed_file_url", sign_avatar) workflow_module.redis_client.hgetall.side_effect = lambda key: ( { @@ -311,7 +314,7 @@ def test_workflow_online_users_filters_inaccessible_workflow( { "user_id": "u-1", "username": "Alice", - "avatar": "avatar-url", + "avatar": "avatar-file-id", "sid": "sid-1", } ) @@ -337,7 +340,7 @@ def test_workflow_online_users_filters_inaccessible_workflow( { "user_id": "u-1", "username": "Alice", - "avatar": "avatar-url", + "avatar": signed_avatar_url, "sid": "sid-1", } ], @@ -347,6 +350,7 @@ def test_workflow_online_users_filters_inaccessible_workflow( workflow_module.redis_client.hgetall.assert_called_once_with( f"{workflow_module.WORKFLOW_ONLINE_USERS_PREFIX}{app_id_1}" ) + sign_avatar.assert_called_once_with("avatar-file-id") def test_workflow_online_users_rejects_excessive_workflow_ids(