user timezone
This commit is contained in:
parent
562fd2692d
commit
7a926b7086
@ -62,6 +62,7 @@
|
|||||||
"remark-math": "^5.1.1",
|
"remark-math": "^5.1.1",
|
||||||
"request-ip": "^3.3.0",
|
"request-ip": "^3.3.0",
|
||||||
"sass": "^1.58.3",
|
"sass": "^1.58.3",
|
||||||
|
"timezones-list": "^3.0.2",
|
||||||
"tunnel": "^0.0.6",
|
"tunnel": "^0.0.6",
|
||||||
"winston": "^3.10.0",
|
"winston": "^3.10.0",
|
||||||
"winston-mongodb": "^5.1.1",
|
"winston-mongodb": "^5.1.1",
|
||||||
|
|||||||
9
client/pnpm-lock.yaml
generated
9
client/pnpm-lock.yaml
generated
@ -164,6 +164,9 @@ dependencies:
|
|||||||
sass:
|
sass:
|
||||||
specifier: ^1.58.3
|
specifier: ^1.58.3
|
||||||
version: registry.npmmirror.com/sass@1.58.3
|
version: registry.npmmirror.com/sass@1.58.3
|
||||||
|
timezones-list:
|
||||||
|
specifier: ^3.0.2
|
||||||
|
version: registry.npmmirror.com/timezones-list@3.0.2
|
||||||
tunnel:
|
tunnel:
|
||||||
specifier: ^0.0.6
|
specifier: ^0.0.6
|
||||||
version: registry.npmmirror.com/tunnel@0.0.6
|
version: registry.npmmirror.com/tunnel@0.0.6
|
||||||
@ -11732,6 +11735,12 @@ packages:
|
|||||||
version: 0.2.0
|
version: 0.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
registry.npmmirror.com/timezones-list@3.0.2:
|
||||||
|
resolution: {integrity: sha512-I698hm6Jp/xxkwyTSOr39pZkYKETL8LDJeSIhjxXBfPUAHM5oZNuQ4o9UK3PSkDBOkjATecSOBb3pR1IkIBUsg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/timezones-list/-/timezones-list-3.0.2.tgz}
|
||||||
|
name: timezones-list
|
||||||
|
version: 3.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
registry.npmmirror.com/tiny-invariant@1.3.1:
|
registry.npmmirror.com/tiny-invariant@1.3.1:
|
||||||
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz}
|
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz}
|
||||||
name: tiny-invariant
|
name: tiny-invariant
|
||||||
|
|||||||
@ -184,6 +184,7 @@
|
|||||||
"Sign Out": "Sign Out",
|
"Sign Out": "Sign Out",
|
||||||
"Source": "Source",
|
"Source": "Source",
|
||||||
"Time": "Time",
|
"Time": "Time",
|
||||||
|
"Timezone": "Timezone",
|
||||||
"Total Amount": "Total Amount",
|
"Total Amount": "Total Amount",
|
||||||
"Update Password": "Update Password",
|
"Update Password": "Update Password",
|
||||||
"Update password failed": "Update password failed",
|
"Update password failed": "Update password failed",
|
||||||
|
|||||||
@ -184,6 +184,7 @@
|
|||||||
"Sign Out": "登出",
|
"Sign Out": "登出",
|
||||||
"Source": "来源",
|
"Source": "来源",
|
||||||
"Time": "时间",
|
"Time": "时间",
|
||||||
|
"Timezone": "时区",
|
||||||
"Total Amount": "总金额",
|
"Total Amount": "总金额",
|
||||||
"Update Password": "修改密码",
|
"Update Password": "修改密码",
|
||||||
"Update password failed": "修改密码异常",
|
"Update password failed": "修改密码异常",
|
||||||
|
|||||||
@ -37,7 +37,7 @@ import { fileDownload } from '@/utils/file';
|
|||||||
import { htmlTemplate } from '@/constants/common';
|
import { htmlTemplate } from '@/constants/common';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useGlobalStore } from '@/store/global';
|
import { useGlobalStore } from '@/store/global';
|
||||||
import { TaskResponseKeyEnum, getDefaultChatVariables } from '@/constants/chat';
|
import { TaskResponseKeyEnum } from '@/constants/chat';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
import { userUpdateChatFeedback, adminUpdateChatFeedback } from '@/api/chat';
|
import { userUpdateChatFeedback, adminUpdateChatFeedback } from '@/api/chat';
|
||||||
@ -350,10 +350,7 @@ const ChatBox = (
|
|||||||
messages,
|
messages,
|
||||||
controller: abortSignal,
|
controller: abortSignal,
|
||||||
generatingMessage,
|
generatingMessage,
|
||||||
variables: {
|
variables
|
||||||
...getDefaultChatVariables(),
|
|
||||||
...variables
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// set finish status
|
// set finish status
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Menu, MenuButton, MenuItem, MenuList, MenuButtonProps } from '@chakra-u
|
|||||||
import { getLangStore, LangEnum, setLangStore } from '@/utils/i18n';
|
import { getLangStore, LangEnum, setLangStore } from '@/utils/i18n';
|
||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
const langMap = {
|
const langMap = {
|
||||||
[LangEnum.en]: {
|
[LangEnum.en]: {
|
||||||
@ -16,6 +17,7 @@ const langMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Language = (props: MenuButtonProps) => {
|
const Language = (props: MenuButtonProps) => {
|
||||||
|
const router = useRouter();
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
|
|
||||||
const [language, setLanguage] = useState<`${LangEnum}`>(getLangStore());
|
const [language, setLanguage] = useState<`${LangEnum}`>(getLangStore());
|
||||||
@ -43,6 +45,7 @@ const Language = (props: MenuButtonProps) => {
|
|||||||
setLangStore(lang);
|
setLangStore(lang);
|
||||||
setLanguage(lang);
|
setLanguage(lang);
|
||||||
i18n?.changeLanguage?.(lang);
|
i18n?.changeLanguage?.(lang);
|
||||||
|
router.reload();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{lang.label}
|
{lang.label}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ interface Props extends ButtonProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MySelect = (
|
const MySelect = (
|
||||||
{ placeholder, value, width = 'auto', list, onchange, ...props }: Props,
|
{ placeholder, value, width = '100%', list, onchange, ...props }: Props,
|
||||||
selectRef: any
|
selectRef: any
|
||||||
) => {
|
) => {
|
||||||
const ref = useRef<HTMLButtonElement>(null);
|
const ref = useRef<HTMLButtonElement>(null);
|
||||||
@ -94,6 +94,8 @@ const MySelect = (
|
|||||||
}
|
}
|
||||||
zIndex={99}
|
zIndex={99}
|
||||||
transform={'translateY(35px) !important'}
|
transform={'translateY(35px) !important'}
|
||||||
|
maxH={'40vh'}
|
||||||
|
overflowY={'auto'}
|
||||||
>
|
>
|
||||||
{list.map((item) => (
|
{list.map((item) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|||||||
@ -67,7 +67,3 @@ export enum OutLinkTypeEnum {
|
|||||||
|
|
||||||
export const HUMAN_ICON = `/icon/human.png`;
|
export const HUMAN_ICON = `/icon/human.png`;
|
||||||
export const LOGO_ICON = `/icon/logo.svg`;
|
export const LOGO_ICON = `/icon/logo.svg`;
|
||||||
|
|
||||||
export const getDefaultChatVariables = () => ({
|
|
||||||
cTime: dayjs().format('YYYY/MM/DD HH:mm:ss')
|
|
||||||
});
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useRef } from 'react';
|
||||||
import { Box, Flex, Button, useDisclosure, useTheme, Divider } from '@chakra-ui/react';
|
import { Box, Flex, Button, useDisclosure, useTheme, Divider, Select } from '@chakra-ui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { UserUpdateParams } from '@/types/user';
|
import { UserUpdateParams } from '@/types/user';
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
@ -11,6 +11,7 @@ import { useSelectFile } from '@/hooks/useSelectFile';
|
|||||||
import { compressImg } from '@/utils/file';
|
import { compressImg } from '@/utils/file';
|
||||||
import { feConfigs } from '@/store/static';
|
import { feConfigs } from '@/store/static';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { timezoneList } from '@/utils/user';
|
||||||
import Loading from '@/components/Loading';
|
import Loading from '@/components/Loading';
|
||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
@ -33,6 +34,7 @@ const UserInfo = () => {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { userInfo, updateUserInfo, initUserInfo } = useUserStore();
|
const { userInfo, updateUserInfo, initUserInfo } = useUserStore();
|
||||||
|
const timezones = useRef(timezoneList());
|
||||||
const { reset } = useForm<UserUpdateParams>({
|
const { reset } = useForm<UserUpdateParams>({
|
||||||
defaultValues: userInfo as UserType
|
defaultValues: userInfo as UserType
|
||||||
});
|
});
|
||||||
@ -59,6 +61,7 @@ const UserInfo = () => {
|
|||||||
async (data: UserType) => {
|
async (data: UserType) => {
|
||||||
await updateUserInfo({
|
await updateUserInfo({
|
||||||
avatar: data.avatar,
|
avatar: data.avatar,
|
||||||
|
timezone: data.timezone,
|
||||||
openaiAccount: data.openaiAccount
|
openaiAccount: data.openaiAccount
|
||||||
});
|
});
|
||||||
reset(data);
|
reset(data);
|
||||||
@ -102,7 +105,13 @@ const UserInfo = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box display={['block', 'flex']} py={[2, 10]} justifyContent={'center'} fontSize={['lg', 'xl']}>
|
<Box
|
||||||
|
display={['block', 'flex']}
|
||||||
|
py={[2, 10]}
|
||||||
|
justifyContent={'center'}
|
||||||
|
alignItems={'flex-start'}
|
||||||
|
fontSize={['lg', 'xl']}
|
||||||
|
>
|
||||||
<Flex
|
<Flex
|
||||||
flexDirection={'column'}
|
flexDirection={'column'}
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
@ -135,11 +144,27 @@ const UserInfo = () => {
|
|||||||
mt={[6, 0]}
|
mt={[6, 0]}
|
||||||
>
|
>
|
||||||
<Flex alignItems={'center'} w={['85%', '300px']}>
|
<Flex alignItems={'center'} w={['85%', '300px']}>
|
||||||
<Box flex={'0 0 50px'}>{t('user.Account')}: </Box>
|
<Box flex={'0 0 80px'}>{t('user.Account')}: </Box>
|
||||||
<Box flex={1}>{userInfo?.username}</Box>
|
<Box flex={1}>{userInfo?.username}</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex mt={6} alignItems={'center'} w={['85%', '300px']}>
|
<Flex mt={6} alignItems={'center'} w={['85%', '300px']}>
|
||||||
<Box flex={'0 0 50px'}>{t('user.Password')}: </Box>
|
<Box flex={'0 0 80px'}>{t('user.Timezone')}: </Box>
|
||||||
|
<Select
|
||||||
|
value={userInfo?.timezone}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (!userInfo) return;
|
||||||
|
onclickSave({ ...userInfo, timezone: e.target.value });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{timezones.current.map((item) => (
|
||||||
|
<option key={item.value} value={item.value}>
|
||||||
|
{item.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Flex>
|
||||||
|
<Flex mt={6} alignItems={'center'} w={['85%', '300px']}>
|
||||||
|
<Box flex={'0 0 80px'}>{t('user.Password')}: </Box>
|
||||||
<Box flex={1}>*****</Box>
|
<Box flex={1}>*****</Box>
|
||||||
<Button size={['sm', 'md']} variant={'base'} ml={5} onClick={onOpenUpdatePsw}>
|
<Button size={['sm', 'md']} variant={'base'} ml={5} onClick={onOpenUpdatePsw}>
|
||||||
{t('user.Change')}
|
{t('user.Change')}
|
||||||
@ -149,7 +174,7 @@ const UserInfo = () => {
|
|||||||
<>
|
<>
|
||||||
<Box mt={6} whiteSpace={'nowrap'} w={['85%', '300px']}>
|
<Box mt={6} whiteSpace={'nowrap'} w={['85%', '300px']}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Box flex={'0 0 50px'}>{t('user.Balance')}: </Box>
|
<Box flex={'0 0 80px'}>{t('user.Balance')}: </Box>
|
||||||
<Box flex={1}>
|
<Box flex={1}>
|
||||||
<strong>{userInfo?.balance.toFixed(3)}</strong> 元
|
<strong>{userInfo?.balance.toFixed(3)}</strong> 元
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -42,6 +42,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
/* user auth */
|
/* user auth */
|
||||||
const { userId, user } = await authUser({ req, authBalance: true });
|
const { userId, user } = await authUser({ req, authBalance: true });
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('user not found');
|
||||||
|
}
|
||||||
|
|
||||||
/* start process */
|
/* start process */
|
||||||
const { responseData } = await dispatchModules({
|
const { responseData } = await dispatchModules({
|
||||||
res,
|
res,
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import { BillSourceEnum } from '@/constants/user';
|
|||||||
import { ChatHistoryItemResType } from '@/types/chat';
|
import { ChatHistoryItemResType } from '@/types/chat';
|
||||||
import { UserModelSchema } from '@/types/mongoSchema';
|
import { UserModelSchema } from '@/types/mongoSchema';
|
||||||
import { SystemInputEnum } from '@/constants/app';
|
import { SystemInputEnum } from '@/constants/app';
|
||||||
|
import { getSystemTime } from '@/utils/user';
|
||||||
|
|
||||||
export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
|
export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
|
||||||
type FastGptWebChatProps = {
|
type FastGptWebChatProps = {
|
||||||
@ -95,9 +96,6 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Error('Account is error');
|
throw new Error('Account is error');
|
||||||
}
|
}
|
||||||
// if (authType === AuthUserTypeEnum.apikey || shareId) {
|
|
||||||
// user.openaiAccount = undefined;
|
|
||||||
// }
|
|
||||||
|
|
||||||
appId = appId ? appId : authAppid;
|
appId = appId ? appId : authAppid;
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
@ -249,6 +247,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* running */
|
||||||
export async function dispatchModules({
|
export async function dispatchModules({
|
||||||
res,
|
res,
|
||||||
modules,
|
modules,
|
||||||
@ -260,12 +259,16 @@ export async function dispatchModules({
|
|||||||
}: {
|
}: {
|
||||||
res: NextApiResponse;
|
res: NextApiResponse;
|
||||||
modules: AppModuleItemType[];
|
modules: AppModuleItemType[];
|
||||||
user?: UserModelSchema;
|
user: UserModelSchema;
|
||||||
params?: Record<string, any>;
|
params?: Record<string, any>;
|
||||||
variables?: Record<string, any>;
|
variables?: Record<string, any>;
|
||||||
stream?: boolean;
|
stream?: boolean;
|
||||||
detail?: boolean;
|
detail?: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
variables = {
|
||||||
|
...getSystemVariable({ timezone: user.timezone }),
|
||||||
|
...variables
|
||||||
|
};
|
||||||
const runningModules = loadModules(modules, variables);
|
const runningModules = loadModules(modules, variables);
|
||||||
|
|
||||||
// let storeData: Record<string, any> = {}; // after module used
|
// let storeData: Record<string, any> = {}; // after module used
|
||||||
@ -390,6 +393,7 @@ export async function dispatchModules({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* init store modules to running modules */
|
||||||
function loadModules(
|
function loadModules(
|
||||||
modules: AppModuleItemType[],
|
modules: AppModuleItemType[],
|
||||||
variables: Record<string, any>
|
variables: Record<string, any>
|
||||||
@ -431,6 +435,7 @@ function loadModules(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* sse response modules staus */
|
||||||
export function responseStatus({
|
export function responseStatus({
|
||||||
res,
|
res,
|
||||||
status,
|
status,
|
||||||
@ -451,6 +456,13 @@ export function responseStatus({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* get system variable */
|
||||||
|
export function getSystemVariable({ timezone }: { timezone: string }) {
|
||||||
|
return {
|
||||||
|
cTime: getSystemTime(timezone)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
bodyParser: {
|
bodyParser: {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { axiosConfig, getAIChatApi, openaiBaseUrl } from '@/service/lib/openai';
|
|||||||
/* update user info */
|
/* update user info */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
const { avatar, openaiAccount } = req.body as UserUpdateParams;
|
const { avatar, timezone, openaiAccount } = req.body as UserUpdateParams;
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const { userId } = await authUser({ req, authToken: true });
|
||||||
|
|
||||||
@ -46,6 +46,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
...(avatar && { avatar }),
|
...(avatar && { avatar }),
|
||||||
|
...(timezone && { timezone }),
|
||||||
openaiAccount: openaiAccount?.key ? openaiAccount : null
|
openaiAccount: openaiAccount?.key ? openaiAccount : null
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@ -57,48 +57,7 @@ const AppSchema = new Schema({
|
|||||||
default: []
|
default: []
|
||||||
},
|
},
|
||||||
// 弃
|
// 弃
|
||||||
chat: {
|
chat: Object
|
||||||
relatedKbs: {
|
|
||||||
type: [Schema.Types.ObjectId],
|
|
||||||
ref: 'kb',
|
|
||||||
default: []
|
|
||||||
},
|
|
||||||
searchSimilarity: {
|
|
||||||
type: Number,
|
|
||||||
default: 0.4
|
|
||||||
},
|
|
||||||
searchLimit: {
|
|
||||||
type: Number,
|
|
||||||
default: 5
|
|
||||||
},
|
|
||||||
searchEmptyText: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
systemPrompt: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
limitPrompt: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
maxToken: {
|
|
||||||
type: Number,
|
|
||||||
default: 4000,
|
|
||||||
min: 100
|
|
||||||
},
|
|
||||||
temperature: {
|
|
||||||
type: Number,
|
|
||||||
min: 0,
|
|
||||||
max: 10,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
chatModel: {
|
|
||||||
// 聊天时使用的模型
|
|
||||||
type: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -49,6 +49,10 @@ const UserSchema = new Schema({
|
|||||||
key: String,
|
key: String,
|
||||||
baseUrl: String
|
baseUrl: String
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
timezone: {
|
||||||
|
type: String,
|
||||||
|
default: 'Asia/Shanghai'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest } from 'next';
|
import type { NextApiRequest } from 'next';
|
||||||
import Cookie from 'cookie';
|
import Cookie from 'cookie';
|
||||||
import { App, OpenApi, User, OutLink, KB } from '../mongo';
|
import { App, OpenApi, User, OutLink, KB } from '../mongo';
|
||||||
import type { AppSchema } from '@/types/mongoSchema';
|
import type { AppSchema, UserModelSchema } from '@/types/mongoSchema';
|
||||||
import { ERROR_ENUM } from '../errorCode';
|
import { ERROR_ENUM } from '../errorCode';
|
||||||
import { authJWT } from './tools';
|
import { authJWT } from './tools';
|
||||||
|
|
||||||
@ -25,7 +25,10 @@ export const authCookieToken = async (cookie?: string, token?: string): Promise<
|
|||||||
|
|
||||||
/* auth balance */
|
/* auth balance */
|
||||||
export const authBalanceByUid = async (uid: string) => {
|
export const authBalanceByUid = async (uid: string) => {
|
||||||
const user = await User.findById(uid);
|
const user = await User.findById<UserModelSchema>(
|
||||||
|
uid,
|
||||||
|
'_id username balance openaiAccount timezone'
|
||||||
|
);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||||
}
|
}
|
||||||
|
|||||||
1
client/src/types/mongoSchema.d.ts
vendored
1
client/src/types/mongoSchema.d.ts
vendored
@ -17,6 +17,7 @@ export interface UserModelSchema {
|
|||||||
inviterId?: string;
|
inviterId?: string;
|
||||||
openaiKey: string;
|
openaiKey: string;
|
||||||
createTime: number;
|
createTime: number;
|
||||||
|
timezone: string;
|
||||||
openaiAccount?: {
|
openaiAccount?: {
|
||||||
key: string;
|
key: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
|
|||||||
2
client/src/types/user.d.ts
vendored
2
client/src/types/user.d.ts
vendored
@ -5,6 +5,7 @@ export interface UserType {
|
|||||||
username: string;
|
username: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
balance: number;
|
balance: number;
|
||||||
|
timezone: string;
|
||||||
promotionRate: UserModelSchema['promotionRate'];
|
promotionRate: UserModelSchema['promotionRate'];
|
||||||
openaiAccount: UserModelSchema['openaiAccount'];
|
openaiAccount: UserModelSchema['openaiAccount'];
|
||||||
}
|
}
|
||||||
@ -12,6 +13,7 @@ export interface UserType {
|
|||||||
export interface UserUpdateParams {
|
export interface UserUpdateParams {
|
||||||
balance?: number;
|
balance?: number;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
|
timezone?: string;
|
||||||
openaiAccount?: UserModelSchema['openaiAccount'];
|
openaiAccount?: UserModelSchema['openaiAccount'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
import { PRICE_SCALE } from '@/constants/common';
|
import { PRICE_SCALE } from '@/constants/common';
|
||||||
import { loginOut } from '@/api/user';
|
import { loginOut } from '@/api/user';
|
||||||
|
import timezones from 'timezones-list';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import utc from 'dayjs/plugin/utc';
|
||||||
|
import timezone from 'dayjs/plugin/timezone';
|
||||||
|
|
||||||
|
dayjs.extend(utc);
|
||||||
|
dayjs.extend(timezone);
|
||||||
|
|
||||||
const tokenKey = 'token';
|
const tokenKey = 'token';
|
||||||
export const clearToken = () => {
|
export const clearToken = () => {
|
||||||
@ -24,3 +31,76 @@ export const getToken = () => {
|
|||||||
export const formatPrice = (val = 0, multiple = 1) => {
|
export const formatPrice = (val = 0, multiple = 1) => {
|
||||||
return Number(((val / PRICE_SCALE) * multiple).toFixed(10));
|
return Number(((val / PRICE_SCALE) * multiple).toFixed(10));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the offset from UTC in hours for the current locale.
|
||||||
|
* @param {string} timeZone Timezone to get offset for
|
||||||
|
* @returns {number} The offset from UTC in hours.
|
||||||
|
*
|
||||||
|
* Generated by Trelent
|
||||||
|
*/
|
||||||
|
export const getTimezoneOffset = (timeZone: string): number => {
|
||||||
|
const now = new Date();
|
||||||
|
const tzString = now.toLocaleString('en-US', {
|
||||||
|
timeZone
|
||||||
|
});
|
||||||
|
const localString = now.toLocaleString('en-US');
|
||||||
|
const diff = (Date.parse(localString) - Date.parse(tzString)) / 3600000;
|
||||||
|
const offset = diff + now.getTimezoneOffset() / 60;
|
||||||
|
return -offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of timezones sorted by their offset from UTC.
|
||||||
|
* @returns {object[]} A list of the given timezones sorted by their offset from UTC.
|
||||||
|
*
|
||||||
|
* Generated by Trelent
|
||||||
|
*/
|
||||||
|
export const timezoneList = () => {
|
||||||
|
const result = timezones
|
||||||
|
.map((timezone) => {
|
||||||
|
try {
|
||||||
|
let display = dayjs().tz(timezone.tzCode).format('Z');
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: `(UTC${display}) ${timezone.tzCode}`,
|
||||||
|
value: timezone.tzCode,
|
||||||
|
time: getTimezoneOffset(timezone.tzCode)
|
||||||
|
};
|
||||||
|
} catch (e) {}
|
||||||
|
})
|
||||||
|
.filter((item) => item);
|
||||||
|
|
||||||
|
result.sort((a, b) => {
|
||||||
|
if (!a || !b) return 0;
|
||||||
|
if (a.time > b.time) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b.time > a.time) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'UTC',
|
||||||
|
time: 0,
|
||||||
|
value: 'UTC'
|
||||||
|
},
|
||||||
|
...result
|
||||||
|
] as {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
time: number;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSystemTime = (timeZone: string) => {
|
||||||
|
const timezoneDiff = getTimezoneOffset(timeZone);
|
||||||
|
const now = Date.now();
|
||||||
|
const targetTime = now + timezoneDiff * 60 * 60 * 1000;
|
||||||
|
return dayjs(targetTime).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user