Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/design/web/common/hook/tableMultiple/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@

1. 选中的值存储在 hook 里,便于判断是否触发底部悬浮层
2. 悬浮层外层 Box 在 hook 里,child 由调用组件实现
3. FastGPT/packages/web/hooks/useTableMultipleSelect.tsx 在这个文件下实现
3. FastGPT/packages/web/hooks/useTableMultipleSelect.tsx 在这个文件下实现
24 changes: 23 additions & 1 deletion packages/global/core/workflow/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import type {
ChatInputGuideConfigType,
AppChatConfigType,
AppAutoExecuteConfigType,
AppQGConfigType
AppQGConfigType,
AppSchema
} from '../app/type';
import { type EditorVariablePickerType } from '../../../web/components/common/Textarea/PromptEditor/type';
import {
Expand Down Expand Up @@ -441,3 +442,24 @@ export const getPluginRunUserQuery = ({
})
};
};

export const removeUnauthModels = async ({
modules,
allowedModels = new Set()
}: {
modules: AppSchema['modules'];
allowedModels?: Set<string>;
}) => {
if (modules) {
modules.forEach((module) => {
module.inputs.forEach((input) => {
if (input.key === 'model') {
if (!allowedModels.has(input.value)) {
input.value = undefined;
}
}
});
});
}
return modules;
};
2 changes: 1 addition & 1 deletion packages/global/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@fastgpt/global",
"version": "1.0.0",
"dependencies": {
"@fastgpt-sdk/plugin": "^0.1.18",
"@fastgpt-sdk/plugin": "^0.1.19",
"@apidevtools/swagger-parser": "^10.1.0",
"@bany/curl-to-json": "^1.2.8",
"axios": "^1.12.1",
Expand Down
3 changes: 2 additions & 1 deletion packages/global/support/permission/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ export const PermissionTypeMap = {
export enum PerResourceTypeEnum {
team = 'team',
app = 'app',
dataset = 'dataset'
dataset = 'dataset',
model = 'model'
}

/* new permission */
Expand Down
11 changes: 11 additions & 0 deletions packages/global/support/permission/model/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {
NullRoleVal,
CommonPerKeyEnum,
CommonRoleList,
CommonPerList,
CommonRoleKeyEnum
} from '../constant';

export const ModelDefaultRole = NullRoleVal;
export const ModelReadPerVal = CommonPerList[CommonPerKeyEnum.read];
export const ModelReadRolVal = CommonRoleList[CommonRoleKeyEnum.read];
3 changes: 3 additions & 0 deletions packages/global/support/permission/model/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Permission } from '../controller';

export class ModelPermission extends Permission {}
9 changes: 3 additions & 6 deletions packages/global/support/permission/type.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { UserModelSchema } from '../user/type';
import type { RequireOnlyOne } from '../../common/type/utils';
import type { TeamMemberSchema } from '../user/team/type';
import type { CommonRoleKeyEnum } from './constant';
import { type CommonPerKeyEnum, type PerResourceTypeEnum } from './constant';
import type { CollaboratorIdType } from './collaborator';

// PermissionValueType, the type of permission's value is a number, which is a bit field actually.
// It is spired by the permission system in Linux.
Expand Down Expand Up @@ -63,11 +63,8 @@ export type ResourcePermissionType = {
resourceType: ResourceType;
permission: PermissionValueType;
resourceId: string;
} & RequireOnlyOne<{
tmbId: string;
groupId: string;
orgId: string;
}>;
resourceName: string;
} & CollaboratorIdType;

export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {
tmbId: TeamMemberSchema & { user: UserModelSchema };
Expand Down
44 changes: 29 additions & 15 deletions packages/service/common/cache/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,46 @@ import { initCache } from './init';

const cachePrefix = `${FASTGPT_REDIS_PREFIX}:VERSION_KEY:`;

export const refreshVersionKey = async (key: `${SystemCacheKeyEnum}`) => {
/**
*
* @param key SystemCacheKeyEnum
* @param id string (teamId, tmbId, etc), if '*' is used, all keys will be refreshed
*/
export const refreshVersionKey = async (key: `${SystemCacheKeyEnum}`, id?: string | '*') => {
const redis = getGlobalRedisConnection();
if (!global.systemCache) initCache();

const val = randomUUID();
await redis.set(`${cachePrefix}${key}`, val);
const versionKey = id ? `${cachePrefix}${key}:${id}` : `${cachePrefix}${key}`;
if (id === '*') {
const pattern = `${cachePrefix}${key}:*`;
const keys = await redis.keys(pattern);
if (keys.length > 0) {
await redis.del(keys);
}
} else {
await redis.set(versionKey, val);
}
};

export const getCachedData = async (key: `${SystemCacheKeyEnum}`) => {
export const getVersionKey = async (key: `${SystemCacheKeyEnum}`, id?: string) => {
const redis = getGlobalRedisConnection();
if (!global.systemCache) initCache();

const getVersionkey = async (key: `${SystemCacheKeyEnum}`) => {
if (!global.systemCache) initCache();

const versionKey = `${cachePrefix}${key}`;
const val = await redis.get(versionKey);
if (val) return val;
const versionKey = id ? `${cachePrefix}${key}:${id}` : `${cachePrefix}${key}`;
const val = await redis.get(versionKey);
if (val) return val;

const newVal = randomUUID();
await redis.set(versionKey, newVal);
return newVal;
};
// if there is no val set to the key, init a new val.
const initVal = randomUUID();
await redis.set(versionKey, initVal);
return initVal;
};

export const getCachedData = async <T extends SystemCacheKeyEnum>(key: T, id?: string) => {
if (!global.systemCache) initCache();

const versionKey = await getVersionkey(key);
const versionKey = await getVersionKey(key, id);
const isDisableCache = process.env.DISABLE_CACHE === 'true';

// 命中缓存
Expand All @@ -40,7 +54,7 @@ export const getCachedData = async (key: `${SystemCacheKeyEnum}`) => {
}

const refreshedData = await global.systemCache[key].refreshFunc();
await refreshVersionKey(key);
await refreshVersionKey(key, id);
global.systemCache[key].data = refreshedData;
global.systemCache[key].versionKey = versionKey;
return global.systemCache[key].data;
Expand Down
5 changes: 5 additions & 0 deletions packages/service/common/cache/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ export const initCache = () => {
versionKey: '',
data: [],
refreshFunc: refreshSystemTools
},
[SystemCacheKeyEnum.modelPermission]: {
versionKey: '',
data: null,
refreshFunc: () => Promise.resolve(null)
}
};
};
4 changes: 3 additions & 1 deletion packages/service/common/cache/type.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { SystemPluginTemplateItemType } from '@fastgpt/global/core/app/plugin/type';

export enum SystemCacheKeyEnum {
systemTool = 'systemTool'
systemTool = 'systemTool',
modelPermission = 'modelPermission'
}

export type SystemCacheDataType = {
[SystemCacheKeyEnum.systemTool]: SystemPluginTemplateItemType[];
[SystemCacheKeyEnum.modelPermission]: null;
};

type SystemCacheType = {
Expand Down
4 changes: 4 additions & 0 deletions packages/service/common/system/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api`
export const FastGPTPluginUrl = process.env.PLUGIN_BASE_URL ? `${process.env.PLUGIN_BASE_URL}` : '';
// @ts-ignore
export const isFastGPTProService = () => !!global.systemConfig;

export const isProVersion = () => {
return !!global.feConfigs?.isPlus;
};
3 changes: 3 additions & 0 deletions packages/service/core/ai/config/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { delay } from '@fastgpt/global/common/system/utils';
import { pluginClient } from '../../../thirdProvider/fastgptPlugin';
import { setCron } from '../../../common/system/cron';
import { preloadModelProviders } from '../../../core/app/provider/controller';
import { refreshVersionKey } from '../../../common/cache';
import { SystemCacheKeyEnum } from '../../../common/cache/type';

export const loadSystemModels = async (init = false, language = 'en') => {
const pushModel = (model: SystemModelItemType) => {
Expand Down Expand Up @@ -253,6 +255,7 @@ export const updatedReloadSystemModel = async () => {
await loadSystemModels(true);
// 2. 更新缓存(仅主节点触发)
await updateFastGPTConfigBuffer();
await refreshVersionKey(SystemCacheKeyEnum.modelPermission, '*');
// 3. 延迟1秒,等待其他节点刷新
await delay(1000);
};
Expand Down
20 changes: 0 additions & 20 deletions packages/service/core/app/tool/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,6 @@ export async function APIGetSystemToolList() {
return Promise.reject(res.body);
}

export async function deleteSystemTool(toolId: string) {
const res = await pluginClient.tool.delete({ body: { toolId } });

if (res.status === 200) {
return res.body;
}

return Promise.reject(res.body);
}

export async function uploadSystemTool(objectName: string) {
const res = await pluginClient.tool.upload({ body: { objectName } });

if (res.status === 200) {
return res.body;
}

return Promise.reject(res.body);
}

const runToolInstance = new RunToolWithStream({
baseUrl: PLUGIN_BASE_URL,
token: PLUGIN_TOKEN
Expand Down
46 changes: 46 additions & 0 deletions packages/service/support/permission/model/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { getGroupsByTmbId } from '../memberGroup/controllers';
import { getOrgsByTmbId } from '../org/controllers';
import { MongoResourcePermission } from '../schema';
import { getCollaboratorId } from '@fastgpt/global/support/permission/utils';
import { isProVersion } from '../../../common/system/constants';

export const getMyModels = async ({
teamId,
tmbId,
isTeamOwner
}: {
teamId: string;
tmbId: string;
isTeamOwner: boolean;
}) => {
if (isTeamOwner || !isProVersion()) {
return global.systemModelList.map((m) => m.model);
}
const [groups, orgs] = await Promise.all([
getGroupsByTmbId({
teamId,
tmbId
}),
getOrgsByTmbId({
teamId,
tmbId
})
]);

const myIdSet = new Set([tmbId, ...groups.map((g) => g._id), ...orgs.map((o) => o._id)]);

const rps = await MongoResourcePermission.find({
teamId,
resourceType: PerResourceTypeEnum.model
}).lean();

const permissionConfiguredModelSet = new Set(rps.map((rp) => rp.resourceName));
const unconfiguredModels = global.systemModelList.filter(
(model) => !permissionConfiguredModelSet.has(model.model)
);

const myModels = rps.filter((rp) => myIdSet.has(getCollaboratorId(rp)));

return [...unconfiguredModels.map((m) => m.model), ...myModels.map((m) => m.resourceName)];
};
Loading
Loading