mirror of
https://github.com/langgenius/dify.git
synced 2026-03-27 23:30:54 +08:00
refactor: move DSL inner API to app/dsl.py with explicit account_email attribution
This commit is contained in:
parent
bbe975c6bc
commit
0ba464e820
@ -16,12 +16,14 @@ api = ExternalApi(
|
||||
inner_api_ns = Namespace("inner_api", description="Internal API operations", path="/")
|
||||
|
||||
from . import mail as _mail
|
||||
from .app import dsl as _app_dsl
|
||||
from .plugin import plugin as _plugin
|
||||
from .workspace import workspace as _workspace
|
||||
|
||||
api.add_namespace(inner_api_ns)
|
||||
|
||||
__all__ = [
|
||||
"_app_dsl",
|
||||
"_mail",
|
||||
"_plugin",
|
||||
"_workspace",
|
||||
|
||||
1
api/controllers/inner_api/app/__init__.py
Normal file
1
api/controllers/inner_api/app/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
||||
116
api/controllers/inner_api/app/dsl.py
Normal file
116
api/controllers/inner_api/app/dsl.py
Normal file
@ -0,0 +1,116 @@
|
||||
"""Inner API endpoints for app DSL import/export.
|
||||
|
||||
Called by the Go admin-api service (dify-enterprise) to import and export
|
||||
app definitions as YAML DSL files. These endpoints delegate to
|
||||
:class:`~services.app_dsl_service.AppDslService` for the actual work.
|
||||
|
||||
Import requires an ``account_email`` identifying the workspace member who
|
||||
will own the imported app. The caller (admin-api) is responsible for
|
||||
deciding which user to attribute the operation to.
|
||||
"""
|
||||
|
||||
from flask import request
|
||||
from flask_restx import Resource
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
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
|
||||
from extensions.ext_database import db
|
||||
from models import Account, App
|
||||
from models.account import TenantAccountJoin
|
||||
from services.app_dsl_service import AppDslService, ImportMode, ImportStatus
|
||||
|
||||
|
||||
class InnerAppDSLImportPayload(BaseModel):
|
||||
yaml_content: str
|
||||
account_email: str
|
||||
name: str | None = None
|
||||
description: str | None = None
|
||||
|
||||
|
||||
@inner_api_ns.route("/enterprise/workspaces/<string:workspace_id>/dsl/import")
|
||||
class EnterpriseAppDSLImport(Resource):
|
||||
@setup_required
|
||||
@enterprise_inner_api_only
|
||||
def post(self, workspace_id: str):
|
||||
"""Import a DSL into a workspace on behalf of a specified account.
|
||||
|
||||
Requires ``account_email`` to identify the workspace member who will
|
||||
own the imported app. The account must be active and belong to the
|
||||
target workspace. Returns 202 when a DSL version mismatch requires
|
||||
confirmation, 400 on business failure, and 200 on success.
|
||||
"""
|
||||
args = InnerAppDSLImportPayload.model_validate(inner_api_ns.payload or {})
|
||||
|
||||
account = _resolve_workspace_account(workspace_id, args.account_email)
|
||||
if account is None:
|
||||
return {
|
||||
"message": f"account '{args.account_email}' not found, inactive, "
|
||||
f"or not a member of workspace '{workspace_id}'"
|
||||
}, 404
|
||||
|
||||
account.set_tenant_id(workspace_id)
|
||||
|
||||
with Session(db.engine) as session:
|
||||
dsl_service = AppDslService(session)
|
||||
result = dsl_service.import_app(
|
||||
account=account,
|
||||
import_mode=ImportMode.YAML_CONTENT,
|
||||
yaml_content=args.yaml_content,
|
||||
name=args.name,
|
||||
description=args.description,
|
||||
)
|
||||
session.commit()
|
||||
|
||||
if result.status == ImportStatus.FAILED:
|
||||
return result.model_dump(mode="json"), 400
|
||||
elif result.status == ImportStatus.PENDING:
|
||||
return result.model_dump(mode="json"), 202
|
||||
return result.model_dump(mode="json"), 200
|
||||
|
||||
|
||||
@inner_api_ns.route("/enterprise/apps/<string:app_id>/dsl")
|
||||
class EnterpriseAppDSLExport(Resource):
|
||||
@setup_required
|
||||
@enterprise_inner_api_only
|
||||
def get(self, app_id: str):
|
||||
"""Export an app's DSL as YAML.
|
||||
|
||||
This is a global lookup by app_id (no tenant scoping) because the
|
||||
admin-api caller has platform-level access via secret-key auth.
|
||||
"""
|
||||
include_secret = request.args.get("include_secret", "false").lower() == "true"
|
||||
|
||||
app_model = db.session.query(App).filter_by(id=app_id).first()
|
||||
if not app_model:
|
||||
return {"message": "app not found"}, 404
|
||||
|
||||
data = AppDslService.export_dsl(
|
||||
app_model=app_model,
|
||||
include_secret=include_secret,
|
||||
)
|
||||
|
||||
return {"data": data}, 200
|
||||
|
||||
|
||||
def _resolve_workspace_account(workspace_id: str, email: str) -> Account | None:
|
||||
"""Look up an active account by email and verify it belongs to the workspace.
|
||||
|
||||
Returns the account with its tenant set, or None if the account doesn't
|
||||
exist, is inactive, or is not a member of the given workspace.
|
||||
"""
|
||||
account = db.session.query(Account).filter_by(email=email).first()
|
||||
if account is None or account.status != "active":
|
||||
return None
|
||||
|
||||
membership = (
|
||||
db.session.query(TenantAccountJoin)
|
||||
.filter_by(tenant_id=workspace_id, account_id=account.id)
|
||||
.first()
|
||||
)
|
||||
if membership is None:
|
||||
return None
|
||||
|
||||
return account
|
||||
Loading…
Reference in New Issue
Block a user