- import uuid
 - from typing import cast
 - 
 - from flask_login import current_user
 - from flask_restx import Resource, fields, inputs, marshal, marshal_with, reqparse
 - from sqlalchemy import select
 - from sqlalchemy.orm import Session
 - from werkzeug.exceptions import BadRequest, Forbidden, abort
 - 
 - from controllers.console import api, console_ns
 - from controllers.console.app.wraps import get_app_model
 - from controllers.console.wraps import (
 -     account_initialization_required,
 -     cloud_edition_billing_resource_check,
 -     enterprise_license_required,
 -     setup_required,
 - )
 - from core.ops.ops_trace_manager import OpsTraceManager
 - from extensions.ext_database import db
 - from fields.app_fields import app_detail_fields, app_detail_fields_with_site, app_pagination_fields
 - from libs.login import login_required
 - from models import Account, App
 - from services.app_dsl_service import AppDslService, ImportMode
 - from services.app_service import AppService
 - from services.enterprise.enterprise_service import EnterpriseService
 - from services.feature_service import FeatureService
 - 
 - ALLOW_CREATE_APP_MODES = ["chat", "agent-chat", "advanced-chat", "workflow", "completion"]
 - 
 - 
 - def _validate_description_length(description):
 -     if description and len(description) > 400:
 -         raise ValueError("Description cannot exceed 400 characters.")
 -     return description
 - 
 - 
 - @console_ns.route("/apps")
 - class AppListApi(Resource):
 -     @api.doc("list_apps")
 -     @api.doc(description="Get list of applications with pagination and filtering")
 -     @api.expect(
 -         api.parser()
 -         .add_argument("page", type=int, location="args", help="Page number (1-99999)", default=1)
 -         .add_argument("limit", type=int, location="args", help="Page size (1-100)", default=20)
 -         .add_argument(
 -             "mode",
 -             type=str,
 -             location="args",
 -             choices=["completion", "chat", "advanced-chat", "workflow", "agent-chat", "channel", "all"],
 -             default="all",
 -             help="App mode filter",
 -         )
 -         .add_argument("name", type=str, location="args", help="Filter by app name")
 -         .add_argument("tag_ids", type=str, location="args", help="Comma-separated tag IDs")
 -         .add_argument("is_created_by_me", type=bool, location="args", help="Filter by creator")
 -     )
 -     @api.response(200, "Success", app_pagination_fields)
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     @enterprise_license_required
 -     def get(self):
 -         """Get app list"""
 - 
 -         def uuid_list(value):
 -             try:
 -                 return [str(uuid.UUID(v)) for v in value.split(",")]
 -             except ValueError:
 -                 abort(400, message="Invalid UUID format in tag_ids.")
 - 
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("page", type=inputs.int_range(1, 99999), required=False, default=1, location="args")
 -         parser.add_argument("limit", type=inputs.int_range(1, 100), required=False, default=20, location="args")
 -         parser.add_argument(
 -             "mode",
 -             type=str,
 -             choices=[
 -                 "completion",
 -                 "chat",
 -                 "advanced-chat",
 -                 "workflow",
 -                 "agent-chat",
 -                 "channel",
 -                 "all",
 -             ],
 -             default="all",
 -             location="args",
 -             required=False,
 -         )
 -         parser.add_argument("name", type=str, location="args", required=False)
 -         parser.add_argument("tag_ids", type=uuid_list, location="args", required=False)
 -         parser.add_argument("is_created_by_me", type=inputs.boolean, location="args", required=False)
 - 
 -         args = parser.parse_args()
 - 
 -         # get app list
 -         app_service = AppService()
 -         app_pagination = app_service.get_paginate_apps(current_user.id, current_user.current_tenant_id, args)
 -         if not app_pagination:
 -             return {"data": [], "total": 0, "page": 1, "limit": 20, "has_more": False}
 - 
 -         if FeatureService.get_system_features().webapp_auth.enabled:
 -             app_ids = [str(app.id) for app in app_pagination.items]
 -             res = EnterpriseService.WebAppAuth.batch_get_app_access_mode_by_id(app_ids=app_ids)
 -             if len(res) != len(app_ids):
 -                 raise BadRequest("Invalid app id in webapp auth")
 - 
 -             for app in app_pagination.items:
 -                 if str(app.id) in res:
 -                     app.access_mode = res[str(app.id)].access_mode
 - 
 -         return marshal(app_pagination, app_pagination_fields), 200
 - 
 -     @api.doc("create_app")
 -     @api.doc(description="Create a new application")
 -     @api.expect(
 -         api.model(
 -             "CreateAppRequest",
 -             {
 -                 "name": fields.String(required=True, description="App name"),
 -                 "description": fields.String(description="App description (max 400 chars)"),
 -                 "mode": fields.String(required=True, enum=ALLOW_CREATE_APP_MODES, description="App mode"),
 -                 "icon_type": fields.String(description="Icon type"),
 -                 "icon": fields.String(description="Icon"),
 -                 "icon_background": fields.String(description="Icon background color"),
 -             },
 -         )
 -     )
 -     @api.response(201, "App created successfully", app_detail_fields)
 -     @api.response(403, "Insufficient permissions")
 -     @api.response(400, "Invalid request parameters")
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     @marshal_with(app_detail_fields)
 -     @cloud_edition_billing_resource_check("apps")
 -     def post(self):
 -         """Create app"""
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("name", type=str, required=True, location="json")
 -         parser.add_argument("description", type=_validate_description_length, location="json")
 -         parser.add_argument("mode", type=str, choices=ALLOW_CREATE_APP_MODES, location="json")
 -         parser.add_argument("icon_type", type=str, location="json")
 -         parser.add_argument("icon", type=str, location="json")
 -         parser.add_argument("icon_background", type=str, location="json")
 -         args = parser.parse_args()
 - 
 -         # The role of the current user in the ta table must be admin, owner, or editor
 -         if not current_user.is_editor:
 -             raise Forbidden()
 - 
 -         if "mode" not in args or args["mode"] is None:
 -             raise BadRequest("mode is required")
 - 
 -         app_service = AppService()
 -         if not isinstance(current_user, Account):
 -             raise ValueError("current_user must be an Account instance")
 -         if current_user.current_tenant_id is None:
 -             raise ValueError("current_user.current_tenant_id cannot be None")
 -         app = app_service.create_app(current_user.current_tenant_id, args, current_user)
 - 
 -         return app, 201
 - 
 - 
 - @console_ns.route("/apps/<uuid:app_id>")
 - class AppApi(Resource):
 -     @api.doc("get_app_detail")
 -     @api.doc(description="Get application details")
 -     @api.doc(params={"app_id": "Application ID"})
 -     @api.response(200, "Success", app_detail_fields_with_site)
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     @enterprise_license_required
 -     @get_app_model
 -     @marshal_with(app_detail_fields_with_site)
 -     def get(self, app_model):
 -         """Get app detail"""
 -         app_service = AppService()
 - 
 -         app_model = app_service.get_app(app_model)
 - 
 -         if FeatureService.get_system_features().webapp_auth.enabled:
 -             app_setting = EnterpriseService.WebAppAuth.get_app_access_mode_by_id(app_id=str(app_model.id))
 -             app_model.access_mode = app_setting.access_mode
 - 
 -         return app_model
 - 
 -     @api.doc("update_app")
 -     @api.doc(description="Update application details")
 -     @api.doc(params={"app_id": "Application ID"})
 -     @api.expect(
 -         api.model(
 -             "UpdateAppRequest",
 -             {
 -                 "name": fields.String(required=True, description="App name"),
 -                 "description": fields.String(description="App description (max 400 chars)"),
 -                 "icon_type": fields.String(description="Icon type"),
 -                 "icon": fields.String(description="Icon"),
 -                 "icon_background": fields.String(description="Icon background color"),
 -                 "use_icon_as_answer_icon": fields.Boolean(description="Use icon as answer icon"),
 -                 "max_active_requests": fields.Integer(description="Maximum active requests"),
 -             },
 -         )
 -     )
 -     @api.response(200, "App updated successfully", app_detail_fields_with_site)
 -     @api.response(403, "Insufficient permissions")
 -     @api.response(400, "Invalid request parameters")
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     @get_app_model
 -     @marshal_with(app_detail_fields_with_site)
 -     def put(self, app_model):
 -         """Update app"""
 -         # The role of the current user in the ta table must be admin, owner, or editor
 -         if not current_user.is_editor:
 -             raise Forbidden()
 - 
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("name", type=str, required=True, nullable=False, location="json")
 -         parser.add_argument("description", type=_validate_description_length, location="json")
 -         parser.add_argument("icon_type", type=str, location="json")
 -         parser.add_argument("icon", type=str, location="json")
 -         parser.add_argument("icon_background", type=str, location="json")
 -         parser.add_argument("use_icon_as_answer_icon", type=bool, location="json")
 -         parser.add_argument("max_active_requests", type=int, location="json")
 -         args = parser.parse_args()
 - 
 -         app_service = AppService()
 -         # Construct ArgsDict from parsed arguments
 -         from services.app_service import AppService as AppServiceType
 - 
 -         args_dict: AppServiceType.ArgsDict = {
 -             "name": args["name"],
 -             "description": args.get("description", ""),
 -             "icon_type": args.get("icon_type", ""),
 -             "icon": args.get("icon", ""),
 -             "icon_background": args.get("icon_background", ""),
 -             "use_icon_as_answer_icon": args.get("use_icon_as_answer_icon", False),
 -             "max_active_requests": args.get("max_active_requests", 0),
 -         }
 -         app_model = app_service.update_app(app_model, args_dict)
 - 
 -         return app_model
 - 
 -     @api.doc("delete_app")
 -     @api.doc(description="Delete application")
 -     @api.doc(params={"app_id": "Application ID"})
 -     @api.response(204, "App deleted successfully")
 -     @api.response(403, "Insufficient permissions")
 -     @get_app_model
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     def delete(self, app_model):
 -         """Delete app"""
 -         # The role of the current user in the ta table must be admin, owner, or editor
 -         if not current_user.is_editor:
 -             raise Forbidden()
 - 
 -         app_service = AppService()
 -         app_service.delete_app(app_model)
 - 
 -         return {"result": "success"}, 204
 - 
 - 
 - @console_ns.route("/apps/<uuid:app_id>/copy")
 - class AppCopyApi(Resource):
 -     @api.doc("copy_app")
 -     @api.doc(description="Create a copy of an existing application")
 -     @api.doc(params={"app_id": "Application ID to copy"})
 -     @api.expect(
 -         api.model(
 -             "CopyAppRequest",
 -             {
 -                 "name": fields.String(description="Name for the copied app"),
 -                 "description": fields.String(description="Description for the copied app"),
 -                 "icon_type": fields.String(description="Icon type"),
 -                 "icon": fields.String(description="Icon"),
 -                 "icon_background": fields.String(description="Icon background color"),
 -             },
 -         )
 -     )
 -     @api.response(201, "App copied successfully", app_detail_fields_with_site)
 -     @api.response(403, "Insufficient permissions")
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     @get_app_model
 -     @marshal_with(app_detail_fields_with_site)
 -     def post(self, app_model):
 -         """Copy app"""
 -         # The role of the current user in the ta table must be admin, owner, or editor
 -         if not current_user.is_editor:
 -             raise Forbidden()
 - 
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("name", type=str, location="json")
 -         parser.add_argument("description", type=_validate_description_length, location="json")
 -         parser.add_argument("icon_type", type=str, location="json")
 -         parser.add_argument("icon", type=str, location="json")
 -         parser.add_argument("icon_background", type=str, location="json")
 -         args = parser.parse_args()
 - 
 -         with Session(db.engine) as session:
 -             import_service = AppDslService(session)
 -             yaml_content = import_service.export_dsl(app_model=app_model, include_secret=True)
 -             account = cast(Account, current_user)
 -             result = import_service.import_app(
 -                 account=account,
 -                 import_mode=ImportMode.YAML_CONTENT.value,
 -                 yaml_content=yaml_content,
 -                 name=args.get("name"),
 -                 description=args.get("description"),
 -                 icon_type=args.get("icon_type"),
 -                 icon=args.get("icon"),
 -                 icon_background=args.get("icon_background"),
 -             )
 -             session.commit()
 - 
 -             stmt = select(App).where(App.id == result.app_id)
 -             app = session.scalar(stmt)
 - 
 -         return app, 201
 - 
 - 
 - @console_ns.route("/apps/<uuid:app_id>/export")
 - class AppExportApi(Resource):
 -     @api.doc("export_app")
 -     @api.doc(description="Export application configuration as DSL")
 -     @api.doc(params={"app_id": "Application ID to export"})
 -     @api.expect(
 -         api.parser()
 -         .add_argument("include_secret", type=bool, location="args", default=False, help="Include secrets in export")
 -         .add_argument("workflow_id", type=str, location="args", help="Specific workflow ID to export")
 -     )
 -     @api.response(
 -         200,
 -         "App exported successfully",
 -         api.model("AppExportResponse", {"data": fields.String(description="DSL export data")}),
 -     )
 -     @api.response(403, "Insufficient permissions")
 -     @get_app_model
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     def get(self, app_model):
 -         """Export app"""
 -         # The role of the current user in the ta table must be admin, owner, or editor
 -         if not current_user.is_editor:
 -             raise Forbidden()
 - 
 -         # Add include_secret params
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("include_secret", type=inputs.boolean, default=False, location="args")
 -         parser.add_argument("workflow_id", type=str, location="args")
 -         args = parser.parse_args()
 - 
 -         return {
 -             "data": AppDslService.export_dsl(
 -                 app_model=app_model, include_secret=args["include_secret"], workflow_id=args.get("workflow_id")
 -             )
 -         }
 - 
 - 
 - @console_ns.route("/apps/<uuid:app_id>/name")
 - class AppNameApi(Resource):
 -     @api.doc("check_app_name")
 -     @api.doc(description="Check if app name is available")
 -     @api.doc(params={"app_id": "Application ID"})
 -     @api.expect(api.parser().add_argument("name", type=str, required=True, location="args", help="Name to check"))
 -     @api.response(200, "Name availability checked")
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     @get_app_model
 -     @marshal_with(app_detail_fields)
 -     def post(self, app_model):
 -         # The role of the current user in the ta table must be admin, owner, or editor
 -         if not current_user.is_editor:
 -             raise Forbidden()
 - 
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("name", type=str, required=True, location="json")
 -         args = parser.parse_args()
 - 
 -         app_service = AppService()
 -         app_model = app_service.update_app_name(app_model, args["name"])
 - 
 -         return app_model
 - 
 - 
 - @console_ns.route("/apps/<uuid:app_id>/icon")
 - class AppIconApi(Resource):
 -     @api.doc("update_app_icon")
 -     @api.doc(description="Update application icon")
 -     @api.doc(params={"app_id": "Application ID"})
 -     @api.expect(
 -         api.model(
 -             "AppIconRequest",
 -             {
 -                 "icon": fields.String(required=True, description="Icon data"),
 -                 "icon_type": fields.String(description="Icon type"),
 -                 "icon_background": fields.String(description="Icon background color"),
 -             },
 -         )
 -     )
 -     @api.response(200, "Icon updated successfully")
 -     @api.response(403, "Insufficient permissions")
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     @get_app_model
 -     @marshal_with(app_detail_fields)
 -     def post(self, app_model):
 -         # The role of the current user in the ta table must be admin, owner, or editor
 -         if not current_user.is_editor:
 -             raise Forbidden()
 - 
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("icon", type=str, location="json")
 -         parser.add_argument("icon_background", type=str, location="json")
 -         args = parser.parse_args()
 - 
 -         app_service = AppService()
 -         app_model = app_service.update_app_icon(app_model, args.get("icon") or "", args.get("icon_background") or "")
 - 
 -         return app_model
 - 
 - 
 - @console_ns.route("/apps/<uuid:app_id>/site-enable")
 - class AppSiteStatus(Resource):
 -     @api.doc("update_app_site_status")
 -     @api.doc(description="Enable or disable app site")
 -     @api.doc(params={"app_id": "Application ID"})
 -     @api.expect(
 -         api.model(
 -             "AppSiteStatusRequest", {"enable_site": fields.Boolean(required=True, description="Enable or disable site")}
 -         )
 -     )
 -     @api.response(200, "Site status updated successfully", app_detail_fields)
 -     @api.response(403, "Insufficient permissions")
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     @get_app_model
 -     @marshal_with(app_detail_fields)
 -     def post(self, app_model):
 -         # The role of the current user in the ta table must be admin, owner, or editor
 -         if not current_user.is_editor:
 -             raise Forbidden()
 - 
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("enable_site", type=bool, required=True, location="json")
 -         args = parser.parse_args()
 - 
 -         app_service = AppService()
 -         app_model = app_service.update_app_site_status(app_model, args["enable_site"])
 - 
 -         return app_model
 - 
 - 
 - @console_ns.route("/apps/<uuid:app_id>/api-enable")
 - class AppApiStatus(Resource):
 -     @api.doc("update_app_api_status")
 -     @api.doc(description="Enable or disable app API")
 -     @api.doc(params={"app_id": "Application ID"})
 -     @api.expect(
 -         api.model(
 -             "AppApiStatusRequest", {"enable_api": fields.Boolean(required=True, description="Enable or disable API")}
 -         )
 -     )
 -     @api.response(200, "API status updated successfully", app_detail_fields)
 -     @api.response(403, "Insufficient permissions")
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     @get_app_model
 -     @marshal_with(app_detail_fields)
 -     def post(self, app_model):
 -         # The role of the current user in the ta table must be admin or owner
 -         if not current_user.is_admin_or_owner:
 -             raise Forbidden()
 - 
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("enable_api", type=bool, required=True, location="json")
 -         args = parser.parse_args()
 - 
 -         app_service = AppService()
 -         app_model = app_service.update_app_api_status(app_model, args["enable_api"])
 - 
 -         return app_model
 - 
 - 
 - @console_ns.route("/apps/<uuid:app_id>/trace")
 - class AppTraceApi(Resource):
 -     @api.doc("get_app_trace")
 -     @api.doc(description="Get app tracing configuration")
 -     @api.doc(params={"app_id": "Application ID"})
 -     @api.response(200, "Trace configuration retrieved successfully")
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     def get(self, app_id):
 -         """Get app trace"""
 -         app_trace_config = OpsTraceManager.get_app_tracing_config(app_id=app_id)
 - 
 -         return app_trace_config
 - 
 -     @api.doc("update_app_trace")
 -     @api.doc(description="Update app tracing configuration")
 -     @api.doc(params={"app_id": "Application ID"})
 -     @api.expect(
 -         api.model(
 -             "AppTraceRequest",
 -             {
 -                 "enabled": fields.Boolean(required=True, description="Enable or disable tracing"),
 -                 "tracing_provider": fields.String(required=True, description="Tracing provider"),
 -             },
 -         )
 -     )
 -     @api.response(200, "Trace configuration updated successfully")
 -     @api.response(403, "Insufficient permissions")
 -     @setup_required
 -     @login_required
 -     @account_initialization_required
 -     def post(self, app_id):
 -         # add app trace
 -         if not current_user.is_editor:
 -             raise Forbidden()
 -         parser = reqparse.RequestParser()
 -         parser.add_argument("enabled", type=bool, required=True, location="json")
 -         parser.add_argument("tracing_provider", type=str, required=True, location="json")
 -         args = parser.parse_args()
 - 
 -         OpsTraceManager.update_app_tracing_config(
 -             app_id=app_id,
 -             enabled=args["enabled"],
 -             tracing_provider=args["tracing_provider"],
 -         )
 - 
 -         return {"result": "success"}
 
 
  |