fix: Application import (#3566)

This commit is contained in:
shaohuzhang1 2025-07-11 21:45:08 +08:00 committed by GitHub
parent 109e8507f1
commit 592ae54e82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 233 additions and 99 deletions

View File

@ -34,7 +34,7 @@ from common.database_model_manage.database_model_manage import DatabaseModelMana
from common.db.search import native_search, native_page_search from common.db.search import native_search, native_page_search
from common.exception.app_exception import AppApiException from common.exception.app_exception import AppApiException
from common.field.common import UploadedFileField from common.field.common import UploadedFileField
from common.utils.common import get_file_content, valid_license, restricted_loads, generate_uuid from common.utils.common import get_file_content, restricted_loads, generate_uuid
from knowledge.models import Knowledge, KnowledgeScope from knowledge.models import Knowledge, KnowledgeScope
from knowledge.serializers.knowledge import KnowledgeSerializer, KnowledgeModelSerializer from knowledge.serializers.knowledge import KnowledgeSerializer, KnowledgeModelSerializer
from maxkb.conf import PROJECT_DIR from maxkb.conf import PROJECT_DIR
@ -377,6 +377,7 @@ class Query(serializers.Serializer):
class ApplicationImportRequest(serializers.Serializer): class ApplicationImportRequest(serializers.Serializer):
file = UploadedFileField(required=True, label=_("file")) file = UploadedFileField(required=True, label=_("file"))
folder_id = serializers.CharField(required=True, label=_("Folder ID"))
class ApplicationEditSerializer(serializers.Serializer): class ApplicationEditSerializer(serializers.Serializer):
@ -478,16 +479,14 @@ class ApplicationSerializer(serializers.Serializer):
QuerySet(ApplicationKnowledgeMapping).bulk_create(application_knowledge_mapping_model_list) QuerySet(ApplicationKnowledgeMapping).bulk_create(application_knowledge_mapping_model_list)
return ApplicationCreateSerializer.ApplicationResponse(application_model).data return ApplicationCreateSerializer.ApplicationResponse(application_model).data
@valid_license(model=Application, count=5,
message=_(
'The community version supports up to 5 applications. If you need more applications, please contact us (https://fit2cloud.com/).'))
@transaction.atomic @transaction.atomic
def import_(self, instance: dict, with_valid=True): def import_(self, instance: dict, is_import_tool, with_valid=True):
if with_valid: if with_valid:
self.is_valid() self.is_valid()
ApplicationImportRequest(data=instance).is_valid(raise_exception=True) ApplicationImportRequest(data=instance).is_valid(raise_exception=True)
user_id = self.data.get('user_id') user_id = self.data.get('user_id')
workspace_id = self.data.get("workspace_id") workspace_id = self.data.get("workspace_id")
folder_id = instance.get('folder_id')
mk_instance_bytes = instance.get('file').read() mk_instance_bytes = instance.get('file').read()
try: try:
mk_instance = restricted_loads(mk_instance_bytes) mk_instance = restricted_loads(mk_instance_bytes)
@ -498,7 +497,7 @@ class ApplicationSerializer(serializers.Serializer):
update_tool_map = {} update_tool_map = {}
if len(tool_list) > 0: if len(tool_list) > 0:
tool_id_list = reduce(lambda x, y: [*x, *y], tool_id_list = reduce(lambda x, y: [*x, *y],
[[tool.get('id'), generate_uuid((tool.get('id') + tool.get('workspace_id') or ''))] [[tool.get('id'), generate_uuid((tool.get('id') + workspace_id or ''))]
for tool for tool
in in
tool_list], []) tool_list], [])
@ -506,7 +505,7 @@ class ApplicationSerializer(serializers.Serializer):
exits_tool_id_list = [str(tool.id) for tool in exits_tool_id_list = [str(tool.id) for tool in
QuerySet(Tool).filter(id__in=tool_id_list, workspace_id=workspace_id)] QuerySet(Tool).filter(id__in=tool_id_list, workspace_id=workspace_id)]
# 需要更新的工具集合 # 需要更新的工具集合
update_tool_map = {tool.get('id'): generate_uuid((tool.get('id') + tool.get('workspace_id') or '')) for tool update_tool_map = {tool.get('id'): generate_uuid((tool.get('id') + workspace_id or '')) for tool
in in
tool_list if tool_list if
not exits_tool_id_list.__contains__( not exits_tool_id_list.__contains__(
@ -515,8 +514,8 @@ class ApplicationSerializer(serializers.Serializer):
tool_list = [{**tool, 'id': update_tool_map.get(tool.get('id'))} for tool in tool_list if tool_list = [{**tool, 'id': update_tool_map.get(tool.get('id'))} for tool in tool_list if
not exits_tool_id_list.__contains__( not exits_tool_id_list.__contains__(
tool.get('id')) and not exits_tool_id_list.__contains__( tool.get('id')) and not exits_tool_id_list.__contains__(
generate_uuid((tool.get('id') + tool.get('workspace_id') or '')))] generate_uuid((tool.get('id') + workspace_id or '')))]
application_model = self.to_application(application, workspace_id, user_id, update_tool_map) application_model = self.to_application(application, workspace_id, user_id, update_tool_map, folder_id)
tool_model_list = [self.to_tool(f, workspace_id, user_id) for f in tool_list] tool_model_list = [self.to_tool(f, workspace_id, user_id) for f in tool_list]
application_model.save() application_model.save()
# 插入授权数据 # 插入授权数据
@ -528,7 +527,14 @@ class ApplicationSerializer(serializers.Serializer):
# 插入认证信息 # 插入认证信息
ApplicationAccessToken(application_id=application_model.id, ApplicationAccessToken(application_id=application_model.id,
access_token=hashlib.md5(str(uuid.uuid7()).encode()).hexdigest()[8:24]).save() access_token=hashlib.md5(str(uuid.uuid7()).encode()).hexdigest()[8:24]).save()
QuerySet(Tool).bulk_create(tool_model_list) if len(tool_model_list) > 0 else None if is_import_tool:
if len(tool_model_list) > 0:
QuerySet(Tool).bulk_create(tool_model_list)
UserResourcePermissionSerializer(data={
'workspace_id': self.data.get('workspace_id'),
'user_id': self.data.get('user_id'),
'auth_target_type': AuthTargetType.APPLICATION.value
}).auth_resource_batch([t.id for t in tool_model_list])
return True return True
@staticmethod @staticmethod
@ -550,7 +556,7 @@ class ApplicationSerializer(serializers.Serializer):
workspace_id=workspace_id) workspace_id=workspace_id)
@staticmethod @staticmethod
def to_application(application, workspace_id, user_id, update_tool_map): def to_application(application, workspace_id, user_id, update_tool_map, folder_id):
work_flow = application.get('work_flow') work_flow = application.get('work_flow')
for node in work_flow.get('nodes', []): for node in work_flow.get('nodes', []):
if node.get('type') == 'tool-lib-node': if node.get('type') == 'tool-lib-node':
@ -563,7 +569,7 @@ class ApplicationSerializer(serializers.Serializer):
user_id=user_id, user_id=user_id,
name=application.get('name'), name=application.get('name'),
workspace_id=workspace_id, workspace_id=workspace_id,
folder_id=workspace_id, folder_id=folder_id,
desc=application.get('desc'), desc=application.get('desc'),
prologue=application.get('prologue'), dialogue_number=application.get('dialogue_number'), prologue=application.get('prologue'), dialogue_number=application.get('dialogue_number'),
knowledge_setting=application.get('knowledge_setting'), knowledge_setting=application.get('knowledge_setting'),

View File

@ -7,7 +7,7 @@ app_name = 'application'
urlpatterns = [ urlpatterns = [
path('workspace/<str:workspace_id>/application', views.ApplicationAPI.as_view(), name='application'), path('workspace/<str:workspace_id>/application', views.ApplicationAPI.as_view(), name='application'),
path('workspace/<str:workspace_id>/application/import', views.ApplicationAPI.Import.as_view()), path('workspace/<str:workspace_id>/application/folder/<str:folder_id>/import', views.ApplicationAPI.Import.as_view()),
path('workspace/<str:workspace_id>/application/<int:current_page>/<int:page_size>', views.ApplicationAPI.Page.as_view(), name='application_page'), path('workspace/<str:workspace_id>/application/<int:current_page>/<int:page_size>', views.ApplicationAPI.Page.as_view(), name='application_page'),
path('workspace/<str:workspace_id>/application/<str:application_id>', views.ApplicationAPI.Operate.as_view()), path('workspace/<str:workspace_id>/application/<str:application_id>', views.ApplicationAPI.Operate.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/publish', views.ApplicationAPI.Publish.as_view()), path('workspace/<str:workspace_id>/application/<str:application_id>/publish', views.ApplicationAPI.Publish.as_view()),

View File

@ -21,7 +21,7 @@ from application.models import Application
from application.serializers.application import ApplicationSerializer, Query, ApplicationOperateSerializer from application.serializers.application import ApplicationSerializer, Query, ApplicationOperateSerializer
from common import result from common import result
from common.auth import TokenAuth from common.auth import TokenAuth
from common.auth.authentication import has_permissions from common.auth.authentication import has_permissions, get_is_permissions
from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants
from common.log.log import log from common.log.log import log
@ -112,10 +112,15 @@ class ApplicationAPI(APIView):
RoleConstants.USER.get_workspace_role(), RoleConstants.USER.get_workspace_role(),
RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) RoleConstants.WORKSPACE_MANAGE.get_workspace_role())
@log(menu='Application', operate="Import Application", ) @log(menu='Application', operate="Import Application", )
def post(self, request: Request, workspace_id: str): def post(self, request: Request, workspace_id: str, folder_id: str):
is_import_tool = get_is_permissions(request, workspace_id=workspace_id, folder_id=folder_id)(
PermissionConstants.TOOL_IMPORT.get_workspace_permission(),
PermissionConstants.TOOL_IMPORT.get_workspace_permission_workspace_manage_role(),
RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), RoleConstants.USER.get_workspace_role()
)
return result.success(ApplicationSerializer( return result.success(ApplicationSerializer(
data={'user_id': request.user.id, 'workspace_id': workspace_id, data={'user_id': request.user.id, 'workspace_id': workspace_id,
}).import_({'file': request.FILES.get('file')})) }).import_({'file': request.FILES.get('file'), 'folder_id': folder_id}, is_import_tool))
class Export(APIView): class Export(APIView):
authentication_classes = [TokenAuth] authentication_classes = [TokenAuth]

View File

@ -83,6 +83,16 @@ def exist(user_role: List[RoleConstants], user_permission: List[PermissionConsta
return exist_permissions(user_role, user_permission, permission, request, **kwargs) return exist_permissions(user_role, user_permission, permission, request, **kwargs)
def get_is_permissions(request, **kwargs):
def is_permissions(*permission, compare=CompareConstants.OR):
exit_list = list(
map(lambda p: exist(request.auth.role_list, request.auth.permission_list, p, request, **kwargs),
permission))
return any(exit_list) if compare == CompareConstants.OR else all(exit_list)
return is_permissions
def has_permissions(*permission, compare=CompareConstants.OR): def has_permissions(*permission, compare=CompareConstants.OR):
""" """
权限 role or permission 权限 role or permission

View File

@ -130,6 +130,32 @@ class UserResourcePermissionSerializer(serializers.Serializer):
else: else:
return wurp.permission_list.__contains__(ResourcePermission.VIEW.value) return wurp.permission_list.__contains__(ResourcePermission.VIEW.value)
def auth_resource_batch(self, resource_id_list: list):
self.is_valid(raise_exception=True)
auth_target_type = self.data.get('auth_target_type')
workspace_id = self.data.get('workspace_id')
user_id = self.data.get('user_id')
wurp = QuerySet(WorkspaceUserResourcePermission).filter(auth_target_type=auth_target_type,
workspace_id=workspace_id, user_id=user_id).first()
auth_type = wurp.auth_type if wurp else (
ResourceAuthType.RESOURCE_PERMISSION_GROUP if edition == 'CE' else ResourceAuthType.ROLE)
workspace_user_resource_permission = [WorkspaceUserResourcePermission(
target=resource_id,
auth_target_type=auth_target_type,
permission_list=[ResourcePermission.VIEW,
ResourcePermission.MANAGE] if auth_type == ResourceAuthType.RESOURCE_PERMISSION_GROUP else [
ResourcePermissionRole.ROLE],
workspace_id=workspace_id,
user_id=user_id,
auth_type=auth_type
) for resource_id in resource_id_list]
QuerySet(WorkspaceUserResourcePermission).bulk_create(workspace_user_resource_permission)
# 刷新缓存
version = Cache_Version.PERMISSION_LIST.get_version()
key = Cache_Version.PERMISSION_LIST.get_key(user_id=user_id)
cache.delete(key, version=version)
return True
def auth_resource(self, resource_id: str): def auth_resource(self, resource_id: str):
self.is_valid(raise_exception=True) self.is_valid(raise_exception=True)
auth_target_type = self.data.get('auth_target_type') auth_target_type = self.data.get('auth_target_type')

View File

@ -140,7 +140,6 @@ const putXpackAccessToken: (
return put(`${prefix.value}/${application_id}/setting`, data, undefined, loading) return put(`${prefix.value}/${application_id}/setting`, data, undefined, loading)
} }
/** /**
* *
*/ */
@ -161,11 +160,12 @@ const exportApplication = (
/** /**
* *
*/ */
const importApplication: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = ( const importApplication: (
data, folder_id: string,
loading, data: any,
) => { loading?: Ref<boolean>,
return post(`${prefix.value}/import`, data, undefined, loading) ) => Promise<Result<any>> = (folder_id, data, loading) => {
return post(`${prefix.value}/folder/${folder_id}/import`, data, undefined, loading)
} }
/** /**

View File

@ -225,9 +225,7 @@
</el-button> </el-button>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item <el-dropdown-item @click.stop="getAccessToken(item.id)">
@click.stop="getAccessToken(item.id)"
>
<AppIcon iconName="app-create-chat"></AppIcon> <AppIcon iconName="app-create-chat"></AppIcon>
{{ $t('views.application.operation.toChat') }} {{ $t('views.application.operation.toChat') }}
</el-dropdown-item> </el-dropdown-item>
@ -268,8 +266,7 @@
@click.stop="deleteApplication(item)" @click.stop="deleteApplication(item)"
v-if="permissionPrecise.delete(item.id)" v-if="permissionPrecise.delete(item.id)"
>{{ $t('common.delete') }} >{{ $t('common.delete') }}
</el-dropdown-item </el-dropdown-item>
>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
@ -366,33 +363,124 @@ const goApp = (item: any) => {
router.push({ path: get_route(item) }) router.push({ path: get_route(item) })
} }
const get_route = (item: any) => { const get_route = (item: any) => {
if (hasPermission([new ComplexPermission([RoleConst.USER], [PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)], [], 'AND'), if (
hasPermission(
[
new ComplexPermission(
[RoleConst.USER],
[PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)],
[],
'AND',
),
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
PermissionConst.APPLICATION_OVERVIEW_READ.getWorkspacePermissionWorkspaceManageRole, PermissionConst.APPLICATION_OVERVIEW_READ.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.APPLICATION_OVERVIEW_READ.getApplicationWorkspaceResourcePermission(item.id)], 'OR')) { PermissionConst.APPLICATION_OVERVIEW_READ.getApplicationWorkspaceResourcePermission(
item.id,
),
],
'OR',
)
) {
return `/application/${item.id}/${item.type}/overview` return `/application/${item.id}/${item.type}/overview`
} else if (hasPermission([new ComplexPermission([RoleConst.USER], [PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)], [], 'AND'), } else if (
hasPermission(
[
new ComplexPermission(
[RoleConst.USER],
[PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)],
[],
'AND',
),
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
PermissionConst.APPLICATION_EDIT.getWorkspacePermissionWorkspaceManageRole, PermissionConst.APPLICATION_EDIT.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.APPLICATION_EDIT.getApplicationWorkspaceResourcePermission(item.id)], 'OR')) { PermissionConst.APPLICATION_EDIT.getApplicationWorkspaceResourcePermission(item.id),
],
'OR',
)
) {
if (item.type == 'WORK_FLOW') { if (item.type == 'WORK_FLOW') {
return `/application/${item.id}/workflow` return `/application/${item.id}/workflow`
} else { } else {
return `/application/${item.id}/${item.type}/setting` return `/application/${item.id}/${item.type}/setting`
} }
} else if (hasPermission([new ComplexPermission([RoleConst.USER], [PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)], [EditionConst.IS_EE, EditionConst.IS_PE], 'AND'), } else if (
new ComplexPermission([RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,], [PermissionConst.APPLICATION_ACCESS_READ.getWorkspacePermissionWorkspaceManageRole], [EditionConst.IS_EE, EditionConst.IS_PE], 'OR'), hasPermission(
new ComplexPermission([], [PermissionConst.APPLICATION_ACCESS_READ.getApplicationWorkspaceResourcePermission(item.id)], [EditionConst.IS_EE, EditionConst.IS_PE], 'OR'),], 'OR')) { [
new ComplexPermission(
[RoleConst.USER],
[PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)],
[EditionConst.IS_EE, EditionConst.IS_PE],
'AND',
),
new ComplexPermission(
[RoleConst.WORKSPACE_MANAGE.getWorkspaceRole],
[PermissionConst.APPLICATION_ACCESS_READ.getWorkspacePermissionWorkspaceManageRole],
[EditionConst.IS_EE, EditionConst.IS_PE],
'OR',
),
new ComplexPermission(
[],
[
PermissionConst.APPLICATION_ACCESS_READ.getApplicationWorkspaceResourcePermission(
item.id,
),
],
[EditionConst.IS_EE, EditionConst.IS_PE],
'OR',
),
],
'OR',
)
) {
return `/application/${item.id}/${item.type}/access` return `/application/${item.id}/${item.type}/access`
} else if (hasPermission([new ComplexPermission([RoleConst.USER], [PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)], [EditionConst.IS_EE, EditionConst.IS_PE], 'AND'), } else if (
new ComplexPermission([RoleConst.WORKSPACE_MANAGE.getWorkspaceRole], [PermissionConst.APPLICATION_CHAT_USER_READ.getWorkspacePermissionWorkspaceManageRole], [EditionConst.IS_EE, EditionConst.IS_PE], 'OR'), hasPermission(
new ComplexPermission([], [PermissionConst.APPLICATION_CHAT_USER_READ.getApplicationWorkspaceResourcePermission(item.id)], [EditionConst.IS_EE, EditionConst.IS_PE], 'OR'),], 'OR')) { [
new ComplexPermission(
[RoleConst.USER],
[PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)],
[EditionConst.IS_EE, EditionConst.IS_PE],
'AND',
),
new ComplexPermission(
[RoleConst.WORKSPACE_MANAGE.getWorkspaceRole],
[PermissionConst.APPLICATION_CHAT_USER_READ.getWorkspacePermissionWorkspaceManageRole],
[EditionConst.IS_EE, EditionConst.IS_PE],
'OR',
),
new ComplexPermission(
[],
[
PermissionConst.APPLICATION_CHAT_USER_READ.getApplicationWorkspaceResourcePermission(
item.id,
),
],
[EditionConst.IS_EE, EditionConst.IS_PE],
'OR',
),
],
'OR',
)
) {
return `/application/${item.id}/${item.type}/chat-user` return `/application/${item.id}/${item.type}/chat-user`
} else if (hasPermission([new ComplexPermission([RoleConst.USER], [PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)], [], 'AND'), } else if (
hasPermission(
[
new ComplexPermission(
[RoleConst.USER],
[PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(item.id)],
[],
'AND',
),
PermissionConst.APPLICATION_CHAT_LOG_READ.getWorkspacePermissionWorkspaceManageRole, PermissionConst.APPLICATION_CHAT_LOG_READ.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.APPLICATION_CHAT_LOG_READ.getApplicationWorkspaceResourcePermission(item.id)], 'OR')) { PermissionConst.APPLICATION_CHAT_LOG_READ.getApplicationWorkspaceResourcePermission(
item.id,
),
],
'OR',
)
) {
return `/application/${item.id}/${item.type}/chat-log` return `/application/${item.id}/${item.type}/chat-log`
} else return `/application/` } else return `/application/`
} }
@ -487,8 +575,7 @@ function deleteApplication(row: any) {
MsgSuccess(t('common.deleteSuccess')) MsgSuccess(t('common.deleteSuccess'))
}) })
}) })
.catch(() => { .catch(() => {})
})
} }
const exportApplication = (application: any) => { const exportApplication = (application: any) => {
@ -506,7 +593,7 @@ const importApplication = (file: any) => {
const formData = new FormData() const formData = new FormData()
formData.append('file', file.raw, file.name) formData.append('file', file.raw, file.name)
elUploadRef.value.clearFiles() elUploadRef.value.clearFiles()
ApplicationApi.importApplication(formData, loading) ApplicationApi.importApplication(folder.currentFolder.id, formData, loading)
.then(async (res: any) => { .then(async (res: any) => {
if (res?.data) { if (res?.data) {
applicationList.value = [] applicationList.value = []