feat: app deployment v2 (#39829)

Co-authored-by: zhangx1n <zhangxin@dify.ai>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Wu Tianwei
2026-08-12 01:55:36 +00:00
committed by GitHub
co-authored by zhangx1n autofix-ci[bot]
parent ef8544b173
commit 7aba539e82
542 changed files with 29450 additions and 13824 deletions
+3
View File
@@ -315,6 +315,9 @@ class WorkflowResponse(ResponseModel):
)
hash: str = Field(validation_alias=AliasChoices("unique_hash", "hash"))
version: str
# NULL for drafts and for versions published before numbering was introduced; those
# render as "Untitled Version" instead of `#N`. Never 0, so clients must test for null.
version_number: int | None = None
marked_name: str
marked_comment: str
created_by: SimpleAccount | None = Field(
+48 -9
View File
@@ -5,13 +5,15 @@ to attribute the created app; workspace/membership validation is done by the
Go admin-api caller.
"""
from uuid import UUID
from flask import request
from flask_restx import Resource
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, ValidationError, field_validator
from sqlalchemy import select
from sqlalchemy.orm import Session
from controllers.common.schema import register_schema_model
from controllers.common.schema import query_params_from_model, register_schema_model
from controllers.console.wraps import setup_required
from controllers.inner_api import inner_api_ns
from controllers.inner_api.wraps import enterprise_inner_api_only
@@ -20,6 +22,7 @@ from models import Account, App
from models.account import AccountStatus
from services.app_dsl_service import AppDslService
from services.entities.dsl_entities import ImportMode, ImportStatus
from services.errors.app import IsDraftWorkflowError, WorkflowNotFoundError
class InnerAppDSLImportPayload(BaseModel):
@@ -29,6 +32,18 @@ class InnerAppDSLImportPayload(BaseModel):
description: str | None = Field(default=None, description="Override app description from DSL")
class EnterpriseAppDSLExportQuery(BaseModel):
include_secret: bool = Field(default=False, description="Whether to include secret values in the exported DSL")
workflow_id: UUID | None = Field(default=None, description="Published workflow version ID to export")
@field_validator("include_secret", mode="before")
@classmethod
def parse_include_secret(cls, value: object) -> bool:
if isinstance(value, str):
return value.lower() == "true"
return bool(value)
register_schema_model(inner_api_ns, InnerAppDSLImportPayload)
@@ -82,24 +97,48 @@ class EnterpriseAppDSLExport(Resource):
@enterprise_inner_api_only
@inner_api_ns.doc(
"enterprise_app_dsl_export",
params=query_params_from_model(EnterpriseAppDSLExportQuery),
responses={
200: "Export successful",
404: "App not found",
400: "Invalid workflow ID or unpublished workflow version",
404: "App or workflow version not found",
},
)
def get(self, app_id: str):
"""Export an app's DSL as YAML."""
include_secret = request.args.get("include_secret", "false").lower() == "true"
try:
query = EnterpriseAppDSLExportQuery.model_validate(request.args.to_dict(flat=True))
except ValidationError:
return {
"code": "invalid_workflow_id",
"message": "workflow_id must be a valid UUID",
"status": 400,
}, 400
workflow_id = str(query.workflow_id) if query.workflow_id else None
app_model = db.session.get(App, app_id)
if not app_model:
return {"message": "app not found"}, 404
data = AppDslService.export_dsl(
app_model=app_model,
session=db.session(),
include_secret=include_secret,
)
if not workflow_id:
data = AppDslService.export_dsl(
app_model=app_model,
session=db.session(),
include_secret=query.include_secret,
)
else:
try:
data = AppDslService.export_dsl(
app_model=app_model,
session=db.session(),
include_secret=query.include_secret,
workflow_id=workflow_id,
)
except WorkflowNotFoundError as exc:
return {"code": "workflow_version_not_found", "message": str(exc), "status": 404}, 404
except IsDraftWorkflowError as exc:
return {"code": "workflow_version_not_published", "message": str(exc), "status": 400}, 400
return {"data": data}, 200