diff --git a/backend/apps/api.py b/backend/apps/api.py index 06dd11b59..23ca0175a 100644 --- a/backend/apps/api.py +++ b/backend/apps/api.py @@ -9,6 +9,7 @@ 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 a3ba2783c..6dfe495f2 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 7af75d76c..f93d1af7c 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 096abb3fd..a4d7caecd 100644 --- a/backend/apps/datasource/api/datasource.py +++ b/backend/apps/datasource/api/datasource.py @@ -30,6 +30,7 @@ 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 @@ def inner(): 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 b70548660..3c9aed5d4 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 786cc5498..2b7258280 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 9154c424e..84236f342 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 83681ccd9..4795d518f 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 000000000..7c19e90a9 --- /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 42a7199c8..918e58e37 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 f19ae1b84..7c5dc27e7 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 c349c85e8..c14b13cdc 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 000000000..5777bd1b7 --- /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 000000000..663e919b6 --- /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 000000000..2303d6f93 --- /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 dd1fbcd23..a7514096f 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 7d0622465..74267635d 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 ca764db42..3805f8bfe 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 91464287f..fdb0b01a5 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 51f8aba45..ca3f1d099 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 36cc985c6..c2a98aa3b 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 bce90f438..a6496ee40 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 3a9fcb282..6fb08ec11 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 000000000..65434e724 --- /dev/null +++ b/frontend/src/views/system/audit/index.vue @@ -0,0 +1,627 @@ + + + + + +