Merge pull request #602 from wanxunyang/fix/app-share-wxy

fix: shared app exposes draft config to recipients before publishing
This commit is contained in:
Mark
2026-03-18 17:52:03 +08:00
committed by GitHub
3 changed files with 104 additions and 13 deletions

View File

@@ -194,6 +194,7 @@ def delete_app(
def copy_app( def copy_app(
app_id: uuid.UUID, app_id: uuid.UUID,
new_name: Optional[str] = None, new_name: Optional[str] = None,
payload: app_schema.CopyAppRequest = None,
db: Session = Depends(get_db), db: Session = Depends(get_db),
current_user=Depends(get_current_user), current_user=Depends(get_current_user),
): ):
@@ -205,6 +206,8 @@ def copy_app(
- 不影响原应用 - 不影响原应用
""" """
workspace_id = current_user.current_workspace_id workspace_id = current_user.current_workspace_id
# body takes precedence over query param for backward compatibility
new_name = (payload.new_name if payload else None) or new_name
logger.info( logger.info(
"用户请求复制应用", "用户请求复制应用",
extra={ extra={
@@ -517,7 +520,7 @@ async def draft_run(
# 提前验证和准备(在流式响应开始前完成) # 提前验证和准备(在流式响应开始前完成)
from app.services.app_service import AppService from app.services.app_service import AppService
from app.services.multi_agent_service import MultiAgentService from app.services.multi_agent_service import MultiAgentService
from app.models import AgentConfig, ModelConfig from app.models import AgentConfig, ModelConfig, AppRelease
from sqlalchemy import select from sqlalchemy import select
from app.core.exceptions import BusinessException from app.core.exceptions import BusinessException
from app.services.draft_run_service import AgentRunService from app.services.draft_run_service import AgentRunService
@@ -556,18 +559,29 @@ async def draft_run(
service._check_agent_config(app_id) service._check_agent_config(app_id)
# 2. 获取 Agent 配置 # 2. 获取 Agent 配置
stmt = select(AgentConfig).where(AgentConfig.app_id == app_id) # 共享应用:从最新发布版本读配置快照,而非草稿
agent_cfg = db.scalars(stmt).first() is_shared = app.workspace_id != workspace_id
if not agent_cfg: if is_shared:
raise BusinessException("Agent 配置不存在", BizCode.AGENT_CONFIG_MISSING) if not app.current_release_id:
raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING)
release = db.get(AppRelease, app.current_release_id)
if not release:
raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING)
agent_cfg = service._agent_config_from_release(release)
model_config = db.get(ModelConfig, release.default_model_config_id) if release.default_model_config_id else None
else:
stmt = select(AgentConfig).where(AgentConfig.app_id == app_id)
agent_cfg = db.scalars(stmt).first()
if not agent_cfg:
raise BusinessException("Agent 配置不存在", BizCode.AGENT_CONFIG_MISSING)
# 3. 获取模型配置 # 3. 获取模型配置
model_config = None model_config = None
if agent_cfg.default_model_config_id: if agent_cfg.default_model_config_id:
model_config = db.get(ModelConfig, agent_cfg.default_model_config_id) model_config = db.get(ModelConfig, agent_cfg.default_model_config_id)
if not model_config: if not model_config:
from app.core.exceptions import ResourceNotFoundException from app.core.exceptions import ResourceNotFoundException
raise ResourceNotFoundException("模型配置", str(agent_cfg.default_model_config_id)) raise ResourceNotFoundException("模型配置", str(agent_cfg.default_model_config_id))
# 流式返回 # 流式返回
if payload.stream: if payload.stream:
@@ -723,7 +737,17 @@ async def draft_run(
msg="多 Agent 任务执行成功" msg="多 Agent 任务执行成功"
) )
elif app.type == AppType.WORKFLOW: # 工作流 elif app.type == AppType.WORKFLOW: # 工作流
config = workflow_service.check_config(app_id) # 共享应用:从最新发布版本读配置快照,而非草稿
is_shared = app.workspace_id != workspace_id
if is_shared:
if not app.current_release_id:
raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING)
release = db.get(AppRelease, app.current_release_id)
if not release:
raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING)
config = service._workflow_config_from_release(release)
else:
config = workflow_service.check_config(app_id)
# 3. 流式返回 # 3. 流式返回
if payload.stream: if payload.stream:
logger.debug( logger.debug(

View File

@@ -525,6 +525,13 @@ class AppRelease(BaseModel):
return int(dt.timestamp() * 1000) if dt else None return int(dt.timestamp() * 1000) if dt else None
# ---------- App Copy Schema ----------
class CopyAppRequest(BaseModel):
"""复制应用请求"""
new_name: Optional[str] = Field(None, description="新应用名称,不填则使用原名称-副本")
# ---------- App Share Schemas ---------- # ---------- App Share Schemas ----------
class AppShareCreate(BaseModel): class AppShareCreate(BaseModel):

View File

@@ -1426,6 +1426,47 @@ class AppService:
logger.info("Agent 配置更新成功", extra={"app_id": str(app_id)}) logger.info("Agent 配置更新成功", extra={"app_id": str(app_id)})
return agent_cfg return agent_cfg
def _agent_config_from_release(self, release: "AppRelease") -> "AgentConfig":
"""从发布版本快照重建 AgentConfig 对象(不入库,仅用于运行)"""
cfg = release.config or {}
now = release.created_at or datetime.datetime.now()
agent_cfg = AgentConfig(
id=uuid.uuid4(),
app_id=release.app_id,
system_prompt=cfg.get("system_prompt", ""),
default_model_config_id=release.default_model_config_id,
model_parameters=cfg.get("model_parameters"),
knowledge_retrieval=cfg.get("knowledge_retrieval"),
memory=cfg.get("memory", {}),
variables=cfg.get("variables", []),
tools=cfg.get("tools", []),
skills=cfg.get("skills", {}),
features=cfg.get("features", {}),
is_active=True,
created_at=now,
updated_at=now,
)
return agent_cfg
def _workflow_config_from_release(self, release: "AppRelease") -> "WorkflowConfig":
"""从发布版本快照重建 WorkflowConfig 对象(不入库,仅用于运行)"""
cfg = release.config or {}
now = release.created_at or datetime.datetime.now()
from app.models.workflow_model import WorkflowConfig as WorkflowConfigModel
wf_cfg = WorkflowConfigModel(
id=uuid.uuid4(),
app_id=release.app_id,
nodes=cfg.get("nodes", []),
edges=cfg.get("edges", []),
variables=cfg.get("variables", []),
execution_config=cfg.get("execution_config", {}),
triggers=cfg.get("triggers", []),
is_active=True,
created_at=now,
updated_at=now,
)
return wf_cfg
def get_agent_config( def get_agent_config(
self, self,
*, *,
@@ -1457,6 +1498,15 @@ class AppService:
# 只读操作,允许访问共享应用 # 只读操作,允许访问共享应用
self._validate_app_accessible(app, workspace_id) self._validate_app_accessible(app, workspace_id)
# 共享应用:返回最新发布版本的配置快照,而非草稿
if workspace_id and app.workspace_id != workspace_id:
if not app.current_release_id:
raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING)
release = self.db.get(AppRelease, app.current_release_id)
if not release:
raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING)
return self._agent_config_from_release(release)
stmt = select(AgentConfig).where( stmt = select(AgentConfig).where(
AgentConfig.app_id == app_id, AgentConfig.app_id == app_id,
AgentConfig.is_active.is_(True) AgentConfig.is_active.is_(True)
@@ -1555,6 +1605,16 @@ class AppService:
# 只读操作,允许访问共享应用 # 只读操作,允许访问共享应用
self._validate_app_accessible(app, workspace_id) self._validate_app_accessible(app, workspace_id)
# 共享应用:返回最新发布版本的配置快照,而非草稿
if workspace_id and app.workspace_id != workspace_id:
if not app.current_release_id:
raise BusinessException("该应用尚未发布,无法使用", BizCode.CONFIG_MISSING)
release = self.db.get(AppRelease, app.current_release_id)
if not release:
raise BusinessException("发布版本不存在", BizCode.CONFIG_MISSING)
return self._workflow_config_from_release(release)
repo = WorkflowConfigRepository(self.db) repo = WorkflowConfigRepository(self.db)
config = repo.get_by_app_id(app_id) config = repo.get_by_app_id(app_id)
if config: if config: