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:
@@ -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(
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user