V4.8.18 feature (#3565)

* feat: org CRUD (#3380)

* feat: add org schema

* feat: org manage UI

* feat: OrgInfoModal

* feat: org tree view

* feat: org management

* fix: init root org

* feat: org permission for app

* feat: org support for dataset

* fix: disable org role control

* styles: opt type signatures

* fix: remove unused permission

* feat: delete org collaborator

* perf: Team org ui (#3499)

* perf: org ui

* perf: org ui

* feat: org auth for app & dataset (#3498)

* feat: auth org resource permission

* feat: org auth support for app & dataset

* perf: org permission check (#3500)

* i18n (#3501)

* name

* i18n

* feat: support dataset changeOwner (#3483)

* feat: support dataset changeOwner

* chore: update dataset change owner api

* feat: permission manage UI for org (#3503)

* perf: password check;perf: image upload check;perf: sso login check (#3509)

* perf: password check

* perf: image upload check

* perf: sso login check

* force show update notification modal & fix login page text (#3512)

* fix login page English text

* update notification modal

* perf: notify account (#3515)

* perf(plugin): improve searXNG empty result handling and documentation (#3507)

* perf(plugin): improve searXNG empty result handling and documentation

* 修改了文档和代码部分无搜索的结果的反馈

* refactor: org pathId (#3516)

* optimize payment process (#3517)

* feat: support wecom sso (#3518)

* feat: support wecom sso

* chore: remove unused wecom js-sdk dependency

* fix qrcode script (#3520)

* fix qrcode script

* i18n

* perf: full text collection and search code;perf: rename function (#3519)

* perf: full text collection and search code

* perf: rename function

* perf: notify modal

* remove invalid code

* perf: sso login

* perf: pay process

* 4.8.18 test (#3524)

* perf: remove local token

* perf: index

* perf: file encoding;perf: leave team code;@c121914yu perf: full text search code (#3528)

* perf: text encoding

* perf: leave team code

* perf: full text search code

* fix: http status

* perf: embedding search and vector avatar

* perf: async read file (#3531)

* refactor: team permission  manager (#3535)

* perf: classify org, group and member

* refactor: team per manager

* fix: missing functions

* 4.8.18 test (#3543)

* perf: login check

* doc

* perf: llm model config

* perf: team clb config

* fix: MemberModal UI (#3553)

* fix: adapt MemberModal title and icon

* fix: adapt member modal

* fix: search input placeholder

* fix: add button text

* perf: org permission (#3556)

* docs:用户答疑的官方文档补充 (#3540)

* docs:用户答疑的官方文档补充

* 问题回答的内容修补

* share link random avatar (#3541)

* share link random avatar

* fix

* delete unused code

* share page avatar (#3558)

* feat: init 4818

* share page avatar

* feat: tmp upgrade code (#3559)

* feat: tmp upgrade code

* fulltext search test

* update action

* full text tmp code (#3561)

* full text tmp code

* fix: init

* fix: init

* remove tmp code

* remove tmp code

* 4818-alpha

* 4.8.18 test (#3562)

* full text tmp code

* fix: init

* upgrade code

* account log

* account log

* perf: dockerfile

* upgrade code

* chore: update docs app template submission (#3564)

---------

Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com>
Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: Jiangween <145003935+Jiangween@users.noreply.github.com>
This commit is contained in:
Archer
2025-01-11 15:15:38 +08:00
committed by GitHub
parent bb669ca3ff
commit 10d8c56e23
205 changed files with 5305 additions and 2428 deletions

View File

@@ -220,7 +220,7 @@ export const streamFetch = ({
});
} else if (event === SseResponseEventEnum.error) {
if (parseJson.statusText === TeamErrEnum.aiPointsNotEnough) {
useSystemStore.getState().setIsNotSufficientModal(true);
useSystemStore.getState().setNotSufficientModalType(TeamErrEnum.aiPointsNotEnough);
}
errMsg = getErrText(parseJson, '流响应错误');
}

View File

@@ -9,6 +9,7 @@ import { TOKEN_ERROR_CODE } from '@fastgpt/global/common/error/errorCode';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { useSystemStore } from '../system/useSystemStore';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import { i18nT } from '@fastgpt/web/i18n/utils';
interface ConfigType {
headers?: { [key: string]: string };
@@ -108,20 +109,23 @@ function responseError(err: any) {
return Promise.reject({ message: err });
}
// 有报错响应
if (err?.code in TOKEN_ERROR_CODE) {
if (
!(window.location.pathname === '/chat/share' || window.location.pathname === '/chat/team')
) {
if (err?.code in TOKEN_ERROR_CODE || err?.response?.data?.code in TOKEN_ERROR_CODE) {
if (!['/chat/share', '/chat/team', '/login'].includes(window.location.pathname)) {
clearToken();
window.location.replace(
getWebReqUrl(`/login?lastRoute=${encodeURIComponent(location.pathname + location.search)}`)
);
}
return Promise.reject({ message: '无权操作' });
return Promise.reject({ message: i18nT('common:unauth_token') });
}
if (err?.statusText === TeamErrEnum.aiPointsNotEnough) {
useSystemStore.getState().setIsNotSufficientModal(true);
if (
err?.statusText === TeamErrEnum.aiPointsNotEnough ||
err?.statusText === TeamErrEnum.datasetSizeNotEnough ||
err?.statusText === TeamErrEnum.datasetAmountNotEnough ||
err?.statusText === TeamErrEnum.appAmountNotEnough
) {
useSystemStore.getState().setNotSufficientModalType(err.statusText);
return Promise.reject(err);
}
if (err?.response?.data) {

View File

@@ -35,30 +35,11 @@ export const uploadFile2DB = ({
});
};
export const getUploadBase64ImgController = (
props: CompressImgProps & UploadImgProps,
retry = 3
): Promise<string> => {
try {
return compressBase64ImgAndUpload({
maxW: 4000,
maxH: 4000,
maxSize: 1024 * 1024 * 5,
...props
});
} catch (error) {
if (retry > 0) {
return getUploadBase64ImgController(props, retry - 1);
}
return Promise.reject(error);
}
};
/**
* compress image. response base64
* @param maxSize The max size of the compressed image
*/
export const compressBase64ImgAndUpload = async ({
const compressBase64ImgAndUpload = async ({
base64Img,
maxW,
maxH,
@@ -89,7 +70,7 @@ export const compressImgFileAndUpload = async ({
reader.readAsDataURL(file);
const base64Img = await new Promise<string>((resolve, reject) => {
reader.onload = async () => {
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = (err) => {

View File

@@ -3,12 +3,17 @@ import { Box } from '@chakra-ui/react';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useI18n } from '@/web/context/I18n';
import { useMemoizedFn } from 'ahooks';
import { compressImgFileAndUpload } from '../controller';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'next-i18next';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
export const useSelectFile = (props?: {
fileType?: string;
multiple?: boolean;
maxCount?: number;
}) => {
const { t } = useTranslation();
const { fileT } = useI18n();
const { fileType = '*', multiple = false, maxCount = 10 } = props || {};
const { toast } = useToast();
@@ -48,8 +53,44 @@ export const useSelectFile = (props?: {
SelectFileDom.current && SelectFileDom.current.click();
}, []);
const { runAsync: onSelectImage, loading } = useRequest2(
async (
e: File[],
{
maxW,
maxH,
callback
}: {
maxW?: number;
maxH?: number;
callback?: (e: string) => any;
}
) => {
const file = e[0];
if (!file) return Promise.resolve('Can not found image');
try {
const src = await compressImgFileAndUpload({
file,
maxW,
maxH
});
console.log(src, '--');
callback?.(src);
return src;
} catch (err: any) {
toast({
title: getErrText(err, t('common:error.upload_image_error')),
status: 'warning'
});
return Promise.reject(getErrText(err, t('common:error.upload_image_error')));
}
}
);
return {
File,
onOpen
onOpen,
onSelectImage,
loading
};
};

View File

@@ -14,9 +14,17 @@ import { InitDateResponse } from '@/global/common/api/systemRes';
import { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types';
import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type';
import { defaultWhisperModel } from '@fastgpt/global/core/ai/model';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
type LoginStoreType = { provider: `${OAuthEnum}`; lastRoute: string; state: string };
export type NotSufficientModalType =
| TeamErrEnum.datasetSizeNotEnough
| TeamErrEnum.aiPointsNotEnough
| TeamErrEnum.datasetAmountNotEnough
| TeamErrEnum.teamMemberOverSize
| TeamErrEnum.appAmountNotEnough;
type State = {
initd: boolean;
setInitd: () => void;
@@ -27,14 +35,15 @@ type State = {
setLastAppListRouteType: (e?: string) => void;
loginStore?: LoginStoreType;
setLoginStore: (e: LoginStoreType) => void;
setLoginStore: (e?: LoginStoreType) => void;
loading: boolean;
setLoading: (val: boolean) => null;
gitStar: number;
loadGitStar: () => Promise<void>;
isNotSufficientModal: boolean;
setIsNotSufficientModal: (val: boolean) => void;
notSufficientModalType?: NotSufficientModalType;
setNotSufficientModalType: (val?: NotSufficientModalType) => void;
initDataBufferId?: string;
feConfigs: FastGPTFeConfigsType;
@@ -105,10 +114,10 @@ export const useSystemStore = create<State>()(
} catch (error) {}
},
isNotSufficientModal: false,
setIsNotSufficientModal(val: boolean) {
notSufficientModalType: undefined,
setNotSufficientModalType(type) {
set((state) => {
state.isNotSufficientModal = val;
state.notSufficientModalType = type;
});
},

View File

@@ -67,6 +67,7 @@ import type {
listExistIdResponse
} from '@/pages/api/core/dataset/apiDataset/listExistId';
import { FeishuServer, YuqueServer } from '@fastgpt/global/core/dataset/apiDataset';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
/* ======================== dataset ======================= */
export const getDatasets = (data: GetDatasetListBody) =>
@@ -100,6 +101,9 @@ export const postCreateDatasetFolder = (data: DatasetFolderCreateBody) =>
export const resumeInheritPer = (datasetId: string) =>
GET(`/core/dataset/resumeInheritPermission`, { datasetId });
export const postChangeOwner = (data: { ownerId: string; datasetId: string }) =>
POST(`/proApi/core/dataset/changeOwner`, data);
/* =========== search test ============ */
export const postSearchText = (data: SearchTestProps) =>
POST<SearchTestResponse>(`/core/dataset/searchTest`, data);

View File

@@ -1,20 +1,9 @@
import { loginOut } from '@/web/support/user/api';
const tokenKey = 'token';
export const clearToken = () => {
try {
localStorage.removeItem(tokenKey);
return loginOut();
} catch (error) {
error;
}
};
export const setToken = (token: string) => {
if (typeof window === 'undefined') return '';
localStorage.setItem(tokenKey, token);
};
export const getToken = () => {
if (typeof window === 'undefined') return '';
return localStorage.getItem(tokenKey) || '';
};

View File

@@ -43,12 +43,12 @@ export const useSendCode = ({ type }: { type: `${UserAuthTypeEnum}` }) => {
const sendCodeText = useMemo(() => {
if (codeSending) return t('common:support.user.auth.Sending Code');
if (codeCountDown >= 10) {
return `${codeCountDown}${t('user:password.get_code_again')}`;
return `${codeCountDown}${t('common:support.user.auth.get_code_again')}`;
}
if (codeCountDown > 0) {
return `0${codeCountDown}${t('user:password.get_code_again')}`;
return `0${codeCountDown}${t('common:support.user.auth.get_code_again')}`;
}
return t('user:password.get_code');
return t('common:support.user.auth.get_code');
}, [codeCountDown, codeSending, t]);
const {

View File

@@ -4,3 +4,6 @@ export enum LoginPageTypeEnum {
forgetPassword = 'forgetPassword',
wechat = 'wechat'
}
export const PasswordRule =
/^(?:(?=.*\d)(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])|(?=.*\d)(?=.*[!@#$%^&*_])|(?=.*[a-z])(?=.*[A-Z])|(?=.*[a-z])(?=.*[!@#$%^&*_])|(?=.*[A-Z])(?=.*[!@#$%^&*_]))[\dA-Za-z!@#$%^&*_]{6,}$/;

View File

@@ -1,5 +1,9 @@
import { GET, POST, PUT, DELETE } from '@/web/common/api/request';
import { UpdatePermissionBody } from '@fastgpt/global/support/permission/collaborator';
import {
CollaboratorItemType,
DeletePermissionQuery,
UpdateClbPermissionProps
} from '@fastgpt/global/support/permission/collaborator';
import {
CreateTeamProps,
InviteMemberProps,
@@ -15,7 +19,6 @@ import {
} from '@fastgpt/global/support/user/team/type.d';
import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
import { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
/* --------------- team ---------------- */
export const getTeamList = (status: `${TeamMemberSchema['status']}`) =>
@@ -37,15 +40,15 @@ export const delRemoveMember = (tmbId: string) =>
DELETE(`/proApi/support/user/team/member/delete`, { tmbId });
export const updateInviteResult = (data: UpdateInviteProps) =>
PUT('/proApi/support/user/team/member/updateInvite', data);
export const delLeaveTeam = (teamId: string) =>
DELETE('/proApi/support/user/team/member/leave', { teamId });
export const getTeamClbs = () =>
GET<ResourcePermissionType[]>(`/proApi/support/user/team/collaborator/list`);
export const delLeaveTeam = () => DELETE('/proApi/support/user/team/member/leave');
/* -------------- team collaborator -------------------- */
export const updateMemberPermission = (data: UpdatePermissionBody) =>
PUT('/proApi/support/user/team/collaborator/updatePermission', data);
export const getTeamClbs = () =>
GET<CollaboratorItemType[]>(`/proApi/support/user/team/collaborator/list`);
export const updateMemberPermission = (data: UpdateClbPermissionProps) =>
PUT('/proApi/support/user/team/collaborator/update', data);
export const deleteMemberPermission = (id: DeletePermissionQuery) =>
DELETE('/proApi/support/user/team/collaborator/delete', id);
/* --------------- team tags ---------------- */
export const getTeamsTags = () => GET<TeamTagSchema[]>(`/proApi/support/user/team/tag/list`);

View File

@@ -0,0 +1,30 @@
import { DELETE, GET, POST, PUT } from '@/web/common/api/request';
import type {
postCreateOrgData,
putUpdateOrgData,
putUpdateOrgMembersData
} from '@fastgpt/global/support/user/team/org/api';
import type { OrgType } from '@fastgpt/global/support/user/team/org/type';
import { putMoveOrgType } from '@fastgpt/global/support/user/team/org/api';
export const getOrgList = () => GET<OrgType[]>('/proApi/support/user/team/org/list');
export const postCreateOrg = (data: postCreateOrgData) =>
POST('/proApi/support/user/team/org/create', data);
export const deleteOrg = (orgId: string) =>
DELETE('/proApi/support/user/team/org/delete', { orgId });
export const deleteOrgMember = (orgId: string, tmbId: string) =>
DELETE('/proApi/support/user/team/org/deleteMember', { orgId, tmbId });
export const putMoveOrg = (data: putMoveOrgType) => PUT('/proApi/support/user/team/org/move', data);
export const putUpdateOrg = (data: putUpdateOrgData) =>
PUT('/proApi/support/user/team/org/update', data);
export const putUpdateOrgMembers = (data: putUpdateOrgMembersData) =>
PUT('/proApi/support/user/team/org/updateMembers', data);
// export const putChnageOrgOwner = (data: putChnageOrgOwnerData) =>
// PUT('/proApi/support/user/team/org/changeOwner', data);

View File

@@ -1,22 +1,28 @@
import type { UserUpdateParams } from '@/types/user';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { getTokenLogin, putUserInfo } from '@/web/support/user/api';
import { getTeamMembers } from '@/web/support/user/team/api';
import type { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type';
import type { OrgMemberSchemaType, OrgType } from '@fastgpt/global/support/user/team/org/type';
import type { TeamMemberItemType } from '@fastgpt/global/support/user/team/type';
import type { UserType } from '@fastgpt/global/support/user/type.d';
import type { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import type { UserUpdateParams } from '@/types/user';
import type { UserType } from '@fastgpt/global/support/user/type.d';
import { getTokenLogin, putUserInfo } from '@/web/support/user/api';
import { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
import { getTeamPlanStatus } from './team/api';
import { getTeamMembers } from '@/web/support/user/team/api';
import { TeamMemberItemType } from '@fastgpt/global/support/user/team/type';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type';
import { getGroupList } from './team/group/api';
import { getOrgList } from './team/org/api';
type State = {
systemMsgReadId: string;
setSysMsgReadId: (id: string) => void;
isUpdateNotification: boolean;
setIsUpdateNotification: (val: boolean) => void;
userInfo: UserType | null;
isTeamAdmin: boolean;
initUserInfo: () => Promise<UserType>;
setUserInfo: (user: UserType | null) => void;
updateUserInfo: (user: UserUpdateParams) => Promise<void>;
@@ -30,6 +36,10 @@ type State = {
teamMemberGroups: MemberGroupListType;
myGroups: MemberGroupListType;
loadAndGetGroups: (init?: boolean) => Promise<MemberGroupListType>;
teamOrgs: OrgType[];
myOrgs: OrgType[];
loadAndGetOrgs: (init?: boolean) => Promise<OrgType[]>;
};
export const useUserStore = create<State>()(
@@ -43,7 +53,15 @@ export const useUserStore = create<State>()(
});
},
isUpdateNotification: true,
setIsUpdateNotification(val: boolean) {
set((state) => {
state.isUpdateNotification = val;
});
},
userInfo: null,
isTeamAdmin: false,
async initUserInfo() {
get().initTeamPlanStatus();
@@ -61,6 +79,7 @@ export const useUserStore = create<State>()(
setUserInfo(user: UserType | null) {
set((state) => {
state.userInfo = user ? user : null;
state.isTeamAdmin = !!user?.team?.permission?.hasManagePer;
});
},
async updateUserInfo(user: UserUpdateParams) {
@@ -107,6 +126,7 @@ export const useUserStore = create<State>()(
return res;
},
teamMemberGroups: [],
teamOrgs: [],
myGroups: [],
loadAndGetGroups: async (init = false) => {
if (!useSystemStore.getState()?.feConfigs?.isPlus) return [];
@@ -123,13 +143,31 @@ export const useUserStore = create<State>()(
);
});
return res;
},
myOrgs: [],
loadAndGetOrgs: async (init = false) => {
if (!useSystemStore.getState()?.feConfigs?.isPlus) return [];
const randomRefresh = Math.random() > 0.7;
if (!randomRefresh && !init && get().myOrgs.length) return Promise.resolve(get().myOrgs);
const res = await getOrgList();
set((state) => {
state.teamOrgs = res;
state.myOrgs = res.filter((item) =>
item.members.map((i) => String(i.tmbId)).includes(String(state.userInfo?.team?.tmbId))
);
});
return res;
}
})),
{
name: 'userStore',
partialize: (state) => ({
systemMsgReadId: state.systemMsgReadId
systemMsgReadId: state.systemMsgReadId,
isUpdateNotification: state.isUpdateNotification
})
}
)