mirror of
https://github.com/YFGaia/dify-plus.git
synced 2026-06-14 20:41:21 +08:00
f26fe2f4d2
# Conflicts:
# .gitignore
# README.md
# api/.env.example
# api/Dockerfile
# api/commands.py
# api/configs/app_config.py
# api/controllers/console/__init__.py
# api/controllers/console/apikey.py
# api/controllers/console/app/statistic.py
# api/controllers/service_api/app/app.py
# api/controllers/service_api/app/audio.py
# api/controllers/service_api/app/completion.py
# api/controllers/service_api/app/conversation.py
# api/controllers/service_api/app/file.py
# api/controllers/service_api/app/message.py
# api/controllers/service_api/app/workflow.py
# api/controllers/service_api/wraps.py
# api/controllers/web/completion.py
# api/core/app/apps/advanced_chat/app_generator.py
# api/core/app/apps/advanced_chat/generate_task_pipeline.py
# api/core/app/apps/agent_chat/app_generator.py
# api/core/app/apps/workflow/app_generator.py
# api/core/app/apps/workflow/generate_task_pipeline.py
# api/core/app/task_pipeline/workflow_cycle_manage.py
# api/core/helper/code_executor/code_executor.py
# api/core/tools/builtin_tool/providers/code/tools/simple_code.py
# api/core/workflow/nodes/code/code_node.py
# api/docker/entrypoint.sh
# api/events/event_handlers/__init__.py
# api/extensions/ext_celery.py
# api/extensions/ext_commands.py
# api/models/model.py
# api/models/workflow.py
# api/poetry.lock
# api/pyproject.toml
# api/services/app_service.py
# api/services/feature_service.py
# api/services/workspace_service.py
# web/.env.example
# web/Dockerfile
# web/app/(commonLayout)/apps/Apps.tsx
# web/app/components/apps/app-card.tsx
# web/app/components/base/chat/embedded-chatbot/index.tsx
# web/app/components/base/mermaid/index.tsx
# web/app/components/develop/index.tsx
# web/app/components/develop/secret-key/secret-key-modal.tsx
# web/app/components/develop/secret-key/style.module.css
# web/app/components/develop/template/template.zh.mdx
# web/app/components/explore/app-list/index.tsx
# web/app/components/explore/category.tsx
# web/app/components/explore/sidebar/index.tsx
# web/app/components/header/account-dropdown/index.tsx
# web/app/components/header/index.tsx
# web/app/components/share/utils.ts
# web/app/layout.tsx
# web/app/signin/components/mail-and-password-auth.tsx
# web/app/signin/normal-form.tsx
# web/app/signin/page.module.css
# web/context/app-context.tsx
# web/i18n/i18next-config.ts
# web/i18n/ja-JP/login.ts
# web/i18n/ko-KR/login.ts
#
if dify_config.WORKFLOW_LOG_CLEANUP_ENABLED:
# 2:00 AM every day
imports.append("schedule.clean_workflow_runlogs_precise")
beat_schedule["clean_workflow_runlogs_precise"] = {
"task": "schedule.clean_workflow_runlogs_precise.clean_workflow_runlogs_precise",
"schedule": crontab(minute="0", hour="2"),
} web/package.json
# web/pnpm-lock.yaml
# web/types/feature.ts
377 lines
13 KiB
Python
377 lines
13 KiB
Python
import uuid
|
|
from typing import cast
|
|
|
|
from flask_login import current_user
|
|
from flask_restx import Resource, 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
|
|
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
|
|
|
|
|
|
class AppListApi(Resource):
|
|
@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:
|
|
# ---------------- start app list
|
|
return {"data": [], "total": 0, "page": 1, "limit": 20, "has_more": False, "recommended_apps": []}
|
|
# ---------------- stop app list
|
|
|
|
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
|
|
|
|
@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()
|
|
app = app_service.create_app(current_user.current_tenant_id, args, current_user)
|
|
|
|
return app, 201
|
|
|
|
|
|
class AppApi(Resource):
|
|
@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
|
|
|
|
@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()
|
|
app_model = app_service.update_app(app_model, args)
|
|
|
|
return app_model
|
|
|
|
@setup_required
|
|
@login_required
|
|
@account_initialization_required
|
|
@get_app_model
|
|
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
|
|
|
|
|
|
class AppCopyApi(Resource):
|
|
@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
|
|
|
|
|
|
class AppExportApi(Resource):
|
|
@setup_required
|
|
@login_required
|
|
@account_initialization_required
|
|
@get_app_model
|
|
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")
|
|
)
|
|
}
|
|
|
|
|
|
class AppNameApi(Resource):
|
|
@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.get("name"))
|
|
|
|
return app_model
|
|
|
|
|
|
class AppIconApi(Resource):
|
|
@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"), args.get("icon_background"))
|
|
|
|
return app_model
|
|
|
|
|
|
class AppSiteStatus(Resource):
|
|
@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.get("enable_site"))
|
|
|
|
return app_model
|
|
|
|
|
|
class AppApiStatus(Resource):
|
|
@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.get("enable_api"))
|
|
|
|
return app_model
|
|
|
|
|
|
class AppTraceApi(Resource):
|
|
@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
|
|
|
|
@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"}
|
|
|
|
|
|
api.add_resource(AppListApi, "/apps")
|
|
api.add_resource(AppApi, "/apps/<uuid:app_id>")
|
|
api.add_resource(AppCopyApi, "/apps/<uuid:app_id>/copy")
|
|
api.add_resource(AppExportApi, "/apps/<uuid:app_id>/export")
|
|
api.add_resource(AppNameApi, "/apps/<uuid:app_id>/name")
|
|
api.add_resource(AppIconApi, "/apps/<uuid:app_id>/icon")
|
|
api.add_resource(AppSiteStatus, "/apps/<uuid:app_id>/site-enable")
|
|
api.add_resource(AppApiStatus, "/apps/<uuid:app_id>/api-enable")
|
|
api.add_resource(AppTraceApi, "/apps/<uuid:app_id>/trace")
|