diff --git a/backend/apps/api.py b/backend/apps/api.py index 06dd11b5..23ca0175 100644 --- a/backend/apps/api.py +++ b/backend/apps/api.py @@ -9,6 +9,7 @@ from apps.system.api import login, user, aimodel, workspace, assistant, paramete from apps.terminology.api import terminology from apps.settings.api import base + api_router = APIRouter() api_router.include_router(login.router) api_router.include_router(user.router) diff --git a/backend/apps/chat/api/chat.py b/backend/apps/chat/api/chat.py index a3ba2783..6dfe495f 100644 --- a/backend/apps/chat/api/chat.py +++ b/backend/apps/chat/api/chat.py @@ -81,14 +81,15 @@ async def rename(session: SessionDep, chat: RenameChat): ) -@router.delete("/{chart_id}", response_model=str, summary=f"{PLACEHOLDER_PREFIX}delete_chat") +@router.delete("/{chart_id}/{brief}", response_model=str, summary=f"{PLACEHOLDER_PREFIX}delete_chat") @system_log(LogConfig( operation_type=OperationType.DELETE_QA, operation_detail=OperationDetails.DELETE_QA_DETAILS, module=OperationModules.QA, - resource_id_expr="chart_id" + resource_id_expr="chart_id", + remark_expr="brief" )) -async def delete(session: SessionDep, chart_id: int): +async def delete(session: SessionDep, chart_id: int, brief: str): try: return delete_chat(session=session, chart_id=chart_id) except Exception as e: diff --git a/backend/apps/dashboard/api/dashboard_api.py b/backend/apps/dashboard/api/dashboard_api.py index 7af75d76..f93d1af7 100644 --- a/backend/apps/dashboard/api/dashboard_api.py +++ b/backend/apps/dashboard/api/dashboard_api.py @@ -30,14 +30,15 @@ async def update_resource_api(session: SessionDep, user: CurrentUser, dashboard: return update_resource(session=session, user=user, dashboard=dashboard) -@router.delete("/delete_resource/{resource_id}") +@router.delete("/delete_resource/{resource_id}/{name}") @system_log(LogConfig( operation_type=OperationType.DELETE_DASHBOARD, operation_detail=OperationDetails.DELETE_DASHBOARD_DETAILS, module=OperationModules.DASHBOARD, - resource_id_expr="resource_id" + resource_id_expr="resource_id", + remark_expr="name" )) -async def delete_resource_api(session: SessionDep, resource_id: str): +async def delete_resource_api(session: SessionDep, resource_id: str, name: str): return delete_resource(session, resource_id) diff --git a/backend/apps/datasource/api/datasource.py b/backend/apps/datasource/api/datasource.py index 096abb3f..a4d7caec 100644 --- a/backend/apps/datasource/api/datasource.py +++ b/backend/apps/datasource/api/datasource.py @@ -30,6 +30,7 @@ from ..models.datasource import CoreDatasource, CreateDatasource, TableObj, Core TableSchemaResponse, ColumnSchemaResponse, PreviewResponse from sqlbot_xpack.audit.models.log_model import OperationType, OperationDetails, OperationModules from sqlbot_xpack.audit.schemas.logger_decorator import system_log, LogConfig + router = APIRouter(tags=["Datasource"], prefix="/datasource") path = settings.EXCEL_PATH @@ -107,15 +108,16 @@ async def update(session: SessionDep, trans: Trans, user: CurrentUser, ds: CoreD return await asyncio.to_thread(inner) -@router.post("/delete/{id}", response_model=None, summary=f"{PLACEHOLDER_PREFIX}ds_delete") +@router.post("/delete/{id}/{name}", response_model=None, summary=f"{PLACEHOLDER_PREFIX}ds_delete") @require_permissions(permission=SqlbotPermission(type='ds', keyExpression="id")) @system_log(LogConfig( operation_type=OperationType.DELETE_DATASOURCE, operation_detail=OperationDetails.DELETE_DATASOURCE_DETAILS, module=OperationModules.DATASOURCE, - resource_id_expr="id" + resource_id_expr="id", + remark_expr="name" )) -async def delete(session: SessionDep, id: int = Path(..., description=f"{PLACEHOLDER_PREFIX}ds_id")): +async def delete(session: SessionDep, id: int = Path(..., description=f"{PLACEHOLDER_PREFIX}ds_id"), name: str = None): return delete_ds(session, id) diff --git a/backend/locales/en.json b/backend/locales/en.json index b7054866..3c9aed5d 100644 --- a/backend/locales/en.json +++ b/backend/locales/en.json @@ -121,5 +121,48 @@ }, "i18n_authentication": { "record_not_exist": "{msg} record does not exist. Please save it first!" + }, + "i18n_audit": { + "success": "Success", + "failed": "Failed", + "create_qa": "Create Q&A", + "delete_qa": "Delete Q&A", + "create_datasource": "Create Datasource", + "update_datasource": "Edit Datasource", + "delete_datasource": "Delete Datasource", + "create_dashboard": "Create Dashboard", + "update_dashboard": "Edit Dashboard", + "delete_dashboard": "Delete Dashboard", + "setting_create_user": "Add Member", + "setting_delete_user": "Remove Member", + "setting_create_rule": "Add Rule Group", + "setting_delete_rule": "Remove Rule Group", + "setting_update_rule": "Configure Rules", + "setting_update_rule_user": "Configure Users", + "setting_create_terminology": "Create Terminology", + "create_qa_details": "Create Q&A【{resource_name}】", + "delete_qa_details": "Delete Q&A【{resource_name}】", + "create_datasource_details": "Create Datasource【{resource_name}】", + "update_datasource_details": "Edit Datasource【{resource_name}】", + "delete_datasource_details": "Delete Datasource【{resource_name}】", + "create_dashboard_details": "Create Dashboard【{resource_name}】", + "update_dashboard_details": "Edit Dashboard【{resource_name}】", + "delete_dashboard_details": "Delete Dashboard【{resource_name}】", + "setting_create_user_details": "Add Member【{resource_name}】", + "setting_delete_user_details": "Remove Member【{resource_name}】", + "setting_create_rule_details": "Add Rule Group【{resource_name}】", + "setting_delete_rule_details": "Remove Rule Group【{resource_name}】", + "setting_update_rule_details": "Configure Rules【{resource_name}】", + "setting_update_rule_user_details": "Configure Users【{resource_name}】", + "setting_create_terminology_details": "Create Terminology【{resource_name}】", + "system_log": "System Log", + "operation_type_name": "Operation Type", + "operation_detail_info": "Operation Details", + "user_name": "Operating User", + "oid_name": "Workspace", + "operation_status_name": "Operation Status", + "error_message": "Error Message", + "ip_address": "IP Address", + "create_time": "Operation Time" } } \ No newline at end of file diff --git a/backend/locales/ko-KR.json b/backend/locales/ko-KR.json index 786cc549..2b725828 100644 --- a/backend/locales/ko-KR.json +++ b/backend/locales/ko-KR.json @@ -121,5 +121,48 @@ }, "i18n_authentication": { "record_not_exist": "{msg} 기록이 존재하지 않습니다. 먼저 저장해 주세요!" + }, + "i18n_audit": { + "success": "성공", + "failed": "실패", + "create_qa": "Q&A 생성", + "delete_qa": "Q&A 삭제", + "create_datasource": "데이터 소스 생성", + "update_datasource": "데이터 소스 편집", + "delete_datasource": "데이터 소스 삭제", + "create_dashboard": "대시보드 생성", + "update_dashboard": "대시보드 편집", + "delete_dashboard": "대시보드 삭제", + "setting_create_user": "멤버 추가", + "setting_delete_user": "멤버 제거", + "setting_create_rule": "규칙 그룹 추가", + "setting_delete_rule": "규칙 그룹 제거", + "setting_update_rule": "규칙 설정", + "setting_update_rule_user": "사용자 설정", + "setting_create_terminology": "용어 생성", + "create_qa_details": "Q&A 생성【{resource_name}】", + "delete_qa_details": "Q&A 삭제【{resource_name}】", + "create_datasource_details": "데이터 소스 생성【{resource_name}】", + "update_datasource_details": "데이터 소스 편집【{resource_name}】", + "delete_datasource_details": "데이터 소스 삭제【{resource_name}】", + "create_dashboard_details": "대시보드 생성【{resource_name}】", + "update_dashboard_details": "대시보드 편집【{resource_name}】", + "delete_dashboard_details": "대시보드 삭제【{resource_name}】", + "setting_create_user_details": "멤버 추가【{resource_name}】", + "setting_delete_user_details": "멤버 제거【{resource_name}】", + "setting_create_rule_details": "규칙 그룹 추가【{resource_name}】", + "setting_delete_rule_details": "규칙 그룹 제거【{resource_name}】", + "setting_update_rule_details": "규칙 설정【{resource_name}】", + "setting_update_rule_user_details": "사용자 설정【{resource_name}】", + "setting_create_terminology_details": "용어 생성【{resource_name}】", + "system_log": "시스템 로그", + "operation_type_name": "작업 유형", + "operation_detail_info": "작업 상세 정보", + "user_name": "작업 사용자", + "oid_name": "작업 공간", + "operation_status_name": "작업 상태", + "error_message": "오류 메시지", + "ip_address": "IP 주소", + "create_time": "작업 시간" } } \ No newline at end of file diff --git a/backend/locales/zh-CN.json b/backend/locales/zh-CN.json index 9154c424..84236f34 100644 --- a/backend/locales/zh-CN.json +++ b/backend/locales/zh-CN.json @@ -121,5 +121,48 @@ }, "i18n_authentication": { "record_not_exist": "{msg} 记录不存在,请先保存!" + }, + "i18n_audit": { + "success": "成功", + "failed": "失败", + "create_qa": "新建问数", + "delete_qa": "删除问数", + "create_datasource": "新建数据源", + "update_datasource": "编辑数据源", + "delete_datasource": "删除数据源", + "create_dashboard": "新建仪表板", + "update_dashboard": "编辑仪表板", + "delete_dashboard": "删除仪表板", + "setting_create_user": "添加成员", + "setting_delete_user": "移除成员", + "setting_create_rule": "添加规则组", + "setting_delete_rule": "移除规则组", + "setting_update_rule": "设置规则", + "setting_update_rule_user": "设置用户", + "setting_create_terminology": "新建术语", + "create_qa_details": "新建问数【{resource_name}】", + "delete_qa_details": "删除问数【{resource_name}】", + "create_datasource_details": "新建数据源【{resource_name}】", + "update_datasource_details": "编辑数据源【{resource_name}】", + "delete_datasource_details": "删除数据源【{resource_name}】", + "create_dashboard_details": "新建仪表板【{resource_name}】", + "update_dashboard_details": "编辑仪表板【{resource_name}】", + "delete_dashboard_details": "删除仪表板【{resource_name}】", + "setting_create_user_details": "添加成员【{resource_name}】", + "setting_delete_user_details": "移除成员【{resource_name}】", + "setting_create_rule_details": "添加规则组【{resource_name}】", + "setting_delete_rule_details": "移除规则组【{resource_name}】", + "setting_update_rule_details": "设置规则【{resource_name}】", + "setting_update_rule_user_details": "设置用户【{resource_name}】", + "setting_create_terminology_details": "新建术语【{resource_name}】", + "system_log": "操作日志", + "operation_type_name": "操作类型", + "operation_detail_info": "操作详情", + "user_name": "操作用户", + "oid_name": "工作空间", + "operation_status_name": "操作状态", + "error_message": "错误信息", + "ip_address": "IP 地址", + "create_time": "操作时间" } -} \ No newline at end of file +} diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 83681ccd..4795d518 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -83,11 +83,11 @@ url = "https://test.pypi.org/simple" explicit = true [tool.uv.sources] -sqlbot-xpack = { index = "testpypi" } torch = [ { index = "pytorch-cpu", extra = "cpu" }, { index = "pytorch-cu128", extra = "cu128" }, ] +sqlbot-xpack = { path = "../../sqlbot-xpack" } [tool.uv] conflicts = [ diff --git a/frontend/src/api/audit.ts b/frontend/src/api/audit.ts new file mode 100644 index 00000000..7c19e90a --- /dev/null +++ b/frontend/src/api/audit.ts @@ -0,0 +1,12 @@ +import { request } from '@/utils/request' + +export const audit = { + getList: (pageNum: any, pageSize: any, params: any) => + request.get(`/system/audit/page/${pageNum}/${pageSize}${params}`), + export2Excel: (params: any) => + request.get(`/system/audit/export`, { + params, + responseType: 'blob', + requestOptions: { customError: true }, + }), +} diff --git a/frontend/src/api/chat.ts b/frontend/src/api/chat.ts index 42a7199c..918e58e3 100644 --- a/frontend/src/api/chat.ts +++ b/frontend/src/api/chat.ts @@ -324,8 +324,8 @@ export const chatApi = { renameChat: (chat_id: number | undefined, brief: string): Promise => { return request.post('/chat/rename', { id: chat_id, brief: brief }) }, - deleteChat: (id: number | undefined): Promise => { - return request.delete(`/chat/${id}`) + deleteChat: (id: number | undefined, brief: string): Promise => { + return request.delete(`/chat/${id}/${brief}`) }, analysis: (record_id: number | undefined, controller?: AbortController) => { return request.fetchStream(`/chat/record/${record_id}/analysis`, {}, controller) diff --git a/frontend/src/api/dashboard.ts b/frontend/src/api/dashboard.ts index f19ae1b8..7c5dc27e 100644 --- a/frontend/src/api/dashboard.ts +++ b/frontend/src/api/dashboard.ts @@ -9,6 +9,6 @@ export const dashboardApi = { update_canvas: (params: any) => request.post('/dashboard/update_canvas', params), check_name: (params: any) => request.post('/dashboard/check_name', params), delete_resource: (params: any) => - request.delete(`/dashboard/delete_resource/${params.id}`, params), + request.delete(`/dashboard/delete_resource/${params.id}/${params.name}`, params), move_resource: (params: any) => request.delete(`/dashboard/move_resource/${params.id}`, params), } diff --git a/frontend/src/api/datasource.ts b/frontend/src/api/datasource.ts index c349c85e..c14b13cd 100644 --- a/frontend/src/api/datasource.ts +++ b/frontend/src/api/datasource.ts @@ -8,7 +8,7 @@ export const datasourceApi = { add: (data: any) => request.post('/datasource/add', data), list: () => request.get('/datasource/list'), update: (data: any) => request.post('/datasource/update', data), - delete: (id: number) => request.post(`/datasource/delete/${id}`), + delete: (id: number, name: str) => request.post(`/datasource/delete/${id}/${name}`), getTables: (id: number) => request.post(`/datasource/getTables/${id}`), getTablesByConf: (data: any) => request.post('/datasource/getTablesByConf', data), getFields: (id: number, table_name: string) => diff --git a/frontend/src/assets/svg/icon_error.svg b/frontend/src/assets/svg/icon_error.svg new file mode 100644 index 00000000..5777bd1b --- /dev/null +++ b/frontend/src/assets/svg/icon_error.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_issue.svg b/frontend/src/assets/svg/icon_issue.svg new file mode 100644 index 00000000..663e919b --- /dev/null +++ b/frontend/src/assets/svg/icon_issue.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_success.svg b/frontend/src/assets/svg/icon_success.svg new file mode 100644 index 00000000..2303d6f9 --- /dev/null +++ b/frontend/src/assets/svg/icon_success.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/components/layout/Menu.vue b/frontend/src/components/layout/Menu.vue index dd1fbcd2..a7514096 100644 --- a/frontend/src/components/layout/Menu.vue +++ b/frontend/src/components/layout/Menu.vue @@ -55,6 +55,7 @@ const routerList = computed(() => { !route.path.includes('prompt') && !route.path.includes('permission') && !route.path.includes('preview') && + !route.path.includes('audit') && route.path !== '/login' && route.path !== '/admin-login' && !route.path.includes('/system') && diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 7d062246..74267635 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -822,6 +822,40 @@ "modelType": { "llm": "Large Language Model" }, + "audit": { + "system_log": "System Log", + "search_log": "Search Log", + "operation_type": "Operation Type", + "operation_details": "Operation Details", + "operation_user_name": "Operating User", + "operation_status": "Operation Status", + "user_name": "User Name", + "oid_name": "Workspace", + "ip_address": "IP Address", + "create_time": "Creation Time", + "no_log": "No Logs Available", + "all_236_terms": "Export all {msg} log entries?", + "export_hint": "Export all logs?", + "export": "Export", + "success": "Success", + "failed": "Failed", + "failed_info": "Operation Failure Details", + "create_qa": "Create Q&A", + "delete_qa": "Delete Q&A", + "create_datasource": "Create Datasource", + "update_datasource": "Edit Datasource", + "delete_datasource": "Delete Datasource", + "create_dashboard": "Create Dashboard", + "update_dashboard": "Edit Dashboard", + "delete_dashboard": "Delete Dashboard", + "setting_create_user": "Add Member", + "setting_delete_user": "Remove Member", + "setting_create_rule": "Add Rule Group", + "setting_delete_rule": "Remove Rule Group", + "setting_update_rule": "Configure Rules", + "setting_update_rule_user": "Configure Users", + "setting_create_terminology": "Create Terminology" + } "api_key": { "info_tips": "The API Key is your credential for accessing the SQLBot API, with full permissions for your account. Please keep it secure! Do not disclose the API Key in any public channels to avoid security risks from unauthorized use.", "create": "Create", diff --git a/frontend/src/i18n/ko-KR.json b/frontend/src/i18n/ko-KR.json index ca764db4..3805f8bf 100644 --- a/frontend/src/i18n/ko-KR.json +++ b/frontend/src/i18n/ko-KR.json @@ -822,6 +822,40 @@ "modelType": { "llm": "대형 언어 모델" }, + "audit": { + "system_log": "시스템 로그", + "search_log": "검색 로그", + "operation_type": "작업 유형", + "operation_details": "작업 상세", + "operation_user_name": "작업 사용자", + "operation_status": "작업 상태", + "user_name": "사용자 이름", + "oid_name": "작업 공간", + "ip_address": "IP 주소", + "create_time": "생성 시간", + "no_log": "로그 없음", + "all_236_terms": "전체 {msg}개의 로그를 내보내시겠습니까?", + "export_hint": "모든 로그를 내보내시겠습니까?", + "export": "내보내기", + "success": "성공", + "failed": "실패", + "failed_info": "작업 실패 상세", + "create_qa": "Q&A 생성", + "delete_qa": "Q&A 삭제", + "create_datasource": "데이터 소스 생성", + "update_datasource": "데이터 소스 편집", + "delete_datasource": "데이터 소스 삭제", + "create_dashboard": "대시보드 생성", + "update_dashboard": "대시보드 편집", + "delete_dashboard": "대시보드 삭제", + "setting_create_user": "멤버 추가", + "setting_delete_user": "멤버 제거", + "setting_create_rule": "규칙 그룹 추가", + "setting_delete_rule": "규칙 그룹 제거", + "setting_update_rule": "규칙 설정", + "setting_update_rule_user": "사용자 설정", + "setting_create_terminology": "용어 생성" + } "api_key": { "info_tips": "API 키는 SQLBot API에 액세스하는 비밀키로 계정의 모든 권한을 갖고 있습니다. 꼭 안전하게 보관하세요! 외부 채널에 어떠한 방식으로도 API 키를 공개하지 마시고, 타인이 악용하여 보안 위협이 발생하는 것을 방지하세요.", "create": "생성", diff --git a/frontend/src/i18n/zh-CN.json b/frontend/src/i18n/zh-CN.json index 91464287..fdb0b01a 100644 --- a/frontend/src/i18n/zh-CN.json +++ b/frontend/src/i18n/zh-CN.json @@ -823,6 +823,40 @@ "modelType": { "llm": "大语言模型" }, + "audit": { + "system_log": "操作日志", + "search_log": "搜索日志", + "operation_type": "操作类型", + "operation_details": "操作详情", + "operation_user_name": "操作用户", + "operation_status": "操作状态", + "user_name": "操作日志", + "oid_name": "工作空间", + "ip_address": "IP 地址", + "create_time": "创建时间", + "no_log": "暂无日志", + "all_236_terms": "是否导出全部 {msg} 条日志?", + "export_hint": "是否导出全部日志?", + "export": "导出", + "success": "成功", + "failed": "失败", + "failed_info": "操作失败详情", + "create_qa": "新建问数", + "delete_qa": "删除问数", + "create_datasource": "新建数据源", + "update_datasource": "编辑数据源", + "delete_datasource": "删除数据源", + "create_dashboard": "新建仪表板", + "update_dashboard": "编辑仪表板", + "delete_dashboard": "删除仪表板", + "setting_create_user": "添加成员", + "setting_delete_user": "移除成员", + "setting_create_rule": "添加规则组", + "setting_delete_rule": "移除规则组", + "setting_update_rule": "设置规则", + "setting_update_rule_user": "设置用户", + "setting_create_terminology": "新建术语" + } "api_key": { "info_tips": "API Key 是您访问 SQLBot API 的密钥,具有账户的完全权限,请您务必妥善保管!不要以任何方式公开 API Key 到外部渠道,避免被他人利用造成安全威胁。", "create": "创建", diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 51f8aba4..ca3f1d09 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -18,6 +18,7 @@ import Member from '@/views/system/member/index.vue' import Professional from '@/views/system/professional/index.vue' import Training from '@/views/system/training/index.vue' import Prompt from '@/views/system/prompt/index.vue' +import Audit from '@/views/system/audit/index.vue' import Appearance from '@/views/system/appearance/index.vue' import Parameter from '@/views/system/parameter/index.vue' import Authentication from '@/views/system/authentication/index.vue' @@ -130,6 +131,12 @@ export const routes = [ component: Prompt, meta: { title: t('prompt.customize_prompt_words') }, }, + { + path: '/set/audit', + name: 'audit', + component: Audit, + meta: { title: t('audit.system_log') }, + }, ], }, { diff --git a/frontend/src/views/chat/ChatList.vue b/frontend/src/views/chat/ChatList.vue index 36cc985c..c2a98aa3 100644 --- a/frontend/src/views/chat/ChatList.vue +++ b/frontend/src/views/chat/ChatList.vue @@ -122,7 +122,7 @@ function handleCommand(command: string | number | object, chat: Chat) { }).then(() => { _loading.value = true chatApi - .deleteChat(chat.id) + .deleteChat(chat.id, chat.brief) .then(() => { ElMessage({ type: 'success', diff --git a/frontend/src/views/dashboard/common/ResourceTree.vue b/frontend/src/views/dashboard/common/ResourceTree.vue index bce90f43..a6496ee4 100644 --- a/frontend/src/views/dashboard/common/ResourceTree.vue +++ b/frontend/src/views/dashboard/common/ResourceTree.vue @@ -230,7 +230,7 @@ const operation = (opt: string, data: SQTreeNode) => { autofocus: false, showClose: false, }).then(() => { - dashboardApi.delete_resource({ id: data.id }).then(() => { + dashboardApi.delete_resource({ id: data.id, name: data.name }).then(() => { ElMessage.success(t('dashboard.delete_success')) getTree() dashboardStore.canvasDataInit() diff --git a/frontend/src/views/ds/Datasource.vue b/frontend/src/views/ds/Datasource.vue index 3a9fcb28..6fb08ec1 100644 --- a/frontend/src/views/ds/Datasource.vue +++ b/frontend/src/views/ds/Datasource.vue @@ -159,7 +159,7 @@ const deleteHandler = (item: any) => { '' ), }).then(() => { - datasourceApi.delete(item.id).then(() => { + datasourceApi.delete(item.id, item.name).then(() => { ElMessage({ type: 'success', message: t('dashboard.delete_success'), diff --git a/frontend/src/views/system/audit/index.vue b/frontend/src/views/system/audit/index.vue new file mode 100644 index 00000000..65434e72 --- /dev/null +++ b/frontend/src/views/system/audit/index.vue @@ -0,0 +1,627 @@ + + + + + +