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 deploy/dev/docker-compose.cn.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ services:
- fastgpt
environment:
- AUTH_TOKEN=token
- S3_ENDPOINT=http://127.0.0.1:9000 # TODO: 改为你 Minio 的实际的 ip 地址
- S3_EXTERNAL_BASE_URL=http://127.0.0.1:9000 # 替换为本机的 ip 地址
- S3_ENDPOINT=fastgpt-minio
- S3_PORT=9000
- S3_USE_SSL=false
Expand Down
2 changes: 1 addition & 1 deletion deploy/dev/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ services:
- fastgpt
environment:
- AUTH_TOKEN=token
- S3_ENDPOINT=http://127.0.0.1:9000 # TODO: 改为你 Minio 的实际的 ip 地址
- S3_EXTERNAL_BASE_URL=http://127.0.0.1:9000 # 替换为本机的 ip 地址
- S3_ENDPOINT=fastgpt-minio
- S3_PORT=9000
- S3_USE_SSL=false
Expand Down
2 changes: 1 addition & 1 deletion deploy/templates/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ services:
- fastgpt
environment:
- AUTH_TOKEN=token
- S3_ENDPOINT=http://127.0.0.1:9000 # TODO: 改为你 Minio 的实际的 ip 地址
- S3_EXTERNAL_BASE_URL=http://127.0.0.1:9000 # 替换为本机的 ip 地址
- S3_ENDPOINT=fastgpt-minio
- S3_PORT=9000
- S3_USE_SSL=false
Expand Down
9 changes: 6 additions & 3 deletions packages/global/support/user/api.d.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import type { MemberGroupSchemaType } from 'support/permission/memberGroup/type';
import { MemberGroupListItemType } from 'support/permission/memberGroup/type';
import type { MemberGroupSchemaType } from '../permission/memberGroup/type';
import { MemberGroupListItemType } from '../permission/memberGroup/type';
import type { OAuthEnum } from './constant';
import type { TrackRegisterParams } from './login/api';
import { TeamMemberStatusEnum } from './team/constant';
import type { OrgType } from './team/org/type';
import type { TeamMemberItemType } from './team/type';
import type { LangEnum } from '../../common/i18n/type';
import type { TrackRegisterParams } from '../marketing/type';

export type PostLoginProps = {
username: string;
password: string;
code: string;
language?: `${LangEnum}`;
};

export type OauthLoginProps = {
type: `${OAuthEnum}`;
callbackUrl: string;
props: Record<string, string>;
language?: `${LangEnum}`;
} & TrackRegisterParams;

export type WxLoginProps = {
Expand Down
7 changes: 6 additions & 1 deletion packages/global/support/user/inform/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,10 @@ export enum SendInformTemplateCodeEnum {
BIND_NOTIFICATION = 'BIND_NOTIFICATION',
LACK_OF_POINTS = 'LACK_OF_POINTS',
CUSTOM = 'CUSTOM',
MANAGE_RENAME = 'MANAGE_RENAME'
MANAGE_RENAME = 'MANAGE_RENAME',
POINTS_THIRTY_PERCENT_REMAIN = 'POINTS_THIRTY_PERCENT_REMAIN',
POINTS_TEN_PERCENT_REMAIN = 'POINTS_TEN_PERCENT_REMAIN',
DATASET_INDEX_NO_REMAIN = 'DATASET_INDEX_NO_REMAIN',
DATASET_INDEX_TEN_PERCENT_REMAIN = 'DATASET_INDEX_TEN_PERCENT_REMAIN',
DATASET_INDEX_THIRTY_PERCENT_REMAIN = 'DATASET_INDEX_THIRTY_PERCENT_REMAIN'
}
2 changes: 2 additions & 0 deletions packages/global/support/user/login/api.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { LangEnum } from '../../../common/i18n/type';
import type { TrackRegisterParams } from '../../marketing/type';

export type GetWXLoginQRResponse = {
Expand All @@ -9,4 +10,5 @@ export type AccountRegisterBody = {
username: string;
code: string;
password: string;
language?: `${LangEnum}`;
} & TrackRegisterParams;
3 changes: 3 additions & 0 deletions packages/global/support/user/type.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { LangEnum } from '../common/i18n/type';
import type { TeamPermission } from '../permission/user/controller';
import type { UserStatusEnum } from './constant';
import type { TeamMemberStatusEnum } from './team/constant';
Expand All @@ -12,6 +13,7 @@ export type UserModelSchema = {
openaiKey: string;
createTime: number;
timezone: string;
language: `${LangEnum}`;
status: `${UserStatusEnum}`;
lastLoginTmbId?: string;
passwordUpdateTime?: Date;
Expand All @@ -26,6 +28,7 @@ export type UserType = {
username: string;
avatar: string; // it should be team member's avatar after 4.8.18
timezone: string;
language?: `${LangEnum}`;
promotionRate: UserModelSchema['promotionRate'];
team: TeamTmbItemType;
notificationAccount?: string;
Expand Down
28 changes: 28 additions & 0 deletions packages/service/common/system/countLimit/const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import z from 'zod';
import { CountLimitTypeEnum } from './type';

export const CountLimitConfigType = z.record(
CountLimitTypeEnum,
z.object({ maxCount: z.number() })
);

export const CountLimitConfig = {
[CountLimitTypeEnum.enum['notice:30PercentPoints']]: {
maxCount: 3
},
[CountLimitTypeEnum.enum['notice:10PercentPoints']]: {
maxCount: 5
},
[CountLimitTypeEnum.enum['notice:LackOfPoints']]: {
maxCount: 5
},
[CountLimitTypeEnum.enum['notice:30PercentDatasetIndexes']]: {
maxCount: 3
},
[CountLimitTypeEnum.enum['notice:10PercentDatasetIndexes']]: {
maxCount: 5
},
[CountLimitTypeEnum.enum['notice:NoDatasetIndexes']]: {
maxCount: 5
}
} satisfies z.infer<typeof CountLimitConfigType>;
78 changes: 78 additions & 0 deletions packages/service/common/system/countLimit/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type z from 'zod';
import type { CountLimitTypeEnum } from './type';
import { CountLimitConfig } from './const';
import { MongoCountLimit } from './schema';
import { mongoSessionRun } from 'common/mongo/sessionRun';

/**
* Update the count limit for a specific type and key.
* @param param0 - The type, key, and update value.
* @returns The updated count limit information.
*/
export const updateCountLimit = async ({
type,
key,
update
}: {
type: z.infer<typeof CountLimitTypeEnum>;
key: string;
update: number;
}) =>
mongoSessionRun(async (session) => {
const maxCount = CountLimitConfig[type].maxCount;
const countLimit = await MongoCountLimit.findOne(
{
type,
key
},
undefined,
{
session
}
).lean();
if (!countLimit) {
// do not exist, create a new one
await MongoCountLimit.create(
{
type,
key,
count: update // 0 + update
},
{
session
}
);
return {
maxCount,
nowCount: update,
remain: maxCount - update
};
}
if (countLimit && countLimit.count >= maxCount) {
return Promise.reject(`Max Count Reached, type: ${type}, key: ${key}`);
}

await MongoCountLimit.updateOne(
{
type,
key
},
{
$inc: { count: update }
},
{ session }
);

return {
maxCount,
nowCount: countLimit.count + update,
remain: maxCount - (countLimit.count + update)
};
});

/** Clean the Count limit, if no key provided, clean all the type */
export const cleanCountLimit = async ({ type, key }: { type: CountLimitTypeEnum; key?: string }) =>
MongoCountLimit.deleteMany({
type,
...(key ? { key } : {})
});
29 changes: 29 additions & 0 deletions packages/service/common/system/countLimit/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { connectionMongo, getMongoModel } from '../../../common/mongo';
import type { CountLimitType } from './type';

const { Schema } = connectionMongo;

const collectionName = 'count_limits';
const CountLimitSchema = new Schema({
key: {
type: String,
required: true
},
type: {
type: String,
required: true
},
count: {
type: Number,
required: true,
default: 0
}
});

try {
CountLimitSchema.index({ type: 1, key: 1 }, { unique: true });
} catch (error) {
console.log(error);
}

export const MongoCountLimit = getMongoModel<CountLimitType>(collectionName, CountLimitSchema);
19 changes: 19 additions & 0 deletions packages/service/common/system/countLimit/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import z from 'zod';

export const CountLimitTypeEnum = z.enum([
'notice:30PercentPoints',
'notice:10PercentPoints',
'notice:LackOfPoints',
'notice:30PercentDatasetIndexes',
'notice:10PercentDatasetIndexes',
'notice:NoDatasetIndexes'
]);

export const CountLimitType = z.object({
type: CountLimitTypeEnum,
key: z.string(),
count: z.number()
});

export type CountLimitType = z.infer<typeof CountLimitType>;
export type CountLimitTypeEnum = z.infer<typeof CountLimitTypeEnum>;
12 changes: 11 additions & 1 deletion packages/service/common/system/timerLock/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type ClientSession } from '../../mongo';
import { MongoTimerLock } from './schema';
import { addMinutes } from 'date-fns';

/*
/*
利用唯一健,使得同一时间只有一个任务在执行,后创建的锁,会因唯一健创建失败,从而无法继续执行任务
*/
export const checkTimerLock = async ({
Expand Down Expand Up @@ -30,3 +30,13 @@ export const checkTimerLock = async ({
return false;
}
};

export const cleanTimerLock = async ({
timerId,
session
}: {
timerId: string;
session?: ClientSession;
}) => {
await MongoTimerLock.deleteOne({ timerId }, { session });
};
3 changes: 2 additions & 1 deletion packages/service/support/user/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export async function getUserDetail({
team: tmb,
notificationAccount: tmb.notificationAccount,
permission: tmb.permission,
contact: user.contact
contact: user.contact,
language: user.language
};
}
5 changes: 5 additions & 0 deletions packages/service/support/user/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { hashStr } from '@fastgpt/global/common/string/tools';
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant';
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
import { LangEnum } from '@fastgpt/global/common/i18n/type';

export const userCollectionName = 'users';

Expand Down Expand Up @@ -46,6 +47,10 @@ const UserSchema = new Schema({
type: String,
default: 'Asia/Shanghai'
},
language: {
type: String,
default: LangEnum.zh_CN
},
lastLoginTmbId: {
type: Schema.Types.ObjectId,
ref: TeamMemberCollectionName
Expand Down
55 changes: 55 additions & 0 deletions packages/web/i18n/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,58 @@
import { type I18nKeyFunction } from './i18next';
import { LangEnum } from '@fastgpt/global/common/i18n/type';
import Cookies from 'js-cookie';

const LANG_KEY = 'NEXT_LOCALE';

const isInIframe = () => {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
};

export const setLangToStorage = (value: string) => {
if (isInIframe()) {
localStorage.setItem(LANG_KEY, value);
} else {
// 不在 iframe 中,同时使用 Cookie 和 localStorage
Cookies.set(LANG_KEY, value, { expires: 30 });
localStorage.setItem(LANG_KEY, value);
}
};

export const getLangFromStorage = () => {
return localStorage.getItem(LANG_KEY) || Cookies.get(LANG_KEY);
};

export const getLangMapping = (lng: string): string => {
const languageMap: Record<string, string> = {
zh: LangEnum.zh_CN,
'zh-CN': LangEnum.zh_CN,
'zh-Hans': LangEnum.zh_CN,
'zh-HK': LangEnum.zh_Hant,
'zh-TW': LangEnum.zh_Hant,
'zh-Hant': LangEnum.zh_Hant,
en: LangEnum.en,
'en-US': LangEnum.en
};

let lang = languageMap[lng];

// 如果没有直接映射,尝试智能回退
if (!lang) {
const langPrefix = lng.split('-')[0];
// 中文相关语言优先回退到简体中文
if (langPrefix === 'zh') {
lang = LangEnum.zh_CN;
}
if (langPrefix === 'en') {
lang = LangEnum.en;
}
}

return lang || LangEnum.zh_CN;
};

export const i18nT: I18nKeyFunction = (key) => key;
20 changes: 18 additions & 2 deletions projects/app/src/components/Select/I18nLngSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,29 @@ import { Box, Flex } from '@chakra-ui/react';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
import { useTranslation } from 'next-i18next';
import { useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import type { LangEnum } from '@fastgpt/global/common/i18n/type';
import { langMap } from '@fastgpt/global/common/i18n/type';
import { useUserStore } from '@/web/support/user/useUserStore';

const I18nLngSelector = () => {
const { i18n } = useTranslation();
const { onChangeLng } = useI18nLng();
const { onChangeLng: onChangeLngI18n } = useI18nLng();
const { userInfo, updateUserInfo } = useUserStore();

const onChangeLng = useCallback(
async (lng: `${LangEnum}`) => {
if (userInfo?.username) {
// logined
await updateUserInfo({
language: lng
});
}
await onChangeLngI18n(lng);
},
[userInfo?.username, onChangeLngI18n, updateUserInfo]
);

const list = useMemo(() => {
return Object.entries(langMap).map(([key, lang]) => ({
Expand Down
Loading
Loading