From e63572b89b563bf3f70ddb3caeb28ec8b0ca6c34 Mon Sep 17 00:00:00 2001 From: zhangzhanwei Date: Thu, 5 Jun 2025 14:08:24 +0800 Subject: [PATCH] feat: System operate log --- apps/application/views/application.py | 27 +++++ apps/application/views/application_api_key.py | 14 +++ apps/application/views/application_version.py | 4 + apps/folders/views/folder.py | 26 +++++ apps/knowledge/serializers/common.py | 14 +++ apps/knowledge/views/common.py | 35 ++++++ apps/knowledge/views/document.py | 106 ++++++++++++++++++ apps/knowledge/views/file.py | 3 + apps/knowledge/views/knowledge.py | 26 +++++ apps/knowledge/views/paragraph.py | 51 +++++++++ apps/knowledge/views/problem.py | 13 +++ apps/models_provider/views/model.py | 44 ++++++++ apps/models_provider/views/provide.py | 2 + apps/system_manage/views/email_setting.py | 26 +++++ apps/system_manage/views/log_management.py | 0 .../views/user_resource_permission.py | 15 ++- apps/tools/views/tool.py | 23 +++- apps/users/views/login.py | 14 +++ apps/users/views/user.py | 33 ++++++ 19 files changed, 474 insertions(+), 2 deletions(-) create mode 100644 apps/knowledge/views/common.py create mode 100644 apps/system_manage/views/log_management.py diff --git a/apps/application/views/application.py b/apps/application/views/application.py index 4d8f17e4a..158c1c005 100644 --- a/apps/application/views/application.py +++ b/apps/application/views/application.py @@ -6,6 +6,7 @@ @date:2025/5/26 16:51 @desc: """ +from django.db.models import QuerySet from django.utils.translation import gettext_lazy as _ from drf_spectacular.utils import extend_schema from rest_framework.parsers import MultiPartParser @@ -14,11 +15,22 @@ from rest_framework.views import APIView from application.api.application_api import ApplicationCreateAPI, ApplicationQueryAPI, ApplicationImportAPI, \ ApplicationExportAPI, ApplicationOperateAPI, ApplicationEditAPI +from application.models import Application from application.serializers.application import ApplicationSerializer, Query, ApplicationOperateSerializer from common import result from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log + + +def get_application_operation_object(application_id): + application_model = QuerySet(model=Application).filter(id=application_id).first() + if application_model is not None: + return{ + 'name': application_model.name + } + return {} class Application(APIView): @@ -35,6 +47,8 @@ class Application(APIView): tags=[_('Application')] # type: ignore ) @has_permissions(PermissionConstants.APPLICATION_READ.get_workspace_permission()) + @log(menu='Application', operate='Create an application', + get_operation_object=lambda r,k: {'name': r.data.get('name')}) def post(self, request: Request, workspace_id: str): return result.success( ApplicationSerializer(data={'workspace_id': workspace_id, 'user_id': request.user.id}).insert(request.data)) @@ -85,11 +99,14 @@ class Application(APIView): tags=[_('Application')] # type: ignore ) @has_permissions(PermissionConstants.APPLICATION_READ) + @log(menu='Application', operate="Import Application") def post(self, request: Request, workspace_id: str): return result.success(ApplicationSerializer( data={'user_id': request.user.id, 'workspace_id': workspace_id, }).import_({'file': request.FILES.get('file')})) + + class Export(APIView): authentication_classes = [TokenAuth] @@ -104,6 +121,8 @@ class Application(APIView): tags=[_('Application')] # type: ignore ) @has_permissions(PermissionConstants.APPLICATION_EXPORT.get_workspace_application_permission()) + @log(menu='Application', operate="Export Application", + get_operation_object=lambda r, k: get_application_operation_object(k.get('application_id'))) def post(self, request: Request, workspace_id: str, application_id: str): return ApplicationOperateSerializer( data={'application_id': application_id, @@ -122,6 +141,9 @@ class Application(APIView): tags=[_('Application')] # type: ignore ) @has_permissions(PermissionConstants.APPLICATION_DELETE.get_workspace_application_permission()) + @log(menu='Application', operate='Deleting application', + get_operation_object=lambda r, k: get_application_operation_object(k.get('application_id')) + ) def delete(self, request: Request, workspace_id: str, application_id: str): return result.success(ApplicationOperateSerializer( data={'application_id': application_id, 'user_id': request.user.id}).delete( @@ -138,6 +160,8 @@ class Application(APIView): tags=[_('Application')] # type: ignore ) @has_permissions(PermissionConstants.APPLICATION_EDIT.get_workspace_application_permission()) + @log(menu='Application', operate="Modify the application", + get_operation_object=lambda r, k: get_application_operation_object(k.get('application_id'))) def put(self, request: Request, workspace_id: str, application_id: str): return result.success( ApplicationOperateSerializer( @@ -172,6 +196,9 @@ class Application(APIView): responses=result.DefaultResultSerializer, tags=[_('Application')] # type: ignore ) + @log(menu='Application', operate='Publishing an application', + get_operation_object=lambda r,k: get_application_operation_object(k.get('application_id')) + ) def put(self, request: Request, application_id: str): return result.success( ApplicationOperateSerializer( diff --git a/apps/application/views/application_api_key.py b/apps/application/views/application_api_key.py index 419472e01..371580344 100644 --- a/apps/application/views/application_api_key.py +++ b/apps/application/views/application_api_key.py @@ -1,14 +1,26 @@ +from django.db.models import QuerySet from drf_spectacular.utils import extend_schema from rest_framework.request import Request from rest_framework.views import APIView from django.utils.translation import gettext_lazy as _ from application.api.application_api_key import ApplicationKeyCreateAPI +from application.models import ApplicationApiKey from application.serializers.application_api_key import ApplicationKeySerializer from common.auth import TokenAuth +from common.log.log import log from common.result import result, success +def get_application_operation_object(application_api_key_id): + application_api_key_model = QuerySet(model=ApplicationApiKey).filter(id=application_api_key_id).first() + if application_api_key_model is not None: + return { + "name": application_api_key_model.name + } + return {} + + class ApplicationKey(APIView): authentication_classes = [TokenAuth] @@ -20,6 +32,8 @@ class ApplicationKey(APIView): parameters=ApplicationKeyCreateAPI.get_parameters(), tags=[_('Application Api Key')] # type: ignore ) + @log(menu='Application', operate="Add ApiKey", + get_operation_object=lambda r, k: get_application_operation_object(k.get('application_api_key_id'))) def post(self,request: Request, application_id: str, workspace_id: str): return result.success(ApplicationKeySerializer( data={'application_id': application_id, 'user_id': request.user.id, diff --git a/apps/application/views/application_version.py b/apps/application/views/application_version.py index f1904ea4c..4fa731b7d 100644 --- a/apps/application/views/application_version.py +++ b/apps/application/views/application_version.py @@ -14,10 +14,12 @@ from rest_framework.views import APIView from application.api.application_version import ApplicationVersionListAPI, ApplicationVersionPageAPI, \ ApplicationVersionAPI, ApplicationVersionOperateAPI from application.serializers.application_version import ApplicationVersionSerializer +from application.views import get_application_operation_object from common import result from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log class ApplicationVersionView(APIView): @@ -88,6 +90,8 @@ class ApplicationVersionView(APIView): responses=ApplicationVersionOperateAPI.get_response(), tags=[_('Application/Version')] # type: ignore ) + @log(menu='Application', operate="Modify application version information", + get_operation_object=lambda r, k: get_application_operation_object(k.get('application_id'))) def put(self, request: Request, workspace_id: str, application_id: str, work_flow_version_id: str): return result.success( ApplicationVersionSerializer.Operate( diff --git a/apps/folders/views/folder.py b/apps/folders/views/folder.py index 0988915d6..e7a488066 100644 --- a/apps/folders/views/folder.py +++ b/apps/folders/views/folder.py @@ -1,3 +1,4 @@ +from django.db.models import QuerySet from django.utils.translation import gettext_lazy as _ from drf_spectacular.utils import extend_schema from rest_framework.request import Request @@ -6,11 +7,29 @@ from rest_framework.views import APIView from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import Permission, Group, Operate +from common.log.log import log from common.result import result from folders.api.folder import FolderCreateAPI, FolderEditAPI, FolderReadAPI, FolderTreeReadAPI, FolderDeleteAPI +from folders.models.folder import FolderCreateRequest, FolderEditRequest from folders.serializers.folder import FolderSerializer, FolderTreeSerializer +def get_folder_create_operation_object(folder_name): + folder_model = QuerySet(model=FolderCreateRequest).filter(name=folder_name).first() + if folder_model is not None: + return { + 'name': folder_model.name + } + return {} + +def get_folder_edit_operation_object(folder_name): + folder_model = QuerySet(model=FolderEditRequest).filter(name=folder_name).first() + if folder_model is not None: + return { + 'name': folder_model.name + } + return {} + class FolderView(APIView): authentication_classes = [TokenAuth] @@ -26,6 +45,9 @@ class FolderView(APIView): ) @has_permissions(lambda r, kwargs: Permission(group=Group(kwargs.get('source')), operate=Operate.CREATE, resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}")) + @log(menu='folder', operate='Create folder', + get_operation_object=lambda r,k: get_folder_create_operation_object(k.get('folder_name')) + ) def post(self, request: Request, workspace_id: str, source: str): return result.success(FolderSerializer.Create( data={'user_id': request.user.id, @@ -64,6 +86,9 @@ class FolderView(APIView): ) @has_permissions(lambda r, kwargs: Permission(group=Group(kwargs.get('source')), operate=Operate.EDIT, resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}")) + @log(menu='folder', operate='Edit folder', + get_operation_object=lambda r, k: get_folder_edit_operation_object(k.get('folder_name')) + ) def put(self, request: Request, workspace_id: str, source: str, folder_id: str): return result.success(FolderSerializer.Operate( data={'id': folder_id, 'workspace_id': workspace_id, 'source': source} @@ -96,6 +121,7 @@ class FolderView(APIView): ) @has_permissions(lambda r, kwargs: Permission(group=Group(kwargs.get('source')), operate=Operate.DELETE, resource_path=f"/WORKSPACE/{kwargs.get('workspace_id')}")) + @log(menu='folder', operate='Delete folder') def delete(self, request: Request, workspace_id: str, source: str, folder_id: str): return result.success(FolderSerializer.Operate( data={'id': folder_id, 'workspace_id': workspace_id, 'source': source} diff --git a/apps/knowledge/serializers/common.py b/apps/knowledge/serializers/common.py index 1a9ff0653..148ee2176 100644 --- a/apps/knowledge/serializers/common.py +++ b/apps/knowledge/serializers/common.py @@ -221,3 +221,17 @@ def or_get(exists_problem_list, content, knowledge_id, document_id, paragraph_id problem = Problem(id=uuid.uuid7(), content=content, knowledge_id=knowledge_id) problem_content_dict[content] = problem, True return problem, document_id, paragraph_id + + + +def get_knowledge_operation_object(knowledge_id: str): + knowledge_model = QuerySet(model=Knowledge).filter(id=knowledge_id).first() + if knowledge_model is not None: + return { + "name": knowledge_model.name, + "desc": knowledge_model.desc, + "type": knowledge_model.type, + "create_time": knowledge_model.create_time, + "update_time": knowledge_model.update_time + } + return {} diff --git a/apps/knowledge/views/common.py b/apps/knowledge/views/common.py new file mode 100644 index 000000000..b149785d5 --- /dev/null +++ b/apps/knowledge/views/common.py @@ -0,0 +1,35 @@ +from django.db.models import QuerySet + +from knowledge.models import Document + + + +def get_document_operation_object(document_id: str): + document_model = QuerySet(model=Document).filter(id=document_id).first() + if document_model is not None: + return { + "name": document_model.name, + "type": document_model.type, + } + return {} + +def get_document_operation_object_batch(document_id_list: str): + document_model_list = QuerySet(model=Document).filter(id__in=document_id_list) + if document_model_list is not None: + return { + "name": f'[{",".join([document_model.name for document_model in document_model_list])}]', + 'document_list': [{'name': document_model.name, 'type': document_model.type} for document_model in + document_model_list] + } + return {} + + +def get_knowledge_document_operation_object(knowledge_dict: dict, document_dict: dict): + return { + 'name': f'{knowledge_dict.get("name", "")}/{document_dict.get("name", "")}', + 'dataset_name': knowledge_dict.get("name", ""), + 'dataset_desc': knowledge_dict.get("desc", ""), + 'dataset_type': knowledge_dict.get("type", ""), + 'document_name': document_dict.get("name", ""), + 'document_type': document_dict.get("type", ""), + } \ No newline at end of file diff --git a/apps/knowledge/views/document.py b/apps/knowledge/views/document.py index 6a85bfd8b..3e794724a 100644 --- a/apps/knowledge/views/document.py +++ b/apps/knowledge/views/document.py @@ -7,13 +7,17 @@ from rest_framework.views import APIView from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log from common.result import result from knowledge.api.document import DocumentSplitAPI, DocumentBatchAPI, DocumentBatchCreateAPI, DocumentCreateAPI, \ DocumentReadAPI, DocumentEditAPI, DocumentDeleteAPI, TableDocumentCreateAPI, QaDocumentCreateAPI, \ WebDocumentCreateAPI, CancelTaskAPI, BatchCancelTaskAPI, SyncWebAPI, RefreshAPI, BatchEditHitHandlingAPI, \ DocumentTreeReadAPI, DocumentSplitPatternAPI, BatchRefreshAPI, BatchGenerateRelatedAPI, TemplateExportAPI, \ DocumentExportAPI, DocumentMigrateAPI, DocumentDownloadSourceAPI +from knowledge.serializers.common import get_knowledge_operation_object from knowledge.serializers.document import DocumentSerializers +from knowledge.views.common import get_knowledge_document_operation_object, get_document_operation_object_batch, \ + get_document_operation_object class DocumentView(APIView): @@ -30,6 +34,10 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_CREATE.get_workspace_knowledge_permission()) + @log(menu='document', operate="Create document", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + {'name': r.data.get('name')})) def post(self, request: Request, workspace_id: str, knowledge_id: str): return result.success( DocumentSerializers.Create( @@ -87,6 +95,12 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission()) + @log(menu='document', operate="Modify document", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return result.success(DocumentSerializers.Operate(data={ 'document_id': document_id, 'knowledge_id': knowledge_id, 'workspace_id': workspace_id @@ -101,6 +115,12 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_DELETE.get_workspace_knowledge_permission()) + @log(menu='document', operate="Delete document", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def delete(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): operate = DocumentSerializers.Operate(data={ 'document_id': document_id, 'knowledge_id': knowledge_id, 'workspace_id': workspace_id @@ -171,6 +191,10 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission()) + @log(menu='document', operate="Modify document hit processing methods in batches", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object_batch(r.data.get('id_list')))) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(DocumentSerializers.Batch( data={'knowledge_id': knowledge_id, 'workspace_id': workspace_id} @@ -190,6 +214,11 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_SYNC.get_workspace_knowledge_permission()) + @log(menu='document', operate="Synchronize web site types", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + )) def get(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return result.success(DocumentSerializers.Sync( data={'document_id': document_id, 'knowledge_id': knowledge_id, 'workspace_id': workspace_id} @@ -209,6 +238,12 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_VECTOR.get_workspace_knowledge_permission()) + @log(menu='document', operate="Refresh document vector library", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return result.success(DocumentSerializers.Operate( data={'document_id': document_id, 'knowledge_id': knowledge_id, 'workspace_id': workspace_id} @@ -227,6 +262,11 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission()) + @log(menu='document', operate="Cancel task", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + )) def put(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return result.success(DocumentSerializers.Operate( data={'document_id': document_id, 'knowledge_id': knowledge_id, 'workspace_id': workspace_id} @@ -245,6 +285,12 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission()) + @log(menu='document', operate="Cancel tasks in batches", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object_batch(r.data.get('id_list')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(DocumentSerializers.Batch(data={ 'knowledge_id': knowledge_id, 'workspace_id': workspace_id} @@ -267,6 +313,12 @@ class DocumentView(APIView): PermissionConstants.KNOWLEDGE_DOCUMENT_CREATE.get_workspace_knowledge_permission(), PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission(), ) + @log(menu='document', operate="Create documents in batches", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + {'name': f'[{",".join([document.get("name") for document in r.data])}]', + 'document_list': r.data}) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(DocumentSerializers.Batch( data={'knowledge_id': knowledge_id, 'workspace_id': workspace_id} @@ -289,6 +341,11 @@ class DocumentView(APIView): PermissionConstants.KNOWLEDGE_DOCUMENT_SYNC.get_workspace_knowledge_permission(), PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission(), ) + @log(menu='document', operate="Batch sync documents", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object_batch(r.data.get('id_list'))) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(DocumentSerializers.Batch( data={'knowledge_id': knowledge_id, 'workspace_id': workspace_id} @@ -311,6 +368,10 @@ class DocumentView(APIView): PermissionConstants.KNOWLEDGE_DOCUMENT_DELETE.get_workspace_knowledge_permission(), PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission(), ) + @log(menu='document', operate="Delete documents in batches", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object_batch(r.data.get('id_list')))) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(DocumentSerializers.Batch( data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id} @@ -332,6 +393,12 @@ class DocumentView(APIView): PermissionConstants.KNOWLEDGE_DOCUMENT_VECTOR.get_workspace_knowledge_permission(), PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission(), ) + @log(menu='document', operate="Batch refresh document vector library", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object_batch(r.data.get('id_list')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success( DocumentSerializers.Batch( @@ -355,6 +422,12 @@ class DocumentView(APIView): PermissionConstants.KNOWLEDGE_DOCUMENT_GENERATE.get_workspace_knowledge_permission(), PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission(), ) + @log(menu='document', operate="Batch generate related documents", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object_batch(r.data.get('document_id_list')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(DocumentSerializers.BatchGenerateRelated( data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id} @@ -396,6 +469,12 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EXPORT.get_workspace_knowledge_permission()) + @log(menu='document', operate="Export document", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def get(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return DocumentSerializers.Operate(data={ 'workspace_id': workspace_id, 'document_id': document_id, 'knowledge_id': knowledge_id @@ -412,6 +491,12 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EXPORT.get_workspace_knowledge_permission()) + @log(menu='document', operate="Export Zip document", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def get(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return DocumentSerializers.Operate(data={ 'workspace_id': workspace_id, 'document_id': document_id, 'knowledge_id': knowledge_id @@ -445,6 +530,12 @@ class DocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_MIGRATE.get_workspace_knowledge_permission()) + @log(menu='document', operate="Migrate documents in batches", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object_batch(r.data) + ) + ) def put(self, request: Request, workspace_id, knowledge_id: str, target_knowledge_id: str): return result.success(DocumentSerializers.Migrate( data={ @@ -469,6 +560,11 @@ class WebDocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_CREATE.get_workspace_knowledge_permission()) + @log(menu='document', operate="Create Web site documents", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + {'name': f'[{",".join([url for url in r.data.get("source_url_list", [])])}]', + 'document_list': [{'name': url} for url in r.data.get("source_url_list", [])]})) def post(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(DocumentSerializers.Create(data={ 'knowledge_id': knowledge_id, 'workspace_id': workspace_id @@ -489,6 +585,11 @@ class QaDocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_CREATE.get_workspace_knowledge_permission()) + @log(menu='document', operate="Import QA and create documentation", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + {'name': f'[{",".join([file.name for file in r.FILES.getlist("file")])}]', + 'document_list': [{'name': file.name} for file in r.FILES.getlist("file")]})) def post(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(DocumentSerializers.Create(data={ 'knowledge_id': knowledge_id, 'workspace_id': workspace_id @@ -509,6 +610,11 @@ class TableDocumentView(APIView): tags=[_('Knowledge Base/Documentation')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_CREATE.get_workspace_knowledge_permission()) + @log(menu='document', operate="Import tables and create documents", + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + {'name': f'[{",".join([file.name for file in r.FILES.getlist("file")])}]', + 'document_list': [{'name': file.name} for file in r.FILES.getlist("file")]})) def post(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(DocumentSerializers.Create( data={'knowledge_id': knowledge_id, 'workspace_id': workspace_id} diff --git a/apps/knowledge/views/file.py b/apps/knowledge/views/file.py index eee9de630..0bfd5c3cc 100644 --- a/apps/knowledge/views/file.py +++ b/apps/knowledge/views/file.py @@ -6,6 +6,7 @@ from rest_framework.views import APIView from rest_framework.views import Request from common.auth import TokenAuth +from common.log.log import log from common.result import result from knowledge.api.file import FileUploadAPI, FileGetAPI from knowledge.serializers.file import FileSerializer @@ -25,6 +26,7 @@ class FileView(APIView): responses=FileUploadAPI.get_response(), tags=[_('File')] # type: ignore ) + @log(menu='file', operate='Upload file') def post(self, request: Request): return result.success(FileSerializer(data={'file': request.FILES.get('file')}).upload()) @@ -50,5 +52,6 @@ class FileView(APIView): responses=FileGetAPI.get_response(), tags=[_('File')] # type: ignore ) + @log(menu='file', operate='Delete file') def delete(self, request: Request, file_id: str): return result.success(FileSerializer.Operate(data={'id': file_id}).delete()) diff --git a/apps/knowledge/views/knowledge.py b/apps/knowledge/views/knowledge.py index fcc5bb56d..fa8847d27 100644 --- a/apps/knowledge/views/knowledge.py +++ b/apps/knowledge/views/knowledge.py @@ -6,10 +6,12 @@ from rest_framework.views import APIView from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log from common.result import result from knowledge.api.knowledge import KnowledgeBaseCreateAPI, KnowledgeWebCreateAPI, KnowledgeTreeReadAPI, \ KnowledgeEditAPI, KnowledgeReadAPI, KnowledgePageAPI, SyncWebAPI, GenerateRelatedAPI, HitTestAPI, EmbeddingAPI, \ GetModelAPI, KnowledgeExportAPI +from knowledge.serializers.common import get_knowledge_operation_object from knowledge.serializers.knowledge import KnowledgeSerializer from models_provider.serializers.model_serializer import ModelSerializer @@ -52,6 +54,8 @@ class KnowledgeView(APIView): tags=[_('Knowledge Base')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_EDIT.get_workspace_knowledge_permission()) + @log(menu='Knowledge Base', operate="Modify knowledge base information", + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('knowledge_id'))) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(KnowledgeSerializer.Operate( data={'user_id': request.user.id, 'workspace_id': workspace_id, 'knowledge_id': knowledge_id} @@ -68,6 +72,8 @@ class KnowledgeView(APIView): tags=[_('Knowledge Base')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DELETE.get_workspace_knowledge_permission()) + @log(menu='Knowledge Base', operate="Delete knowledge base", + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('knowledge_id'))) def delete(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(KnowledgeSerializer.Operate( data={'user_id': request.user.id, 'workspace_id': workspace_id, 'knowledge_id': knowledge_id} @@ -126,6 +132,8 @@ class KnowledgeView(APIView): tags=[_('Knowledge Base')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_SYNC.get_workspace_knowledge_permission()) + @log(menu='Knowledge Base', operate="Synchronize the knowledge base of the website", + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('knowledge_id'))) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(KnowledgeSerializer.SyncWeb( data={ @@ -177,6 +185,8 @@ class KnowledgeView(APIView): tags=[_('Knowledge Base')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_VECTOR.get_workspace_knowledge_permission()) + @log(menu='Knowledge Base', operate='Re-vectorize', + get_operation_object=lambda r,k: get_knowledge_operation_object(k.get('knowledge_id'))) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(KnowledgeSerializer.Operate( data={'knowledge_id': knowledge_id, 'workspace_id': workspace_id, 'user_id': request.user.id} @@ -193,6 +203,8 @@ class KnowledgeView(APIView): tags=[_('Knowledge Base')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_EXPORT.get_workspace_knowledge_permission()) + @log(menu='Knowledge Base', operate="Export knowledge base", + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('knowledge_id'))) def get(self, request: Request, workspace_id: str, knowledge_id: str): return KnowledgeSerializer.Operate(data={ 'workspace_id': workspace_id, 'knowledge_id': knowledge_id, 'user_id': request.user.id @@ -209,6 +221,8 @@ class KnowledgeView(APIView): tags=[_('Knowledge Base')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_EXPORT.get_workspace_knowledge_permission()) + @log(menu='Knowledge Base', operate="Export knowledge base containing images", + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('knowledge_id'))) def get(self, request: Request, workspace_id: str, knowledge_id: str): return KnowledgeSerializer.Operate(data={ 'workspace_id': workspace_id, 'knowledge_id': knowledge_id, 'user_id': request.user.id @@ -228,6 +242,9 @@ class KnowledgeView(APIView): tags=[_('Knowledge Base')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_GENERATE.get_workspace_knowledge_permission()) + @log(menu='document', operate='Generate related documents', + get_operation_object=lambda r,k: get_knowledge_operation_object(k.get('knowledge_id')) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(KnowledgeSerializer.Operate( data={'knowledge_id': knowledge_id, 'workspace_id': workspace_id, 'user_id': request.user.id} @@ -290,6 +307,8 @@ class KnowledgeBaseView(APIView): tags=[_('Knowledge Base')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_CREATE.get_workspace_permission()) + @log(menu='knowledge Base', operate='Create base knowledge', + get_operation_object=lambda r,k: {'name': r.data.get('name'), 'desc': r.data.get('desc')}) def post(self, request: Request, workspace_id: str): return result.success(KnowledgeSerializer.Create( data={'user_id': request.user.id, 'workspace_id': workspace_id} @@ -310,6 +329,13 @@ class KnowledgeWebView(APIView): tags=[_('Knowledge Base')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_CREATE.get_workspace_permission()) + @log(menu='Knowledge Base', operate="Create a web site knowledge base", + get_operation_object=lambda r,k: {'name': r.data.get('name'),'desc': r.data.get('desc'), + 'first_list': r.FILES.getlist('file'), + 'meta': {'source_url': r.data.get('source_url'), + 'selector': r.data.get('selector'), + 'embedding_mode_id': r.data.get('embedding_mode_id')}} + ) def post(self, request: Request, workspace_id: str): return result.success(KnowledgeSerializer.Create( data={'user_id': request.user.id, 'workspace_id': workspace_id} diff --git a/apps/knowledge/views/paragraph.py b/apps/knowledge/views/paragraph.py index e95733123..c94bf89a5 100644 --- a/apps/knowledge/views/paragraph.py +++ b/apps/knowledge/views/paragraph.py @@ -6,12 +6,15 @@ from rest_framework.views import Request from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log from common.result import result from common.utils.common import query_params_to_single_dict from knowledge.api.paragraph import ParagraphReadAPI, ParagraphCreateAPI, ParagraphBatchDeleteAPI, ParagraphEditAPI, \ ParagraphGetAPI, ProblemCreateAPI, UnAssociationAPI, AssociationAPI, ParagraphPageAPI, \ ParagraphBatchGenerateRelatedAPI, ParagraphMigrateAPI +from knowledge.serializers.common import get_knowledge_operation_object from knowledge.serializers.paragraph import ParagraphSerializers +from knowledge.views import get_knowledge_document_operation_object, get_document_operation_object class ParagraphView(APIView): @@ -47,6 +50,12 @@ class ParagraphView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_CREATE.get_workspace_knowledge_permission()) + @log(menu='Paragraph', operate='Create Paragraph', + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def post(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return result.success(ParagraphSerializers.Create( data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id, 'document_id': document_id} @@ -83,6 +92,12 @@ class ParagraphView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_MIGRATE.get_workspace_knowledge_permission()) + @log(menu='Paragraph', operate='Migrate paragraphs in batches', + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str, target_knowledge_id: str, target_document_id): return result.success( @@ -109,6 +124,12 @@ class ParagraphView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_GENERATE.get_workspace_knowledge_permission()) + @log(menu='Paragraph', operate='Batch generate related', + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return result.success(ParagraphSerializers.Batch( data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id, 'document_id': document_id} @@ -128,6 +149,12 @@ class ParagraphView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission()) + @log(menu='Paragraph', operate='Modify paragraph data', + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str, paragraph_id: str): o = ParagraphSerializers.Operate( data={ @@ -171,6 +198,12 @@ class ParagraphView(APIView): responses=ParagraphGetAPI.get_response(), tags=[_('Knowledge Base/Documentation/Paragraph')]) # type: ignore @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission()) + @log(menu='Paragraph', operate='Delete paragraph', + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def delete(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str, paragraph_id: str): o = ParagraphSerializers.Operate( data={ @@ -197,6 +230,12 @@ class ParagraphView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission()) + @log(menu='Paragraph', operate='Add associated questions', + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def post(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str, paragraph_id: str): return result.success(ParagraphSerializers.Problem( data={ @@ -241,6 +280,12 @@ class ParagraphView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission()) + @log(menu='Paragraph', operate='Disassociation issue', + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return result.success(ParagraphSerializers.Association( data={ @@ -266,6 +311,12 @@ class ParagraphView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission()) + @log(menu='Paragraph', operate='Related questions', + get_operation_object=lambda r, keywords: get_knowledge_document_operation_object( + get_knowledge_operation_object(keywords.get('knowledge_id')), + get_document_operation_object(keywords.get('document_id')) + ) + ) def put(self, request: Request, workspace_id: str, knowledge_id: str, document_id: str): return result.success(ParagraphSerializers.Association( data={ diff --git a/apps/knowledge/views/problem.py b/apps/knowledge/views/problem.py index 92dc3e929..7ed29ee4b 100644 --- a/apps/knowledge/views/problem.py +++ b/apps/knowledge/views/problem.py @@ -6,10 +6,12 @@ from rest_framework.views import Request from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log from common.result import result from common.utils.common import query_params_to_single_dict from knowledge.api.problem import ProblemReadAPI, ProblemBatchCreateAPI, BatchAssociationAPI, BatchDeleteAPI, \ ProblemPageAPI, ProblemDeleteAPI, ProblemEditAPI, ProblemParagraphAPI +from knowledge.serializers.common import get_knowledge_operation_object from knowledge.serializers.problem import ProblemSerializers @@ -48,6 +50,9 @@ class ProblemView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph/Question')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_PROBLEM_EDIT.get_workspace_knowledge_permission()) + @log(menu='problem', operate='Create question', + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('dataset_id')) + ) def post(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(ProblemSerializers.Create( data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id} @@ -88,6 +93,8 @@ class ProblemView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph/Question')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_PROBLEM_EDIT.get_workspace_knowledge_permission()) + @log(menu='problem', operate='Batch associated paragraphs', + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('dataset_id'))) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(ProblemSerializers.BatchOperate( data={'knowledge_id': knowledge_id, 'workspace_id': workspace_id} @@ -107,6 +114,8 @@ class ProblemView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph/Question')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_PROBLEM_EDIT.get_workspace_knowledge_permission()) + @log(menu='problem', operate='Batch deletion issues', + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('dataset_id'))) def put(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(ProblemSerializers.BatchOperate( data={'knowledge_id': knowledge_id, 'workspace_id': workspace_id} @@ -125,6 +134,8 @@ class ProblemView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph/Question')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_PROBLEM_DELETE.get_workspace_knowledge_permission()) + @log(menu='problem', operate='Delete question', + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('dataset_id'))) def delete(self, request: Request, workspace_id: str, knowledge_id: str, problem_id: str): return result.success(ProblemSerializers.Operate( data={ @@ -146,6 +157,8 @@ class ProblemView(APIView): tags=[_('Knowledge Base/Documentation/Paragraph/Question')] # type: ignore ) @has_permissions(PermissionConstants.KNOWLEDGE_PROBLEM_EDIT.get_workspace_knowledge_permission()) + @log(menu='problem', operate='Modify question', + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('dataset_id'))) def put(self, request: Request, workspace_id: str, knowledge_id: str, problem_id: str): return result.success(ProblemSerializers.Operate( data={ diff --git a/apps/models_provider/views/model.py b/apps/models_provider/views/model.py index 0d7676712..62e6a20df 100644 --- a/apps/models_provider/views/model.py +++ b/apps/models_provider/views/model.py @@ -6,6 +6,7 @@ @date:2025/4/14 19:25 @desc: """ +from django.db.models import QuerySet from drf_spectacular.utils import extend_schema from rest_framework.views import APIView from django.utils.translation import gettext_lazy as _ @@ -14,11 +15,42 @@ from rest_framework.request import Request from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log from common.result import result from common.utils.common import query_params_to_single_dict from models_provider.api.model import ModelCreateAPI, GetModelApi, ModelEditApi, ModelListResponse, DefaultModelResponse from models_provider.api.provide import ProvideApi +from models_provider.models import Model from models_provider.serializers.model_serializer import ModelSerializer +from system_manage.views import encryption_str + + +def encryption_credential(credential): + if isinstance(credential, dict): + return {key: encryption_str(credential.get(key)) for key in credential} + return credential + + + +def get_edit_model_details(request): + path = request.path + body = request.data + query = request.query_params + credential = body.get('credential', {}) + credential_encryption_ed = encryption_credential(credential) + return { + 'path': path, + 'body': {**body, 'credential':credential_encryption_ed}, + 'query': query + } + +def get_model_operation_object(model_id): + model_model = QuerySet(model=Model).filter(id=model_id).first() + if model_model is not None: + return { + "name":model_model.name + } + return {} class Model(APIView): @@ -33,6 +65,10 @@ class Model(APIView): request=ModelCreateAPI.get_request(), responses=ModelCreateAPI.get_response()) @has_permissions(PermissionConstants.MODEL_CREATE.get_workspace_permission()) + @log(menu='model', operate='Create model', + get_operation_object=lambda r,k: {'name': r.date.get('name')}, + get_details=get_edit_model_details + ) def post(self, request: Request, workspace_id: str): return result.success( ModelSerializer.Create(data={**request.data, 'user_id': request.user.id}).insert(workspace_id, @@ -76,6 +112,10 @@ class Model(APIView): responses=ModelEditApi.get_response(), tags=[_('Model')]) # type: ignore @has_permissions(PermissionConstants.MODEL_EDIT.get_workspace_permission()) + @log(menu='model', operate='Update model', + get_operation_object=lambda r,k: get_model_operation_object(k.get('model_id')), + get_details=get_edit_model_details + ) def put(self, request: Request, workspace_id, model_id: str): return result.success( ModelSerializer.Operate(data={'id': model_id, 'user_id': request.user.id}).edit(request.data, @@ -89,6 +129,8 @@ class Model(APIView): responses=DefaultModelResponse.get_response(), tags=[_('Model')]) # type: ignore @has_permissions(PermissionConstants.MODEL_DELETE.get_workspace_permission()) + @log(menu='model', operate='Delete model', + get_operation_object=lambda r, k: get_model_operation_object(k.get('model_id'))) def delete(self, request: Request, workspace_id: str, model_id: str): return result.success( ModelSerializer.Operate(data={'id': model_id, 'user_id': request.user.id}).delete()) @@ -129,6 +171,8 @@ class Model(APIView): responses=ProvideApi.ModelParamsForm.get_response(), tags=[_('Model')]) # type: ignore @has_permissions(PermissionConstants.MODEL_READ.get_workspace_permission()) + @log(menu='model', operate='Save model parameter form', + get_operation_object=lambda r,k: get_model_operation_object(k.get('model_id'))) def put(self, request: Request, workspace_id: str, model_id: str): return result.success( ModelSerializer.ModelParams(data={'id': model_id}).save_model_params_form(request.data)) diff --git a/apps/models_provider/views/provide.py b/apps/models_provider/views/provide.py index 5969d3869..f14ada053 100644 --- a/apps/models_provider/views/provide.py +++ b/apps/models_provider/views/provide.py @@ -9,6 +9,7 @@ from common import result from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log from models_provider.api.provide import ProvideApi from models_provider.constants.model_provider_constants import ModelProvideConstants from models_provider.serializers.model_serializer import get_default_model_params_setting @@ -24,6 +25,7 @@ class Provide(APIView): responses=ProvideApi.get_response(), tags=[_('Model')]) # type: ignore @has_permissions(PermissionConstants.MODEL_READ) + @log(menu='model',operate='Get a list of model suppliers') def get(self, request: Request): model_type = request.query_params.get('model_type') if model_type: diff --git a/apps/system_manage/views/email_setting.py b/apps/system_manage/views/email_setting.py index f28d36be8..61deb8937 100644 --- a/apps/system_manage/views/email_setting.py +++ b/apps/system_manage/views/email_setting.py @@ -7,6 +7,7 @@ @desc: """ from drf_spectacular.utils import extend_schema +from networkx.algorithms.traversal import dfs_successors from rest_framework.request import Request from rest_framework.views import APIView @@ -16,11 +17,31 @@ from common.constants.permission_constants import PermissionConstants from django.utils.translation import gettext_lazy as _ +from common.log.log import log from common.result import result +from common.utils.common import encryption from models_provider.api.model import DefaultModelResponse from system_manage.api.email_setting import EmailSettingAPI from system_manage.serializers.email_setting import EmailSettingSerializer +def encryption_str(_value): + if isinstance(_value, str): + return encryption(_value) + return _value + + + +def get_email_details(request): + path = request.path + body = request.data + query = request.query_params + email_host_password = body.get('email_host_password', '') + return { + 'path': path, + 'body': {**body, 'email_host_password': encryption_str(email_host_password)}, + 'query': query + } + class SystemSetting(APIView): class Email(APIView): @@ -33,6 +54,8 @@ class SystemSetting(APIView): request=EmailSettingAPI.get_request(), responses=EmailSettingAPI.get_response(), tags=[_('Email Settings')]) # type: ignore + @log(menu='Email settings', operate='Create or update email settings', + get_details=get_email_details) @has_permissions(PermissionConstants.EMAIL_SETTING_EDIT) def put(self, request: Request): return result.success( @@ -48,6 +71,9 @@ class SystemSetting(APIView): tags=[_('Email Settings')] # type: ignore ) @has_permissions(PermissionConstants.EMAIL_SETTING_EDIT) + @log(menu='Email settings',operate='Test email settings', + get_details=get_email_details + ) def post(self, request: Request): return result.success( EmailSettingSerializer.Create( diff --git a/apps/system_manage/views/log_management.py b/apps/system_manage/views/log_management.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/system_manage/views/user_resource_permission.py b/apps/system_manage/views/user_resource_permission.py index 674d7fd17..088d1e532 100644 --- a/apps/system_manage/views/user_resource_permission.py +++ b/apps/system_manage/views/user_resource_permission.py @@ -6,7 +6,7 @@ @date:2025/4/28 16:38 @desc: """ - +from django.db.models import QuerySet from django.utils.translation import gettext_lazy as _ from drf_spectacular.utils import extend_schema from rest_framework.request import Request @@ -16,11 +16,21 @@ from common import result from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log from common.result import DefaultResultSerializer from system_manage.api.user_resource_permission import UserResourcePermissionAPI, EditUserResourcePermissionAPI from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer +from users.models import User +def get_user_operation_object(user_id): + user_model = QuerySet(model=User).filter(id=user_id).first() + if user_model is not None: + return { + "name": user_model.username + } + return {} + class WorkSpaceUserResourcePermissionView(APIView): authentication_classes = [TokenAuth] @@ -47,6 +57,9 @@ class WorkSpaceUserResourcePermissionView(APIView): responses=DefaultResultSerializer(), tags=[_('Resources authorization')] # type: ignore ) + @log(menu='System', operate='Modify the resource authorization list', + get_operation_object=lambda r, k: get_user_operation_object(k.get('user_id')) + ) def put(self, request: Request, workspace_id: str, user_id: str): return result.success(UserResourcePermissionSerializer( data={'workspace_id': workspace_id, 'user_id': user_id} diff --git a/apps/tools/views/tool.py b/apps/tools/views/tool.py index 70484edf2..d05c1e596 100644 --- a/apps/tools/views/tool.py +++ b/apps/tools/views/tool.py @@ -1,3 +1,4 @@ +from django.db.models import QuerySet from django.utils.translation import gettext_lazy as _ from drf_spectacular.utils import extend_schema from rest_framework.parsers import MultiPartParser @@ -7,13 +8,22 @@ from rest_framework.views import APIView from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants +from common.log.log import log from common.result import result from tools.api.tool import ToolCreateAPI, ToolEditAPI, ToolReadAPI, ToolDeleteAPI, ToolTreeReadAPI, ToolDebugApi, \ ToolExportAPI, ToolImportAPI, ToolPageAPI, PylintAPI, EditIconAPI -from tools.models import ToolScope +from tools.models import ToolScope, Tool from tools.serializers.tool import ToolSerializer, ToolTreeSerializer +def get_tool_operation_object(tool_id): + tool_model = QuerySet(model=Tool).filter(id=tool_id).first() + if tool_model is not None: + return { + "name": tool_model.name + } + return {} + class ToolView(APIView): authentication_classes = [TokenAuth] @@ -28,6 +38,8 @@ class ToolView(APIView): tags=[_('Tool')] # type: ignore ) @has_permissions(PermissionConstants.TOOL_CREATE.get_workspace_permission()) + @log(menu="Tool", operate="Create tool", + get_operation_object=lambda r,k: r.data.get('name')) def post(self, request: Request, workspace_id: str): return result.success(ToolSerializer.Create( data={'user_id': request.user.id, 'workspace_id': workspace_id} @@ -80,6 +92,8 @@ class ToolView(APIView): tags=[_('Tool')] # type: ignore ) @has_permissions(PermissionConstants.TOOL_EDIT.get_workspace_permission()) + @log(menu='Tool', operate='Update tool', + get_operation_object=lambda r, k: get_tool_operation_object(k.get('tool_id'))) def put(self, request: Request, workspace_id: str, tool_id: str): return result.success(ToolSerializer.Operate( data={'id': tool_id, 'workspace_id': workspace_id} @@ -95,6 +109,7 @@ class ToolView(APIView): tags=[_('Tool')] # type: ignore ) @has_permissions(PermissionConstants.TOOL_READ.get_workspace_permission()) + @log(menu='Tool', operate='Get tool') def get(self, request: Request, workspace_id: str, tool_id: str): return result.success(ToolSerializer.Operate( data={'id': tool_id, 'workspace_id': workspace_id} @@ -110,6 +125,8 @@ class ToolView(APIView): tags=[_('Tool')] # type: ignore ) @has_permissions(PermissionConstants.TOOL_DELETE.get_workspace_permission()) + @log(menu='Tool', operate="Delete tool", + get_operation_object=lambda r,k: get_tool_operation_object(k.get('tool_id'))) def delete(self, request: Request, workspace_id: str, tool_id: str): return result.success(ToolSerializer.Operate( data={'id': tool_id, 'workspace_id': workspace_id} @@ -128,6 +145,7 @@ class ToolView(APIView): tags=[_('Tool')] # type: ignore ) @has_permissions(PermissionConstants.TOOL_READ.get_workspace_permission()) + @log(menu='Tool', operate='Get tool list') def get(self, request: Request, workspace_id: str, current_page: int, page_size: int): return result.success(ToolTreeSerializer.Query( data={ @@ -153,6 +171,7 @@ class ToolView(APIView): tags=[_("Tool")] # type: ignore ) @has_permissions(PermissionConstants.TOOL_IMPORT.get_workspace_permission()) + @log(menu='Tool', operate='Import tool') def post(self, request: Request, workspace_id: str): return result.success(ToolSerializer.Import( data={'workspace_id': workspace_id, 'file': request.FILES.get('file'), 'user_id': request.user.id} @@ -171,6 +190,8 @@ class ToolView(APIView): tags=[_("Tool")] # type: ignore ) @has_permissions(PermissionConstants.TOOL_EXPORT.get_workspace_permission()) + @log(menu='Tool', operate="Export function", + get_operation_object=lambda r,k: get_tool_operation_object(k.get('id'))) def get(self, request: Request, tool_id: str, workspace_id: str): return ToolSerializer.Operate( data={'id': tool_id, 'workspace_id': workspace_id} diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 7f47f9c8f..4cc03fc12 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -12,9 +12,20 @@ from rest_framework.request import Request from rest_framework.views import APIView from common import result +from common.log.log import log +from common.utils.common import encryption from users.api.login import LoginAPI, CaptchaAPI from users.serializers.login import LoginSerializer, CaptchaSerializer +def _get_details(request): + path = request.path + body = request.data + query = request.query_params + return { + 'path':path, + 'body':{**body, 'password': encryption(body.get('password',''))}, + 'query': query + } class LoginView(APIView): @extend_schema(methods=['POST'], @@ -24,6 +35,9 @@ class LoginView(APIView): tags=[_("User Management")], # type: ignore request=LoginAPI.get_request(), responses=LoginAPI.get_response()) + @log(menu='User management', operate='Log in', get_user=lambda r: {'username': r.data.get('username', None)}, + get_details=_get_details, + get_operation_object=lambda r,k: {'name': r.data.get('username')}) def post(self, request: Request): return result.success(LoginSerializer().login(request.data)) diff --git a/apps/users/views/user.py b/apps/users/views/user.py index 7ee15ba80..c0cc55210 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -6,6 +6,7 @@ @date:2025/4/14 19:25 @desc: """ +from django.db.models import QuerySet from django.utils.translation import gettext_lazy as _ from drf_spectacular.utils import extend_schema from rest_framework.request import Request @@ -14,16 +15,40 @@ from rest_framework.views import APIView from common.auth.authenticate import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants, Permission, Group, Operate +from common.log.log import log from common.result import result from maxkb.const import CONFIG from models_provider.api.model import DefaultModelResponse +from tools.serializers.tool import encryption from users.api.user import UserProfileAPI, TestWorkspacePermissionUserApi, DeleteUserApi, EditUserApi, \ ChangeUserPasswordApi, UserPageApi, UserListApi, UserPasswordResponse, WorkspaceUserAPI +from users.models import User from users.serializers.user import UserProfileSerializer, UserManageSerializer default_password = CONFIG.get('default_password', 'MaxKB@123..') +def get_user_operation_object(user_id): + user_model = QuerySet(model=User).filter(id=user_id).first() + if user_model is not None: + return { + "name":user_model.name + } + return {} + +def get_re_password_details(request): + path = request.path + body = request.data + query = request.query_params + return { + "path": path, + "body": {**body, 'password': encryption(body.get('password', '')), + 're_password': encryption(body.get('re_password',''))}, + "query": query + } + + + class UserProfileView(APIView): authentication_classes = [TokenAuth] @@ -32,6 +57,7 @@ class UserProfileView(APIView): description=_("Get current user information"), operation_id=_("Get current user information"), # type: ignore tags=[_("User Management")], # type: ignore + responses=UserProfileAPI.get_response()) def get(self, request: Request): return result.success(UserProfileSerializer().profile(request.user, request.auth)) @@ -118,6 +144,8 @@ class UserManage(APIView): parameters=DeleteUserApi.get_parameters(), responses=DefaultModelResponse.get_response()) @has_permissions(PermissionConstants.USER_DELETE) + @log(menu='User management', operate='Delete user', + get_operation_object= lambda r,k: get_user_operation_object(k.get('user_id'))) def delete(self, request: Request, user_id): return result.success(UserManageSerializer.Operate(data={'id': user_id}).delete(with_valid=True)) @@ -156,6 +184,8 @@ class UserManage(APIView): request=DeleteUserApi.get_request(), responses=DefaultModelResponse.get_response()) @has_permissions(PermissionConstants.USER_DELETE) + @log(menu='User management', operate='Batch delete user', + get_operation_object= lambda r,k: get_user_operation_object(k.get('user_id'))) def post(self, request: Request): return result.success(UserManageSerializer.BatchDelete(data=request.data).batch_delete(with_valid=True)) @@ -170,6 +200,9 @@ class UserManage(APIView): parameters=DeleteUserApi.get_parameters(), request=ChangeUserPasswordApi.get_request(), responses=DefaultModelResponse.get_response()) + @log(menu='User management', operate='Change password', + get_operation_object= lambda r,k: get_user_operation_object(k.get('user_id')), + get_details=get_re_password_details) def put(self, request: Request, user_id): return result.success( UserManageSerializer.Operate(data={'id': user_id}).re_password(request.data, with_valid=True))