Commit 9fb5d058 by gggaaallleee Committed by GitHub

add audit (#4923)

* add audit

* update audit

* update audit
parent b9745741
## Premise
Since FastGPT is managed in the same way as monorepo, it is recommended to install ‘make’ first during development.
monorepo Project Name:
- app: main project
-......
## Dev
```sh
# Give automatic script code execution permission (on non-Linux systems, you can manually execute the postinstall.sh file content)
chmod -R +x ./scripts/
# Executing under the code root directory installs all dependencies within the root package, projects, and packages
pnpm i
# Not make cmd
cd projects/app
pnpm dev
# Make cmd
make dev name=app
```
Note: If the Node version is >= 20, you need to pass the `--no-node-snapshot` parameter to Node when running `pnpm i`
```sh
NODE_OPTIONS=--no-node-snapshot pnpm i
```
### Jest
https://fael3z0zfze.feishu.cn/docx/ZOI1dABpxoGhS7xzhkXcKPxZnDL
## I18N
### Install i18n-ally Plugin
1. Open the Extensions Marketplace in VSCode, search for and install the `i18n Ally` plugin.
### Code Optimization Examples
#### Fetch Specific Namespace Translations in `getServerSideProps`
```typescript
// pages/yourPage.tsx
export async function getServerSideProps(context: any) {
return {
props: {
currentTab: context?.query?.currentTab || TabEnum.info,
...(await serverSideTranslations(context.locale, ['publish', 'user']))
}
};
}
```
#### Use useTranslation Hook in Page
```typescript
// pages/yourPage.tsx
import { useTranslation } from 'next-i18next';
const YourComponent = () => {
const { t } = useTranslation();
return (
<Button
variant="outline"
size="sm"
mr={2}
onClick={() => setShowSelected(false)}
>
{t('common:close')}
</Button>
);
};
export default YourComponent;
```
#### Handle Static File Translations
```typescript
// utils/i18n.ts
import { i18nT } from '@fastgpt/web/i18n/utils';
const staticContent = {
id: 'simpleChat',
avatar: 'core/workflow/template/aiChat',
name: i18nT('app:template.simple_robot'),
};
export default staticContent;
```
### Standardize Translation Format
- Use the t(namespace:key) format to ensure consistent naming.
- Translation keys should use lowercase letters and underscores, e.g., common.close.
## Build
```sh
# Docker cmd: Build image, not proxy
docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 . --build-arg name=app
# Make cmd: Build image, not proxy
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1
# Docker cmd: Build image with proxy
docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 . --build-arg name=app --build-arg proxy=taobao
# Make cmd: Build image with proxy
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 proxy=taobao
```
## Premise
Since FastGPT is managed in the same way as monorepo, it is recommended to install ‘make’ first during development.
monorepo Project Name:
- app: main project
-......
## Dev
```sh
# Give automatic script code execution permission (on non-Linux systems, you can manually execute the postinstall.sh file content)
chmod -R +x ./scripts/
# Executing under the code root directory installs all dependencies within the root package, projects, and packages
pnpm i
# Not make cmd
cd projects/app
pnpm dev
# Make cmd
make dev name=app
```
Note: If the Node version is >= 20, you need to pass the `--no-node-snapshot` parameter to Node when running `pnpm i`
```sh
NODE_OPTIONS=--no-node-snapshot pnpm i
```
### Jest
https://fael3z0zfze.feishu.cn/docx/ZOI1dABpxoGhS7xzhkXcKPxZnDL
## I18N
### Install i18n-ally Plugin
1. Open the Extensions Marketplace in VSCode, search for and install the `i18n Ally` plugin.
### Code Optimization Examples
#### Fetch Specific Namespace Translations in `getServerSideProps`
```typescript
// pages/yourPage.tsx
export async function getServerSideProps(context: any) {
return {
props: {
currentTab: context?.query?.currentTab || TabEnum.info,
...(await serverSideTranslations(context.locale, ['publish', 'user']))
}
};
}
```
#### Use useTranslation Hook in Page
```typescript
// pages/yourPage.tsx
import { useTranslation } from 'next-i18next';
const YourComponent = () => {
const { t } = useTranslation();
return (
<Button
variant="outline"
size="sm"
mr={2}
onClick={() => setShowSelected(false)}
>
{t('common:close')}
</Button>
);
};
export default YourComponent;
```
#### Handle Static File Translations
```typescript
// utils/i18n.ts
import { i18nT } from '@fastgpt/web/i18n/utils';
const staticContent = {
id: 'simpleChat',
avatar: 'core/workflow/template/aiChat',
name: i18nT('app:template.simple_robot'),
};
export default staticContent;
```
### Standardize Translation Format
- Use the t(namespace:key) format to ensure consistent naming.
- Translation keys should use lowercase letters and underscores, e.g., common.close.
## audit
Please fill the OperationLogEventEnum and operationLog/audit function is added to the ts, and on the corresponding position to fill i18n, at the same time to add the location of the log using addOpearationLog function add function
## Build
```sh
# Docker cmd: Build image, not proxy
docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 . --build-arg name=app
# Make cmd: Build image, not proxy
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1
# Docker cmd: Build image with proxy
docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 . --build-arg name=app --build-arg proxy=taobao
# Make cmd: Build image with proxy
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 proxy=taobao
```
export enum OperationLogEventEnum {
//Team
LOGIN = 'LOGIN',
CREATE_INVITATION_LINK = 'CREATE_INVITATION_LINK',
JOIN_TEAM = 'JOIN_TEAM',
......@@ -11,5 +12,52 @@ export enum OperationLogEventEnum {
RELOCATE_DEPARTMENT = 'RELOCATE_DEPARTMENT',
CREATE_GROUP = 'CREATE_GROUP',
DELETE_GROUP = 'DELETE_GROUP',
ASSIGN_PERMISSION = 'ASSIGN_PERMISSION'
ASSIGN_PERMISSION = 'ASSIGN_PERMISSION',
//APP
CREATE_APP = 'CREATE_APP',
UPDATE_APP_INFO = 'UPDATE_APP_INFO',
MOVE_APP = 'MOVE_APP',
DELETE_APP = 'DELETE_APP',
UPDATE_APP_COLLABORATOR = 'UPDATE_APP_COLLABORATOR',
DELETE_APP_COLLABORATOR = 'DELETE_APP_COLLABORATOR',
TRANSFER_APP_OWNERSHIP = 'TRANSFER_APP_OWNERSHIP',
CREATE_APP_COPY = 'CREATE_APP_COPY',
CREATE_APP_FOLDER = 'CREATE_APP_FOLDER',
UPDATE_PUBLISH_APP = 'UPDATE_PUBLISH_APP',
CREATE_APP_PUBLISH_CHANNEL = 'CREATE_APP_PUBLISH_CHANNEL',
UPDATE_APP_PUBLISH_CHANNEL = 'UPDATE_APP_PUBLISH_CHANNEL',
DELETE_APP_PUBLISH_CHANNEL = 'DELETE_APP_PUBLISH_CHANNEL',
EXPORT_APP_CHAT_LOG = 'EXPORT_APP_CHAT_LOG',
//Dataset
CREATE_DATASET = 'CREATE_DATASET',
UPDATE_DATASET = 'UPDATE_DATASET',
DELETE_DATASET = 'DELETE_DATASET',
MOVE_DATASET = 'MOVE_DATASET',
UPDATE_DATASET_COLLABORATOR = 'UPDATE_DATASET_COLLABORATOR',
DELETE_DATASET_COLLABORATOR = 'DELETE_DATASET_COLLABORATOR',
TRANSFER_DATASET_OWNERSHIP = 'TRANSFER_DATASET_OWNERSHIP',
EXPORT_DATASET = 'EXPORT_DATASET',
CREATE_DATASET_FOLDER = 'CREATE_DATASET_FOLDER',
//Collection
CREATE_COLLECTION = 'CREATE_COLLECTION',
UPDATE_COLLECTION = 'UPDATE_COLLECTION',
DELETE_COLLECTION = 'DELETE_COLLECTION',
RETRAIN_COLLECTION = 'RETRAIN_COLLECTION',
//Data
CREATE_DATA = 'CREATE_DATA',
UPDATE_DATA = 'UPDATE_DATA',
DELETE_DATA = 'DELETE_DATA',
//SearchTest
SEARCH_TEST = 'SEARCH_TEST',
//Account
CHANGE_PASSWORD = 'CHANGE_PASSWORD',
CHANGE_NOTIFICATION_SETTINGS = 'CHANGE_NOTIFICATION_SETTINGS',
CHANGE_MEMBER_NAME_ACCOUNT = 'CHANGE_MEMBER_NAME_ACCOUNT',
PURCHASE_PLAN = 'PURCHASE_PLAN',
EXPORT_BILL_RECORDS = 'EXPORT_BILL_RECORDS',
CREATE_INVOICE = 'CREATE_INVOICE',
SET_INVOICE_HEADER = 'SET_INVOICE_HEADER',
CREATE_API_KEY = 'CREATE_API_KEY',
UPDATE_API_KEY = 'UPDATE_API_KEY',
DELETE_API_KEY = 'DELETE_API_KEY'
}
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { i18nT } from '../../../web/i18n/utils';
export function getI18nAppType(type: AppTypeEnum): string {
if (type === AppTypeEnum.folder) return i18nT('account_team:type.Folder');
if (type === AppTypeEnum.simple) return i18nT('account_team:type.Simple bot');
if (type === AppTypeEnum.workflow) return i18nT('account_team:type.Workflow bot');
if (type === AppTypeEnum.plugin) return i18nT('account_team:type.Plugin');
if (type === AppTypeEnum.httpPlugin) return i18nT('account_team:type.Http plugin');
if (type === AppTypeEnum.toolSet) return i18nT('account_team:type.Tool set');
if (type === AppTypeEnum.tool) return i18nT('account_team:type.Tool');
return i18nT('common:UnKnow');
}
export function getI18nCollaboratorItemType(
tmbId: string | undefined,
groupId: string | undefined,
orgId: string | undefined
): string {
if (tmbId) return i18nT('account_team:member');
if (groupId) return i18nT('account_team:group');
if (orgId) return i18nT('account_team:department');
return i18nT('common:UnKnow');
}
export function getI18nDatasetType(type: DatasetTypeEnum | string): string {
if (type === DatasetTypeEnum.folder) return i18nT('account_team:dataset.folder_dataset');
if (type === DatasetTypeEnum.dataset) return i18nT('account_team:dataset.common_dataset');
if (type === DatasetTypeEnum.websiteDataset) return i18nT('account_team:dataset.website_dataset');
if (type === DatasetTypeEnum.externalFile) return i18nT('account_team:dataset.external_file');
if (type === DatasetTypeEnum.apiDataset) return i18nT('account_team:dataset.api_file');
if (type === DatasetTypeEnum.feishu) return i18nT('account_team:dataset.feishu_dataset');
if (type === DatasetTypeEnum.yuque) return i18nT('account_team:dataset.yuque_dataset');
return i18nT('common:UnKnow');
}
......@@ -197,6 +197,9 @@
"type.MCP tools": "MCP Toolset",
"type.MCP_tools_url": "MCP Address",
"type.Plugin": "Plugin",
"type.Folder": "Folder",
"type.Tool set": "Toolset",
"type.Tool": "Tool",
"type.Simple bot": "Simple App",
"type.Workflow bot": "Workflow",
"type.error.Workflow data is empty": "No workflow data was obtained",
......@@ -238,4 +241,4 @@
"workflow.user_file_input_desc": "Links to documents and images uploaded by users.",
"workflow.user_select": "User Select",
"workflow.user_select_tip": "This module can configure multiple options for selection during the dialogue. Different options can lead to different workflow branches."
}
}
\ No newline at end of file
......@@ -215,6 +215,7 @@
"core.app.Interval timer run": "Scheduled Execution",
"core.app.Interval timer tip": "Can Execute App on Schedule",
"core.app.Make a brief introduction of your app": "Give Your AI App an Introduction",
"core.app.name": "name",
"core.app.Name and avatar": "Avatar & Name",
"core.app.Publish": "Publish",
"core.app.Publish Confirm": "Confirm to Publish App? This Will Immediately Update the App Status on All Publishing Channels.",
......@@ -1305,4 +1306,4 @@
"zoomin_tip_mac": "Zoom Out ⌘ -",
"zoomout_tip": "Zoom In ctrl +",
"zoomout_tip_mac": "Zoom In ⌘ +"
}
}
\ No newline at end of file
{
"account_team.delete_dataset": "删除知识库",
"active_model": "可用模型",
"add_default_model": "添加预设模型",
"api_key": "API 密钥",
......
......@@ -189,6 +189,7 @@
"type.Create simple bot tip": "通过填表单形式,创建简单的 AI 应用,适合新手",
"type.Create workflow bot": "创建工作流",
"type.Create workflow tip": "通过低代码的方式,构建逻辑复杂的多轮对话 AI 应用,推荐高级玩家使用",
"type.Folder": "文件夹",
"type.Http plugin": "HTTP 插件",
"type.Import from json": "导入 JSON 配置",
"type.Import from json tip": "通过 JSON 配置文件,直接创建应用",
......@@ -198,6 +199,8 @@
"type.MCP_tools_url": "MCP 地址",
"type.Plugin": "插件",
"type.Simple bot": "简易应用",
"type.Tool": "工具",
"type.Tool set": "工具集",
"type.Workflow bot": "工作流",
"type.error.Workflow data is empty": "没有获取到工作流数据",
"type.error.workflowresponseempty": "响应内容为空",
......@@ -238,4 +241,4 @@
"workflow.user_file_input_desc": "用户上传的文档和图片链接",
"workflow.user_select": "用户选择",
"workflow.user_select_tip": "该模块可配置多个选项,以供对话时选择。不同选项可导向不同工作流支线"
}
}
\ No newline at end of file
......@@ -215,6 +215,7 @@
"core.app.Interval timer run": "定时执行",
"core.app.Interval timer tip": "可定时执行应用",
"core.app.Make a brief introduction of your app": "给你的 AI 应用一个介绍",
"core.app.name": "名称",
"core.app.Name and avatar": "头像 & 名称",
"core.app.Publish": "发布",
"core.app.Publish Confirm": "确认发布应用?会立即更新所有发布渠道的应用状态。",
......@@ -1305,4 +1306,4 @@
"zoomin_tip_mac": "缩小 ⌘ -",
"zoomout_tip": "放大 ctrl +",
"zoomout_tip_mac": "放大 ⌘ +"
}
}
\ No newline at end of file
......@@ -198,6 +198,9 @@
"type.MCP_tools_url": "MCP 地址",
"type.Plugin": "外掛",
"type.Simple bot": "簡易應用程式",
"type.Folder": "資料夾",
"type.Tool set": "工具集",
"type.Tool": "工具",
"type.Workflow bot": "工作流程",
"type.error.Workflow data is empty": "沒有獲取到工作流數據",
"type.error.workflowresponseempty": "響應內容為空",
......@@ -238,4 +241,4 @@
"workflow.user_file_input_desc": "使用者上傳的檔案和圖片連結",
"workflow.user_select": "使用者選擇",
"workflow.user_select_tip": "這個模組可以設定多個選項,供對話時選擇。不同選項可以導向不同的工作流程支線"
}
}
\ No newline at end of file
......@@ -215,6 +215,7 @@
"core.app.Interval timer run": "排程執行",
"core.app.Interval timer tip": "可排程執行應用程式",
"core.app.Make a brief introduction of your app": "為您的 AI 應用程式寫一段介紹",
"core.app.name": "名稱",
"core.app.Name and avatar": "頭像與名稱",
"core.app.Publish": "發布",
"core.app.Publish Confirm": "確認發布應用程式?這將立即更新所有發布管道的應用程式狀態。",
......@@ -1305,4 +1306,4 @@
"zoomin_tip_mac": "縮小 ⌘ -",
"zoomout_tip": "放大 ctrl +",
"zoomout_tip_mac": "放大 ⌘ +"
}
}
\ No newline at end of file
......@@ -26,6 +26,7 @@ import MultipleSelect, {
} from '@fastgpt/web/components/common/MySelect/MultipleSelect';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { getTeamMembers } from '@/web/support/user/team/api';
import { createMetadataProcessorMap, type MetadataProcessor } from './processors';
function OperationLogTable({ Tabs }: { Tabs: React.ReactNode }) {
const { t } = useTranslation();
......@@ -58,6 +59,14 @@ function OperationLogTable({ Tabs }: { Tabs: React.ReactNode }) {
[t]
);
const processMetadataByEvent = useMemo(() => {
const metadataProcessorMap = createMetadataProcessorMap();
return (event: string, metadata: any) => {
const processor = metadataProcessorMap[event as OperationLogEventEnum];
return processor ? processor(metadata, t) : metadata;
};
}, [t]);
const {
data: operationLogs = [],
isLoading: loadingLogs,
......@@ -159,17 +168,7 @@ function OperationLogTable({ Tabs }: { Tabs: React.ReactNode }) {
<Tbody>
{operationLogs?.map((log) => {
const i18nData = operationLogMap[log.event];
const metadata = { ...log.metadata };
if (log.event === OperationLogEventEnum.ASSIGN_PERMISSION) {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new TeamPermission({ per: permissionValue });
metadata.appCreate = permission.hasAppCreatePer ? '✔' : '✘';
metadata.datasetCreate = permission.hasDatasetCreatePer ? '✔' : '✘';
metadata.apiKeyCreate = permission.hasApikeyCreatePer ? '✔' : '✘';
metadata.manage = permission.hasManagePer ? '✔' : '✘';
}
const metadata = processMetadataByEvent(log.event, { ...log.metadata });
return i18nData ? (
<Tr key={log._id} overflow={'unset'}>
......
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
import { createSpecialProcessor } from './commonProcessor';
export const processUpdateAppCollaboratorSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new AppPermission({ per: permissionValue });
return {
...metadata,
readPermission: permission.hasReadPer ? '✔' : '✘',
writePermission: permission.hasWritePer ? '✔' : '✘',
managePermission: permission.hasManagePer ? '✔' : '✘'
};
};
export const createAppProcessors = () => ({
UPDATE_APP_COLLABORATOR: createSpecialProcessor(processUpdateAppCollaboratorSpecific)
});
export interface CommonMetadataFields {
appType?: string;
datasetType?: string;
operationName?: string;
itemName?: string;
newItemNames?: string[] | string;
[key: string]: any;
}
export const defaultMetadataProcessor = (metadata: CommonMetadataFields, t: any): any => {
const result = { ...metadata };
const translatableFields = ['appType', 'datasetType', 'operationName', 'itemName'];
Object.entries(metadata)
.filter(([key, value]) => translatableFields.includes(key) && value)
.forEach(([key, value]) => {
result[key] = t(value as any);
});
if (metadata.newItemNames) {
if (Array.isArray(metadata.newItemNames)) {
result.newItemNames = metadata.newItemNames
.map((itemName: string) => t(itemName as any))
.join(',');
} else if (typeof metadata.newItemNames === 'string') {
result.newItemNames = metadata.newItemNames
.split(',')
.map((itemName: string) => t(itemName as any))
.join(',');
}
}
return result;
};
export const createSpecialProcessor = (specificProcessor: (metadata: any) => any) => {
return (metadata: any, t: any) => {
let processedMetadata = defaultMetadataProcessor(metadata, t);
processedMetadata = specificProcessor(processedMetadata);
return processedMetadata;
};
};
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
import { createSpecialProcessor } from './commonProcessor';
export const processUpdateDatasetCollaboratorSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new DatasetPermission({ per: permissionValue });
return {
...metadata,
readPermission: permission.hasReadPer ? '✔' : '✘',
writePermission: permission.hasWritePer ? '✔' : '✘',
managePermission: permission.hasManagePer ? '✔' : '✘'
};
};
export const createDatasetProcessors = () => ({
UPDATE_DATASET_COLLABORATOR: createSpecialProcessor(processUpdateDatasetCollaboratorSpecific)
});
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { defaultMetadataProcessor } from './commonProcessor';
import { createTeamProcessors } from './teamProcessors';
import { createAppProcessors } from './appProcessors';
import { createDatasetProcessors } from './datasetProcessors';
export type MetadataProcessor = (metadata: any, t: any) => any;
export const createMetadataProcessorMap = (): Record<OperationLogEventEnum, MetadataProcessor> => {
const specialProcessors: Partial<Record<OperationLogEventEnum, MetadataProcessor>> = {
...createTeamProcessors(),
...createAppProcessors(),
...createDatasetProcessors()
};
const processorMap = {} as Record<OperationLogEventEnum, MetadataProcessor>;
Object.values(OperationLogEventEnum).forEach((event) => {
processorMap[event] =
specialProcessors[event] ||
((metadata: any, t: any) => defaultMetadataProcessor(metadata, t));
});
return processorMap;
};
export * from './commonProcessor';
export * from './teamProcessors';
export * from './appProcessors';
export * from './datasetProcessors';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { createSpecialProcessor } from './commonProcessor';
export const processAssignPermissionSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new TeamPermission({ per: permissionValue });
return {
...metadata,
appCreate: permission.hasAppCreatePer ? '✔' : '✘',
datasetCreate: permission.hasDatasetCreatePer ? '✔' : '✘',
apiKeyCreate: permission.hasApikeyCreatePer ? '✔' : '✘',
manage: permission.hasManagePer ? '✔' : '✘'
};
};
export const createTeamProcessors = () => ({
ASSIGN_PERMISSION: createSpecialProcessor(processAssignPermissionSpecific)
});
......@@ -5,7 +5,10 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { onCreateApp } from './create';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { getI18nAppType } from '@fastgpt/service/support/operationLog/util';
export type copyAppQuery = {};
export type copyAppBody = { appId: string };
......@@ -18,7 +21,7 @@ async function handler(
req: ApiRequestProps<copyAppBody, copyAppQuery>,
res: ApiResponseType<any>
): Promise<copyAppResponse> {
const { app } = await authApp({
const { app, teamId } = await authApp({
req,
authToken: true,
per: WritePermissionVal,
......@@ -42,6 +45,17 @@ async function handler(
tmbId,
pluginData: app.pluginData
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CREATE_APP_COPY,
params: {
appName: app.name,
appType: getI18nAppType(app.type)
}
});
})();
return { appId };
}
......
......@@ -19,6 +19,9 @@ import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nAppType } from '@fastgpt/service/support/operationLog/util';
export type CreateAppBody = {
parentId?: ParentIdType;
......@@ -148,6 +151,17 @@ export const onCreateApp = async ({
{ session, ordered: true }
);
}
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CREATE_APP,
params: {
appName: name!,
appType: getI18nAppType(type!)
}
});
})();
await refreshSourceAvatar(avatar, undefined, session);
......
......@@ -19,7 +19,10 @@ import { deleteChatFiles } from '@fastgpt/service/core/chat/controller';
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
import { MongoOpenApi } from '@fastgpt/service/support/openapi/schema';
import { removeImageByPath } from '@fastgpt/service/common/file/image/controller';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { getI18nAppType } from '@fastgpt/service/support/operationLog/util';
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const { appId } = req.query as { appId: string };
......@@ -39,6 +42,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
teamId,
appId
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.DELETE_APP,
params: {
appName: app.name,
appType: getI18nAppType(app.type)
}
});
})();
// Tracks
pushTrack.countAppNodes({ teamId, tmbId, uid: userId, appId });
......
......@@ -18,7 +18,8 @@ import { syncCollaborators } from '@fastgpt/service/support/permission/inheritPe
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
export type CreateAppFolderBody = {
parentId?: ParentIdType;
name: string;
......@@ -83,6 +84,16 @@ async function handler(req: ApiRequestProps<CreateAppFolderBody>) {
);
}
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CREATE_APP_FOLDER,
params: {
folderName: name
}
});
})();
}
export default NextAPI(handler);
......@@ -13,6 +13,9 @@ import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { type PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nAppType } from '@fastgpt/service/support/operationLog/util';
async function handler(
req: NextApiRequest,
......@@ -33,7 +36,12 @@ async function handler(
}
// 凭证校验
const { teamId } = await authApp({ req, authToken: true, appId, per: WritePermissionVal });
const { teamId, tmbId, app } = await authApp({
req,
authToken: true,
appId,
per: WritePermissionVal
});
const where = {
teamId: new Types.ObjectId(teamId),
......@@ -139,6 +147,18 @@ async function handler(
const listWithoutTmbId = list.filter((item) => !item.tmbId);
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.EXPORT_APP_CHAT_LOG,
params: {
appName: app.name,
appType: getI18nAppType(app.type)
}
});
})();
return {
list: listWithSourceMember.concat(listWithoutTmbId),
total
......
......@@ -24,6 +24,10 @@ import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/u
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nAppType } from '@fastgpt/service/support/operationLog/util';
import { i18nT } from '@fastgpt/web/i18n/utils';
export type AppUpdateQuery = {
appId: string;
......@@ -54,7 +58,7 @@ async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
// this step is to get the app and its permission, and we will check the permission manually for
// different cases
const { app, permission } = await authApp({
const { app, permission, teamId, tmbId } = await authApp({
req,
authToken: true,
appId,
......@@ -65,11 +69,23 @@ async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
Promise.reject(AppErrEnum.unExist);
}
let targetName = '';
if (isMove) {
if (parentId) {
// move to a folder, check the target folder's permission
await authApp({ req, authToken: true, appId: parentId, per: ManagePermissionVal });
const { app: targetApp } = await authApp({
req,
authToken: true,
appId: parentId,
per: ManagePermissionVal
});
targetName = targetApp.name;
} else {
targetName = 'root';
}
if (app.parentId) {
// move from a folder, check the (old) folder's permission
await authApp({ req, authToken: true, appId: app.parentId, per: ManagePermissionVal });
......@@ -160,6 +176,7 @@ async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
session
});
} else {
logAppMove({ tmbId, teamId, app, targetName });
// Not folder, delete all clb
await MongoResourcePermission.deleteMany(
{ resourceType: PerResourceTypeEnum.app, teamId: app.teamId, resourceId: app._id },
......@@ -169,8 +186,85 @@ async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
return onUpdate(session);
});
} else {
logAppUpdate({ tmbId, teamId, app, name, intro });
return onUpdate();
}
}
export default NextAPI(handler);
const logAppMove = ({
tmbId,
teamId,
app,
targetName
}: {
tmbId: string;
teamId: string;
app: any;
targetName: string;
}) => {
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.MOVE_APP,
params: {
appName: app.name,
targetFolderName: targetName,
appType: getI18nAppType(app.type)
}
});
})();
};
const logAppUpdate = ({
tmbId,
teamId,
app,
name,
intro
}: {
tmbId: string;
teamId: string;
app: any;
name?: string;
intro?: string;
}) => {
(async () => {
const getUpdateItems = () => {
const names: string[] = [];
const values: string[] = [];
if (name !== undefined) {
names.push(i18nT('common:core.app.name'));
values.push(name);
}
if (intro !== undefined) {
names.push(i18nT('common:Intro'));
values.push(intro);
}
return {
names,
values
};
};
const { names: newItemNames, values: newItemValues } = getUpdateItems();
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.UPDATE_APP_INFO,
params: {
appName: app.name,
newItemNames: newItemNames,
newItemValues: newItemValues,
appType: getI18nAppType(app.type)
}
});
})();
};
......@@ -11,12 +11,20 @@ import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'
import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { rewriteAppWorkflowToSimple } from '@fastgpt/service/core/app/utils';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nAppType } from '@fastgpt/service/support/operationLog/util';
import { i18nT } from '@fastgpt/web/i18n/utils';
async function handler(req: ApiRequestProps<PostPublishAppProps>, res: NextApiResponse<any>) {
const { appId } = req.query as { appId: string };
const { nodes = [], edges = [], chatConfig, isPublish, versionName, autoSave } = req.body;
const { app, tmbId } = await authApp({ appId, req, per: WritePermissionVal, authToken: true });
const { app, tmbId, teamId } = await authApp({
appId,
req,
per: WritePermissionVal,
authToken: true
});
const { nodes: formatNodes } = beforeUpdateAppFormat({
nodes,
......@@ -26,12 +34,26 @@ async function handler(req: ApiRequestProps<PostPublishAppProps>, res: NextApiRe
await rewriteAppWorkflowToSimple(formatNodes);
if (autoSave) {
return MongoApp.findByIdAndUpdate(appId, {
await MongoApp.findByIdAndUpdate(appId, {
modules: formatNodes,
edges,
chatConfig,
updateTime: new Date()
});
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.UPDATE_PUBLISH_APP,
params: {
appName: app.name,
operationName: i18nT('account_team:update'),
appId,
appType: getI18nAppType(app.type)
}
});
return;
}
await mongoSessionRun(async (session) => {
......@@ -79,6 +101,22 @@ async function handler(req: ApiRequestProps<PostPublishAppProps>, res: NextApiRe
}
);
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.UPDATE_PUBLISH_APP,
params: {
appName: app.name,
operationName: isPublish
? i18nT('account_team:save_and_publish')
: i18nT('account_team:update'),
appId,
appType: getI18nAppType(app.type)
}
});
})();
}
export default NextAPI(handler);
......@@ -4,11 +4,14 @@ import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller';
import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
async function handler(req: NextApiRequest) {
const body = req.body as CreateDatasetCollectionParams;
const { teamId, tmbId } = await authDataset({
const { teamId, tmbId, dataset } = await authDataset({
req,
authToken: true,
authApiKey: true,
......@@ -21,6 +24,20 @@ async function handler(req: NextApiRequest) {
teamId,
tmbId
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CREATE_COLLECTION,
params: {
collectionName: body.name,
datasetName: dataset.name,
datasetType: getI18nDatasetType(dataset.type)
}
});
})();
return _id;
}
......
......@@ -14,6 +14,9 @@ import { authDatasetCollection } from '@fastgpt/service/support/permission/datas
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { i18nT } from '@fastgpt/web/i18n/utils';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
type RetrainingCollectionResponse = {
collectionId: string;
......@@ -124,6 +127,19 @@ async function handler(
}
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.RETRAIN_COLLECTION,
params: {
collectionName: collection.name,
datasetName: collection.dataset?.name || '',
datasetType: getI18nDatasetType(collection.dataset?.type || '')
}
});
})();
return { collectionId };
});
}
......
......@@ -6,7 +6,9 @@ import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
async function handler(req: NextApiRequest) {
const { id: collectionId } = req.query as { id: string };
......@@ -14,7 +16,7 @@ async function handler(req: NextApiRequest) {
return Promise.reject(CommonErrEnum.missingParams);
}
const { teamId, collection } = await authDatasetCollection({
const { teamId, collection, tmbId } = await authDatasetCollection({
req,
authToken: true,
authApiKey: true,
......@@ -39,6 +41,19 @@ async function handler(req: NextApiRequest) {
session
})
);
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.DELETE_COLLECTION,
params: {
collectionName: collection.name,
datasetName: collection.dataset?.name || '',
datasetType: getI18nDatasetType(collection.dataset?.type || '')
}
});
})();
}
export default NextAPI(handler);
......@@ -12,7 +12,9 @@ import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant
import { type ClientSession } from '@fastgpt/service/common/mongo';
import { type CollectionWithDatasetType } from '@fastgpt/global/core/dataset/type';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
export type UpdateDatasetCollectionParams = {
id?: string;
parentId?: string;
......@@ -88,7 +90,7 @@ async function handler(req: ApiRequestProps<UpdateDatasetCollectionParams>) {
}
// 凭证校验
const { collection, teamId } = await authDatasetCollection({
const { collection, teamId, tmbId } = await authDatasetCollection({
req,
authToken: true,
authApiKey: true,
......@@ -131,6 +133,19 @@ async function handler(req: ApiRequestProps<UpdateDatasetCollectionParams>) {
});
}
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.UPDATE_COLLECTION,
params: {
collectionName: collection.name,
datasetName: collection.dataset?.name || '',
datasetType: getI18nDatasetType(collection.dataset?.type || '')
}
});
})();
}
export default NextAPI(handler);
......@@ -18,6 +18,9 @@ import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { checkTeamDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
export type DatasetCreateQuery = {};
export type DatasetCreateBody = CreateDatasetParams;
......@@ -102,6 +105,18 @@ async function handler(
uid: userId
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CREATE_DATASET,
params: {
datasetName: name,
datasetType: getI18nDatasetType(type)
}
});
})();
return datasetId;
}
export default NextAPI(handler);
......@@ -4,7 +4,9 @@ import { deleteDatasetData } from '@/service/core/dataset/data/controller';
import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
async function handler(req: NextApiRequest) {
const { id: dataId } = req.query as {
id: string;
......@@ -15,7 +17,7 @@ async function handler(req: NextApiRequest) {
}
// 凭证校验
const { datasetData } = await authDatasetData({
const { datasetData, tmbId, teamId, collection } = await authDatasetData({
req,
authToken: true,
authApiKey: true,
......@@ -24,7 +26,18 @@ async function handler(req: NextApiRequest) {
});
await deleteDatasetData(datasetData);
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.DELETE_DATA,
params: {
collectionName: collection.name,
datasetName: collection.dataset?.name || '',
datasetType: getI18nDatasetType(collection.dataset?.type || '')
}
});
})();
return 'success';
}
......
......@@ -17,6 +17,9 @@ import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { getLLMMaxChunkSize } from '@fastgpt/global/core/dataset/training/utils';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
async function handler(req: NextApiRequest) {
const { collectionId, q, a, indexes } = req.body as InsertOneDatasetDataProps;
......@@ -30,7 +33,7 @@ async function handler(req: NextApiRequest) {
}
// 凭证校验
const { teamId, tmbId } = await authDatasetCollection({
const { teamId, tmbId, collection } = await authDatasetCollection({
req,
authToken: true,
authApiKey: true,
......@@ -96,6 +99,18 @@ async function handler(req: NextApiRequest) {
model: vectorModelData.model
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CREATE_DATA,
params: {
collectionName: collection.name,
datasetName: collection.dataset?.name || '',
datasetType: getI18nDatasetType(collection.dataset?.type || '')
}
});
})();
return insertId;
}
......
......@@ -5,7 +5,9 @@ import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { authDatasetData } from '@fastgpt/service/support/permission/dataset/auth';
import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
async function handler(req: ApiRequestProps<UpdateDatasetDataProps>) {
const { dataId, q, a, indexes = [] } = req.body;
......@@ -15,7 +17,8 @@ async function handler(req: ApiRequestProps<UpdateDatasetDataProps>) {
dataset: { vectorModel }
},
teamId,
tmbId
tmbId,
collection
} = await authDatasetData({
req,
authToken: true,
......@@ -39,6 +42,19 @@ async function handler(req: ApiRequestProps<UpdateDatasetDataProps>) {
inputTokens: tokens,
model: vectorModel
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.UPDATE_DATA,
params: {
collectionName: collection.name,
datasetName: collection.dataset?.name || '',
datasetType: getI18nDatasetType(collection.dataset?.type || '')
}
});
})();
} else {
// await MongoDatasetData.findByIdAndUpdate(dataId, {
// ...(forbid !== undefined && { forbid })
......
......@@ -11,6 +11,9 @@ import { MongoDatasetCollectionTags } from '@fastgpt/service/core/dataset/tag/sc
import { removeImageByPath } from '@fastgpt/service/common/file/image/controller';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { removeWebsiteSyncJobScheduler } from '@fastgpt/service/core/dataset/websiteSync';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
async function handler(req: NextApiRequest) {
const { id: datasetId } = req.query as {
......@@ -22,7 +25,7 @@ async function handler(req: NextApiRequest) {
}
// auth owner
const { teamId } = await authDataset({
const { teamId, tmbId, dataset } = await authDataset({
req,
authToken: true,
authApiKey: true,
......@@ -66,6 +69,18 @@ async function handler(req: NextApiRequest) {
await removeImageByPath(dataset.avatar, session);
}
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.DELETE_DATASET,
params: {
datasetName: dataset.name,
datasetType: getI18nDatasetType(dataset.type)
}
});
})();
}
export default NextAPI(handler);
......@@ -17,6 +17,8 @@ import { syncCollaborators } from '@fastgpt/service/support/permission/inheritPe
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
export type DatasetFolderCreateQuery = {};
export type DatasetFolderCreateBody = {
parentId?: string;
......@@ -92,6 +94,16 @@ async function handler(
);
}
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CREATE_DATASET_FOLDER,
params: {
folderName: name
}
});
})();
return {};
}
......
......@@ -14,7 +14,9 @@ import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { useIPFrequencyLimit } from '@fastgpt/service/common/middle/reqFrequencyLimit';
import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { getRerankModel } from '@fastgpt/service/core/ai/model';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTestResponse> {
const {
datasetId,
......@@ -130,6 +132,17 @@ async function handler(req: ApiRequestProps<SearchTestProps>): Promise<SearchTes
totalPoints: embeddingTotalPoints + reRankTotalPoints
});
}
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.SEARCH_TEST,
params: {
datasetName: dataset.name,
datasetType: getI18nDatasetType(dataset.type)
}
});
})();
return {
list: searchRes,
......
......@@ -37,6 +37,9 @@ import {
} from '@fastgpt/service/core/dataset/websiteSync';
import { delDatasetRelevantData } from '@fastgpt/service/core/dataset/controller';
import { isEqual } from 'lodash';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
export type DatasetUpdateQuery = {};
export type DatasetUpdateResponse = any;
......@@ -79,16 +82,27 @@ async function handler(
const isMove = parentId !== undefined;
const { dataset, permission } = await authDataset({
const { dataset, permission, tmbId, teamId } = await authDataset({
req,
authToken: true,
datasetId: id,
per: ReadPermissionVal
});
let targetName = '';
if (isMove) {
if (parentId) {
// move to a folder, check the target folder's permission
await authDataset({ req, authToken: true, datasetId: parentId, per: ManagePermissionVal });
const { dataset: targetDataset } = await authDataset({
req,
authToken: true,
datasetId: parentId,
per: ManagePermissionVal
});
targetName = targetDataset.name;
} else {
targetName = 'root';
}
if (dataset.parentId) {
// move from a folder, check the (old) folder's permission
......@@ -221,7 +235,9 @@ async function handler(
collaborators: parentClbsAndGroups,
session
});
logDatasetMove({ tmbId, teamId, dataset, targetName });
} else {
logDatasetMove({ tmbId, teamId, dataset, targetName });
// Not folder, delete all clb
await MongoResourcePermission.deleteMany(
{ resourceId: id, teamId: dataset.teamId, resourceType: PerResourceTypeEnum.dataset },
......@@ -230,6 +246,7 @@ async function handler(
}
return onUpdate(session);
} else {
logDatasetUpdate({ tmbId, teamId, dataset });
return onUpdate(session);
}
});
......@@ -315,3 +332,50 @@ const updateSyncSchedule = async ({
}
}
};
const logDatasetMove = ({
tmbId,
teamId,
dataset,
targetName
}: {
tmbId: string;
teamId: string;
dataset: any;
targetName: string;
}) => {
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.MOVE_DATASET,
params: {
datasetName: dataset.name,
targetFolderName: targetName,
datasetType: getI18nDatasetType(dataset.type)
}
});
})();
};
const logDatasetUpdate = ({
tmbId,
teamId,
dataset
}: {
tmbId: string;
teamId: string;
dataset: any;
}) => {
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.UPDATE_DATASET,
params: {
datasetName: dataset.name,
datasetType: getI18nDatasetType(dataset.type)
}
});
})();
};
......@@ -8,7 +8,8 @@ import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { OpenApiErrEnum } from '@fastgpt/global/common/error/code/openapi';
import { TeamApikeyCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
async function handler(req: ApiRequestProps<EditApiKeyProps>): Promise<string> {
const { appId, name, limit } = req.body;
const { tmbId, teamId } = await (async () => {
......@@ -48,6 +49,18 @@ async function handler(req: ApiRequestProps<EditApiKeyProps>): Promise<string> {
name,
limit
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CREATE_API_KEY,
params: {
keyName: name
}
});
})();
return apiKey;
}
......
......@@ -4,7 +4,8 @@ import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant'
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
export type OpenAPIDeleteQuery = { id: string };
export type OpenAPIDeleteBody = {};
export type OpenAPIDeleteResponse = {};
......@@ -19,9 +20,26 @@ async function handler(
return Promise.reject(CommonErrEnum.missingParams);
}
await authOpenApiKeyCrud({ req, authToken: true, id, per: OwnerPermissionVal });
const { tmbId, teamId, openapi } = await authOpenApiKeyCrud({
req,
authToken: true,
id,
per: OwnerPermissionVal
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.DELETE_API_KEY,
params: {
keyName: openapi.name
}
});
})();
await MongoOpenApi.deleteOne({ _id: id });
return {};
}
......
......@@ -4,11 +4,28 @@ import { authOpenApiKeyCrud } from '@fastgpt/service/support/permission/auth/ope
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
async function handler(req: ApiRequestProps<EditApiKeyProps & { _id: string }>): Promise<void> {
const { _id, name, limit } = req.body;
await authOpenApiKeyCrud({ req, authToken: true, id: _id, per: OwnerPermissionVal });
const { tmbId, teamId } = await authOpenApiKeyCrud({
req,
authToken: true,
id: _id,
per: OwnerPermissionVal
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.UPDATE_API_KEY,
params: {
keyName: name
}
});
})();
await MongoOpenApi.findByIdAndUpdate(_id, {
...(name && { name }),
......
......@@ -6,7 +6,9 @@ import type { PublishChannelEnum } from '@fastgpt/global/support/outLink/constan
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nAppType } from '@fastgpt/service/support/operationLog/util';
/* create a shareChat */
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
......@@ -23,7 +25,7 @@ async function handler(
): Promise<OutLinkCreateResponse> {
const { appId, ...props } = req.body;
const { teamId, tmbId } = await authApp({
const { teamId, tmbId, app } = await authApp({
req,
authToken: true,
appId,
......@@ -39,6 +41,19 @@ async function handler(
...props
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CREATE_APP_PUBLISH_CHANNEL,
params: {
appName: app.name,
channelName: props.name,
appType: getI18nAppType(app.type)
}
});
})();
return shareId;
}
......
......@@ -3,6 +3,9 @@ import { authOutLinkCrud } from '@fastgpt/service/support/permission/publish/aut
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nAppType } from '@fastgpt/service/support/operationLog/util';
export type OutLinkDeleteQuery = {
id: string;
......@@ -15,8 +18,28 @@ async function handler(
req: ApiRequestProps<OutLinkDeleteBody, OutLinkDeleteQuery>
): Promise<OutLinkDeleteResponse> {
const { id } = req.query;
await authOutLinkCrud({ req, outLinkId: id, authToken: true, per: OwnerPermissionVal });
const { tmbId, teamId, outLink, app } = await authOutLinkCrud({
req,
outLinkId: id,
authToken: true,
per: OwnerPermissionVal
});
await MongoOutLink.findByIdAndDelete(id);
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.DELETE_APP_PUBLISH_CHANNEL,
params: {
appName: app.name,
channelName: outLink.name,
appType: getI18nAppType(app.type)
}
});
})();
return {};
}
......
......@@ -5,7 +5,9 @@ import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nAppType } from '@fastgpt/service/support/operationLog/util';
export type OutLinkUpdateQuery = {};
// {
......@@ -30,7 +32,17 @@ async function handler(
return Promise.reject(CommonErrEnum.missingParams);
}
await authOutLinkCrud({ req, outLinkId: _id, authToken: true, per: ManagePermissionVal });
const {
tmbId,
teamId,
outLink,
app: logApp
} = await authOutLinkCrud({
req,
outLinkId: _id,
authToken: true,
per: ManagePermissionVal
});
await MongoOutLink.findByIdAndUpdate(_id, {
name,
......@@ -41,6 +53,19 @@ async function handler(
limit,
app
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.UPDATE_APP_PUBLISH_CHANNEL,
params: {
appName: logApp.name,
channelName: outLink.name,
appType: getI18nAppType(logApp.type)
}
});
})();
return {};
}
export default NextAPI(handler);
......@@ -5,7 +5,8 @@ import { MongoUser } from '@fastgpt/service/support/user/schema';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { i18nT } from '@fastgpt/web/i18n/utils';
import { NextAPI } from '@/service/middleware/entry';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const { oldPsw, newPsw } = req.body as { oldPsw: string; newPsw: string };
......@@ -13,7 +14,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
return Promise.reject('Params is missing');
}
const { tmbId } = await authCert({ req, authToken: true });
const { tmbId, teamId } = await authCert({ req, authToken: true });
const tmb = await MongoTeamMember.findById(tmbId);
if (!tmb) {
return Promise.reject('can not find it');
......@@ -39,6 +40,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
passwordUpdateTime: new Date()
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.CHANGE_PASSWORD,
params: {}
});
})();
return user;
}
......
......@@ -3,6 +3,9 @@ import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { checkExportDatasetLimit } from '@fastgpt/service/support/user/utils';
import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { addOperationLog } from '@fastgpt/service/support/operationLog/addOperationLog';
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
import { getI18nDatasetType } from '@fastgpt/service/support/operationLog/util';
async function handler(req: NextApiRequest) {
const { datasetId } = req.query as {
......@@ -14,7 +17,7 @@ async function handler(req: NextApiRequest) {
}
// 凭证校验
const { teamId } = await authDataset({
const { teamId, tmbId, dataset } = await authDataset({
req,
authToken: true,
datasetId,
......@@ -25,6 +28,18 @@ async function handler(req: NextApiRequest) {
teamId,
limitMinutes: global.feConfigs?.limit?.exportDatasetLimitMinutes
});
(async () => {
addOperationLog({
tmbId,
teamId,
event: OperationLogEventEnum.EXPORT_DATASET,
params: {
datasetName: dataset.name,
datasetType: getI18nDatasetType(dataset.type)
}
});
})();
}
export default NextAPI(handler);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment