Commit 58254a47 by Archer Committed by GitHub

app openapi (#7031)

* app openapi

* openapi fix

* feat: add app openapi schemas

* refactor: split permission schemas

* fix: update wallet sub test mock

* refactor: avoid schema re-exports from permission types

* refactor: remove openapi route type aliases

* refactor: use undefined for empty api responses

* test: align empty image upload response

* submodule

* doc
parent 86e301ce
...@@ -144,6 +144,37 @@ export type MessageParam = z.infer<typeof MessageParamSchema>; ...@@ -144,6 +144,37 @@ export type MessageParam = z.infer<typeof MessageParamSchema>;
--- ---
### Zod Schema 与 OpenAPI 风格
Zod schema 同时承担运行时校验、TypeScript 类型推导和 OpenAPI 生成来源。新增或调整 API 时,按下面规则组织:
- 业务通用结构放在业务归属目录,例如 `packages/global/core/app/type.ts``packages/global/core/workflow/type/node.ts``packages/global/support/permission/**/controller.ts``packages/global/openapi/**` 只声明接口 query/body/response/path、接口专用兼容处理和 OpenAPI 文档信息,不把通用配置类型、权限对象、工具配置等只为文档复制到 openapi 目录。
- OpenAPI schema 优先复用业务 schema。需要字段说明时,优先在业务 schema 上补齐 `meta`;只属于某个接口视角的说明,可以在 openapi schema 里用 `SomeSchema.shape.field.meta(...)` 补充。不要重复建立同构 schema,也不要用 `export const A = B` 这种重命名 alias 当作新 schema 导出。
- 只有接口边界确实需要特殊兼容时,才在 openapi 目录定义专用 wrapper,例如把 `{}` 兼容为 `undefined`、或去掉 JSON Schema 不支持的 function 字段。此类 wrapper 附近要写清楚原因。
- API response schema 默认声明业务 `data` 结构,不重复声明统一响应 envelope,例如 `code``statusText`。只有路由实际直接返回这些字段时,才把它们写进 schema。
- 没有业务返回数据的成功响应,schema 用 `z.undefined().meta({ description: '操作成功' })`,handler 里返回 `SomeResponseSchema.parse(undefined)`,或在没有 response schema 的旧路由里直接不写 `return`。不要再写 `z.null()``z.object({})``return null``return {}``return 'success'` 表示空成功响应;统一响应中间件会把 `undefined` 包成 `data: null`
- 每个对外字段补齐 `description`,关键入参和返回值补 `example`。如果字段语义属于业务通用结构,优先把 `meta` 写到通用 schema;如果只是某接口视角,写到 openapi schema。
- API 入参、客户端传输结构和需要容错解析的配置字段,优先使用 `packages/global/common/zod` 里的 `BoolSchema``NumSchema``IntSchema`。不要直接写 `z.coerce.number()`;普通数值用 `NumSchema`,非负整数、数量、分页、limit 用 `IntSchema`,布尔配置和查询参数用 `BoolSchema`。只有明确需要严格拒绝字符串/数字形式时,才保留 `z.number()``z.boolean()`
- 废弃字段用 `.meta({ deprecated: true })` 标记,可同时保留业务说明,例如 `description: '旧版团队标签'`。不要只写 `/** @deprecated */`,也不要只把“已废弃”写进 `description`
- Tag 归属按能力复用判断:通用模块接口被业务模块使用时,同时加通用模块 tag 和业务模块 tag;业务模块自己的状态查询或状态操作,只加业务模块 tag。API key 文档只给实际开放接口加 apikey 专用 tag,不开放的接口不要为了分类加 tag。
- 仅客户端使用的 API 也要以客户端实际传参为准定义 schema,避免 `schema.parse``number``boolean` 的字符串形态导致业务不可用。
```typescript
import { BoolSchema, IntSchema, NumSchema } from '@fastgpt/global/common/zod';
export const UpdateConfigSchema = z.object({
enabled: BoolSchema.meta({ description: '是否启用' }),
limit: IntSchema.optional().meta({ description: '最大数量' }),
temperature: NumSchema.optional().meta({ description: '温度参数' }),
teamTags: z.array(z.string()).optional().meta({
description: '旧版团队标签',
deprecated: true
})
});
```
---
## 可选链调用回调 ## 可选链调用回调
`?.()` 调用可选回调,取代 `if (fn) fn()` 的冗余写法。 `?.()` 调用可选回调,取代 `if (fn) fn()` 的冗余写法。
......
...@@ -99,6 +99,14 @@ export type getAppChatLogsResponseType = z.infer<typeof GetAppChatLogsResponseSc ...@@ -99,6 +99,14 @@ export type getAppChatLogsResponseType = z.infer<typeof GetAppChatLogsResponseSc
*/ */
``` ```
**OpenAPI Tag 归属规则**:
- 如果接口能力属于通用模块 A,但会被业务模块 B 使用,则该接口必须同时声明 A 模块 tag 和 B 模块 tag。
- 如果同一个通用接口也被业务模块 C 使用,则继续追加 C 模块 tag。
- 通用模块 tag 表示接口能力和实现抽象归属;业务模块 tag 表示该接口应出现在对应业务文档视角里。
- 如果接口只是业务模块自己的状态查询或状态操作,不属于通用模块能力,则只声明业务模块 tag,不要为了实现位置或相邻目录误加通用模块 tag。
- 示例:`协作者管理` 是通用权限能力,应用协作者接口需要同时声明 `协作者管理` 和应用侧 `权限管理``获取应用权限``恢复应用继承权限` 是应用自身权限状态接口,只声明应用侧 `权限管理`
**Schema 定义规范**: **Schema 定义规范**:
#### ✅ 字段定义规范 #### ✅ 字段定义规范
......
...@@ -15,3 +15,5 @@ description: 'FastGPT V4.15.0-beta4 更新说明' ...@@ -15,3 +15,5 @@ description: 'FastGPT V4.15.0-beta4 更新说明'
## 🐛 修复 ## 🐛 修复
## 🛠️ 代码优化 ## 🛠️ 代码优化
1. 将 app API 接口全部用 zod schema 编写并生成文档。
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
"content/guide/build/general/ai_settings.en.mdx": "2026-05-08T18:08:04+08:00", "content/guide/build/general/ai_settings.en.mdx": "2026-05-08T18:08:04+08:00",
"content/guide/build/general/ai_settings.mdx": "2026-05-08T18:08:04+08:00", "content/guide/build/general/ai_settings.mdx": "2026-05-08T18:08:04+08:00",
"content/guide/build/general/chat_input_guide.en.mdx": "2026-05-07T15:06:40+08:00", "content/guide/build/general/chat_input_guide.en.mdx": "2026-05-07T15:06:40+08:00",
"content/guide/build/general/chat_input_guide.mdx": "2026-05-07T15:06:40+08:00", "content/guide/build/general/chat_input_guide.mdx": "2026-06-01T17:19:55+08:00",
"content/guide/build/general/fileInput.en.mdx": "2026-05-07T15:06:40+08:00", "content/guide/build/general/fileInput.en.mdx": "2026-05-07T15:06:40+08:00",
"content/guide/build/general/fileInput.mdx": "2026-05-07T15:06:40+08:00", "content/guide/build/general/fileInput.mdx": "2026-05-07T15:06:40+08:00",
"content/guide/build/publish/dingtalk.en.mdx": "2026-05-07T15:06:40+08:00", "content/guide/build/publish/dingtalk.en.mdx": "2026-05-07T15:06:40+08:00",
...@@ -282,6 +282,8 @@ ...@@ -282,6 +282,8 @@
"content/self-host/upgrading/4-15/41502.mdx": "2026-05-25T11:21:30+08:00", "content/self-host/upgrading/4-15/41502.mdx": "2026-05-25T11:21:30+08:00",
"content/self-host/upgrading/4-15/41503.en.mdx": "2026-05-28T16:21:09+08:00", "content/self-host/upgrading/4-15/41503.en.mdx": "2026-05-28T16:21:09+08:00",
"content/self-host/upgrading/4-15/41503.mdx": "2026-05-28T16:21:09+08:00", "content/self-host/upgrading/4-15/41503.mdx": "2026-05-28T16:21:09+08:00",
"content/self-host/upgrading/4-15/41504.en.mdx": "2026-06-01T17:19:55+08:00",
"content/self-host/upgrading/4-15/41504.mdx": "2026-06-01T17:19:55+08:00",
"content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00",
...@@ -422,6 +424,6 @@ ...@@ -422,6 +424,6 @@
"content/self-host/upgrading/outdated/499.mdx": "2026-05-07T15:06:40+08:00", "content/self-host/upgrading/outdated/499.mdx": "2026-05-07T15:06:40+08:00",
"content/self-host/upgrading/upgrade-intruction.en.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/upgrade-intruction.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/upgrade-intruction.mdx": "2026-04-26T21:08:47+08:00", "content/self-host/upgrading/upgrade-intruction.mdx": "2026-04-26T21:08:47+08:00",
"content/toc.en.mdx": "2026-05-25T11:21:30+08:00", "content/toc.en.mdx": "2026-06-01T17:19:55+08:00",
"content/toc.mdx": "2026-05-25T11:21:30+08:00" "content/toc.mdx": "2026-06-01T17:19:55+08:00"
} }
\ No newline at end of file
...@@ -2,8 +2,12 @@ import z from 'zod'; ...@@ -2,8 +2,12 @@ import z from 'zod';
export const SecretValueTypeSchema = z export const SecretValueTypeSchema = z
.object({ .object({
value: z.string().optional(), value: z.string().optional().meta({
secret: z.string().optional() description: '密钥明文值;提交明文时会优先使用该值'
}),
secret: z.string().optional().meta({
description: '已加密或脱敏后的密钥值'
})
}) })
.meta({ .meta({
description: '密钥值类型,value 为明文,secret 为密文,如果了 value 则优先使用 value。', description: '密钥值类型,value 为明文,secret 为密文,如果了 value 则优先使用 value。',
......
...@@ -2,10 +2,17 @@ import { ObjectIdSchema } from '../../../common/type/mongo'; ...@@ -2,10 +2,17 @@ import { ObjectIdSchema } from '../../../common/type/mongo';
import { ChatSourceEnum } from '../../chat/constants'; import { ChatSourceEnum } from '../../chat/constants';
import { AppLogKeysEnum } from './constants'; import { AppLogKeysEnum } from './constants';
import z from 'zod'; import z from 'zod';
import { BoolSchema, NumSchema } from '../../../common/zod';
export const AppLogKeysSchema = z.object({ export const AppLogKeysSchema = z.object({
key: z.enum(AppLogKeysEnum), key: z.enum(AppLogKeysEnum).meta({
enable: z.boolean() example: AppLogKeysEnum.SOURCE,
description: '日志列标识,对应导出和列表中可展示的日志字段'
}),
enable: BoolSchema.meta({
example: true,
description: '该日志列是否在当前应用中启用'
})
}); });
export type AppLogKeysType = z.infer<typeof AppLogKeysSchema>; export type AppLogKeysType = z.infer<typeof AppLogKeysSchema>;
...@@ -27,13 +34,13 @@ export const AppChatLogSchema = z.object({ ...@@ -27,13 +34,13 @@ export const AppChatLogSchema = z.object({
createTime: z.coerce.date(), createTime: z.coerce.date(),
updateTime: z.coerce.date(), updateTime: z.coerce.date(),
chatItemCount: z.number(), chatItemCount: NumSchema,
errorCount: z.number(), errorCount: NumSchema,
totalPoints: z.number(), totalPoints: NumSchema,
goodFeedbackCount: z.number(), goodFeedbackCount: NumSchema,
badFeedbackCount: z.number(), badFeedbackCount: NumSchema,
totalResponseTime: z.number(), totalResponseTime: NumSchema,
isFirstChat: z.boolean() // whether this is the user's first session in the app isFirstChat: BoolSchema // whether this is the user's first session in the app
}); });
export type AppChatLogSchema = z.infer<typeof AppChatLogSchema>; export type AppChatLogSchema = z.infer<typeof AppChatLogSchema>;
...@@ -4,10 +4,18 @@ import { ContentTypes } from '../../../workflow/constants'; ...@@ -4,10 +4,18 @@ import { ContentTypes } from '../../../workflow/constants';
import z from 'zod'; import z from 'zod';
const PathDataTypeSchema = z.object({ const PathDataTypeSchema = z.object({
name: z.string(), name: z.string().meta({
description: z.string(), description: '接口名称'
method: z.string(), }),
path: z.string(), description: z.string().meta({
description: '接口能力说明'
}),
method: z.string().meta({
description: '接口 HTTP 方法'
}),
path: z.string().meta({
description: '接口路径'
}),
params: z.array(z.any()), params: z.array(z.any()),
request: z.any(), request: z.any(),
response: z.any() response: z.any()
...@@ -21,24 +29,105 @@ export const OpenApiJsonSchemaSchema = z.object({ ...@@ -21,24 +29,105 @@ export const OpenApiJsonSchemaSchema = z.object({
export type OpenApiJsonSchema = z.infer<typeof OpenApiJsonSchemaSchema>; export type OpenApiJsonSchema = z.infer<typeof OpenApiJsonSchemaSchema>;
export const HttpToolConfigTypeSchema = z.object({ export const HttpToolConfigTypeSchema = z.object({
name: z.string(), name: z.string().meta({
description: z.string(), example: 'search',
inputSchema: JSONSchemaInputTypeSchema.optional(), description: 'HTTP 工具名称,用于工作流节点选择和调用'
outputSchema: JSONSchemaOutputTypeSchema.optional(), }),
path: z.string(), description: z.string().meta({
method: z.string(), example: 'Search public documents',
requestSchema: JSONSchemaInputTypeSchema.optional(), description: 'HTTP 工具能力说明,会作为调用工具时的语义描述'
}),
inputSchema: JSONSchemaInputTypeSchema.optional().meta({
description: 'HTTP 工具入参 JSON Schema,由 OpenAPI 参数或手动配置生成'
}),
outputSchema: JSONSchemaOutputTypeSchema.optional().meta({
description: 'HTTP 工具出参 JSON Schema,用于描述远端接口返回结构'
}),
path: z.string().meta({
example: '/search',
description: 'HTTP 工具请求路径'
}),
method: z.string().meta({
example: 'POST',
description: 'HTTP 工具请求方法'
}),
requestSchema: JSONSchemaInputTypeSchema.optional().meta({
description: 'HTTP 工具原始请求结构 JSON Schema'
}),
// manual // manual
staticParams: z.array(z.object({ key: z.string(), value: z.string() })).optional(), staticParams: z
staticHeaders: z.array(z.object({ key: z.string(), value: z.string() })).optional(), .array(
z
.object({
key: z.string().meta({
description: '固定参数名称'
}),
value: z.string().meta({
description: '固定参数值'
})
})
.meta({
description: 'HTTP 工具固定 Query 参数项'
})
)
.optional()
.meta({
description: '手动配置工具时固定追加到 Query 的参数'
}),
staticHeaders: z
.array(
z
.object({
key: z.string().meta({
description: '固定请求头名称'
}),
value: z.string().meta({
description: '固定请求头值'
})
})
.meta({
description: 'HTTP 工具固定请求头项'
})
)
.optional()
.meta({
description: '手动配置工具时固定追加到请求头的参数'
}),
staticBody: z staticBody: z
.object({ .object({
type: z.enum(ContentTypes), type: z.enum(ContentTypes).meta({
content: z.string().optional(), description: '静态请求体内容类型'
formData: z.array(z.object({ key: z.string(), value: z.string() })).optional() }),
content: z.string().optional().meta({
description: '静态请求体文本内容,适用于 JSON、XML 或 Raw 文本'
}),
formData: z
.array(
z
.object({
key: z.string().meta({
description: '表单字段名称'
}),
value: z.string().meta({
description: '表单字段值'
})
})
.meta({
description: 'HTTP 工具固定表单字段项'
})
)
.optional()
.meta({
description: '表单类型请求体的固定字段列表'
})
}) })
.optional(), .optional()
headerSecret: StoreSecretValueTypeSchema.nullish() .meta({
description: '手动配置工具时固定发送的请求体'
}),
headerSecret: StoreSecretValueTypeSchema.nullish().meta({
description: 'HTTP 工具请求头密钥配置'
})
}); });
export type HttpToolConfigType = z.infer<typeof HttpToolConfigTypeSchema>; export type HttpToolConfigType = z.infer<typeof HttpToolConfigTypeSchema>;
...@@ -3,9 +3,17 @@ import { JSONSchemaInputTypeSchema } from '../../jsonschema'; ...@@ -3,9 +3,17 @@ import { JSONSchemaInputTypeSchema } from '../../jsonschema';
import z from 'zod'; import z from 'zod';
export const McpToolConfigSchema = z.object({ export const McpToolConfigSchema = z.object({
name: z.string(), name: z.string().meta({
description: z.string(), example: 'search',
inputSchema: JSONSchemaInputTypeSchema.optional() description: 'MCP 工具名称,用于工作流节点选择和远端调用'
}),
description: z.string().meta({
example: 'Search tool',
description: 'MCP 工具能力说明,会用于工具选择和调用提示'
}),
inputSchema: JSONSchemaInputTypeSchema.optional().meta({
description: 'MCP 工具入参 JSON Schema'
})
}); });
export type McpToolConfigType = z.infer<typeof McpToolConfigSchema>; export type McpToolConfigType = z.infer<typeof McpToolConfigSchema>;
......
import z from 'zod'; import z from 'zod';
import { BoolSchema, IntSchema } from '../../../common/zod';
/* app chat file select config type */ /* app chat file select config type */
export const AppFileSelectConfigTypeSchema = z.object({ export const AppFileSelectConfigTypeSchema = z.object({
maxFiles: z.number().optional(), maxFiles: IntSchema.optional().meta({
canSelectFile: z.boolean().optional(), description: '单次对话允许选择的最大文件数量'
customPdfParse: z.boolean().optional(), }),
canSelectImg: z.boolean().optional(), canSelectFile: BoolSchema.optional().meta({
canSelectVideo: z.boolean().optional(), description: '是否允许在对话中选择普通文件'
canSelectAudio: z.boolean().optional(), }),
canSelectCustomFileExtension: z.boolean().optional(), customPdfParse: BoolSchema.optional().meta({
customFileExtensionList: z.array(z.string()).optional() description: '是否使用自定义 PDF 解析配置'
}),
canSelectImg: BoolSchema.optional().meta({
description: '是否允许在对话中选择图片'
}),
canSelectVideo: BoolSchema.optional().meta({
description: '是否允许在对话中选择视频'
}),
canSelectAudio: BoolSchema.optional().meta({
description: '是否允许在对话中选择音频'
}),
canSelectCustomFileExtension: BoolSchema.optional().meta({
description: '是否允许选择自定义扩展名的文件'
}),
customFileExtensionList: z.array(z.string()).optional().meta({
description: '允许选择的自定义文件扩展名列表'
})
}); });
export type AppFileSelectConfigType = z.infer<typeof AppFileSelectConfigTypeSchema>; export type AppFileSelectConfigType = z.infer<typeof AppFileSelectConfigTypeSchema>;
...@@ -169,17 +169,21 @@ export const DatasetDataIndexItemSchema = z.object({ ...@@ -169,17 +169,21 @@ export const DatasetDataIndexItemSchema = z.object({
.enum(DatasetDataIndexTypeEnum) .enum(DatasetDataIndexTypeEnum)
.optional() .optional()
.default(DatasetDataIndexTypeEnum.custom) .default(DatasetDataIndexTypeEnum.custom)
.meta({ description: '索引类型' }), .meta({ description: '索引类型, 外部接口不需要传,都 custom,其他 type 是系统自动生成的' }),
dataId: z.string().meta({ description: 'vectorDB ID' }), dataId: z
.string()
.meta({ description: '向量库里数据 ID(可选),可用于相同索引不重复创建,节省一次向量更新' }),
text: z.string().meta({ text: z.string().meta({
description: `默认就是索引的文本内容,特殊的: description: `默认就是索引的文本内容,特殊的:
imageEmbedding - 图片的 objectKey/url` imageEmbedding - 图片的 objectKey/url`
}) })
}); });
const DatasetDataIndexOptionalSchema = DatasetDataIndexItemSchema.omit({ dataId: true }).extend({ const DatasetDataIndexOptionalSchema = DatasetDataIndexItemSchema.omit({
dataId: true
}).extend({
dataId: z.string().optional().meta({ dataId: z.string().optional().meta({
example: '68ad85a7463006c963799a05', example: '68ad85a7463006c963799a05',
description: 'PG 数据 ID(可选)' description: '向量库里数据 ID(可选),可用于相同索引不重复创建,节省一次向量更新'
}) })
}); });
export type DatasetDataIndexItemType = z.infer<typeof DatasetDataIndexItemSchema>; export type DatasetDataIndexItemType = z.infer<typeof DatasetDataIndexItemSchema>;
......
import z from 'zod'; import z from 'zod';
export const StoreEdgeItemTypeSchema = z.object({ export const StoreEdgeItemTypeSchema = z.object({
source: z.string(), source: z.string().meta({
sourceHandle: z.string(), description: '连线起点节点 ID'
target: z.string(), }),
targetHandle: z.string() sourceHandle: z.string().meta({
description: '连线起点输出桩 ID'
}),
target: z.string().meta({
description: '连线终点节点 ID'
}),
targetHandle: z.string().meta({
description: '连线终点输入桩 ID'
})
}); });
export type StoreEdgeItemType = z.infer<typeof StoreEdgeItemTypeSchema>; export type StoreEdgeItemType = z.infer<typeof StoreEdgeItemTypeSchema>;
......
import type { OpenAPIPath } from '../../../type'; import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag'; import { TagsMap } from '../../../tag';
import { import {
CopyAppBodySchema,
CopyAppResponseSchema,
CreateAppBodySchema, CreateAppBodySchema,
CreateAppResponseSchema, CreateAppResponseSchema,
GetAppPermissionQuerySchema, DeleteAppQuerySchema,
GetAppPermissionResponseSchema DeleteAppResponseSchema,
GetAppBasicInfoBodySchema,
GetAppBasicInfoResponseSchema,
GetAppDetailQuerySchema,
GetAppDetailResponseSchema,
ListAppBodySchema,
ListAppResponseSchema,
TransitionWorkflowBodySchema,
TransitionWorkflowResponseSchema,
UpdateAppBodySchema,
UpdateAppQuerySchema,
UpdateAppResponseSchema
} from './api'; } from './api';
export const AppCommonPath: OpenAPIPath = { export const AppCommonPath: OpenAPIPath = {
'/core/app/list': {
post: {
summary: '获取应用列表',
description: '获取当前团队下当前用户可读的应用或文件夹列表',
tags: [TagsMap.appCommon],
requestBody: {
content: {
'application/json': {
schema: ListAppBodySchema
}
}
},
responses: {
200: {
description: '成功获取应用列表',
content: {
'application/json': {
schema: ListAppResponseSchema
}
}
}
}
}
},
'/core/app/create': { '/core/app/create': {
post: { post: {
summary: '创建应用', summary: '创建应用',
...@@ -32,20 +69,139 @@ export const AppCommonPath: OpenAPIPath = { ...@@ -32,20 +69,139 @@ export const AppCommonPath: OpenAPIPath = {
} }
} }
}, },
'/core/app/getPermission': { '/core/app/detail': {
get: { get: {
summary: '获取应用权限', summary: '获取应用详情',
description: '根据应用 ID 获取当前用户对该应用的权限信息', description: '获取应用完整详情。无写权限时会隐藏编排节点和边',
tags: [TagsMap.appPer], tags: [TagsMap.appCommon],
requestParams: { requestParams: {
query: GetAppPermissionQuerySchema query: GetAppDetailQuerySchema
},
responses: {
200: {
description: '成功获取应用详情',
content: {
'application/json': {
schema: GetAppDetailResponseSchema
}
}
}
}
}
},
'/core/app/update': {
put: {
summary: '更新应用',
description: '更新应用基础信息、编排信息或移动应用位置',
tags: [TagsMap.appCommon],
requestParams: {
query: UpdateAppQuerySchema
},
requestBody: {
content: {
'application/json': {
schema: UpdateAppBodySchema
}
}
},
responses: {
200: {
description: '成功更新应用',
content: {
'application/json': {
schema: UpdateAppResponseSchema
}
}
}
}
}
},
'/core/app/del': {
delete: {
summary: '删除应用',
description: '删除应用或文件夹,并返回被删除的非文件夹应用 ID 列表',
tags: [TagsMap.appCommon],
requestParams: {
query: DeleteAppQuerySchema
},
responses: {
200: {
description: '成功删除应用',
content: {
'application/json': {
schema: DeleteAppResponseSchema
}
}
}
}
}
},
'/core/app/copy': {
post: {
summary: '复制应用',
description: '复制指定应用并返回新应用 ID',
tags: [TagsMap.appCommon],
requestBody: {
content: {
'application/json': {
schema: CopyAppBodySchema
}
}
},
responses: {
200: {
description: '成功复制应用',
content: {
'application/json': {
schema: CopyAppResponseSchema
}
}
}
}
}
},
'/core/app/getBasicInfo': {
post: {
summary: '批量获取应用基础信息',
description: '根据应用 ID 列表批量获取应用名称和头像',
tags: [TagsMap.appCommon],
requestBody: {
content: {
'application/json': {
schema: GetAppBasicInfoBodySchema
}
}
},
responses: {
200: {
description: '成功获取应用基础信息',
content: {
'application/json': {
schema: GetAppBasicInfoResponseSchema
}
}
}
}
}
},
'/core/app/transitionWorkflow': {
post: {
summary: '转换为工作流应用',
description: '将简易应用转换为工作流应用,可选择复制为新应用',
tags: [TagsMap.appCommon],
requestBody: {
content: {
'application/json': {
schema: TransitionWorkflowBodySchema
}
}
}, },
responses: { responses: {
200: { 200: {
description: '成功获取应用权限', description: '成功转换应用',
content: { content: {
'application/json': { 'application/json': {
schema: GetAppPermissionResponseSchema schema: TransitionWorkflowResponseSchema
} }
} }
} }
......
import z from 'zod';
import { GetPathPropsSchema, ParentIdSchema } from '../../../../common/parentFolder/type';
import { AppTypeEnum } from '../../../../core/app/constants';
/* ============================================================================
* API: 创建应用文件夹
* Route: POST /api/core/app/folder/create
* Method: POST
* Description: 在根目录或指定父级文件夹下创建应用文件夹或工具文件夹。
* Tags: ['文件夹管理']
* ============================================================================ */
export const CreateAppFolderBodySchema = z
.object({
parentId: ParentIdSchema.optional().meta({
example: '68ad85a7463006c963799a05',
description: '父级文件夹 ID,为空时创建在根目录'
}),
name: z.string().min(1).meta({
example: '默认文件夹',
description: '文件夹名称'
}),
intro: z.string().optional().meta({
example: '文件夹介绍',
description: '文件夹介绍'
}),
type: z.enum([AppTypeEnum.folder, AppTypeEnum.toolFolder]).meta({
example: AppTypeEnum.folder,
description: '文件夹类型'
})
})
.meta({
example: {
name: '默认文件夹',
type: AppTypeEnum.folder
}
});
export type CreateAppFolderBodyType = z.infer<typeof CreateAppFolderBodySchema>;
export const CreateAppFolderResponseSchema = z.undefined().meta({
description: '创建成功'
});
export type CreateAppFolderResponseType = z.infer<typeof CreateAppFolderResponseSchema>;
/* ============================================================================
* API: 获取应用文件夹路径
* Route: GET /api/core/app/folder/path
* Method: GET
* Description: 获取指定应用或文件夹的父级路径。
* Tags: ['文件夹管理']
* ============================================================================ */
export const GetAppFolderPathQuerySchema = GetPathPropsSchema.extend({
sourceId: ParentIdSchema.optional().meta({
example: '68ad85a7463006c963799a05',
description: '目标应用或文件夹 ID,为空时返回根路径'
}),
type: z.enum(['current', 'parent']).optional().meta({
example: 'current',
description: '路径查询方式:current 返回当前节点路径,parent 返回父级路径'
})
}).meta({
example: {
sourceId: '68ad85a7463006c963799a05',
type: 'current'
}
});
export type GetAppFolderPathQueryType = z.infer<typeof GetAppFolderPathQuerySchema>;
const AppFolderPathItemSchema = z
.object({
parentId: ParentIdSchema.meta({
description: '路径节点 ID;根目录节点为空'
}),
parentName: z.string().meta({
example: '默认文件夹',
description: '路径节点名称'
})
})
.meta({
description: '应用文件夹路径节点'
});
export const GetAppFolderPathResponseSchema = z.array(AppFolderPathItemSchema).meta({
description: '应用文件夹路径'
});
export type GetAppFolderPathResponseType = z.infer<typeof GetAppFolderPathResponseSchema>;
import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag';
import {
CreateAppFolderBodySchema,
CreateAppFolderResponseSchema,
GetAppFolderPathQuerySchema,
GetAppFolderPathResponseSchema
} from './api';
export const AppFolderPath: OpenAPIPath = {
'/core/app/folder/create': {
post: {
summary: '创建应用文件夹',
description: '在根目录或指定父级文件夹下创建应用文件夹或工具文件夹',
tags: [TagsMap.appFolder],
requestBody: {
content: {
'application/json': {
schema: CreateAppFolderBodySchema
}
}
},
responses: {
200: {
description: '成功创建应用文件夹',
content: {
'application/json': {
schema: CreateAppFolderResponseSchema
}
}
}
}
}
},
'/core/app/folder/path': {
get: {
summary: '获取应用文件夹路径',
description: '获取指定应用或文件夹的父级路径',
tags: [TagsMap.appFolder],
requestParams: {
query: GetAppFolderPathQuerySchema
},
responses: {
200: {
description: '成功获取应用文件夹路径',
content: {
'application/json': {
schema: GetAppFolderPathResponseSchema
}
}
}
}
}
}
};
import z from 'zod'; import z from 'zod';
import { ObjectIdSchema } from '../../../../common/type/mongo'; import { ObjectIdSchema } from '../../../../common/type/mongo';
import { StoreSecretValueTypeSchema } from '../../../../common/secret/type'; import { StoreSecretValueTypeSchema } from '../../../../common/secret/type';
import { CreateAppBodySchema, CreateAppResponseSchema } from '../common/api'; import { CreateAppBodySchema } from '../common/api';
import { HttpToolConfigTypeSchema } from '../../../../core/app/tool/httpTool/type'; import { HttpToolConfigTypeSchema } from '../../../../core/app/tool/httpTool/type';
import { HttpToolTypeEnum } from '../../../../core/app/tool/httpTool/constants'; import { HttpToolTypeEnum } from '../../../../core/app/tool/httpTool/constants';
...@@ -30,12 +30,9 @@ export const CreateHttpToolsBodySchema = CreateAppBodySchema.omit({ ...@@ -30,12 +30,9 @@ export const CreateHttpToolsBodySchema = CreateAppBodySchema.omit({
}); });
export type CreateHttpToolsBodyType = z.infer<typeof CreateHttpToolsBodySchema>; export type CreateHttpToolsBodyType = z.infer<typeof CreateHttpToolsBodySchema>;
export const CreateHttpToolsResponseSchema = CreateAppResponseSchema;
export type CreateHttpToolsResponseType = z.infer<typeof CreateHttpToolsResponseSchema>;
/* ============================================================================ /* ============================================================================
* API: 更新 HTTP 工具集 * API: 更新 HTTP 工具集
* Route: POST /core/app/httpTools/update * Route: PUT /core/app/httpTools/update
* ============================================================================ */ * ============================================================================ */
export const UpdateHttpToolsBodySchema = z export const UpdateHttpToolsBodySchema = z
.object({ .object({
...@@ -72,6 +69,11 @@ export const UpdateHttpToolsBodySchema = z ...@@ -72,6 +69,11 @@ export const UpdateHttpToolsBodySchema = z
}); });
export type UpdateHttpToolsBodyType = z.infer<typeof UpdateHttpToolsBodySchema>; export type UpdateHttpToolsBodyType = z.infer<typeof UpdateHttpToolsBodySchema>;
export const UpdateHttpToolsResponseSchema = z.undefined().meta({
description: '更新成功'
});
export type UpdateHttpToolsResponseType = z.infer<typeof UpdateHttpToolsResponseSchema>;
/* ============================================================================ /* ============================================================================
* API: 通过 URL 解析 OpenAPI Schema * API: 通过 URL 解析 OpenAPI Schema
* Route: POST /core/app/httpTools/getApiSchemaByUrl * Route: POST /core/app/httpTools/getApiSchemaByUrl
...@@ -113,14 +115,17 @@ export const RunHttpToolBodySchema = z ...@@ -113,14 +115,17 @@ export const RunHttpToolBodySchema = z
example: '/search', example: '/search',
description: '工具路径' description: '工具路径'
}), }),
method: z.string().meta({ method: z.string().optional().default('POST').meta({
example: 'POST', example: 'POST',
description: 'HTTP 请求方法' description: 'HTTP 请求方法;未传时默认 POST'
}),
customHeaders: z.record(z.string(), z.string()).optional().meta({
example: {},
description: '自定义请求头'
}), }),
customHeaders: z
.record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
.optional()
.meta({
example: {},
description: '自定义请求头'
}),
headerSecret: StoreSecretValueTypeSchema.optional().meta({ headerSecret: StoreSecretValueTypeSchema.optional().meta({
example: { Authorization: { value: 'token' } }, example: { Authorization: { value: 'token' } },
description: '请求头密钥' description: '请求头密钥'
......
import type { OpenAPIPath } from '../../../type'; import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag'; import { TagsMap } from '../../../tag';
import { CreateAppResponseSchema } from '../common/api';
import { import {
CreateHttpToolsBodySchema, CreateHttpToolsBodySchema,
CreateHttpToolsResponseSchema,
UpdateHttpToolsBodySchema, UpdateHttpToolsBodySchema,
UpdateHttpToolsResponseSchema,
GetApiSchemaByUrlBodySchema, GetApiSchemaByUrlBodySchema,
GetApiSchemaByUrlResponseSchema, GetApiSchemaByUrlResponseSchema,
RunHttpToolBodySchema, RunHttpToolBodySchema,
...@@ -28,7 +29,7 @@ export const HttpToolsPath: OpenAPIPath = { ...@@ -28,7 +29,7 @@ export const HttpToolsPath: OpenAPIPath = {
description: '成功创建 HTTP 工具集', description: '成功创建 HTTP 工具集',
content: { content: {
'application/json': { 'application/json': {
schema: CreateHttpToolsResponseSchema schema: CreateAppResponseSchema
} }
} }
} }
...@@ -36,7 +37,7 @@ export const HttpToolsPath: OpenAPIPath = { ...@@ -36,7 +37,7 @@ export const HttpToolsPath: OpenAPIPath = {
} }
}, },
'/core/app/httpTools/update': { '/core/app/httpTools/update': {
post: { put: {
summary: '更新 HTTP 工具集', summary: '更新 HTTP 工具集',
description: '更新 HTTP 工具集配置', description: '更新 HTTP 工具集配置',
tags: [TagsMap.httpTools], tags: [TagsMap.httpTools],
...@@ -49,7 +50,12 @@ export const HttpToolsPath: OpenAPIPath = { ...@@ -49,7 +50,12 @@ export const HttpToolsPath: OpenAPIPath = {
}, },
responses: { responses: {
200: { 200: {
description: '成功更新 HTTP 工具集' description: '成功更新 HTTP 工具集',
content: {
'application/json': {
schema: UpdateHttpToolsResponseSchema
}
}
} }
} }
} }
......
...@@ -4,11 +4,19 @@ import { PublishChannelPath } from './publishChannel'; ...@@ -4,11 +4,19 @@ import { PublishChannelPath } from './publishChannel';
import { AppCommonPath } from './common'; import { AppCommonPath } from './common';
import { McpToolsPath } from './mcpTools'; import { McpToolsPath } from './mcpTools';
import { HttpToolsPath } from './httpTools'; import { HttpToolsPath } from './httpTools';
import { AppFolderPath } from './folder';
import { AppVersionPath } from './version';
import { AppTemplatePath } from './template';
import { AppPermissionPath } from './permission';
export const AppPath: OpenAPIPath = { export const AppPath: OpenAPIPath = {
...AppCommonPath,
...AppFolderPath,
...AppPermissionPath,
...AppVersionPath,
...AppTemplatePath,
...AppLogPath, ...AppLogPath,
...PublishChannelPath, ...PublishChannelPath,
...AppCommonPath,
...McpToolsPath, ...McpToolsPath,
...HttpToolsPath ...HttpToolsPath
}; };
import type { OpenAPIPath } from '../../../type'; import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag'; import { TagsMap } from '../../../tag';
import { ApiKeyTagMap } from '../../../apikey/tag'; import { ApiKeyTagMap } from '../../../apikey/tag';
import z from 'zod';
import { import {
GetAppChatLogsBodySchema, GetAppChatLogsBodySchema,
GetAppChatLogsResponseSchema, GetAppChatLogsResponseSchema,
ExportChatLogsBodySchema, ExportChatLogsBodySchema,
ExportChatLogsResponseSchema,
GetChartDataBodySchema, GetChartDataBodySchema,
GetChartDataResponseSchema, GetChartDataResponseSchema,
GetTotalDataQuerySchema, GetTotalDataQuerySchema,
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
GetLogKeysQuerySchema, GetLogKeysQuerySchema,
GetLogKeysResponseSchema, GetLogKeysResponseSchema,
UpdateLogKeysBodySchema, UpdateLogKeysBodySchema,
UpdateLogKeysResponseSchema,
GetLogUsersBodySchema, GetLogUsersBodySchema,
GetLogUsersResponseSchema GetLogUsersResponseSchema
} from './api'; } from './api';
...@@ -39,7 +40,7 @@ export const AppLogPath: OpenAPIPath = { ...@@ -39,7 +40,7 @@ export const AppLogPath: OpenAPIPath = {
} }
}, },
'/core/app/logs/updateLogKeys': { '/core/app/logs/updateLogKeys': {
post: { put: {
summary: '更新应用日志键', summary: '更新应用日志键',
description: '更新应用的日志键列表', description: '更新应用的日志键列表',
tags: [TagsMap.appLog], tags: [TagsMap.appLog],
...@@ -55,7 +56,7 @@ export const AppLogPath: OpenAPIPath = { ...@@ -55,7 +56,7 @@ export const AppLogPath: OpenAPIPath = {
description: '成功更新应用日志键', description: '成功更新应用日志键',
content: { content: {
'application/json': { 'application/json': {
schema: z.object({}) schema: UpdateLogKeysResponseSchema
} }
} }
} }
...@@ -103,7 +104,7 @@ export const AppLogPath: OpenAPIPath = { ...@@ -103,7 +104,7 @@ export const AppLogPath: OpenAPIPath = {
description: '成功导出应用日志,返回 CSV 文件', description: '成功导出应用日志,返回 CSV 文件',
content: { content: {
'text/csv': { 'text/csv': {
schema: z.string() schema: ExportChatLogsResponseSchema
} }
} }
} }
......
import z from 'zod'; import z from 'zod';
import { ObjectIdSchema } from '../../../../common/type/mongo'; import { ObjectIdSchema } from '../../../../common/type/mongo';
import { StoreSecretValueTypeSchema } from '../../../../common/secret/type'; import { StoreSecretValueTypeSchema } from '../../../../common/secret/type';
import { CreateAppBodySchema, CreateAppResponseSchema } from '../common/api'; import { CreateAppBodySchema } from '../common/api';
import { McpToolConfigSchema } from '../../../../core/app/tool/mcpTool/type'; import { McpToolConfigSchema } from '../../../../core/app/tool/mcpTool/type';
// Create mcp tool // Create mcp tool
...@@ -41,9 +41,6 @@ export const CreateMcpToolsBodySchema = CreateAppBodySchema.omit({ ...@@ -41,9 +41,6 @@ export const CreateMcpToolsBodySchema = CreateAppBodySchema.omit({
}); });
export type CreateMcpToolsBodyType = z.infer<typeof CreateMcpToolsBodySchema>; export type CreateMcpToolsBodyType = z.infer<typeof CreateMcpToolsBodySchema>;
export const CreateMcpToolsResponseSchema = CreateAppResponseSchema;
export type CreateMcpToolsResponseType = z.infer<typeof CreateMcpToolsResponseSchema>;
// Update mcp tool // Update mcp tool
export const UpdateMcpToolsBodySchema = z export const UpdateMcpToolsBodySchema = z
.object({ .object({
...@@ -65,6 +62,11 @@ export const UpdateMcpToolsBodySchema = z ...@@ -65,6 +62,11 @@ export const UpdateMcpToolsBodySchema = z
}); });
export type UpdateMcpToolsBodyType = z.infer<typeof UpdateMcpToolsBodySchema>; export type UpdateMcpToolsBodyType = z.infer<typeof UpdateMcpToolsBodySchema>;
export const UpdateMcpToolsResponseSchema = z.undefined().meta({
description: '更新成功'
});
export type UpdateMcpToolsResponseType = z.infer<typeof UpdateMcpToolsResponseSchema>;
// Get mcp children // Get mcp children
export const GetMcpChildrenQuerySchema = z.object({ export const GetMcpChildrenQuerySchema = z.object({
id: ObjectIdSchema.meta({ id: ObjectIdSchema.meta({
......
import type { OpenAPIPath } from '../../../type'; import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag'; import { TagsMap } from '../../../tag';
import { CreateAppResponseSchema } from '../common/api';
import { import {
CreateMcpToolsBodySchema, CreateMcpToolsBodySchema,
CreateMcpToolsResponseSchema,
GetMcpChildrenQuerySchema, GetMcpChildrenQuerySchema,
GetMcpChildrenResponseSchema, GetMcpChildrenResponseSchema,
UpdateMcpToolsBodySchema, UpdateMcpToolsBodySchema,
UpdateMcpToolsResponseSchema,
GetMcpToolsBodySchema, GetMcpToolsBodySchema,
GetMcpToolsResponseSchema, GetMcpToolsResponseSchema,
RunMcpToolBodySchema, RunMcpToolBodySchema,
...@@ -78,7 +79,7 @@ export const McpToolsPath: OpenAPIPath = { ...@@ -78,7 +79,7 @@ export const McpToolsPath: OpenAPIPath = {
description: '成功创建 MCP 工具集', description: '成功创建 MCP 工具集',
content: { content: {
'application/json': { 'application/json': {
schema: CreateMcpToolsResponseSchema schema: CreateAppResponseSchema
} }
} }
} }
...@@ -86,7 +87,7 @@ export const McpToolsPath: OpenAPIPath = { ...@@ -86,7 +87,7 @@ export const McpToolsPath: OpenAPIPath = {
} }
}, },
'/core/app/mcpTools/update': { '/core/app/mcpTools/update': {
post: { put: {
summary: '更新 MCP 工具集', summary: '更新 MCP 工具集',
description: '更新 MCP 工具集配置', description: '更新 MCP 工具集配置',
tags: [TagsMap.mcpTools], tags: [TagsMap.mcpTools],
...@@ -99,7 +100,12 @@ export const McpToolsPath: OpenAPIPath = { ...@@ -99,7 +100,12 @@ export const McpToolsPath: OpenAPIPath = {
}, },
responses: { responses: {
200: { 200: {
description: '成功更新 MCP 工具集' description: '成功更新 MCP 工具集',
content: {
'application/json': {
schema: UpdateMcpToolsResponseSchema
}
}
} }
} }
} }
......
import z from 'zod';
import { ObjectIdSchema } from '../../../../common/type/mongo';
/* ============================================================================
* API: 获取应用权限
* Route: GET /api/core/app/getPermission
* Method: GET
* Description: 根据应用 ID 获取当前用户对该应用的权限信息。
* Tags: ['权限管理']
* ============================================================================ */
export const GetAppPermissionQuerySchema = z.object({
appId: ObjectIdSchema.meta({
example: '68ad85a7463006c963799a05',
description: '应用 ID'
})
});
export type GetAppPermissionQueryType = z.infer<typeof GetAppPermissionQuerySchema>;
/* ============================================================================
* API: 恢复继承权限
* Route: PUT /api/core/app/resumeInheritPermission
* Method: PUT
* Description: 恢复指定应用的继承权限配置。
* Tags: ['权限管理']
* ============================================================================ */
export const ResumeInheritPermissionQuerySchema = z.object({
appId: ObjectIdSchema.meta({
example: '68ad85a7463006c963799a05',
description: '应用 ID'
})
});
export type ResumeInheritPermissionQueryType = z.infer<typeof ResumeInheritPermissionQuerySchema>;
export const ResumeInheritPermissionBodySchema = z.object({}).meta({
description: '恢复继承权限不需要请求体'
});
export type ResumeInheritPermissionBodyType = z.infer<typeof ResumeInheritPermissionBodySchema>;
export const ResumeInheritPermissionResponseSchema = z.undefined().meta({
description: '恢复成功'
});
export type ResumeInheritPermissionResponseType = z.infer<
typeof ResumeInheritPermissionResponseSchema
>;
import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag';
import { AppPermissionCheckSchema } from '../../../../support/permission/app/controller.schema';
import {
GetAppPermissionQuerySchema,
ResumeInheritPermissionQuerySchema,
ResumeInheritPermissionResponseSchema
} from './api';
export const AppPermissionPath: OpenAPIPath = {
'/core/app/getPermission': {
get: {
summary: '获取应用权限',
description: '根据应用 ID 获取当前用户对该应用的权限信息',
tags: [TagsMap.appPer],
requestParams: {
query: GetAppPermissionQuerySchema
},
responses: {
200: {
description: '成功获取应用权限',
content: {
'application/json': {
schema: AppPermissionCheckSchema
}
}
}
}
}
},
'/core/app/resumeInheritPermission': {
put: {
summary: '恢复继承权限',
description: '恢复指定应用的继承权限配置',
tags: [TagsMap.appPer],
requestParams: {
query: ResumeInheritPermissionQuerySchema
},
responses: {
200: {
description: '成功恢复继承权限',
content: {
'application/json': {
schema: ResumeInheritPermissionResponseSchema
}
}
}
}
}
}
};
import z from 'zod'; import z from 'zod';
import { ObjectIdSchema } from '../../../../../common/type/mongo'; import { ObjectIdSchema } from '../../../../../common/type/mongo';
import { PlaygroundVisibilityConfigSchema } from '../../../../../support/outLink/type';
// Playground Visibility Config Fields
const PlaygroundVisibilityConfigFieldsSchema = z.object({
showRunningStatus: z.boolean().meta({
example: true,
description: '是否显示运行状态'
}),
showCite: z.boolean().meta({
example: true,
description: '是否显示引用'
}),
showFullText: z.boolean().meta({
example: true,
description: '是否显示全文'
}),
canDownloadSource: z.boolean().meta({
example: true,
description: '是否可下载来源'
})
});
// Get Playground Visibility Config Parameters // Get Playground Visibility Config Parameters
export const GetPlaygroundVisibilityConfigParamsSchema = z.object({ export const GetPlaygroundVisibilityConfigParamsSchema = z.object({
...@@ -32,12 +13,6 @@ export type GetPlaygroundVisibilityConfigParamsType = z.infer< ...@@ -32,12 +13,6 @@ export type GetPlaygroundVisibilityConfigParamsType = z.infer<
typeof GetPlaygroundVisibilityConfigParamsSchema typeof GetPlaygroundVisibilityConfigParamsSchema
>; >;
// Playground Visibility Config Response
export const PlaygroundVisibilityConfigResponseSchema = PlaygroundVisibilityConfigFieldsSchema;
export type PlaygroundVisibilityConfigResponseType = z.infer<
typeof PlaygroundVisibilityConfigResponseSchema
>;
// Update Playground Visibility Config Parameters // Update Playground Visibility Config Parameters
export const UpdatePlaygroundVisibilityConfigParamsSchema = z export const UpdatePlaygroundVisibilityConfigParamsSchema = z
.object({ .object({
...@@ -46,7 +21,12 @@ export const UpdatePlaygroundVisibilityConfigParamsSchema = z ...@@ -46,7 +21,12 @@ export const UpdatePlaygroundVisibilityConfigParamsSchema = z
description: '应用 ID' description: '应用 ID'
}) })
}) })
.extend(PlaygroundVisibilityConfigFieldsSchema.shape); .extend(PlaygroundVisibilityConfigSchema.shape);
export type UpdatePlaygroundVisibilityConfigParamsType = z.infer< export type UpdatePlaygroundVisibilityConfigParamsType = z.infer<
typeof UpdatePlaygroundVisibilityConfigParamsSchema typeof UpdatePlaygroundVisibilityConfigParamsSchema
>; >;
export const PlaygroundUpdateResponseSchema = z.undefined().meta({
description: '更新成功'
});
export type PlaygroundUpdateResponseType = z.infer<typeof PlaygroundUpdateResponseSchema>;
import z from 'zod';
import type { OpenAPIPath } from '../../../../type'; import type { OpenAPIPath } from '../../../../type';
import { import {
GetPlaygroundVisibilityConfigParamsSchema, GetPlaygroundVisibilityConfigParamsSchema,
PlaygroundVisibilityConfigResponseSchema, UpdatePlaygroundVisibilityConfigParamsSchema,
UpdatePlaygroundVisibilityConfigParamsSchema PlaygroundUpdateResponseSchema
} from './api'; } from './api';
import { TagsMap } from '../../../../tag'; import { TagsMap } from '../../../../tag';
import { PlaygroundVisibilityConfigSchema } from '../../../../../support/outLink/type';
import z from 'zod';
export const PlaygroundPath: OpenAPIPath = { export const PlaygroundPath: OpenAPIPath = {
'/api/support/outLink/playground/config': { '/support/outLink/playground/config': {
get: { get: {
summary: '获取门户配置', summary: '获取门户配置',
description: description:
...@@ -22,7 +23,7 @@ export const PlaygroundPath: OpenAPIPath = { ...@@ -22,7 +23,7 @@ export const PlaygroundPath: OpenAPIPath = {
description: '成功返回门户配置', description: '成功返回门户配置',
content: { content: {
'application/json': { 'application/json': {
schema: PlaygroundVisibilityConfigResponseSchema schema: PlaygroundVisibilityConfigSchema
} }
} }
}, },
...@@ -30,12 +31,7 @@ export const PlaygroundPath: OpenAPIPath = { ...@@ -30,12 +31,7 @@ export const PlaygroundPath: OpenAPIPath = {
description: '请求参数错误', description: '请求参数错误',
content: { content: {
'application/json': { 'application/json': {
schema: z.object({ schema: z.null()
code: z.literal(500),
statusText: z.literal('Invalid Params'),
message: z.string(),
data: z.null()
})
} }
} }
}, },
...@@ -43,20 +39,15 @@ export const PlaygroundPath: OpenAPIPath = { ...@@ -43,20 +39,15 @@ export const PlaygroundPath: OpenAPIPath = {
description: '用户未授权', description: '用户未授权',
content: { content: {
'application/json': { 'application/json': {
schema: z.object({ schema: z.null()
code: z.literal(401),
statusText: z.literal('unAuthorization'),
message: z.string(),
data: z.null()
})
} }
} }
} }
} }
} }
}, },
'/api/support/outLink/playground/update': { '/support/outLink/playground/update': {
post: { put: {
summary: '更新门户配置', summary: '更新门户配置',
description: description:
'更新指定应用的门户聊天界面的可见性配置,包括节点状态、响应详情、全文显示和原始来源显示的设置。如果配置不存在则创建新配置', '更新指定应用的门户聊天界面的可见性配置,包括节点状态、响应详情、全文显示和原始来源显示的设置。如果配置不存在则创建新配置',
...@@ -73,7 +64,7 @@ export const PlaygroundPath: OpenAPIPath = { ...@@ -73,7 +64,7 @@ export const PlaygroundPath: OpenAPIPath = {
description: '成功更新门户配置', description: '成功更新门户配置',
content: { content: {
'application/json': { 'application/json': {
schema: z.null() schema: PlaygroundUpdateResponseSchema
} }
} }
}, },
...@@ -81,12 +72,7 @@ export const PlaygroundPath: OpenAPIPath = { ...@@ -81,12 +72,7 @@ export const PlaygroundPath: OpenAPIPath = {
description: '请求参数错误', description: '请求参数错误',
content: { content: {
'application/json': { 'application/json': {
schema: z.object({ schema: z.null()
code: z.literal(500),
statusText: z.literal('Invalid Params'),
message: z.string(),
data: z.null()
})
} }
} }
}, },
...@@ -94,12 +80,7 @@ export const PlaygroundPath: OpenAPIPath = { ...@@ -94,12 +80,7 @@ export const PlaygroundPath: OpenAPIPath = {
description: '用户未授权', description: '用户未授权',
content: { content: {
'application/json': { 'application/json': {
schema: z.object({ schema: z.null()
code: z.literal(401),
statusText: z.literal('unAuthorization'),
message: z.string(),
data: z.null()
})
} }
} }
} }
......
import z from 'zod';
import { BoolSchema, NumSchema } from '../../../../common/zod';
import { AppTypeEnum } from '../../../../core/app/constants';
import { AppTemplateSchema } from '../../../../core/app/type';
/* ============================================================================
* API: 获取应用模板列表
* Route: GET /api/core/app/template/list
* Method: GET
* Description: 获取应用模板市场列表,列表项不返回完整 workflow 内容。
* Tags: ['模板管理']
* ============================================================================ */
export const ListAppTemplateQuerySchema = z.object({
isQuickTemplate: BoolSchema.optional().meta({
example: false,
description: '是否只查询快捷模板'
}),
randomNumber: NumSchema.optional().meta({
example: 6,
description: '随机返回数量'
}),
type: z
.union([z.enum(AppTypeEnum), z.literal('all')])
.optional()
.meta({
example: 'all',
description: '应用类型'
}),
excludeIds: z.string().optional().meta({
example: '["template-a","template-b"]',
description: '需要排除的模板 ID JSON 字符串'
})
});
export type ListAppTemplateQueryType = z.infer<typeof ListAppTemplateQuerySchema>;
export const AppTemplateListItemSchema = AppTemplateSchema.omit({
workflow: true
}).extend({
workflow: z.record(z.string(), z.never()).meta({
description: '列表接口不返回完整 workflow,固定为空对象'
})
});
export type AppTemplateListItemType = z.infer<typeof AppTemplateListItemSchema>;
export const ListAppTemplateResponseSchema = z.object({
list: z.array(AppTemplateListItemSchema).meta({
description: '模板列表'
}),
total: NumSchema.meta({
example: 100,
description: '模板总数'
})
});
export type ListAppTemplateResponseType = z.infer<typeof ListAppTemplateResponseSchema>;
/* ============================================================================
* API: 获取应用模板详情
* Route: GET /api/core/app/template/detail
* Method: GET
* Description: 获取应用模板详情。
* Tags: ['模板管理']
* ============================================================================ */
export const GetAppTemplateDetailQuerySchema = z.object({
templateId: z.string().min(1).meta({
example: 'template-simple-chat',
description: '模板 ID'
})
});
export type GetAppTemplateDetailQueryType = z.infer<typeof GetAppTemplateDetailQuerySchema>;
export const GetAppTemplateDetailResponseSchema = AppTemplateSchema.optional().meta({
description: '模板详情;未找到模板时为空'
});
export type GetAppTemplateDetailResponseType = z.infer<typeof GetAppTemplateDetailResponseSchema>;
import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag';
import {
GetAppTemplateDetailQuerySchema,
GetAppTemplateDetailResponseSchema,
ListAppTemplateQuerySchema,
ListAppTemplateResponseSchema
} from './api';
export const AppTemplatePath: OpenAPIPath = {
'/core/app/template/list': {
get: {
summary: '获取应用模板列表',
description: '获取应用模板市场列表,列表项不返回完整 workflow 内容',
tags: [TagsMap.appTemplate],
requestParams: {
query: ListAppTemplateQuerySchema
},
responses: {
200: {
description: '成功获取应用模板列表',
content: {
'application/json': {
schema: ListAppTemplateResponseSchema
}
}
}
}
}
},
'/core/app/template/detail': {
get: {
summary: '获取应用模板详情',
description: '获取应用模板详情',
tags: [TagsMap.appTemplate],
requestParams: {
query: GetAppTemplateDetailQuerySchema
},
responses: {
200: {
description: '成功获取应用模板详情',
content: {
'application/json': {
schema: GetAppTemplateDetailResponseSchema
}
}
}
}
}
}
};
import z from 'zod';
import { VersionListItemSchema } from '../../../../core/app/version/type';
import { ObjectIdSchema } from '../../../../common/type/mongo';
import { PaginationSchema } from '../../../api';
import { OpenAPIStoreNodeItemTypeSchema } from '../../workflow/node';
import { AppResourceRefsSchema, AppSchemaTypeSchema } from '../../../../core/app/type';
import { OpenAPIAppChatConfigSchema } from '../common/api';
import { BoolSchema, NumSchema } from '../../../../common/zod';
export const PublishAppQuerySchema = z.object({
appId: ObjectIdSchema.meta({
example: '68ad85a7463006c963799a05',
description: '应用 ID'
})
});
export type PublishAppQueryType = z.infer<typeof PublishAppQuerySchema>;
const AppVersionNodesSchema = z.array(OpenAPIStoreNodeItemTypeSchema).meta({
description: '版本内保存的应用节点配置'
});
const AppVersionEdgesSchema = AppSchemaTypeSchema.shape.edges.default([]).meta({
description: '版本内保存的应用连线配置'
});
const AppVersionChatConfigSchema = OpenAPIAppChatConfigSchema.default({}).meta({
description: '版本内保存的应用对话配置'
});
const AppVersionResourceRefsSchema = AppResourceRefsSchema.optional().meta({
description: '该版本引用的外部资源集合'
});
const OpenAPIVersionListItemSchema = VersionListItemSchema.extend({
_id: ObjectIdSchema.meta({
description: '版本记录 ID'
}),
appId: ObjectIdSchema.meta({
description: '版本所属应用 ID'
}),
versionName: z.string().meta({
example: '正式发布版',
description: '版本名称'
}),
time: z.coerce.date().meta({
description: '版本创建时间'
}),
isPublish: BoolSchema.optional().meta({
description: '是否为已发布版本'
}),
tmbId: ObjectIdSchema.meta({
description: '创建或更新该版本的团队成员 ID'
}),
sourceMember: VersionListItemSchema.shape.sourceMember.meta({
description: '创建或更新该版本的成员信息'
})
}).meta({
description: '应用版本列表项'
});
export const PublishAppBodySchema = z.object({
nodes: AppVersionNodesSchema.optional().meta({
description: '本次保存的应用节点配置;未传时按空节点保存'
}),
edges: AppVersionEdgesSchema.optional().meta({
description: '本次保存的应用连线配置;未传时按空连线保存'
}),
chatConfig: AppVersionChatConfigSchema.optional().meta({
description: '本次保存的应用对话配置'
}),
isPublish: BoolSchema.optional().meta({
example: true,
description: '是否将该版本发布为线上运行版本'
}),
versionName: z.string().optional().meta({
example: '正式发布版',
description: '版本名称;自动保存时服务端会使用自动保存文案'
}),
autoSave: BoolSchema.optional().meta({
example: false,
description: '是否为编辑器自动保存;自动保存会覆盖当前自动保存记录'
})
});
export type PublishAppBodyType = z.infer<typeof PublishAppBodySchema>;
/* ============================================================================
* API: 发布或保存应用版本
* Route: POST /api/core/app/version/publish
* Method: POST
* Description: 保存应用版本,支持自动保存、普通保存和发布。
* Tags: ['版本管理']
* ============================================================================ */
export const PublishAppResponseSchema = z.undefined().meta({
description: '保存成功'
});
export type PublishAppResponseType = z.infer<typeof PublishAppResponseSchema>;
/* ============================================================================
* API: 获取应用版本列表
* Route: POST /api/core/app/version/list
* Method: POST
* Description: 分页获取指定应用的版本列表。
* Tags: ['版本管理']
* ============================================================================ */
export const AppVersionListBodySchema = PaginationSchema.extend({
appId: ObjectIdSchema.meta({
example: '68ad85a7463006c963799a05',
description: '应用 ID'
}),
isPublish: BoolSchema.optional().meta({
example: true,
description: '是否只查询发布版本'
})
});
export type AppVersionListBodyType = z.infer<typeof AppVersionListBodySchema>;
export const AppVersionListResponseSchema = z.object({
total: NumSchema.optional().default(0).meta({
example: 100,
description: '版本总数'
}),
list: z.array(OpenAPIVersionListItemSchema).optional().default([]).meta({
description: '版本列表'
})
});
export type AppVersionListResponseType = z.infer<typeof AppVersionListResponseSchema>;
/* ============================================================================
* API: 获取应用版本详情
* Route: GET /api/core/app/version/detail
* Method: GET
* Description: 获取指定应用版本的完整编排详情。
* Tags: ['版本管理']
* ============================================================================ */
export const GetAppVersionDetailQuerySchema = z.object({
versionId: ObjectIdSchema.meta({
example: '68ad85a7463006c963799a05',
description: '版本 ID'
}),
appId: ObjectIdSchema.meta({
example: '68ad85a7463006c963799a06',
description: '应用 ID'
})
});
export type GetAppVersionDetailQueryType = z.infer<typeof GetAppVersionDetailQuerySchema>;
export const GetAppVersionDetailResponseSchema = z.object({
_id: ObjectIdSchema.meta({
description: '版本记录 ID'
}),
tmbId: ObjectIdSchema.meta({
description: '创建或更新该版本的团队成员 ID'
}),
appId: ObjectIdSchema.meta({
description: '版本所属应用 ID'
}),
time: z.coerce.date().meta({
description: '版本创建时间'
}),
nodes: AppVersionNodesSchema.default([]).meta({
description: '版本内保存的应用节点配置'
}),
edges: AppVersionEdgesSchema,
chatConfig: AppVersionChatConfigSchema,
isPublish: BoolSchema.optional().meta({
description: '是否为已发布版本'
}),
isAutoSave: BoolSchema.optional().meta({
description: '是否为编辑器自动保存版本'
}),
versionName: z.string().meta({
example: '正式发布版',
description: '版本名称'
}),
resourceRefs: AppVersionResourceRefsSchema
});
export type GetAppVersionDetailResponseType = z.infer<typeof GetAppVersionDetailResponseSchema>;
/* ============================================================================
* API: 获取应用最新版本
* Route: GET /api/core/app/version/latest
* Method: GET
* Description: 获取应用最新版本的节点、连线和聊天配置。
* Tags: ['版本管理']
* ============================================================================ */
export const GetLatestAppVersionQuerySchema = z.object({
appId: ObjectIdSchema.meta({
example: '68ad85a7463006c963799a05',
description: '应用 ID'
})
});
export type GetLatestAppVersionQueryType = z.infer<typeof GetLatestAppVersionQuerySchema>;
export const GetLatestAppVersionBodySchema = z.object({}).meta({
description: '获取最新版本不需要请求体'
});
export type GetLatestAppVersionBodyType = z.infer<typeof GetLatestAppVersionBodySchema>;
export const GetLatestAppVersionResponseSchema = z.object({
versionId: z.string().optional().meta({
description: '当前线上版本 ID;未发布过版本时可能为空'
}),
versionName: z.string().optional().meta({
description: '当前线上版本名称;未发布过版本时通常为应用名称'
}),
nodes: AppVersionNodesSchema.default([]).meta({
description: '版本内保存的应用节点配置'
}),
edges: AppVersionEdgesSchema,
chatConfig: AppVersionChatConfigSchema
});
export type GetLatestAppVersionResponseType = z.infer<typeof GetLatestAppVersionResponseSchema>;
/* ============================================================================
* API: 更新应用版本名称
* Route: PUT /api/core/app/version/update
* Method: PUT
* Description: 更新指定应用版本的版本名称。
* Tags: ['版本管理']
* ============================================================================ */
export const UpdateAppVersionBodySchema = z.object({
appId: ObjectIdSchema.meta({
example: '68ad85a7463006c963799a05',
description: '应用 ID'
}),
versionId: ObjectIdSchema.meta({
example: '68ad85a7463006c963799a06',
description: '版本 ID'
}),
versionName: z.string().trim().min(1).meta({
example: '正式发布版',
description: '版本名称'
})
});
export type UpdateAppVersionBodyType = z.infer<typeof UpdateAppVersionBodySchema>;
export const UpdateAppVersionResponseSchema = z.undefined().meta({
description: '更新成功'
});
export type UpdateAppVersionResponseType = z.infer<typeof UpdateAppVersionResponseSchema>;
import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag';
import {
AppVersionListBodySchema,
AppVersionListResponseSchema,
GetAppVersionDetailQuerySchema,
GetAppVersionDetailResponseSchema,
GetLatestAppVersionQuerySchema,
GetLatestAppVersionResponseSchema,
PublishAppBodySchema,
PublishAppQuerySchema,
PublishAppResponseSchema,
UpdateAppVersionBodySchema,
UpdateAppVersionResponseSchema
} from './api';
export const AppVersionPath: OpenAPIPath = {
'/core/app/version/publish': {
post: {
summary: '发布或保存应用版本',
description: '保存应用版本,支持自动保存、普通保存和发布',
tags: [TagsMap.appVersion],
requestParams: {
query: PublishAppQuerySchema
},
requestBody: {
content: {
'application/json': {
schema: PublishAppBodySchema
}
}
},
responses: {
200: {
description: '成功保存应用版本',
content: {
'application/json': {
schema: PublishAppResponseSchema
}
}
}
}
}
},
'/core/app/version/list': {
post: {
summary: '获取应用版本列表',
description: '分页获取指定应用的版本列表',
tags: [TagsMap.appVersion],
requestBody: {
content: {
'application/json': {
schema: AppVersionListBodySchema
}
}
},
responses: {
200: {
description: '成功获取应用版本列表',
content: {
'application/json': {
schema: AppVersionListResponseSchema
}
}
}
}
}
},
'/core/app/version/detail': {
get: {
summary: '获取应用版本详情',
description: '获取指定应用版本的完整编排详情',
tags: [TagsMap.appVersion],
requestParams: {
query: GetAppVersionDetailQuerySchema
},
responses: {
200: {
description: '成功获取应用版本详情',
content: {
'application/json': {
schema: GetAppVersionDetailResponseSchema
}
}
}
}
}
},
'/core/app/version/latest': {
get: {
summary: '获取应用最新版本',
description: '获取应用最新版本的节点、连线和聊天配置',
tags: [TagsMap.appVersion],
requestParams: {
query: GetLatestAppVersionQuerySchema
},
responses: {
200: {
description: '成功获取应用最新版本',
content: {
'application/json': {
schema: GetLatestAppVersionResponseSchema
}
}
}
}
}
},
'/core/app/version/update': {
put: {
summary: '更新应用版本名称',
description: '更新指定应用版本的版本名称',
tags: [TagsMap.appVersion],
requestBody: {
content: {
'application/json': {
schema: UpdateAppVersionBodySchema
}
}
},
responses: {
200: {
description: '成功更新应用版本名称',
content: {
'application/json': {
schema: UpdateAppVersionResponseSchema
}
}
}
}
}
}
};
...@@ -64,7 +64,7 @@ export const AdminUpdateFeedbackBodySchema = z.object({ ...@@ -64,7 +64,7 @@ export const AdminUpdateFeedbackBodySchema = z.object({
}); });
export type AdminUpdateFeedbackBodyType = z.infer<typeof AdminUpdateFeedbackBodySchema>; export type AdminUpdateFeedbackBodyType = z.infer<typeof AdminUpdateFeedbackBodySchema>;
export const AdminUpdateFeedbackResponseSchema = z.object({}); export const AdminUpdateFeedbackResponseSchema = z.undefined().meta({ description: '更新成功' });
export type AdminUpdateFeedbackResponseType = z.infer<typeof AdminUpdateFeedbackResponseSchema>; export type AdminUpdateFeedbackResponseType = z.infer<typeof AdminUpdateFeedbackResponseSchema>;
/* =============== closeCustom =============== */ /* =============== closeCustom =============== */
...@@ -88,7 +88,7 @@ export const CloseCustomFeedbackBodySchema = z.object({ ...@@ -88,7 +88,7 @@ export const CloseCustomFeedbackBodySchema = z.object({
}); });
export type CloseCustomFeedbackBodyType = z.infer<typeof CloseCustomFeedbackBodySchema>; export type CloseCustomFeedbackBodyType = z.infer<typeof CloseCustomFeedbackBodySchema>;
export const CloseCustomFeedbackResponseSchema = z.object({}); export const CloseCustomFeedbackResponseSchema = z.undefined().meta({ description: '关闭成功' });
export type CloseCustomFeedbackResponseType = z.infer<typeof CloseCustomFeedbackResponseSchema>; export type CloseCustomFeedbackResponseType = z.infer<typeof CloseCustomFeedbackResponseSchema>;
/* =============== updateUserFeedback =============== */ /* =============== updateUserFeedback =============== */
...@@ -116,7 +116,7 @@ export const UpdateUserFeedbackBodySchema = z.object({ ...@@ -116,7 +116,7 @@ export const UpdateUserFeedbackBodySchema = z.object({
}); });
export type UpdateUserFeedbackBodyType = z.infer<typeof UpdateUserFeedbackBodySchema>; export type UpdateUserFeedbackBodyType = z.infer<typeof UpdateUserFeedbackBodySchema>;
export const UpdateUserFeedbackResponseSchema = z.object({}); export const UpdateUserFeedbackResponseSchema = z.undefined().meta({ description: '更新成功' });
export type UpdateUserFeedbackResponseType = z.infer<typeof UpdateUserFeedbackResponseSchema>; export type UpdateUserFeedbackResponseType = z.infer<typeof UpdateUserFeedbackResponseSchema>;
/* =============== getFeedbackRecordIds =============== */ /* =============== getFeedbackRecordIds =============== */
......
...@@ -98,7 +98,7 @@ export const DeleteChatInputGuideBodySchema = z.object({ ...@@ -98,7 +98,7 @@ export const DeleteChatInputGuideBodySchema = z.object({
}); });
export type DeleteChatInputGuideBodyType = z.infer<typeof DeleteChatInputGuideBodySchema>; export type DeleteChatInputGuideBodyType = z.infer<typeof DeleteChatInputGuideBodySchema>;
export const DeleteChatInputGuideResponseSchema = z.object({}); export const DeleteChatInputGuideResponseSchema = z.undefined().meta({ description: '删除成功' });
export type DeleteChatInputGuideResponseType = z.infer<typeof DeleteChatInputGuideResponseSchema>; export type DeleteChatInputGuideResponseType = z.infer<typeof DeleteChatInputGuideResponseSchema>;
/* ============================================================================ /* ============================================================================
...@@ -114,7 +114,9 @@ export const DeleteAllChatInputGuideBodySchema = z.object({ ...@@ -114,7 +114,9 @@ export const DeleteAllChatInputGuideBodySchema = z.object({
}); });
export type DeleteAllChatInputGuideBodyType = z.infer<typeof DeleteAllChatInputGuideBodySchema>; export type DeleteAllChatInputGuideBodyType = z.infer<typeof DeleteAllChatInputGuideBodySchema>;
export const DeleteAllChatInputGuideResponseSchema = z.object({}); export const DeleteAllChatInputGuideResponseSchema = z
.undefined()
.meta({ description: '删除成功' });
export type DeleteAllChatInputGuideResponseType = z.infer< export type DeleteAllChatInputGuideResponseType = z.infer<
typeof DeleteAllChatInputGuideResponseSchema typeof DeleteAllChatInputGuideResponseSchema
>; >;
...@@ -153,5 +155,5 @@ export const UpdateChatInputGuideBodySchema = z.object({ ...@@ -153,5 +155,5 @@ export const UpdateChatInputGuideBodySchema = z.object({
}); });
export type UpdateChatInputGuideBodyType = z.infer<typeof UpdateChatInputGuideBodySchema>; export type UpdateChatInputGuideBodyType = z.infer<typeof UpdateChatInputGuideBodySchema>;
export const UpdateChatInputGuideResponseSchema = z.object({}); export const UpdateChatInputGuideResponseSchema = z.undefined().meta({ description: '更新成功' });
export type UpdateChatInputGuideResponseType = z.infer<typeof UpdateChatInputGuideResponseSchema>; export type UpdateChatInputGuideResponseType = z.infer<typeof UpdateChatInputGuideResponseSchema>;
...@@ -42,7 +42,7 @@ export const DeleteChatRecordBodySchema = OutLinkChatAuthSchema.extend({ ...@@ -42,7 +42,7 @@ export const DeleteChatRecordBodySchema = OutLinkChatAuthSchema.extend({
}); });
export type DeleteChatRecordBodyType = z.infer<typeof DeleteChatRecordBodySchema>; export type DeleteChatRecordBodyType = z.infer<typeof DeleteChatRecordBodySchema>;
export const DeleteChatRecordResponseSchema = z.object({}); export const DeleteChatRecordResponseSchema = z.undefined().meta({ description: '删除成功' });
export type DeleteChatRecordResponseType = z.infer<typeof DeleteChatRecordResponseSchema>; export type DeleteChatRecordResponseType = z.infer<typeof DeleteChatRecordResponseSchema>;
/* ============================================================================ /* ============================================================================
......
...@@ -119,7 +119,7 @@ export const DeleteDatasetDataQuerySchema = z.object({ ...@@ -119,7 +119,7 @@ export const DeleteDatasetDataQuerySchema = z.object({
}); });
export type DeleteDatasetDataQuery = z.infer<typeof DeleteDatasetDataQuerySchema>; export type DeleteDatasetDataQuery = z.infer<typeof DeleteDatasetDataQuerySchema>;
export const DeleteDatasetDataResponseSchema = z.literal('success'); export const DeleteDatasetDataResponseSchema = z.undefined().meta({ description: '删除成功' });
export type DeleteDatasetDataResponse = z.infer<typeof DeleteDatasetDataResponseSchema>; export type DeleteDatasetDataResponse = z.infer<typeof DeleteDatasetDataResponseSchema>;
/* ============================================================================ /* ============================================================================
...@@ -138,7 +138,7 @@ export const DeleteDatasetDataIndexBodySchema = z.object({ ...@@ -138,7 +138,7 @@ export const DeleteDatasetDataIndexBodySchema = z.object({
}); });
export type DeleteDatasetDataIndexBody = z.infer<typeof DeleteDatasetDataIndexBodySchema>; export type DeleteDatasetDataIndexBody = z.infer<typeof DeleteDatasetDataIndexBodySchema>;
export const DeleteDatasetDataIndexResponseSchema = z.object({}); export const DeleteDatasetDataIndexResponseSchema = z.undefined().meta({ description: '删除成功' });
export type DeleteDatasetDataIndexResponse = z.infer<typeof DeleteDatasetDataIndexResponseSchema>; export type DeleteDatasetDataIndexResponse = z.infer<typeof DeleteDatasetDataIndexResponseSchema>;
/* ============================================================================ /* ============================================================================
...@@ -226,7 +226,7 @@ export const InsertImagesBodySchema = z.object({ ...@@ -226,7 +226,7 @@ export const InsertImagesBodySchema = z.object({
}); });
export type InsertImagesBody = z.infer<typeof InsertImagesBodySchema>; export type InsertImagesBody = z.infer<typeof InsertImagesBodySchema>;
export const InsertImagesResponseSchema = z.object({}); export const InsertImagesResponseSchema = z.undefined().meta({ description: '插入成功' });
export type InsertImagesResponse = z.infer<typeof InsertImagesResponseSchema>; export type InsertImagesResponse = z.infer<typeof InsertImagesResponseSchema>;
/* ============================================================================ /* ============================================================================
......
...@@ -36,7 +36,7 @@ export const UpdateTrainingDataBodySchema = z.object({ ...@@ -36,7 +36,7 @@ export const UpdateTrainingDataBodySchema = z.object({
}); });
export type UpdateTrainingDataBody = z.infer<typeof UpdateTrainingDataBodySchema>; export type UpdateTrainingDataBody = z.infer<typeof UpdateTrainingDataBodySchema>;
export const UpdateTrainingDataResponseSchema = z.object({}); export const UpdateTrainingDataResponseSchema = z.undefined().meta({ description: '更新成功' });
export type UpdateTrainingDataResponse = z.infer<typeof UpdateTrainingDataResponseSchema>; export type UpdateTrainingDataResponse = z.infer<typeof UpdateTrainingDataResponseSchema>;
/* ============================================================================ /* ============================================================================
...@@ -55,7 +55,7 @@ export const RebuildEmbeddingBodySchema = z.object({ ...@@ -55,7 +55,7 @@ export const RebuildEmbeddingBodySchema = z.object({
}); });
export type RebuildEmbeddingBody = z.infer<typeof RebuildEmbeddingBodySchema>; export type RebuildEmbeddingBody = z.infer<typeof RebuildEmbeddingBodySchema>;
export const RebuildEmbeddingResponseSchema = z.object({}); export const RebuildEmbeddingResponseSchema = z.undefined().meta({ description: '重建成功' });
export type RebuildEmbeddingResponse = z.infer<typeof RebuildEmbeddingResponseSchema>; export type RebuildEmbeddingResponse = z.infer<typeof RebuildEmbeddingResponseSchema>;
/* ============================================================================ /* ============================================================================
...@@ -78,7 +78,7 @@ export const DeleteTrainingDataBodySchema = z.object({ ...@@ -78,7 +78,7 @@ export const DeleteTrainingDataBodySchema = z.object({
}); });
export type DeleteTrainingDataBody = z.infer<typeof DeleteTrainingDataBodySchema>; export type DeleteTrainingDataBody = z.infer<typeof DeleteTrainingDataBodySchema>;
export const DeleteTrainingDataResponseSchema = z.object({}); export const DeleteTrainingDataResponseSchema = z.undefined().meta({ description: '删除成功' });
export type DeleteTrainingDataResponse = z.infer<typeof DeleteTrainingDataResponseSchema>; export type DeleteTrainingDataResponse = z.infer<typeof DeleteTrainingDataResponseSchema>;
/* ============================================================================ /* ============================================================================
......
import z from 'zod'; import z from 'zod';
import { FlowNodeOutputItemTypeSchema } from '../../../core/workflow/type/io'; import {
import { StoreNodeItemTypeSchema } from '../../../core/workflow/type/node'; FlowNodeInputItemTypeSchema,
FlowNodeOutputItemTypeSchema
} from '../../../core/workflow/type/io';
import {
NodeToolConfigTypeSchema,
StoreNodeItemTypeSchema,
ToolDataSchema
} from '../../../core/workflow/type/node';
import { BoolSchema, NumSchema } from '../../../common/zod';
const OpenAPIFlowNodeInputItemTypeSchema = FlowNodeInputItemTypeSchema.meta({
description: '工作流节点输入配置'
});
// `invalidCondition` in FlowNodeOutputItemTypeSchema is a Zod function schema used only // `invalidCondition` in FlowNodeOutputItemTypeSchema is a Zod function schema used only
// by the editor to validate outputs; function schemas cannot be represented in JSON // by the editor to validate outputs; function schemas cannot be represented in JSON
// Schema, so we strip it before exposing via OpenAPI. // Schema, so we strip it before exposing via OpenAPI.
const OpenAPIFlowNodeOutputItemTypeSchema = FlowNodeOutputItemTypeSchema.omit({ const OpenAPIFlowNodeOutputItemTypeSchema = FlowNodeOutputItemTypeSchema.omit({
invalidCondition: true invalidCondition: true
}).meta({
description: '工作流节点输出配置'
}); });
export const OpenAPIStoreNodeItemTypeSchema = StoreNodeItemTypeSchema.omit({ const OpenAPIToolDataSchema = ToolDataSchema.meta({
outputs: true description: '节点关联工具展示信息'
}).extend({ });
outputs: z.array(OpenAPIFlowNodeOutputItemTypeSchema)
const OpenAPIToolRefSchema = z
.object({
toolId: z.string().meta({
description: '节点引用的工具 ID'
})
})
.meta({
description: '节点引用的工具配置'
});
const OpenAPINodeToolConfigTypeSchema = NodeToolConfigTypeSchema.extend({
mcpTool: OpenAPIToolRefSchema.optional().meta({
description: '节点绑定的 MCP 单工具配置'
}),
systemTool: OpenAPIToolRefSchema.optional().meta({
description: '节点绑定的系统单工具配置'
}),
httpTool: OpenAPIToolRefSchema.optional().meta({
description: '节点绑定的 HTTP 单工具配置'
})
}).meta({
description: '节点工具配置'
}); });
export const OpenAPIStoreNodeItemTypeSchema = StoreNodeItemTypeSchema.omit({
inputs: true,
outputs: true,
pluginData: true,
toolConfig: true,
position: true
})
.extend({
parentNodeId: z.string().optional().meta({
description: '父节点 ID,用于循环、分组等嵌套节点场景'
}),
flowNodeType: StoreNodeItemTypeSchema.shape.flowNodeType.meta({
description: '工作流节点类型'
}),
abandon: BoolSchema.optional().meta({
description: '节点是否被废弃或隐藏'
}),
avatar: z.string().optional().meta({
description: '节点头像'
}),
avatarLinear: z.string().optional().meta({
description: '节点头像渐变色配置'
}),
colorSchema: StoreNodeItemTypeSchema.shape.colorSchema.meta({
description: '节点在编辑器中的配色方案'
}),
name: z.string().meta({
description: '节点名称'
}),
intro: z.string().optional().meta({
description: '节点简介'
}),
toolDescription: z.string().optional().meta({
description: '节点作为工具被调用时的能力说明'
}),
showStatus: BoolSchema.optional().meta({
description: '对话运行时是否展示该节点执行状态'
}),
version: z.string().optional().meta({
description: '节点实现版本'
}),
versionLabel: z.string().optional().meta({
description: '节点版本展示名称'
}),
isLatestVersion: BoolSchema.optional().meta({
description: '该节点是否为当前最新版本'
}),
catchError: BoolSchema.optional().meta({
description: '节点执行异常时是否进入错误捕获流程'
}),
inputs: z.array(OpenAPIFlowNodeInputItemTypeSchema).meta({
description: '节点输入配置列表'
}),
outputs: z.array(OpenAPIFlowNodeOutputItemTypeSchema).meta({
description: '节点输出配置列表'
}),
pluginId: z.string().optional().meta({
description: '节点关联的插件 ID'
}),
isFolder: BoolSchema.optional().meta({
description: '该节点是否为文件夹节点'
}),
pluginData: OpenAPIToolDataSchema.optional().meta({
description: '节点关联插件或工具的展示信息'
}),
toolConfig: OpenAPINodeToolConfigTypeSchema.optional().meta({
description: '节点绑定的工具配置'
}),
currentCost: NumSchema.optional().meta({
description: '节点当前预估积分消耗'
}),
systemKeyCost: NumSchema.optional().meta({
description: '节点使用系统密钥产生的额外积分费用'
}),
hasTokenFee: BoolSchema.optional().meta({
description: '节点运行是否会产生模型 token 费用'
}),
hasSystemSecret: BoolSchema.optional().meta({
description: '节点是否使用系统级密钥'
}),
nodeId: z.string().meta({
description: '工作流节点 ID'
}),
position: z
.object({
x: NumSchema.meta({
description: '节点在画布中的横坐标'
}),
y: NumSchema.meta({
description: '节点在画布中的纵坐标'
})
})
.optional()
.meta({
description: '节点在工作流画布中的位置'
})
})
.meta({
description: '应用工作流节点配置'
});
...@@ -20,23 +20,31 @@ export const openAPIPaths: NonNullable<OpenAPIPath> = { ...@@ -20,23 +20,31 @@ export const openAPIPaths: NonNullable<OpenAPIPath> = {
export const openAPITagGroups = [ export const openAPITagGroups = [
{ {
name: '我的应用/工具管理', name: '核心-应用管理',
tags: [TagsMap.appCommon, TagsMap.mcpTools, TagsMap.httpTools, TagsMap.appPer] tags: [
TagsMap.appCommon,
TagsMap.appFolder,
TagsMap.appPer,
TagsMap.appVersion,
TagsMap.appTemplate,
TagsMap.appLog,
TagsMap.publishChannel
]
}, },
{ {
name: 'Agent 应用', name: '核心-工具管理',
tags: [TagsMap.appLog, TagsMap.publishChannel, TagsMap.mcpServer] tags: [TagsMap.httpTools, TagsMap.mcpTools, TagsMap.mcpServer]
}, },
{ {
name: 'AI 相关', name: '核心-AI 相关',
tags: [TagsMap.aiSkill, TagsMap.sandbox, TagsMap.aiCommon] tags: [TagsMap.aiSkill, TagsMap.sandbox, TagsMap.aiCommon]
}, },
{ {
name: '对话模块配置', name: '核心-对话模块配置',
tags: [TagsMap.chatSetting, TagsMap.chatPage, TagsMap.chatInputGuide] tags: [TagsMap.chatSetting, TagsMap.chatPage, TagsMap.chatInputGuide]
}, },
{ {
name: '对话模块使用', name: '核心-对话模块使用',
tags: [ tags: [
TagsMap.chatHistory, TagsMap.chatHistory,
TagsMap.chatFeedback, TagsMap.chatFeedback,
...@@ -46,7 +54,7 @@ export const openAPITagGroups = [ ...@@ -46,7 +54,7 @@ export const openAPITagGroups = [
] ]
}, },
{ {
name: '知识库', name: '核心-知识库',
tags: [ tags: [
TagsMap.datasetCommon, TagsMap.datasetCommon,
TagsMap.datasetCollection, TagsMap.datasetCollection,
...@@ -58,16 +66,20 @@ export const openAPITagGroups = [ ...@@ -58,16 +66,20 @@ export const openAPITagGroups = [
] ]
}, },
{ {
name: '插件系统', name: '核心-插件系统',
tags: [TagsMap.pluginToolTag, TagsMap.pluginTeam] tags: [TagsMap.pluginToolTag, TagsMap.pluginTeam]
}, },
{ {
name: '用户体系', name: '辅助-用户体系',
tags: [TagsMap.userInform, TagsMap.walletBill, TagsMap.walletDiscountCoupon, TagsMap.userLogin] tags: [TagsMap.userInform, TagsMap.walletBill, TagsMap.walletDiscountCoupon, TagsMap.userLogin]
}, },
{ {
name: '通用-辅助功能', name: '辅助-权限管理',
tags: [TagsMap.customDomain, TagsMap.apiKey] tags: [TagsMap.permissionResource, TagsMap.permissionCollaborator]
},
{
name: '通用-基础功能',
tags: [TagsMap.apiKey, TagsMap.customDomain]
}, },
{ {
name: '管理员-插件管理', name: '管理员-插件管理',
......
...@@ -5,10 +5,12 @@ import { ApiKeyPath } from './openapi'; ...@@ -5,10 +5,12 @@ import { ApiKeyPath } from './openapi';
import { CustomDomainPath } from './customDomain'; import { CustomDomainPath } from './customDomain';
import { OutLinkPath } from './outLink'; import { OutLinkPath } from './outLink';
import { McpPath } from './mcpServer'; import { McpPath } from './mcpServer';
import { PermissionPath } from './permission';
export const SupportPath: OpenAPIPath = { export const SupportPath: OpenAPIPath = {
...UserPath, ...UserPath,
...WalletPath, ...WalletPath,
...PermissionPath,
...ApiKeyPath, ...ApiKeyPath,
...CustomDomainPath, ...CustomDomainPath,
...OutLinkPath, ...OutLinkPath,
......
...@@ -61,7 +61,7 @@ export const McpCreateBodySchema = z.object({ ...@@ -61,7 +61,7 @@ export const McpCreateBodySchema = z.object({
}); });
export type McpCreateBodyType = z.infer<typeof McpCreateBodySchema>; export type McpCreateBodyType = z.infer<typeof McpCreateBodySchema>;
export const McpCreateResponseSchema = z.object({}).meta({ description: '创建成功' }); export const McpCreateResponseSchema = z.undefined().meta({ description: '创建成功' });
export type McpCreateResponseType = z.infer<typeof McpCreateResponseSchema>; export type McpCreateResponseType = z.infer<typeof McpCreateResponseSchema>;
/* ============================================================================ /* ============================================================================
...@@ -76,7 +76,7 @@ export const McpUpdateBodySchema = z.object({ ...@@ -76,7 +76,7 @@ export const McpUpdateBodySchema = z.object({
}); });
export type McpUpdateBodyType = z.infer<typeof McpUpdateBodySchema>; export type McpUpdateBodyType = z.infer<typeof McpUpdateBodySchema>;
export const McpUpdateResponseSchema = z.object({}).meta({ description: '更新成功' }); export const McpUpdateResponseSchema = z.undefined().meta({ description: '更新成功' });
export type McpUpdateResponseType = z.infer<typeof McpUpdateResponseSchema>; export type McpUpdateResponseType = z.infer<typeof McpUpdateResponseSchema>;
/* ============================================================================ /* ============================================================================
...@@ -89,5 +89,5 @@ export const McpDeleteQuerySchema = z.object({ ...@@ -89,5 +89,5 @@ export const McpDeleteQuerySchema = z.object({
}); });
export type McpDeleteQueryType = z.infer<typeof McpDeleteQuerySchema>; export type McpDeleteQueryType = z.infer<typeof McpDeleteQuerySchema>;
export const McpDeleteResponseSchema = z.object({}).meta({ description: '删除成功' }); export const McpDeleteResponseSchema = z.undefined().meta({ description: '删除成功' });
export type McpDeleteResponseType = z.infer<typeof McpDeleteResponseSchema>; export type McpDeleteResponseType = z.infer<typeof McpDeleteResponseSchema>;
import z from 'zod'; import z from 'zod';
import { ObjectIdSchema } from '../../../common/type/mongo';
import { getErrorResponse } from '../../type';
const OptionalDateSchema = z.preprocess((value) => {
if (value === undefined || value === null || value === '') return undefined;
if (value instanceof Date && Number.isNaN(value.getTime())) return undefined;
return value;
}, z.coerce.date().optional());
export const ApiKeyLimitSchema = z
.object({
expiredTime: OptionalDateSchema.meta({ description: '过期时间' }),
maxUsagePoints: z.number().optional().default(-1).meta({
example: -1,
description: '最大积分用量限制'
})
})
.optional()
.meta({ description: 'API Key 使用限制' });
export const OpenApiKeySchema = z.object({
_id: ObjectIdSchema.meta({ description: 'API Key 记录 ID' }),
teamId: ObjectIdSchema.meta({ description: '团队 ID' }),
tmbId: ObjectIdSchema.meta({ description: '团队成员 ID' }),
createTime: z.coerce.date().meta({ description: '创建时间' }),
lastUsedTime: z.coerce.date().optional().meta({ description: '最后使用时间' }),
apiKey: z.string().meta({ description: 'API Key,列表接口返回脱敏值' }),
appId: ObjectIdSchema.optional().meta({ description: '绑定应用 ID' }),
name: z.string().optional().default('Api Key').meta({ description: 'API Key 名称' }),
usagePoints: z.number().optional().default(0).meta({ description: '累计使用积分' }),
limit: ApiKeyLimitSchema.meta({
description: 'API Key 使用限制,未配置时表示不限制过期时间和积分用量'
})
});
export type OpenApiKeySchemaType = z.infer<typeof OpenApiKeySchema>;
/* ============================================================================
* API: 创建 API Key
* Route: POST /api/support/openapi/create
* Method: POST
* Description: 创建团队级或应用级 API Key。
* Tags: ['API Key 管理']
* ============================================================================ */
export const CreateApiKeyBodySchema = z.object({
appId: ObjectIdSchema.optional().meta({ description: '绑定应用 ID,不传则创建团队级 API Key' }),
name: z.string().min(1).meta({ example: '生产环境 Key', description: 'API Key 名称' }),
limit: ApiKeyLimitSchema.meta({
description: 'API Key 使用限制,未配置时表示不限制过期时间和积分用量'
})
});
export type CreateApiKeyBodyType = z.infer<typeof CreateApiKeyBodySchema>;
export const CreateApiKeyResponseSchema = z.string().meta({
description: '新创建的 API Key 明文'
});
export type CreateApiKeyResponseType = z.infer<typeof CreateApiKeyResponseSchema>;
/* ============================================================================
* API: 获取 API Key 列表
* Route: GET /api/support/openapi/list
* Method: GET
* Description: 获取团队级或指定应用下的 API Key 列表。
* Tags: ['API Key 管理']
* ============================================================================ */
export const GetApiKeyListQuerySchema = z.object({
appId: ObjectIdSchema.optional().meta({ description: '应用 ID,不传则查询团队级 API Key' })
});
export type GetApiKeyListQueryType = z.infer<typeof GetApiKeyListQuerySchema>;
export const GetApiKeyListResponseSchema = z.array(OpenApiKeySchema).meta({
description: 'API Key 列表'
});
export type GetApiKeyListResponseType = z.infer<typeof GetApiKeyListResponseSchema>;
/* ============================================================================
* API: 更新 API Key
* Route: PUT /api/support/openapi/update
* Method: PUT
* Description: 更新 API Key 名称或使用限制。
* Tags: ['API Key 管理']
* ============================================================================ */
export const UpdateApiKeyBodySchema = CreateApiKeyBodySchema.partial()
.extend({
_id: ObjectIdSchema.meta({ description: 'API Key 记录 ID' })
})
.refine(({ name, limit }) => name !== undefined || limit !== undefined, {
message: 'name or limit is required'
});
export type UpdateApiKeyBodyType = z.infer<typeof UpdateApiKeyBodySchema>;
export const UpdateApiKeyResponseSchema = z.undefined().meta({
description: '更新成功'
});
export type UpdateApiKeyResponseType = z.infer<typeof UpdateApiKeyResponseSchema>;
/* ============================================================================
* API: 删除 API Key
* Route: DELETE /api/support/openapi/delete
* Method: DELETE
* Description: 删除指定 API Key。
* Tags: ['API Key 管理']
* ============================================================================ */
export const DeleteApiKeyQuerySchema = z.object({
id: ObjectIdSchema.meta({ description: 'API Key 记录 ID' })
});
export type DeleteApiKeyQueryType = z.infer<typeof DeleteApiKeyQuerySchema>;
export const DeleteApiKeyBodySchema = z.object({}).meta({
description: '删除 API Key 不需要请求体'
});
export type DeleteApiKeyBodyType = z.infer<typeof DeleteApiKeyBodySchema>;
export const DeleteApiKeyResponseSchema = z.undefined().meta({
description: '删除成功'
});
export type DeleteApiKeyResponseType = z.infer<typeof DeleteApiKeyResponseSchema>;
export const ApiKeyHealthParamsSchema = z.object({ export const ApiKeyHealthParamsSchema = z.object({
apiKey: z.string().nonempty() apiKey: z.string().nonempty().meta({
example: 'fastgpt-xxxxxxxx',
description: '待校验的 API Key 明文'
})
}); });
export type ApiKeyHealthParamsType = z.infer<typeof ApiKeyHealthParamsSchema>; export type ApiKeyHealthParamsType = z.infer<typeof ApiKeyHealthParamsSchema>;
export const ApiKeyHealthResponseSchema = z.object({ export const ApiKeyHealthResponseSchema = z.object({
appId: z.string().optional().meta({ appId: ObjectIdSchema.optional().meta({
description: '如果有关联的应用,会返回应用ID' example: '68ad85a7463006c963799a05',
description: 'API Key 绑定的应用 ID;团队级 API Key 不返回'
}) })
}); });
export type ApiKeyHealthResponseType = z.infer<typeof ApiKeyHealthResponseSchema>; export type ApiKeyHealthResponseType = z.infer<typeof ApiKeyHealthResponseSchema>;
export const ApiKeyHealthErrorResponseSchema = getErrorResponse({
message: 'APIKey invalid'
}).meta({
description: 'API Key 无效时的统一错误响应'
});
export type ApiKeyHealthErrorResponseType = z.infer<typeof ApiKeyHealthErrorResponseSchema>;
import z from 'zod';
import { type OpenAPIPath } from '../../type'; import { type OpenAPIPath } from '../../type';
import { ApiKeyHealthParamsSchema, ApiKeyHealthResponseSchema } from './api'; import {
ApiKeyHealthParamsSchema,
ApiKeyHealthErrorResponseSchema,
ApiKeyHealthResponseSchema,
CreateApiKeyBodySchema,
CreateApiKeyResponseSchema,
DeleteApiKeyQuerySchema,
DeleteApiKeyResponseSchema,
GetApiKeyListQuerySchema,
GetApiKeyListResponseSchema,
UpdateApiKeyBodySchema,
UpdateApiKeyResponseSchema
} from './api';
import { TagsMap } from '../../tag'; import { TagsMap } from '../../tag';
export const ApiKeyPath: OpenAPIPath = { export const ApiKeyPath: OpenAPIPath = {
'/support/openapi/create': {
post: {
summary: '创建 API Key',
description: '创建团队级或应用级 API Key',
tags: [TagsMap.apiKey],
requestBody: {
content: {
'application/json': {
schema: CreateApiKeyBodySchema
}
}
},
responses: {
200: {
description: '成功创建 API Key',
content: {
'application/json': {
schema: CreateApiKeyResponseSchema
}
}
}
}
}
},
'/support/openapi/list': {
get: {
summary: '获取 API Key 列表',
description: '获取团队级或指定应用下的 API Key 列表',
tags: [TagsMap.apiKey],
requestParams: {
query: GetApiKeyListQuerySchema
},
responses: {
200: {
description: '成功获取 API Key 列表',
content: {
'application/json': {
schema: GetApiKeyListResponseSchema
}
}
}
}
}
},
'/support/openapi/update': {
put: {
summary: '更新 API Key',
description: '更新 API Key 名称或使用限制',
tags: [TagsMap.apiKey],
requestBody: {
content: {
'application/json': {
schema: UpdateApiKeyBodySchema
}
}
},
responses: {
200: {
description: '成功更新 API Key',
content: {
'application/json': {
schema: UpdateApiKeyResponseSchema
}
}
}
}
}
},
'/support/openapi/delete': {
delete: {
summary: '删除 API Key',
description: '删除指定 API Key',
tags: [TagsMap.apiKey],
requestParams: {
query: DeleteApiKeyQuerySchema
},
responses: {
200: {
description: '成功删除 API Key',
content: {
'application/json': {
schema: DeleteApiKeyResponseSchema
}
}
}
}
}
},
'/support/openapi/health': { '/support/openapi/health': {
get: { get: {
summary: '检查 API Key 是否健康', summary: '检查 API Key 是否健康',
...@@ -24,7 +123,7 @@ export const ApiKeyPath: OpenAPIPath = { ...@@ -24,7 +123,7 @@ export const ApiKeyPath: OpenAPIPath = {
description: 'ApiKey错误', description: 'ApiKey错误',
content: { content: {
'application/json': { 'application/json': {
schema: z.object({ message: z.literal('APIKey invalid') }) schema: ApiKeyHealthErrorResponseSchema
} }
} }
} }
......
...@@ -2,9 +2,159 @@ import z from 'zod'; ...@@ -2,9 +2,159 @@ import z from 'zod';
import { PublishChannelEnum } from '../../../support/outLink/constant'; import { PublishChannelEnum } from '../../../support/outLink/constant';
import { ObjectIdSchema } from '../../../common/type/mongo'; import { ObjectIdSchema } from '../../../common/type/mongo';
const OptionalDateSchema = z.preprocess((value) => {
if (value === undefined || value === null || value === '') return undefined;
if (value instanceof Date && Number.isNaN(value.getTime())) return undefined;
return value;
}, z.coerce.date().optional());
const OutLinkLimitSchema = z
.object({
expiredTime: OptionalDateSchema.meta({ description: '过期时间' }),
QPM: z.number().optional().default(1000).meta({ example: 60, description: '每分钟问题数限制' }),
maxUsagePoints: z.number().optional().default(-1).meta({
example: -1,
description: '最大积分用量限制'
}),
hookUrl: z.string().optional().meta({ description: '校验消息回调地址' })
})
.meta({ description: '发布渠道限制配置' });
const OutLinkAppConfigSchema = z.any().optional().meta({
description: '第三方平台配置,不同发布渠道结构不同'
});
export const OutLinkEditSchema = z.object({
_id: ObjectIdSchema.optional().meta({ description: '发布渠道 ID,更新时必填' }),
name: z.string().min(1).meta({ example: '公开访问链接', description: '发布渠道名称' }),
showCite: z.boolean().optional().meta({ description: '是否显示引用' }),
showRunningStatus: z.boolean().optional().meta({ description: '是否显示运行状态' }),
showSkillReferences: z.boolean().optional().meta({ description: '是否显示技能引用' }),
showFullText: z.boolean().optional().meta({ description: '是否显示全文' }),
canDownloadSource: z.boolean().optional().meta({ description: '是否允许下载来源文件' }),
immediateResponse: z.string().optional().meta({ description: '立即回复内容' }),
defaultResponse: z.string().optional().meta({ description: '默认回复内容' }),
limit: OutLinkLimitSchema.optional().meta({
description: '发布渠道访问限制配置'
}),
app: OutLinkAppConfigSchema
});
export type OutLinkEditType = z.infer<typeof OutLinkEditSchema>;
export const OutLinkSchema = z.object({
_id: ObjectIdSchema.meta({ description: '发布渠道 ID' }),
shareId: z.string().meta({ description: '发布渠道访问 ID' }),
teamId: ObjectIdSchema.meta({ description: '团队 ID' }),
tmbId: ObjectIdSchema.meta({ description: '团队成员 ID' }),
appId: ObjectIdSchema.meta({ description: '应用 ID' }),
name: z.string().meta({ description: '发布渠道名称' }),
usagePoints: z.number().optional().default(0).meta({ description: '累计使用积分' }),
lastTime: z.coerce.date().optional().meta({ description: '最后使用时间' }),
type: z.enum(PublishChannelEnum).meta({ description: '发布渠道类型' }),
showCite: z.boolean().optional().default(false).meta({ description: '是否显示引用' }),
showRunningStatus: z
.boolean()
.optional()
.default(false)
.meta({ description: '是否显示运行状态' }),
showSkillReferences: z
.boolean()
.optional()
.default(false)
.meta({ description: '是否显示技能引用' }),
showFullText: z.boolean().optional().default(false).meta({ description: '是否显示全文' }),
canDownloadSource: z.boolean().optional().default(false).meta({
description: '是否允许下载来源文件'
}),
showWholeResponse: z.boolean().optional().default(true).meta({ description: '是否显示完整响应' }),
immediateResponse: z.string().optional().meta({ description: '立即回复内容' }),
defaultResponse: z.string().optional().meta({ description: '默认回复内容' }),
limit: OutLinkLimitSchema.optional().meta({
description: '发布渠道访问限制配置'
}),
app: OutLinkAppConfigSchema,
responseDetail: z.boolean().optional().meta({ description: '已废弃:是否显示响应详情' }),
showNodeStatus: z.boolean().optional().meta({ description: '已废弃:是否显示节点状态' }),
showRawSource: z.boolean().optional().meta({ description: '已废弃:是否显示原始来源' })
});
export type OutLinkSchemaType = z.infer<typeof OutLinkSchema>;
// ============= OutLink List ============= // ============= OutLink List =============
export const OutLinkListQuerySchema = z.object({ export const OutLinkListQuerySchema = z.object({
appId: ObjectIdSchema.describe('应用ID'), appId: ObjectIdSchema.meta({
type: z.enum(PublishChannelEnum).describe('发布渠道类型') example: '68ad85a7463006c963799a05',
description: '应用 ID'
}),
type: z.enum(PublishChannelEnum).meta({
example: PublishChannelEnum.share,
description: '发布渠道类型'
})
}); });
export type OutLinkListQueryType = z.infer<typeof OutLinkListQuerySchema>; export type OutLinkListQueryType = z.infer<typeof OutLinkListQuerySchema>;
export const OutLinkListResponseSchema = z.array(OutLinkSchema).meta({
description: '发布渠道列表'
});
export type OutLinkListResponseType = z.infer<typeof OutLinkListResponseSchema>;
/* ============================================================================
* API: 创建发布渠道
* Route: POST /api/support/outLink/create
* Method: POST
* Description: 为指定应用创建发布渠道。
* Tags: ['发布渠道']
* ============================================================================ */
export const OutLinkCreateBodySchema = OutLinkEditSchema.omit({
_id: true
}).extend({
appId: ObjectIdSchema.meta({ description: '应用 ID' }),
type: z.enum(PublishChannelEnum).meta({ description: '发布渠道类型' })
});
export type OutLinkCreateBodyType = z.infer<typeof OutLinkCreateBodySchema>;
export const OutLinkCreateResponseSchema = z.string().meta({
description: '发布渠道 shareId'
});
export type OutLinkCreateResponseType = z.infer<typeof OutLinkCreateResponseSchema>;
/* ============================================================================
* API: 更新发布渠道
* Route: PUT /api/support/outLink/update
* Method: PUT
* Description: 更新发布渠道配置。
* Tags: ['发布渠道']
* ============================================================================ */
export const OutLinkUpdateBodySchema = OutLinkEditSchema.extend({
_id: ObjectIdSchema.meta({ description: '发布渠道 ID' })
});
export type OutLinkUpdateBodyType = z.infer<typeof OutLinkUpdateBodySchema>;
export const OutLinkUpdateResponseSchema = z.string().meta({
description: '发布渠道 shareId'
});
export type OutLinkUpdateResponseType = z.infer<typeof OutLinkUpdateResponseSchema>;
/* ============================================================================
* API: 删除发布渠道
* Route: DELETE /api/support/outLink/delete
* Method: DELETE
* Description: 删除指定发布渠道。
* Tags: ['发布渠道']
* ============================================================================ */
export const OutLinkDeleteQuerySchema = z.object({
id: ObjectIdSchema.meta({ description: '发布渠道 ID' })
});
export type OutLinkDeleteQueryType = z.infer<typeof OutLinkDeleteQuerySchema>;
export const OutLinkDeleteBodySchema = z.object({}).meta({
description: '删除发布渠道不需要请求体'
});
export type OutLinkDeleteBodyType = z.infer<typeof OutLinkDeleteBodySchema>;
export const OutLinkDeleteResponseSchema = z.undefined().meta({
description: '删除成功'
});
export type OutLinkDeleteResponseType = z.infer<typeof OutLinkDeleteResponseSchema>;
import type { OpenAPIPath } from '../../type'; import type { OpenAPIPath } from '../../type';
import { TagsMap } from '../../tag'; import { TagsMap } from '../../tag';
import { OutLinkListQuerySchema } from './api'; import {
OutLinkCreateBodySchema,
OutLinkCreateResponseSchema,
OutLinkDeleteQuerySchema,
OutLinkDeleteResponseSchema,
OutLinkListQuerySchema,
OutLinkListResponseSchema,
OutLinkUpdateBodySchema,
OutLinkUpdateResponseSchema
} from './api';
export const OutLinkPath: OpenAPIPath = { export const OutLinkPath: OpenAPIPath = {
'/support/outLink/list': { '/support/outLink/list': {
...@@ -13,7 +22,80 @@ export const OutLinkPath: OpenAPIPath = { ...@@ -13,7 +22,80 @@ export const OutLinkPath: OpenAPIPath = {
}, },
responses: { responses: {
200: { 200: {
description: '成功返回发布渠道列表' description: '成功返回发布渠道列表',
content: {
'application/json': {
schema: OutLinkListResponseSchema
}
}
}
}
}
},
'/support/outLink/create': {
post: {
summary: '创建发布渠道',
description: '为指定应用创建发布渠道',
tags: [TagsMap.publishChannel],
requestBody: {
content: {
'application/json': {
schema: OutLinkCreateBodySchema
}
}
},
responses: {
200: {
description: '成功创建发布渠道',
content: {
'application/json': {
schema: OutLinkCreateResponseSchema
}
}
}
}
}
},
'/support/outLink/update': {
put: {
summary: '更新发布渠道',
description: '更新发布渠道配置',
tags: [TagsMap.publishChannel],
requestBody: {
content: {
'application/json': {
schema: OutLinkUpdateBodySchema
}
}
},
responses: {
200: {
description: '成功更新发布渠道',
content: {
'application/json': {
schema: OutLinkUpdateResponseSchema
}
}
}
}
}
},
'/support/outLink/delete': {
delete: {
summary: '删除发布渠道',
description: '删除指定发布渠道',
tags: [TagsMap.publishChannel],
requestParams: {
query: OutLinkDeleteQuerySchema
},
responses: {
200: {
description: '成功删除发布渠道',
content: {
'application/json': {
schema: OutLinkDeleteResponseSchema
}
}
} }
} }
} }
......
import z from 'zod';
import { ObjectIdSchema } from '../../../common/type/mongo';
import {
CollaboratorItemSchema,
CollaboratorListSchema
} from '../../../support/permission/collaborator.schema';
const AppIdSchema = ObjectIdSchema.meta({
example: '68ad85a7463006c963799a05',
description: '应用 ID'
});
export const CollaboratorListResponseSchema = CollaboratorListSchema;
export type CollaboratorListResponseType = z.infer<typeof CollaboratorListResponseSchema>;
export const EmptyPermissionResponseSchema = z.undefined().meta({
description: '操作成功'
});
/* ============================================================================
* API: 获取应用协作者列表
* Route: GET /api/proApi/core/app/collaborator/list
* Method: GET
* Description: 获取应用或应用文件夹的协作者列表,包含继承权限场景下的父级协作者信息。
* Tags: ['协作者管理', '权限管理']
* ============================================================================ */
export const GetAppCollaboratorListQuerySchema = z.object({
appId: AppIdSchema
});
export type GetAppCollaboratorListQueryType = z.infer<typeof GetAppCollaboratorListQuerySchema>;
export const GetAppCollaboratorListResponseSchema = CollaboratorListResponseSchema;
export type GetAppCollaboratorListResponseType = z.infer<
typeof GetAppCollaboratorListResponseSchema
>;
/* ============================================================================
* API: 更新应用协作者
* Route: POST /api/proApi/core/app/collaborator/update
* Method: POST
* Description: 覆盖更新应用或应用文件夹的协作者权限;继承权限场景会按资源类型处理继承关系。
* Tags: ['协作者管理', '权限管理']
* ============================================================================ */
export const UpdateAppCollaboratorBodySchema = z
.object({
appId: AppIdSchema,
collaborators: z.array(CollaboratorItemSchema).meta({ description: '更新后的协作者权限列表' })
})
.meta({
example: {
appId: '68ad85a7463006c963799a05',
collaborators: [
{
tmbId: '68ad85a7463006c963799a06',
permission: 4
}
]
}
});
export type UpdateAppCollaboratorBodyType = z.infer<typeof UpdateAppCollaboratorBodySchema>;
export const UpdateAppCollaboratorResponseSchema = EmptyPermissionResponseSchema;
export type UpdateAppCollaboratorResponseType = z.infer<typeof UpdateAppCollaboratorResponseSchema>;
import type { OpenAPIPath } from '../../type';
import { TagsMap } from '../../tag';
import {
GetAppCollaboratorListQuerySchema,
GetAppCollaboratorListResponseSchema,
UpdateAppCollaboratorBodySchema,
UpdateAppCollaboratorResponseSchema
} from './api';
export const PermissionPath: OpenAPIPath = {
'/proApi/core/app/collaborator/list': {
get: {
summary: '获取应用协作者列表',
description: '获取应用或应用文件夹的协作者列表,包含继承权限场景下的父级协作者信息',
tags: [TagsMap.permissionCollaborator, TagsMap.appPer],
requestParams: {
query: GetAppCollaboratorListQuerySchema
},
responses: {
200: {
description: '成功获取应用协作者列表',
content: {
'application/json': {
schema: GetAppCollaboratorListResponseSchema
}
}
}
}
}
},
'/proApi/core/app/collaborator/update': {
post: {
summary: '更新应用协作者',
description: '覆盖更新应用或应用文件夹的协作者权限',
tags: [TagsMap.permissionCollaborator, TagsMap.appPer],
requestBody: {
content: {
'application/json': {
schema: UpdateAppCollaboratorBodySchema
}
}
},
responses: {
200: {
description: '成功更新应用协作者',
content: {
'application/json': {
schema: UpdateAppCollaboratorResponseSchema
}
}
}
}
}
}
};
...@@ -46,7 +46,7 @@ export const ResetExpiredPswBodySchema = z ...@@ -46,7 +46,7 @@ export const ResetExpiredPswBodySchema = z
}); });
export type ResetExpiredPswBodyType = z.infer<typeof ResetExpiredPswBodySchema>; export type ResetExpiredPswBodyType = z.infer<typeof ResetExpiredPswBodySchema>;
export const ResetExpiredPswResponseSchema = z.object({}).meta({ export const ResetExpiredPswResponseSchema = z.undefined().meta({
description: '重置成功' description: '重置成功'
}); });
export type ResetExpiredPswResponseType = z.infer<typeof ResetExpiredPswResponseSchema>; export type ResetExpiredPswResponseType = z.infer<typeof ResetExpiredPswResponseSchema>;
......
...@@ -5,7 +5,7 @@ export const TeamChangeOwnerBodySchema = z.object({ ...@@ -5,7 +5,7 @@ export const TeamChangeOwnerBodySchema = z.object({
userId: z.string().describe("the New Owner's UserId.") userId: z.string().describe("the New Owner's UserId.")
}); });
export const TeamChangeOwnerResponseSchema = z.object(); export const TeamChangeOwnerResponseSchema = z.undefined().meta({ description: '操作成功' });
export type TeamChangeOwnerBodyType = z.infer<typeof TeamChangeOwnerBodySchema>; export type TeamChangeOwnerBodyType = z.infer<typeof TeamChangeOwnerBodySchema>;
export type TeamChangeOwnerResponseType = z.infer<typeof TeamChangeOwnerResponseSchema>; export type TeamChangeOwnerResponseType = z.infer<typeof TeamChangeOwnerResponseSchema>;
...@@ -52,5 +52,5 @@ export const UpdateTeamBodySchema = z.object({ ...@@ -52,5 +52,5 @@ export const UpdateTeamBodySchema = z.object({
}); });
export type UpdateTeamBodyType = z.infer<typeof UpdateTeamBodySchema>; export type UpdateTeamBodyType = z.infer<typeof UpdateTeamBodySchema>;
export const UpdateTeamResponseSchema = z.object({}); export const UpdateTeamResponseSchema = z.undefined().meta({ description: '更新成功' });
export type UpdateTeamResponseType = z.infer<typeof UpdateTeamResponseSchema>; export type UpdateTeamResponseType = z.infer<typeof UpdateTeamResponseSchema>;
export const TagsMap = { export const TagsMap = {
/* Core */ /* 核心-应用管理 */
// Helper appCommon: '基础管理',
helperBot: '辅助助手', appFolder: '文件夹管理',
// Agent - log appPer: '权限管理',
appLog: 'Agent 日志', appVersion: '版本管理',
// Ai -skill appTemplate: '模板管理',
aiSkill: 'AI技能管理', appLog: '日志管理',
// AI publishChannel: '发布渠道',
aiCommon: 'AI 通用接口',
// Sandbox
sandbox: 'AI 沙盒',
// App 管理 /* 核心-工具管理 */
// Agent - common
appCommon: '应用管理',
appPer: '应用权限',
mcpTools: 'MCP 工具管理', mcpTools: 'MCP 工具管理',
httpTools: 'HTTP 工具管理', httpTools: 'HTTP 工具管理',
mcpServer: 'MCP 发布管理',
/* 核心-AI 相关 */
aiSkill: 'AI技能管理',
sandbox: 'AI 沙盒',
aiCommon: 'AI 通用接口',
/* ===== Chat ===== */ /* 核心-对话模块配置 */
chatPage: '对话页面通用', chatSetting: '门户页配置',
chatPage: '对话页面配置',
chatInputGuide: '对话输入引导',
/* 核心-对话模块使用 */
chatHistory: '历史记录管理', chatHistory: '历史记录管理',
chatFeedback: '对话反馈', chatFeedback: '对话反馈',
chatFile: '文件操作', chatFile: '文件操作',
chatRecord: '对话记录管理', chatRecord: '对话记录管理',
chatController: '对话操作', chatController: '对话操作',
chatSetting: '门户页配置',
// 辅助功能
chatInputGuide: '对话输入引导',
// Dataset /* 核心-知识库 */
datasetCommon: '知识库管理', datasetCommon: '知识库管理',
datasetCollection: '集合管理', datasetCollection: '集合管理',
datasetCollectionCrteate: '知识库集合创建', datasetCollectionCrteate: '知识库集合创建',
...@@ -38,37 +39,35 @@ export const TagsMap = { ...@@ -38,37 +39,35 @@ export const TagsMap = {
datasetApiDataset: 'API 数据集管理', datasetApiDataset: 'API 数据集管理',
datasetFile: '文件管理', datasetFile: '文件管理',
// Plugin /* 核心-插件系统 */
pluginToolTag: '工具标签', pluginToolTag: '工具标签',
pluginTeam: '团队插件管理', pluginTeam: '团队插件管理',
// Publish Channel /* 辅助-用户体系 */
publishChannel: '发布渠道', userInform: '用户通知',
mcpServer: 'MCP 发布管理', userLogin: '用户账号',
/* Support */
// Team
teamManage: '团队管理',
// Wallet
walletBill: '订单', walletBill: '订单',
walletDiscountCoupon: '优惠券', walletDiscountCoupon: '优惠券',
/* 辅助-权限管理 */
permissionResource: '资源权限',
permissionCollaborator: '协作者管理',
/* 通用-基础功能 */
apiKey: 'API Key 管理',
customDomain: '自定义域名', customDomain: '自定义域名',
// User helperBot: '辅助助手',
userInform: '用户通知',
userLogin: '用户账号',
/* Common */ /* 辅助-团队管理 */
// APIKey teamManage: '团队管理',
apiKey: 'APIKey',
/* Admin */ /* 管理员-插件管理 */
// Plugin
pluginMarketplace: '插件市场', pluginMarketplace: '插件市场',
pluginAdmin: '管理员插件管理', pluginAdmin: '管理员插件管理',
pluginToolAdmin: '管理员系统工具管理', pluginToolAdmin: '管理员系统工具管理',
// Data
/* 管理员-系统管理 */
adminDashboard: '仪表盘', adminDashboard: '仪表盘',
// Inform
adminInform: '通知管理', adminInform: '通知管理',
adminApps: '应用管理' adminApps: '应用管理'
}; };
import z from 'zod'; import z from 'zod';
import type { HistoryItemType } from '../../core/chat/type'; import type { OutLinkSchemaType } from './type';
import type { OutLinkSchemaType, PlaygroundVisibilityConfigType } from './type';
import { PlaygroundVisibilityConfigSchema } from './type'; import { PlaygroundVisibilityConfigSchema } from './type';
export type AuthOutLinkInitProps = { export type AuthOutLinkInitProps = {
...@@ -24,8 +23,3 @@ export const PlaygroundVisibilityConfigQuerySchema = z.object({ ...@@ -24,8 +23,3 @@ export const PlaygroundVisibilityConfigQuerySchema = z.object({
appId: z.string().min(1, 'App ID is required') appId: z.string().min(1, 'App ID is required')
}); });
export type PlaygroundVisibilityConfigQuery = z.infer<typeof PlaygroundVisibilityConfigQuerySchema>; export type PlaygroundVisibilityConfigQuery = z.infer<typeof PlaygroundVisibilityConfigQuerySchema>;
export const PlaygroundVisibilityConfigResponseSchema = PlaygroundVisibilityConfigSchema;
export type PlaygroundVisibilityConfigResponse = z.infer<
typeof PlaygroundVisibilityConfigResponseSchema
>;
import z from 'zod'; import z from 'zod';
import type { PublishChannelEnum } from './constant'; import type { PublishChannelEnum } from './constant';
import { BoolSchema } from '../../common/zod';
// Feishu Config interface // Feishu Config interface
export interface FeishuAppType { export interface FeishuAppType {
...@@ -126,12 +127,30 @@ export type OutLinkEditType<T extends OutlinkAppType = undefined> = { ...@@ -126,12 +127,30 @@ export type OutLinkEditType<T extends OutlinkAppType = undefined> = {
export type OutLinkSchema<T extends OutlinkAppType = undefined> = OutLinkSchemaType<T>; export type OutLinkSchema<T extends OutlinkAppType = undefined> = OutLinkSchemaType<T>;
export const PlaygroundVisibilityConfigSchema = z.object({ export const PlaygroundVisibilityConfigSchema = z.object({
showRunningStatus: z.boolean(), showRunningStatus: BoolSchema.meta({
showSkillReferences: z.boolean().optional().default(true), example: true,
showCite: z.boolean().optional().default(true), description: '是否显示运行状态'
showFullText: z.boolean().optional().default(true), }),
canDownloadSource: z.boolean().optional().default(true), showSkillReferences: BoolSchema.optional().default(false).meta({
showWholeResponse: z.boolean().optional().default(true) example: false,
description: '是否显示技能引用'
}),
showCite: BoolSchema.optional().default(true).meta({
example: true,
description: '是否显示引用'
}),
showFullText: BoolSchema.optional().default(true).meta({
example: true,
description: '是否显示全文'
}),
canDownloadSource: BoolSchema.optional().default(true).meta({
example: true,
description: '是否可下载来源'
}),
showWholeResponse: BoolSchema.optional().default(true).meta({
example: true,
description: '是否显示完整响应按钮'
})
}); });
export type PlaygroundVisibilityConfigType = z.infer<typeof PlaygroundVisibilityConfigSchema>; export type PlaygroundVisibilityConfigType = z.infer<typeof PlaygroundVisibilityConfigSchema>;
import z from 'zod';
import { BoolSchema, IntSchema } from '../../../common/zod';
import type { AppPermission } from './controller';
// HTTP 响应会把 AppPermission 类实例序列化为普通对象,声明前端实际依赖的应用权限字段。
const AppPermissionObjectSchema = z
.object({
role: IntSchema.meta({ description: '应用权限角色值' }),
isOwner: BoolSchema.meta({ description: '是否为应用所有者' }),
hasManagePer: BoolSchema.meta({ description: '是否拥有应用管理权限' }),
hasWritePer: BoolSchema.meta({ description: '是否拥有应用写权限' }),
hasReadPer: BoolSchema.meta({ description: '是否拥有应用读权限' }),
hasManageRole: BoolSchema.meta({ description: '是否包含应用管理角色' }),
hasWriteRole: BoolSchema.meta({ description: '是否包含应用写角色' }),
hasReadRole: BoolSchema.meta({ description: '是否包含应用读角色' }),
hasReadChatLogPer: BoolSchema.meta({ description: '是否拥有读取应用对话日志权限' }),
hasReadChatLogRole: BoolSchema.meta({ description: '是否包含读取应用对话日志角色' })
})
.passthrough()
.meta({ description: '应用权限对象序列化后的 JSON 结构' });
export const AppPermissionSchema = AppPermissionObjectSchema as unknown as z.ZodType<AppPermission>;
export const AppPermissionCheckSchema = AppPermissionObjectSchema.pick({
hasReadPer: true,
hasWritePer: true,
hasManagePer: true,
hasReadChatLogPer: true,
isOwner: true
}).meta({
description: '当前用户的应用权限检查结果'
});
export type AppPermissionCheckType = z.infer<typeof AppPermissionCheckSchema>;
import z from 'zod';
import { ObjectIdSchema } from '../../common/type/mongo';
import { PermissionSchema } from './controller';
import type {
CollaboratorItemDetailType,
CollaboratorItemType,
CollaboratorListType
} from './collaborator';
const CollaboratorIdShape = {
tmbId: ObjectIdSchema.optional().meta({
example: '68ad85a7463006c963799a06',
description: '授权给单个团队成员时使用的成员 ID'
}),
groupId: ObjectIdSchema.optional().meta({
example: '68ad85a7463006c963799a07',
description: '授权给成员组时使用的成员组 ID'
}),
orgId: ObjectIdSchema.optional().meta({
example: '68ad85a7463006c963799a08',
description: '授权给组织节点时使用的组织 ID'
})
};
const refineCollaboratorId = <T extends { tmbId?: string; groupId?: string; orgId?: string }>(
schema: z.ZodType<T>
) =>
schema.refine(
({ tmbId, groupId, orgId }) => [tmbId, groupId, orgId].filter(Boolean).length === 1,
{
message: 'tmbId, groupId or orgId is required, and only one can be provided'
}
);
export const CollaboratorItemSchema = refineCollaboratorId(
z.object({
...CollaboratorIdShape,
permission: z.number().int().nonnegative().meta({
example: 4,
description: '权限角色值'
})
})
).meta({
description: '协作者权限配置'
}) as z.ZodType<CollaboratorItemType>;
export const CollaboratorItemDetailSchema = refineCollaboratorId(
z.object({
...CollaboratorIdShape,
teamId: ObjectIdSchema.meta({ description: '团队 ID' }),
permission: PermissionSchema,
name: z.string().meta({ description: '协作者名称' }),
avatar: z.string().meta({ description: '协作者头像' })
})
).meta({
description: '协作者详情'
}) as z.ZodType<CollaboratorItemDetailType>;
export const CollaboratorListSchema = z
.object({
clbs: z.array(CollaboratorItemDetailSchema).meta({ description: '当前资源协作者列表' }),
parentClbs: z
.array(CollaboratorItemDetailSchema)
.optional()
.meta({ description: '父级资源协作者列表' })
})
.meta({
description: '资源协作者列表,包含当前资源和可继承的父级资源协作者'
}) as z.ZodType<CollaboratorListType>;
...@@ -137,7 +137,17 @@ export class Permission { ...@@ -137,7 +137,17 @@ export class Permission {
} }
} }
// 仅用于 TypeScript 类型推导,运行时不做实例验证 // HTTP 响应会把 Permission 类实例序列化为普通对象,OpenAPI 需要声明前端实际依赖的 JSON 字段。
export const PermissionSchema = z export const PermissionSchema = z
.custom<Permission>(() => true) .object({
.meta({ description: '权限对象(Permission 类实例)' }); role: z.number().int().meta({ description: '权限角色值' }),
isOwner: z.boolean().meta({ description: '是否为所有者' }),
hasManagePer: z.boolean().meta({ description: '是否拥有管理权限' }),
hasWritePer: z.boolean().meta({ description: '是否拥有写权限' }),
hasReadPer: z.boolean().meta({ description: '是否拥有读权限' }),
hasManageRole: z.boolean().meta({ description: '是否包含管理角色' }),
hasWriteRole: z.boolean().meta({ description: '是否包含写角色' }),
hasReadRole: z.boolean().meta({ description: '是否包含读角色' })
})
.passthrough()
.meta({ description: '权限对象序列化后的 JSON 结构' }) as unknown as z.ZodType<Permission>;
...@@ -14,7 +14,7 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchemaType) => ...@@ -14,7 +14,7 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchemaType) =>
if (version) { if (version) {
return { return {
versionId: version._id, versionId: String(version._id),
versionName: version.versionName, versionName: version.versionName,
nodes: version.nodes, nodes: version.nodes,
edges: version.edges, edges: version.edges,
...@@ -48,7 +48,7 @@ export const getAppVersionById = async ({ ...@@ -48,7 +48,7 @@ export const getAppVersionById = async ({
if (version) { if (version) {
return { return {
versionId: version._id, versionId: String(version._id),
versionName: version.versionName, versionName: version.versionName,
nodes: version.nodes, nodes: version.nodes,
edges: version.edges, edges: version.edges,
......
...@@ -2,6 +2,7 @@ import { type SourceMemberType } from '@fastgpt/global/support/user/type'; ...@@ -2,6 +2,7 @@ import { type SourceMemberType } from '@fastgpt/global/support/user/type';
import { MongoTeam } from './team/teamSchema'; import { MongoTeam } from './team/teamSchema';
import { MongoTeamMember } from './team/teamMemberSchema'; import { MongoTeamMember } from './team/teamMemberSchema';
import { type ClientSession } from '../../common/mongo'; import { type ClientSession } from '../../common/mongo';
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
/* export dataset limit */ /* export dataset limit */
export const updateExportDatasetLimit = async (teamId: string) => { export const updateExportDatasetLimit = async (teamId: string) => {
...@@ -9,7 +10,7 @@ export const updateExportDatasetLimit = async (teamId: string) => { ...@@ -9,7 +10,7 @@ export const updateExportDatasetLimit = async (teamId: string) => {
await MongoTeam.findByIdAndUpdate(teamId, { await MongoTeam.findByIdAndUpdate(teamId, {
'limit.lastExportDatasetTime': new Date() 'limit.lastExportDatasetTime': new Date()
}); });
} catch (error) {} } catch {}
}; };
export const checkExportDatasetLimit = async ({ export const checkExportDatasetLimit = async ({
teamId, teamId,
...@@ -43,7 +44,7 @@ export const updateWebSyncLimit = async (teamId: string) => { ...@@ -43,7 +44,7 @@ export const updateWebSyncLimit = async (teamId: string) => {
await MongoTeam.findByIdAndUpdate(teamId, { await MongoTeam.findByIdAndUpdate(teamId, {
'limit.lastWebsiteSyncTime': new Date() 'limit.lastWebsiteSyncTime': new Date()
}); });
} catch (error) {} } catch {}
}; };
export const checkWebSyncLimit = async ({ export const checkWebSyncLimit = async ({
teamId, teamId,
...@@ -109,7 +110,11 @@ export async function addSourceMember<T extends { tmbId: string }>({ ...@@ -109,7 +110,11 @@ export async function addSourceMember<T extends { tmbId: string }>({
return { return {
...formatItem, ...formatItem,
sourceMember: { name: tmb.name, avatar: tmb.avatar, status: tmb.status } sourceMember: {
name: tmb.name,
avatar: tmb.avatar,
status: tmb.status ?? TeamMemberStatusEnum.active
}
}; };
}) })
.filter(Boolean) as Array<T & { sourceMember: SourceMemberType }>; .filter(Boolean) as Array<T & { sourceMember: SourceMemberType }>;
......
...@@ -695,8 +695,10 @@ describe('getTeamStandPlan', () => { ...@@ -695,8 +695,10 @@ describe('getTeamStandPlan', () => {
currentExtraDatasetSize: 0 currentExtraDatasetSize: 0
}; };
// getTeamStandPlan 不使用 .lean(),直接返回数组 const mockQuery = {
vi.spyOn(MongoTeamSub, 'find').mockResolvedValue([mockPlan] as any); lean: vi.fn().mockResolvedValue([mockPlan])
};
vi.spyOn(MongoTeamSub, 'find').mockReturnValue(mockQuery as any);
(global as any).subPlans = { (global as any).subPlans = {
standard: { standard: {
......
...@@ -69,7 +69,7 @@ const EditFolderModal = ({ ...@@ -69,7 +69,7 @@ const EditFolderModal = ({
isOpen isOpen
onClose={onClose} onClose={onClose}
title={typeMap.title} title={typeMap.title}
size={'md'} size={'sm'}
isCentered isCentered
borderRadius={'10px'} borderRadius={'10px'}
closeOnOverlayClick={false} closeOnOverlayClick={false}
......
Subproject commit 69af7442cacaf436a45cf28998edcd4a0cd189be Subproject commit 2e81134914c8db3b10051254721fbd8b0f566e17
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import type { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import type { AppSchemaType } from '@fastgpt/global/core/app/type'; import type { AppSchemaType } from '@fastgpt/global/core/app/type';
export type AppUpdateParams = {
parentId?: ParentIdType;
name?: string;
type?: AppTypeEnum;
avatar?: string;
intro?: string;
nodes?: AppSchemaType['modules'];
edges?: AppSchemaType['edges'];
chatConfig?: AppSchemaType['chatConfig'];
teamTags?: AppSchemaType['teamTags'];
};
export type PostPublishAppProps = { export type PostPublishAppProps = {
nodes: AppSchemaType['modules']; nodes: AppSchemaType['modules'];
edges: AppSchemaType['edges']; edges: AppSchemaType['edges'];
......
...@@ -67,11 +67,11 @@ const AppCard = ({ ...@@ -67,11 +67,11 @@ const AppCard = ({
return postTransition2Workflow({ appId, createNew: transitionCreateNew }); return postTransition2Workflow({ appId, createNew: transitionCreateNew });
}, },
{ {
onSuccess: ({ id }) => { onSuccess: (response) => {
if (id) { if (response?.id) {
router.replace({ router.replace({
query: { query: {
appId: id appId: response.id
} }
}); });
} else { } else {
......
...@@ -278,7 +278,7 @@ function EditLinkModal({ ...@@ -278,7 +278,7 @@ function EditLinkModal({
setValue, setValue,
watch, watch,
handleSubmit: submitShareChat handleSubmit: submitShareChat
} = useForm({ } = useForm<OutLinkEditType>({
defaultValues: defaultData defaultValues: defaultData
}); });
...@@ -503,7 +503,9 @@ function EditLinkModal({ ...@@ -503,7 +503,9 @@ function EditLinkModal({
</Button> </Button>
<Button <Button
isLoading={creating || updating} isLoading={creating || updating}
onClick={submitShareChat((data) => (isEdit ? onclickUpdate(data) : onclickCreate(data)))} onClick={submitShareChat((data) =>
isEdit && data._id ? onclickUpdate({ ...data, _id: data._id }) : onclickCreate(data)
)}
> >
{t('common:Confirm')} {t('common:Confirm')}
</Button> </Button>
......
...@@ -217,15 +217,19 @@ const TeamCloud = ({ ...@@ -217,15 +217,19 @@ const TeamCloud = ({
const { runAsync: onUpdateVersion, loading: isEditing } = useRequest( const { runAsync: onUpdateVersion, loading: isEditing } = useRequest(
async (item: VersionListItemType, name: string) => { async (item: VersionListItemType, name: string) => {
const versionName = name.trim();
if (!versionName) {
setEditIndex(undefined);
return;
}
await updateAppVersion({ await updateAppVersion({
appId: item.appId, appId: item.appId,
versionName: name, versionName,
versionId: item._id versionId: item._id
}); });
setData((state) => setData((state) =>
state.map((version) => state.map((version) => (version._id === item._id ? { ...version, versionName } : version))
version._id === item._id ? { ...version, versionName: name } : version
)
); );
setEditIndex(undefined); setEditIndex(undefined);
} }
......
...@@ -12,7 +12,8 @@ import { delAppById, getAppDetailById, putAppById } from '@/web/core/app/api'; ...@@ -12,7 +12,8 @@ import { delAppById, getAppDetailById, putAppById } from '@/web/core/app/api';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { type AppChatConfigType, type AppDetailType } from '@fastgpt/global/core/app/type'; import { type AppChatConfigType, type AppDetailType } from '@fastgpt/global/core/app/type';
import { type AppUpdateParams, type PostPublishAppProps } from '@/global/core/app/api'; import { type PostPublishAppProps } from '@/global/core/app/api';
import { type UpdateAppBodyType } from '@fastgpt/global/openapi/core/app/common/api';
import { postPublishApp, getAppLatestVersion } from '@/web/core/app/api/version'; import { postPublishApp, getAppLatestVersion } from '@/web/core/app/api/version';
import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { useRequest } from '@fastgpt/web/hooks/useRequest';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
...@@ -40,7 +41,7 @@ type AppContextType = { ...@@ -40,7 +41,7 @@ type AppContextType = {
appDetail: AppDetailType; appDetail: AppDetailType;
setAppDetail: Dispatch<SetStateAction<AppDetailType>>; setAppDetail: Dispatch<SetStateAction<AppDetailType>>;
loadingApp: boolean; loadingApp: boolean;
updateAppDetail: (data: AppUpdateParams) => Promise<void>; updateAppDetail: (data: UpdateAppBodyType) => Promise<void>;
onOpenInfoEdit: () => void; onOpenInfoEdit: () => void;
onOpenTeamTagModal: () => void; onOpenTeamTagModal: () => void;
onDelApp: () => void; onDelApp: () => void;
...@@ -64,7 +65,7 @@ export const AppContext = createContext<AppContextType>({ ...@@ -64,7 +65,7 @@ export const AppContext = createContext<AppContextType>({
}, },
appDetail: defaultApp, appDetail: defaultApp,
loadingApp: false, loadingApp: false,
updateAppDetail: function (data: AppUpdateParams): Promise<void> { updateAppDetail: function (data: UpdateAppBodyType): Promise<void> {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}, },
setAppDetail: function (value: SetStateAction<AppDetailType>): void { setAppDetail: function (value: SetStateAction<AppDetailType>): void {
...@@ -152,7 +153,7 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => { ...@@ -152,7 +153,7 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
} }
); );
const { runAsync: updateAppDetail } = useRequest(async (data: AppUpdateParams) => { const { runAsync: updateAppDetail } = useRequest(async (data: UpdateAppBodyType) => {
await putAppById(appId, data); await putAppById(appId, data);
setAppDetail((state) => ({ setAppDetail((state) => ({
...state, ...state,
......
...@@ -11,9 +11,10 @@ import { navbarWidth } from '@/components/Layout'; ...@@ -11,9 +11,10 @@ import { navbarWidth } from '@/components/Layout';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { getTemplateMarketItemList, getTemplateTagList } from '@/web/core/app/api/template'; import { getTemplateMarketItemList, getTemplateTagList } from '@/web/core/app/api/template';
import type { AppTemplateSchemaType, TemplateTypeSchemaType } from '@fastgpt/global/core/app/type'; import type { TemplateTypeSchemaType } from '@fastgpt/global/core/app/type';
import TeamPlanStatusCard from './TeamPlanStatusCard'; import TeamPlanStatusCard from './TeamPlanStatusCard';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import type { AppTemplateListItemType } from '@fastgpt/global/openapi/core/app/template/api';
export enum TabEnum { export enum TabEnum {
agent = 'agent', agent = 'agent',
...@@ -31,7 +32,7 @@ const DashboardContainer = ({ ...@@ -31,7 +32,7 @@ const DashboardContainer = ({
}: { }: {
children: (e: { children: (e: {
templateTags: TemplateTypeSchemaType[]; templateTags: TemplateTypeSchemaType[];
templateList: AppTemplateSchemaType[]; templateList: AppTemplateListItemType[];
MenuIcon: JSX.Element; MenuIcon: JSX.Element;
}) => React.ReactNode; }) => React.ReactNode;
}) => { }) => {
......
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
type ParentIdType, type ParentIdType,
type ParentTreePathItemType type ParentTreePathItemType
} from '@fastgpt/global/common/parentFolder/type'; } from '@fastgpt/global/common/parentFolder/type';
import { type AppUpdateParams } from '@/global/core/app/api'; import { type UpdateAppBodyType } from '@fastgpt/global/openapi/core/app/common/api';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
...@@ -25,7 +25,7 @@ type AppListContextType = { ...@@ -25,7 +25,7 @@ type AppListContextType = {
isFetchingApps: boolean; isFetchingApps: boolean;
folderDetail: AppDetailType | undefined | null; folderDetail: AppDetailType | undefined | null;
paths: ParentTreePathItemType[]; paths: ParentTreePathItemType[];
onUpdateApp: (id: string, data: AppUpdateParams) => Promise<any>; onUpdateApp: (id: string, data: UpdateAppBodyType) => Promise<any>;
setMoveAppId: React.Dispatch<React.SetStateAction<string | undefined>>; setMoveAppId: React.Dispatch<React.SetStateAction<string | undefined>>;
refetchFolderDetail: () => Promise<AppDetailType | null>; refetchFolderDetail: () => Promise<AppDetailType | null>;
searchKey: string; searchKey: string;
...@@ -41,7 +41,7 @@ export const AppListContext = createContext<AppListContextType>({ ...@@ -41,7 +41,7 @@ export const AppListContext = createContext<AppListContextType>({
isFetchingApps: false, isFetchingApps: false,
folderDetail: undefined, folderDetail: undefined,
paths: [], paths: [],
onUpdateApp: function (id: string, data: AppUpdateParams): Promise<any> { onUpdateApp: function (id: string, data: UpdateAppBodyType): Promise<any> {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}, },
setMoveAppId: function (value: React.SetStateAction<string | undefined>): void { setMoveAppId: function (value: React.SetStateAction<string | undefined>): void {
...@@ -131,7 +131,7 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => { ...@@ -131,7 +131,7 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
} }
); );
const { runAsync: onUpdateApp } = useRequest((id: string, data: AppUpdateParams) => const { runAsync: onUpdateApp } = useRequest((id: string, data: UpdateAppBodyType) =>
putAppById(id, data).then(async (res) => { putAppById(id, data).then(async (res) => {
await Promise.all([refetchFolderDetail(), refetchPaths(), loadMyApps()]); await Promise.all([refetchFolderDetail(), refetchPaths(), loadMyApps()]);
return res; return res;
......
...@@ -329,7 +329,7 @@ const EditMcpModal = ({ ...@@ -329,7 +329,7 @@ const EditMcpModal = ({
<FormLabel required mb={0.5}> <FormLabel required mb={0.5}>
{t('common:input_name')} {t('common:input_name')}
</FormLabel> </FormLabel>
<Input {...register('name', { required: true })} bg={'myGray.50'} /> <Input {...register('name', { required: true })} maxLength={100} bg={'myGray.50'} />
</Box> </Box>
<Box mt={6}> <Box mt={6}>
<Flex justifyContent={'space-between'} alignItems={'center'}> <Flex justifyContent={'space-between'} alignItems={'center'}>
...@@ -358,6 +358,7 @@ const EditMcpModal = ({ ...@@ -358,6 +358,7 @@ const EditMcpModal = ({
<Td> <Td>
<Input <Input
{...register(`apps.${index}.toolName`, { required: true })} {...register(`apps.${index}.toolName`, { required: true })}
maxLength={100}
placeholder={t('dashboard_mcp:tool_name_placeholder')} placeholder={t('dashboard_mcp:tool_name_placeholder')}
bg={'myGray.50'} bg={'myGray.50'}
w={'100%'} w={'100%'}
...@@ -367,6 +368,7 @@ const EditMcpModal = ({ ...@@ -367,6 +368,7 @@ const EditMcpModal = ({
<Td> <Td>
<Input <Input
{...register(`apps.${index}.description`, { required: true })} {...register(`apps.${index}.description`, { required: true })}
maxLength={2000}
bg={'myGray.50'} bg={'myGray.50'}
w={'100%'} w={'100%'}
/> />
......
...@@ -3,7 +3,7 @@ import { WritePermissionVal } from '@fastgpt/global/support/permission/constant' ...@@ -3,7 +3,7 @@ import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'
import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant'; import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { onCreateApp } from './create'; import { onCreateApp } from './create';
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants'; import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
import { addAuditLog } from '@fastgpt/service/support/user/audit/util'; import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
...@@ -11,24 +11,25 @@ import { getI18nAppType } from '@fastgpt/service/support/user/audit/util'; ...@@ -11,24 +11,25 @@ import { getI18nAppType } from '@fastgpt/service/support/user/audit/util';
import { copyAvatarImage } from '@fastgpt/service/common/file/image/controller'; import { copyAvatarImage } from '@fastgpt/service/common/file/image/controller';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { getS3AvatarSource } from '@fastgpt/service/common/s3/sources/avatar'; import { getS3AvatarSource } from '@fastgpt/service/common/s3/sources/avatar';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
import {
CopyAppBodySchema,
CopyAppResponseSchema,
type CopyAppBodyType,
type CopyAppResponseType
} from '@fastgpt/global/openapi/core/app/common/api';
export type copyAppQuery = {}; async function handler(req: ApiRequestProps<CopyAppBodyType>): Promise<CopyAppResponseType> {
const { appId: sourceAppId } = parseApiInput({
export type copyAppBody = { appId: string }; req,
bodySchema: CopyAppBodySchema
export type copyAppResponse = { }).body;
appId: string;
};
async function handler(
req: ApiRequestProps<copyAppBody, copyAppQuery>,
res: ApiResponseType<any>
): Promise<copyAppResponse> {
const { app, teamId } = await authApp({ const { app, teamId } = await authApp({
req, req,
authToken: true, authToken: true,
per: WritePermissionVal, per: WritePermissionVal,
appId: req.body.appId appId: sourceAppId
}); });
const { tmbId } = app.parentId const { tmbId } = app.parentId
...@@ -76,7 +77,7 @@ async function handler( ...@@ -76,7 +77,7 @@ async function handler(
}); });
})(); })();
return { appId }; return CopyAppResponseSchema.parse({ appId });
} }
export default NextAPI(handler); export default NextAPI(handler);
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest } from 'next';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant'; import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
...@@ -11,9 +11,18 @@ import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils'; ...@@ -11,9 +11,18 @@ import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
import { addAuditLog } from '@fastgpt/service/support/user/audit/util'; import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants'; import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
import { getI18nAppType } from '@fastgpt/service/support/user/audit/util'; import { getI18nAppType } from '@fastgpt/service/support/user/audit/util';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
import {
DeleteAppQuerySchema,
DeleteAppResponseSchema,
type DeleteAppResponseType
} from '@fastgpt/global/openapi/core/app/common/api';
async function handler(req: NextApiRequest, res: NextApiResponse<string[]>) { async function handler(req: NextApiRequest): Promise<DeleteAppResponseType> {
const { appId } = req.query as { appId: string }; const { appId } = parseApiInput({
req,
querySchema: DeleteAppQuerySchema
}).query;
if (!appId) { if (!appId) {
return Promise.reject('参数错误'); return Promise.reject('参数错误');
...@@ -68,9 +77,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse<string[]>) { ...@@ -68,9 +77,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse<string[]>) {
// Tracks // Tracks
pushTrack.countAppNodes({ teamId, tmbId, uid: userId, appId }); pushTrack.countAppNodes({ teamId, tmbId, uid: userId, appId });
return deleteAppsList const ids = deleteAppsList
.filter((app) => !['folder'].includes(app.type)) .filter((app) => !['folder'].includes(app.type))
.map((app) => String(app._id)); .map((app) => String(app._id));
return DeleteAppResponseSchema.parse(ids);
} }
export default NextAPI(handler); export default NextAPI(handler);
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest } from 'next';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { rewriteAppWorkflowToDetail } from '@fastgpt/service/core/app/utils'; import { rewriteAppWorkflowToDetail } from '@fastgpt/service/core/app/utils';
import { getLocale } from '@fastgpt/service/common/middle/i18n'; import { getLocale } from '@fastgpt/service/common/middle/i18n';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
import {
GetAppDetailQuerySchema,
GetAppDetailResponseSchema,
type GetAppDetailResponseType
} from '@fastgpt/global/openapi/core/app/common/api';
/* 获取应用详情 */ /* 获取应用详情 */
async function handler(req: NextApiRequest, res: NextApiResponse<any>) { async function handler(req: NextApiRequest): Promise<GetAppDetailResponseType> {
const { appId } = req.query as { appId: string }; const { appId } = parseApiInput({
req,
querySchema: GetAppDetailQuerySchema
}).query;
if (!appId) { if (!appId) {
Promise.reject(CommonErrEnum.missingParams); Promise.reject(CommonErrEnum.missingParams);
...@@ -30,14 +39,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) { ...@@ -30,14 +39,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
}); });
if (!app.permission.hasWritePer) { if (!app.permission.hasWritePer) {
return { return GetAppDetailResponseSchema.parse({
...app, ...app,
modules: [], modules: [],
edges: [] edges: []
}; });
} }
return app; return GetAppDetailResponseSchema.parse(app);
} }
export default NextAPI(handler); export default NextAPI(handler);
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { FolderImgUrl } from '@fastgpt/global/common/file/image/constants'; import { FolderImgUrl } from '@fastgpt/global/common/file/image/constants';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils'; import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { import {
...@@ -18,15 +17,21 @@ import { type ApiRequestProps } from '@fastgpt/service/type/next'; ...@@ -18,15 +17,21 @@ import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { addAuditLog } from '@fastgpt/service/support/user/audit/util'; import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants'; import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
import { checkTeamAppTypeLimit } from '@fastgpt/service/support/permission/teamLimit'; import { checkTeamAppTypeLimit } from '@fastgpt/service/support/permission/teamLimit';
export type CreateAppFolderBody = { import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
parentId?: ParentIdType; import {
name: string; CreateAppFolderBodySchema,
intro?: string; CreateAppFolderResponseSchema,
type: AppTypeEnum.folder | AppTypeEnum.toolFolder; type CreateAppFolderBodyType,
}; type CreateAppFolderResponseType
} from '@fastgpt/global/openapi/core/app/folder/api';
async function handler(req: ApiRequestProps<CreateAppFolderBody>) { async function handler(
const { name, intro, parentId, type } = req.body; req: ApiRequestProps<CreateAppFolderBodyType>
): Promise<CreateAppFolderResponseType> {
const { name, intro, parentId, type } = parseApiInput({
req,
bodySchema: CreateAppFolderBodySchema
}).body;
if (!name || !type) { if (!name || !type) {
return Promise.reject(CommonErrEnum.missingParams); return Promise.reject(CommonErrEnum.missingParams);
...@@ -72,6 +77,8 @@ async function handler(req: ApiRequestProps<CreateAppFolderBody>) { ...@@ -72,6 +77,8 @@ async function handler(req: ApiRequestProps<CreateAppFolderBody>) {
} }
}); });
})(); })();
return CreateAppFolderResponseSchema.parse(undefined);
} }
export default NextAPI(handler); export default NextAPI(handler);
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest } from 'next';
import type { import type {
GetPathProps,
ParentIdType, ParentIdType,
ParentTreePathItemType ParentTreePathItemType
} from '@fastgpt/global/common/parentFolder/type'; } from '@fastgpt/global/common/parentFolder/type';
...@@ -8,12 +7,17 @@ import { NextAPI } from '@/service/middleware/entry'; ...@@ -8,12 +7,17 @@ import { NextAPI } from '@/service/middleware/entry';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { MongoApp } from '@fastgpt/service/core/app/schema'; import { MongoApp } from '@fastgpt/service/core/app/schema';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
async function handler( import {
req: NextApiRequest, GetAppFolderPathQuerySchema,
res: NextApiResponse<any> GetAppFolderPathResponseSchema
): Promise<ParentTreePathItemType[]> { } from '@fastgpt/global/openapi/core/app/folder/api';
const { sourceId: appId, type = 'current' } = req.query as GetPathProps;
async function handler(req: NextApiRequest): Promise<ParentTreePathItemType[]> {
const { sourceId: appId, type = 'current' } = parseApiInput({
req,
querySchema: GetAppFolderPathQuerySchema
}).query;
if (!appId) { if (!appId) {
return []; return [];
...@@ -21,7 +25,9 @@ async function handler( ...@@ -21,7 +25,9 @@ async function handler(
const { app } = await authApp({ req, authToken: true, appId, per: ReadPermissionVal }); const { app } = await authApp({ req, authToken: true, appId, per: ReadPermissionVal });
return await getParents(type === 'current' ? appId : app.parentId); return GetAppFolderPathResponseSchema.parse(
await getParents(type === 'current' ? appId : app.parentId)
);
} }
export default NextAPI(handler); export default NextAPI(handler);
......
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { getAppBasicInfoByIds } from '@fastgpt/service/core/app/controller'; import { getAppBasicInfoByIds } from '@fastgpt/service/core/app/controller';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
export type getBasicInfoQuery = {}; import {
GetAppBasicInfoBodySchema,
export type getBasicInfoBody = { GetAppBasicInfoResponseSchema,
ids: string[]; type GetAppBasicInfoBodyType,
}; type GetAppBasicInfoResponseType
} from '@fastgpt/global/openapi/core/app/common/api';
export type getBasicInfoResponse = {
id: string;
name: string;
avatar: string;
}[];
async function handler( async function handler(
req: ApiRequestProps<getBasicInfoBody, getBasicInfoQuery>, req: ApiRequestProps<GetAppBasicInfoBodyType>
res: ApiResponseType<any> ): Promise<GetAppBasicInfoResponseType> {
): Promise<getBasicInfoResponse> { const { ids } = parseApiInput({
const { ids } = req.body; req,
bodySchema: GetAppBasicInfoBodySchema
}).body;
const { teamId } = await authCert({ req, authToken: true }); const { teamId } = await authCert({ req, authToken: true });
const apps = await getAppBasicInfoByIds({ const apps = await getAppBasicInfoByIds({
...@@ -27,7 +24,7 @@ async function handler( ...@@ -27,7 +24,7 @@ async function handler(
ids ids
}); });
return apps; return GetAppBasicInfoResponseSchema.parse(apps);
} }
export default NextAPI(handler); export default NextAPI(handler);
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { GetAppPermissionQuerySchema } from '@fastgpt/global/openapi/core/app/permission/api';
import { import {
GetAppPermissionQuerySchema, AppPermissionCheckSchema,
GetAppPermissionResponseSchema, type AppPermissionCheckType
type GetAppPermissionResponseType } from '@fastgpt/global/support/permission/app/controller.schema';
} from '@fastgpt/global/openapi/core/app/common/api'; import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
/* Get app permission */ /* Get app permission */
async function handler( async function handler(req: ApiRequestProps): Promise<AppPermissionCheckType> {
req: ApiRequestProps, const { appId } = parseApiInput({
res: ApiResponseType<any> req,
): Promise<GetAppPermissionResponseType> { querySchema: GetAppPermissionQuerySchema
const { appId } = GetAppPermissionQuerySchema.parse(req.query); }).query;
// Auth app permission // Auth app permission
try { try {
...@@ -24,15 +25,15 @@ async function handler( ...@@ -24,15 +25,15 @@ async function handler(
per: ReadPermissionVal per: ReadPermissionVal
}); });
return GetAppPermissionResponseSchema.parse({ return AppPermissionCheckSchema.parse({
hasReadPer: app.permission.hasReadPer, hasReadPer: app.permission.hasReadPer,
hasWritePer: app.permission.hasWritePer, hasWritePer: app.permission.hasWritePer,
hasManagePer: app.permission.hasManagePer, hasManagePer: app.permission.hasManagePer,
hasReadChatLogPer: app.permission.hasReadChatLogPer, hasReadChatLogPer: app.permission.hasReadChatLogPer,
isOwner: app.permission.isOwner isOwner: app.permission.isOwner
}); });
} catch (error) { } catch {
return GetAppPermissionResponseSchema.parse({ return AppPermissionCheckSchema.parse({
hasReadPer: false, hasReadPer: false,
hasWritePer: false, hasWritePer: false,
hasManagePer: false, hasManagePer: false,
......
import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { onCreateApp } from '../create'; import { onCreateApp } from '../create';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
...@@ -10,19 +10,26 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth'; ...@@ -10,19 +10,26 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant'; import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
import { checkTeamAppTypeLimit } from '@fastgpt/service/support/permission/teamLimit'; import { checkTeamAppTypeLimit } from '@fastgpt/service/support/permission/teamLimit';
import { getHTTPToolSetRuntimeNode } from '@fastgpt/global/core/app/tool/httpTool/utils'; import { getHTTPToolSetRuntimeNode } from '@fastgpt/global/core/app/tool/httpTool/utils';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
import { import {
CreateHttpToolsBodySchema, CreateHttpToolsBodySchema,
CreateHttpToolsResponseSchema, type CreateHttpToolsBodyType
type CreateHttpToolsBodyType,
type CreateHttpToolsResponseType
} from '@fastgpt/global/openapi/core/app/httpTools/api'; } from '@fastgpt/global/openapi/core/app/httpTools/api';
import {
CreateAppResponseSchema,
type CreateAppResponseType
} from '@fastgpt/global/openapi/core/app/common/api';
import { HttpToolTypeEnum } from '@fastgpt/global/core/app/tool/httpTool/constants'; import { HttpToolTypeEnum } from '@fastgpt/global/core/app/tool/httpTool/constants';
async function handler( async function handler(
req: ApiRequestProps<CreateHttpToolsBodyType>, req: ApiRequestProps<CreateHttpToolsBodyType>
res: ApiResponseType ): Promise<CreateAppResponseType> {
): Promise<CreateHttpToolsResponseType> { const {
const { name, avatar, intro, parentId, createType } = CreateHttpToolsBodySchema.parse(req.body); body: { name, avatar, intro, parentId, createType }
} = parseApiInput({
req,
bodySchema: CreateHttpToolsBodySchema
});
const { teamId, tmbId, userId } = parentId const { teamId, tmbId, userId } = parentId
? await authApp({ req, appId: parentId, per: TeamAppCreatePermissionVal, authToken: true }) ? await authApp({ req, appId: parentId, per: TeamAppCreatePermissionVal, authToken: true })
...@@ -66,7 +73,7 @@ async function handler( ...@@ -66,7 +73,7 @@ async function handler(
tmbId tmbId
}); });
return CreateHttpToolsResponseSchema.parse(httpToolsetId); return CreateAppResponseSchema.parse(httpToolsetId);
} }
export default NextAPI(handler); export default NextAPI(handler);
import { loadOpenAPISchemaFromUrl } from '@fastgpt/global/common/string/swagger'; import { loadOpenAPISchemaFromUrl } from '@fastgpt/global/common/string/swagger';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { isInternalAddress } from '@fastgpt/service/common/system/utils'; import { isInternalAddress } from '@fastgpt/service/common/system/utils';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
import { import {
GetApiSchemaByUrlBodySchema, GetApiSchemaByUrlBodySchema,
GetApiSchemaByUrlResponseSchema,
type GetApiSchemaByUrlBodyType, type GetApiSchemaByUrlBodyType,
type GetApiSchemaByUrlResponseType type GetApiSchemaByUrlResponseType
} from '@fastgpt/global/openapi/core/app/httpTools/api'; } from '@fastgpt/global/openapi/core/app/httpTools/api';
async function handler( async function handler(
req: ApiRequestProps<GetApiSchemaByUrlBodyType>, req: ApiRequestProps<GetApiSchemaByUrlBodyType>
res: ApiResponseType
): Promise<GetApiSchemaByUrlResponseType> { ): Promise<GetApiSchemaByUrlResponseType> {
const { url } = GetApiSchemaByUrlBodySchema.parse(req.body); const {
body: { url }
} = parseApiInput({
req,
bodySchema: GetApiSchemaByUrlBodySchema
});
await authCert({ req, authToken: true }); await authCert({ req, authToken: true });
...@@ -21,7 +27,7 @@ async function handler( ...@@ -21,7 +27,7 @@ async function handler(
return Promise.reject('Invalid url'); return Promise.reject('Invalid url');
} }
return await loadOpenAPISchemaFromUrl(url); return GetApiSchemaByUrlResponseSchema.parse(await loadOpenAPISchemaFromUrl(url));
} }
export default NextAPI(handler); export default NextAPI(handler);
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { runHTTPTool } from '@fastgpt/service/core/app/http'; import { runHTTPTool } from '@fastgpt/service/core/app/http';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
import { import {
RunHttpToolBodySchema, RunHttpToolBodySchema,
RunHttpToolResponseSchema,
type RunHttpToolBodyType, type RunHttpToolBodyType,
type RunHttpToolResponseType type RunHttpToolResponseType
} from '@fastgpt/global/openapi/core/app/httpTools/api'; } from '@fastgpt/global/openapi/core/app/httpTools/api';
async function handler( async function handler(
req: ApiRequestProps<RunHttpToolBodyType>, req: ApiRequestProps<RunHttpToolBodyType>
res: ApiResponseType
): Promise<RunHttpToolResponseType> { ): Promise<RunHttpToolResponseType> {
await authCert({ req, authToken: true }); await authCert({ req, authToken: true });
...@@ -24,19 +25,28 @@ async function handler( ...@@ -24,19 +25,28 @@ async function handler(
staticParams, staticParams,
staticHeaders, staticHeaders,
staticBody staticBody
} = RunHttpToolBodySchema.parse(req.body); } = parseApiInput({
req,
bodySchema: RunHttpToolBodySchema
}).body;
return runHTTPTool({ return RunHttpToolResponseSchema.parse(
baseUrl, await runHTTPTool({
toolPath, baseUrl,
method, toolPath,
params, method,
headerSecret, params,
customHeaders, headerSecret,
staticParams, customHeaders: customHeaders
staticHeaders, ? Object.fromEntries(
staticBody Object.entries(customHeaders).map(([key, value]) => [key, String(value)])
}); )
: undefined,
staticParams,
staticHeaders,
staticBody
})
);
} }
export default NextAPI(handler); export default NextAPI(handler);
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { getHTTPToolSetRuntimeNode } from '@fastgpt/global/core/app/tool/httpTool/utils'; import { getHTTPToolSetRuntimeNode } from '@fastgpt/global/core/app/tool/httpTool/utils';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant'; import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
import { MongoApp } from '@fastgpt/service/core/app/schema'; import { MongoApp } from '@fastgpt/service/core/app/schema';
import { storeSecretValue } from '@fastgpt/service/common/secret/utils'; import { storeSecretValue } from '@fastgpt/service/common/secret/utils';
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema'; import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
import { updateParentFoldersUpdateTime } from '@fastgpt/service/core/app/controller'; import { updateParentFoldersUpdateTime } from '@fastgpt/service/core/app/controller';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
import { import {
UpdateHttpToolsBodySchema, UpdateHttpToolsBodySchema,
type UpdateHttpToolsBodyType UpdateHttpToolsResponseSchema,
type UpdateHttpToolsBodyType,
type UpdateHttpToolsResponseType
} from '@fastgpt/global/openapi/core/app/httpTools/api'; } from '@fastgpt/global/openapi/core/app/httpTools/api';
async function handler(req: ApiRequestProps<UpdateHttpToolsBodyType>, res: ApiResponseType) { async function handler(
const { appId, baseUrl, apiSchemaStr, toolList, headerSecret, customHeaders } = req: ApiRequestProps<UpdateHttpToolsBodyType>
UpdateHttpToolsBodySchema.parse(req.body); ): Promise<UpdateHttpToolsResponseType> {
const { appId, baseUrl, apiSchemaStr, toolList, headerSecret, customHeaders } = parseApiInput({
req,
bodySchema: UpdateHttpToolsBodySchema
}).body;
const { app } = await authApp({ req, authToken: true, appId, per: ManagePermissionVal }); const { app } = await authApp({ req, authToken: true, appId, per: ManagePermissionVal });
...@@ -58,6 +65,8 @@ async function handler(req: ApiRequestProps<UpdateHttpToolsBodyType>, res: ApiRe ...@@ -58,6 +65,8 @@ async function handler(req: ApiRequestProps<UpdateHttpToolsBodyType>, res: ApiRe
updateParentFoldersUpdateTime({ updateParentFoldersUpdateTime({
parentId: app.parentId parentId: app.parentId
}); });
return UpdateHttpToolsResponseSchema.parse(undefined);
} }
export default NextAPI(handler); export default NextAPI(handler);
import { MongoApp } from '@fastgpt/service/core/app/schema'; import { MongoApp } from '@fastgpt/service/core/app/schema';
import { type AppListItemType } from '@fastgpt/global/core/app/type';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { import {
...@@ -8,7 +7,6 @@ import { ...@@ -8,7 +7,6 @@ import {
} from '@fastgpt/global/support/permission/constant'; } from '@fastgpt/global/support/permission/constant';
import { AppPermission } from '@fastgpt/global/support/permission/app/controller'; import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
import { type ApiRequestProps } from '@fastgpt/service/type/next'; import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils'; import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
...@@ -19,12 +17,13 @@ import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permissio ...@@ -19,12 +17,13 @@ import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permissio
import { addSourceMember } from '@fastgpt/service/support/user/utils'; import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { sumPer } from '@fastgpt/global/support/permission/utils'; import { sumPer } from '@fastgpt/global/support/permission/utils';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
export type ListAppBody = { import {
parentId?: ParentIdType; ListAppBodySchema,
type?: AppTypeEnum | AppTypeEnum[]; ListAppResponseSchema,
searchKey?: string; type ListAppBodyType,
}; type ListAppResponseType
} from '@fastgpt/global/openapi/core/app/common/api';
/* /*
获取 APP 列表权限 获取 APP 列表权限
...@@ -36,8 +35,11 @@ export type ListAppBody = { ...@@ -36,8 +35,11 @@ export type ListAppBody = {
6. 再根据 read 权限进行一次过滤。 6. 再根据 read 权限进行一次过滤。
*/ */
async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemType[]> { async function handler(req: ApiRequestProps<ListAppBodyType>): Promise<ListAppResponseType> {
const { parentId, type, searchKey } = req.body; const { parentId, type, searchKey } = parseApiInput({
req,
bodySchema: ListAppBodySchema
}).body;
// Auth user permission // Auth user permission
const [{ tmbId, teamId, permission: teamPer }] = await Promise.all([ const [{ tmbId, teamId, permission: teamPer }] = await Promise.all([
...@@ -215,9 +217,11 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy ...@@ -215,9 +217,11 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
}) })
.filter((app) => app.permission.hasReadPer); .filter((app) => app.permission.hasReadPer);
return addSourceMember({ const list = await addSourceMember({
list: formatApps list: formatApps
}); });
return ListAppResponseSchema.parse(list);
} }
export default NextAPI(handler); export default NextAPI(handler);
...@@ -28,6 +28,7 @@ import { getLocationFromIp } from '@fastgpt/service/common/geo'; ...@@ -28,6 +28,7 @@ import { getLocationFromIp } from '@fastgpt/service/common/geo';
import { getLocale } from '@fastgpt/service/common/middle/i18n'; import { getLocale } from '@fastgpt/service/common/middle/i18n';
import { AppVersionCollectionName } from '@fastgpt/service/core/app/version/schema'; import { AppVersionCollectionName } from '@fastgpt/service/core/app/version/schema';
import { ExportChatLogsBodySchema } from '@fastgpt/global/openapi/core/app/log/api'; import { ExportChatLogsBodySchema } from '@fastgpt/global/openapi/core/app/log/api';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
const logger = getLogger(LogCategories.MODULE.APP.LOGS); const logger = getLogger(LogCategories.MODULE.APP.LOGS);
const formatJsonString = (data: any) => { const formatJsonString = (data: any) => {
...@@ -39,7 +40,7 @@ const formatJsonString = (data: any) => { ...@@ -39,7 +40,7 @@ const formatJsonString = (data: any) => {
}; };
async function handler(req: ApiRequestProps, res: NextApiResponse) { async function handler(req: ApiRequestProps, res: NextApiResponse) {
let { const {
appId, appId,
dateStart, dateStart,
dateEnd, dateEnd,
...@@ -53,7 +54,10 @@ async function handler(req: ApiRequestProps, res: NextApiResponse) { ...@@ -53,7 +54,10 @@ async function handler(req: ApiRequestProps, res: NextApiResponse) {
feedbackType, feedbackType,
unreadOnly, unreadOnly,
errorFilter errorFilter
} = ExportChatLogsBodySchema.parse(req.body); } = parseApiInput({
req,
bodySchema: ExportChatLogsBodySchema
}).body;
const locale = getLocale(req); const locale = getLocale(req);
const timezoneCode = getTimezoneCodeFromStr(dateStart); const timezoneCode = getTimezoneCodeFromStr(dateStart);
......
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { MongoAppLogKeys } from '@fastgpt/service/core/app/logs/logkeysSchema'; import { MongoAppLogKeys } from '@fastgpt/service/core/app/logs/logkeysSchema';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
...@@ -8,16 +8,15 @@ import { ...@@ -8,16 +8,15 @@ import {
GetLogKeysResponseSchema, GetLogKeysResponseSchema,
type getLogKeysResponseType type getLogKeysResponseType
} from '@fastgpt/global/openapi/core/app/log/api'; } from '@fastgpt/global/openapi/core/app/log/api';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
export type getLogKeysQuery = {}; async function handler(req: ApiRequestProps): Promise<getLogKeysResponseType> {
const {
export type getLogKeysBody = {}; query: { appId }
} = parseApiInput({
async function handler( req,
req: ApiRequestProps, querySchema: GetLogKeysQuerySchema
res: ApiResponseType<any> });
): Promise<getLogKeysResponseType> {
const { appId } = GetLogKeysQuerySchema.parse(req.query);
const { teamId } = await authApp({ const { teamId } = await authApp({
req, req,
......
import type { NextApiResponse } from 'next';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { Types } from '@fastgpt/service/common/mongo'; import { Types } from '@fastgpt/service/common/mongo';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
...@@ -11,13 +10,20 @@ import type { ApiRequestProps } from '@fastgpt/service/type/next'; ...@@ -11,13 +10,20 @@ import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { replaceRegChars } from '@fastgpt/global/common/string/tools'; import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { import {
GetLogUsersBodySchema, GetLogUsersBodySchema,
GetLogUsersResponseSchema,
type LogUserType, type LogUserType,
type GetLogUsersResponse type GetLogUsersResponse
} from '@fastgpt/global/openapi/core/app/log/api'; } from '@fastgpt/global/openapi/core/app/log/api';
import { DEFAULT_USER_AVATAR } from '@fastgpt/global/common/system/constants'; import { DEFAULT_USER_AVATAR } from '@fastgpt/global/common/system/constants';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
async function handler(req: ApiRequestProps, _res: NextApiResponse): Promise<GetLogUsersResponse> { async function handler(req: ApiRequestProps): Promise<GetLogUsersResponse> {
const { appId, dateStart, dateEnd, searchKey, sources } = GetLogUsersBodySchema.parse(req.body); const {
body: { appId, dateStart, dateEnd, searchKey, sources }
} = parseApiInput({
req,
bodySchema: GetLogUsersBodySchema
});
if (!appId) { if (!appId) {
return Promise.reject(CommonErrEnum.missingParams); return Promise.reject(CommonErrEnum.missingParams);
...@@ -94,11 +100,11 @@ async function handler(req: ApiRequestProps, _res: NextApiResponse): Promise<Get ...@@ -94,11 +100,11 @@ async function handler(req: ApiRequestProps, _res: NextApiResponse): Promise<Get
return { outLinkUid, tmbId, name, avatar, count: item.count }; return { outLinkUid, tmbId, name, avatar, count: item.count };
}); });
return { return GetLogUsersResponseSchema.parse({
list: searchPattern list: searchPattern
? list.filter((item) => !searchPattern || searchPattern.test(item.name)).slice(0, 50) ? list.filter((item) => !searchPattern || searchPattern.test(item.name)).slice(0, 50)
: list.slice(0, 50) : list.slice(0, 50)
}; });
} }
export default NextAPI(handler); export default NextAPI(handler);
import type { NextApiResponse } from 'next';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { Types } from '@fastgpt/service/common/mongo'; import { Types } from '@fastgpt/service/common/mongo';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
...@@ -22,11 +21,9 @@ import { ...@@ -22,11 +21,9 @@ import {
GetAppChatLogsResponseSchema, GetAppChatLogsResponseSchema,
type getAppChatLogsResponseType type getAppChatLogsResponseType
} from '@fastgpt/global/openapi/core/app/log/api'; } from '@fastgpt/global/openapi/core/app/log/api';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
async function handler( async function handler(req: ApiRequestProps): Promise<getAppChatLogsResponseType> {
req: ApiRequestProps,
_res: NextApiResponse
): Promise<getAppChatLogsResponseType> {
const { const {
appId, appId,
dateStart, dateStart,
...@@ -38,7 +35,10 @@ async function handler( ...@@ -38,7 +35,10 @@ async function handler(
feedbackType, feedbackType,
unreadOnly, unreadOnly,
errorFilter errorFilter
} = GetAppChatLogsBodySchema.parse(req.body); } = parseApiInput({
req,
bodySchema: GetAppChatLogsBodySchema
}).body;
const { pageSize = 20, offset } = parsePaginationRequest(req); const { pageSize = 20, offset } = parsePaginationRequest(req);
......
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { MongoAppLogKeys } from '@fastgpt/service/core/app/logs/logkeysSchema'; import { MongoAppLogKeys } from '@fastgpt/service/core/app/logs/logkeysSchema';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { UpdateLogKeysBodySchema } from '@fastgpt/global/openapi/core/app/log/api'; import {
UpdateLogKeysBodySchema,
UpdateLogKeysResponseSchema,
type updateLogKeysResponseType
} from '@fastgpt/global/openapi/core/app/log/api';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
async function handler(req: ApiRequestProps, res: ApiResponseType<any>): Promise<{}> { async function handler(req: ApiRequestProps): Promise<updateLogKeysResponseType> {
const { appId, logKeys } = UpdateLogKeysBodySchema.parse(req.body); const {
body: { appId, logKeys }
} = parseApiInput({
req,
bodySchema: UpdateLogKeysBodySchema
});
const { teamId } = await authApp({ const { teamId } = await authApp({
req, req,
...@@ -17,7 +27,7 @@ async function handler(req: ApiRequestProps, res: ApiResponseType<any>): Promise ...@@ -17,7 +27,7 @@ async function handler(req: ApiRequestProps, res: ApiResponseType<any>): Promise
await MongoAppLogKeys.findOneAndUpdate({ teamId, appId }, { logKeys }, { upsert: true }); await MongoAppLogKeys.findOneAndUpdate({ teamId, appId }, { logKeys }, { upsert: true });
return {}; return UpdateLogKeysResponseSchema.parse(undefined);
} }
export default NextAPI(handler); export default NextAPI(handler);
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant'; import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
...@@ -11,20 +11,20 @@ import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils'; ...@@ -11,20 +11,20 @@ import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
import { checkTeamAppTypeLimit } from '@fastgpt/service/support/permission/teamLimit'; import { checkTeamAppTypeLimit } from '@fastgpt/service/support/permission/teamLimit';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { storeSecretValue } from '@fastgpt/service/common/secret/utils'; import { storeSecretValue } from '@fastgpt/service/common/secret/utils';
import { parseApiInput } from '@fastgpt/service/common/zod/requestParseError';
import { import {
CreateMcpToolsBodySchema, CreateMcpToolsBodySchema,
CreateMcpToolsResponseSchema, type CreateMcpToolsBodyType
type CreateMcpToolsBodyType,
type CreateMcpToolsResponseType
} from '@fastgpt/global/openapi/core/app/mcpTools/api'; } from '@fastgpt/global/openapi/core/app/mcpTools/api';
import {
CreateAppResponseSchema,
type CreateAppResponseType
} from '@fastgpt/global/openapi/core/app/common/api';
import { assertMCPUrlNotInternal } from '@fastgpt/service/core/app/mcp'; import { assertMCPUrlNotInternal } from '@fastgpt/service/core/app/mcp';
export type createMCPToolsQuery = {};
async function handler( async function handler(
req: ApiRequestProps<CreateMcpToolsBodyType>, req: ApiRequestProps<CreateMcpToolsBodyType>
res: ApiResponseType ): Promise<CreateAppResponseType> {
): Promise<CreateMcpToolsResponseType> {
const { const {
name, name,
avatar, avatar,
...@@ -32,7 +32,10 @@ async function handler( ...@@ -32,7 +32,10 @@ async function handler(
url, url,
headerSecret = {}, headerSecret = {},
parentId parentId
} = CreateMcpToolsBodySchema.parse(req.body); } = parseApiInput({
req,
bodySchema: CreateMcpToolsBodySchema
}).body;
const { teamId, tmbId, userId } = parentId const { teamId, tmbId, userId } = parentId
? await authApp({ req, appId: parentId, per: WritePermissionVal, authToken: true }) ? await authApp({ req, appId: parentId, per: WritePermissionVal, authToken: true })
...@@ -75,7 +78,7 @@ async function handler( ...@@ -75,7 +78,7 @@ async function handler(
tmbId tmbId
}); });
return CreateMcpToolsResponseSchema.parse(mcpToolsId); return CreateAppResponseSchema.parse(mcpToolsId);
} }
export default NextAPI(handler); 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