From a6f44ed01bb0b638a83a8d9609b556aee72ac61e Mon Sep 17 00:00:00 2001 From: zhujingyang <72259332+zjy365@users.noreply.github.com> Date: Wed, 30 Aug 2023 17:32:40 +0800 Subject: [PATCH] feat:desktop support theme (#3800) * feat:desktop support theme Signed-off-by: jingyang <3161362058@qq.com> * fix --------- Signed-off-by: jingyang <3161362058@qq.com> --- frontend/READMD.md | 2 +- frontend/desktop/package.json | 3 +- .../desktop/public/locales/en/common.json | 31 +- .../desktop/public/locales/zh/common.json | 37 +- .../src/components/desktop_content/index.tsx | 8 +- .../desktop/src/components/layout/index.tsx | 24 - .../components/signin/auth/useAuthList.tsx | 73 ++ .../components/signin/auth/useCustomError.tsx | 62 ++ .../components/signin/auth/useLanguage.tsx | 34 + .../components/signin/auth/usePassword.tsx | 241 ++++++ .../components/signin/auth/useProtocol.tsx | 70 ++ .../src/components/signin/auth/useSms.tsx | 206 +++++ .../desktop/src/components/signin/index.tsx | 194 +++++ .../src/components/user_menu/index.tsx | 14 +- .../components/withThemeFallback/index.tsx | 33 + frontend/desktop/src/pages/404.tsx | 13 + .../src/pages/api/auth/password/exist.ts | 4 +- .../desktop/src/pages/api/platform/getEnv.ts | 22 +- frontend/desktop/src/pages/index.tsx | 18 +- frontend/desktop/src/pages/signin.tsx | 736 +----------------- frontend/desktop/src/services/request.ts | 2 +- frontend/desktop/src/types/index.ts | 2 + frontend/desktop/src/types/login.ts | 5 + frontend/desktop/src/types/system.ts | 16 + frontend/pnpm-lock.yaml | 3 + 25 files changed, 1040 insertions(+), 813 deletions(-) delete mode 100644 frontend/desktop/src/components/layout/index.tsx create mode 100644 frontend/desktop/src/components/signin/auth/useAuthList.tsx create mode 100644 frontend/desktop/src/components/signin/auth/useCustomError.tsx create mode 100644 frontend/desktop/src/components/signin/auth/useLanguage.tsx create mode 100644 frontend/desktop/src/components/signin/auth/usePassword.tsx create mode 100644 frontend/desktop/src/components/signin/auth/useProtocol.tsx create mode 100644 frontend/desktop/src/components/signin/auth/useSms.tsx create mode 100644 frontend/desktop/src/components/signin/index.tsx create mode 100644 frontend/desktop/src/components/withThemeFallback/index.tsx create mode 100644 frontend/desktop/src/pages/404.tsx create mode 100644 frontend/desktop/src/types/login.ts diff --git a/frontend/READMD.md b/frontend/READMD.md index 690d904fe08..8575cce6144 100644 --- a/frontend/READMD.md +++ b/frontend/READMD.md @@ -22,7 +22,7 @@ echo '121.41.82.246 apiserver.cluster.local' | sudo tee -a /etc/hosts - It is best to use `sealos/frontend` as the workspace directory to develop applications. - Before dev, you should install `pnpm` first. [pnpm](https://pnpm.io/zh/) -- The `sealos/frontend/packages/*` are local dependencies, you need run `pnpm -r --filter=./packages/* run build` in the `sealos/frontend` directory tobuild them. +- The `sealos/frontend/packages/*` are local dependencies, you need run `pnpm -r --filter ./packages/* run build` in the `sealos/frontend` directory tobuild them. - The `sealos/frontend/providers/*` are sub applications.. - The `sealos/frontend/desktop` is desktop app. diff --git a/frontend/desktop/package.json b/frontend/desktop/package.json index 379201330ae..8c89e37052b 100644 --- a/frontend/desktop/package.json +++ b/frontend/desktop/package.json @@ -44,7 +44,8 @@ "sass": "^1.63.6", "sealos-desktop-sdk": "workspace:*", "uuid": "^9.0.0", - "zustand": "^4.3.9" + "zustand": "^4.3.9", + "react-hook-form": "^7.45.2" }, "devDependencies": { "@types/js-cookie": "^3.0.3", diff --git a/frontend/desktop/public/locales/en/common.json b/frontend/desktop/public/locales/en/common.json index 87723e98298..693e9ef7f7c 100644 --- a/frontend/desktop/public/locales/en/common.json +++ b/frontend/desktop/public/locales/en/common.json @@ -13,37 +13,38 @@ "Username": "Username", "Log Out": "Log Out", "Log In": "Log In", - "Password Login":"with Password", - "Verification Code Login":"with Phone", + "Password Login": "with Password", + "Verification Code Login": "with Phone", "Loading": "Loading", "From": "From", "Balance": "Balance", "password tips": "Password must be 8 characters or more", - "username tips":"Username must be 3-16 characters, including letters, numbers", + "username tips": "Username must be 3-16 characters, including letters, numbers", "Password": "Password", "Verify password": "Verify password", - "verify code tips":"6-digit Verification Code", - "phone number tips":"Phone Number", + "verify code tips": "6-digit Verification Code", + "phone number tips": "Phone Number", "Invalid phone number": "Invalid phone number", "Invalid username or password": "Invalid username or password", - "Invalid verification code":"Invalid verification code", - "Get code failed":"Get code failed", - "Read and agree":"Please read and agree to the agreement below", - "agree policy":"I have read and agree to the", - "Service Agreement":"Service Agreement", - "and":"and", - "Privacy Policy":"Privacy Policy", - "Get Code":"verification", + "Invalid verification code": "Invalid verification code", + "Get code failed": "Get code failed", + "Read and agree": "Please read and agree to the agreement below", + "agree policy": "I have read and agree to the", + "Service Agreement": "Service Agreement", + "and": "and", + "Privacy Policy": "Privacy Policy", + "Get Code": "verification", "Total Amount": "Total Amount", "Payment Result": "Payment Result", "Payment Successful": "Payment Successful", "In Payment": "In Payment ...", "Bonus": "Bonus", "Select Amount": "Select Amount", - "View Discount Rules":"View recharge discount rules.", + "View Discount Rules": "View recharge discount rules.", "Payment Status": "Payment Status", "Scan with WeChat": "Scan with WeChat", "Charge": "Charge", "Order Number": "Order Number", - "Confirm": "Confirm" + "Confirm": "Confirm", + "Login to your account": "Login to your account" } \ No newline at end of file diff --git a/frontend/desktop/public/locales/zh/common.json b/frontend/desktop/public/locales/zh/common.json index 216e6a82e76..b975df58a05 100644 --- a/frontend/desktop/public/locales/zh/common.json +++ b/frontend/desktop/public/locales/zh/common.json @@ -5,39 +5,40 @@ "Unread": "未读", "Read All": "全部已读", "Username": "用户名", - "Password Login":"密码登录", - "Verification Code Login":"手机号登录", - "Password":"密码", + "Password Login": "密码登录", + "Verification Code Login": "手机号登录", + "Password": "密码", "Log In": "登录", "Loading": "加载中", "Log Out": "退出账号", "From": "来自", "Balance": "余额", - "verify code tips":"6位验证码", - "phone number tips":"手机号码", + "verify code tips": "6位验证码", + "phone number tips": "手机号码", "password tips": "密码为8位以上字符", - "username tips":"用户名为3-16位的英文或数字的字符", + "username tips": "用户名为3-16位的英文或数字的字符", "Verify password": "确认密码", "Invalid username or password": "用户名或密码错误", - "Invalid phone number":"无效的手机号码", - "Invalid verification code":"无效的验证码", - "Get code failed":"获取验证码失败", - "Read and agree":"请阅读并同意下方协议", - "agree policy":"我已阅读并同意", - "and":"和", - "Service Agreement":"服务协议", - "Privacy Policy":"隐私政策", - "Get Code":"获取验证码", - "Bonus":"赠", + "Invalid phone number": "无效的手机号码", + "Invalid verification code": "无效的验证码", + "Get code failed": "获取验证码失败", + "Read and agree": "请阅读并同意下方协议", + "agree policy": "我已阅读并同意", + "and": "和", + "Service Agreement": "服务协议", + "Privacy Policy": "隐私政策", + "Get Code": "获取验证码", + "Bonus": "赠", "Payment Result": "支付结果", "Payment Successful": "支付成功", "In Payment": "支付中 ...", "Recharge Amount": "充值金额", "Select Amount": "选择金额", - "View Discount Rules":"查看优惠规则", + "View Discount Rules": "查看优惠规则", "Payment Status": "支付状态", "Scan with WeChat": "微信扫码支付", "Charge": "充值", "Order Number": "订单号", - "Confirm": "确认" + "Confirm": "确认", + "Login to your account": "登录您的帐户" } \ No newline at end of file diff --git a/frontend/desktop/src/components/desktop_content/index.tsx b/frontend/desktop/src/components/desktop_content/index.tsx index 34c7eb98261..4da3b39b4d1 100644 --- a/frontend/desktop/src/components/desktop_content/index.tsx +++ b/frontend/desktop/src/components/desktop_content/index.tsx @@ -69,7 +69,13 @@ export default function DesktopContent(props: any) { }, [openDesktopApp]); return ( - + diff --git a/frontend/desktop/src/components/layout/index.tsx b/frontend/desktop/src/components/layout/index.tsx deleted file mode 100644 index 88f90b38e5e..00000000000 --- a/frontend/desktop/src/components/layout/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Box } from '@chakra-ui/react'; -import Head from 'next/head'; - -export default function Layout(props: any) { - return ( - <> - - sealos Cloud - - - - {props.children} - - - ); -} diff --git a/frontend/desktop/src/components/signin/auth/useAuthList.tsx b/frontend/desktop/src/components/signin/auth/useAuthList.tsx new file mode 100644 index 00000000000..423bd2d9cf1 --- /dev/null +++ b/frontend/desktop/src/components/signin/auth/useAuthList.tsx @@ -0,0 +1,73 @@ +import useSessionStore from '@/stores/session'; +import { Button, Flex, Image } from '@chakra-ui/react'; +import { MouseEventHandler } from 'react'; + +type useAuthListProps = { + needGithub: boolean; + needWechat: boolean; + wechat_client_id: string; + github_client_id: string; + callback_url: string; +}; + +const useAuthList = ({ + needGithub, + needWechat, + wechat_client_id, + github_client_id, + callback_url +}: useAuthListProps) => { + const { generateState, setProvider } = useSessionStore(); + + const oauthLogin = async ({ url, provider }: { url: string; provider?: 'github' | 'wechat' }) => { + setProvider(provider); + window.location.href = url; + }; + + const authList: { src: string; cb: MouseEventHandler; need: boolean }[] = [ + { + src: '/images/github.svg', + cb: (e) => { + e.preventDefault(); + const state = generateState(); + oauthLogin({ + provider: 'github', + url: `https://github.com/login/oauth/authorize?client_id=${github_client_id}&redirect_uri=${callback_url}&scope=user:email%20read:user&state=${state}` + }); + }, + need: needGithub + }, + { + src: '/images/wechat.svg', + cb: (e) => { + e.preventDefault(); + const state = generateState(); + oauthLogin({ + provider: 'wechat', + url: `https://open.weixin.qq.com/connect/qrconnect?appid=${wechat_client_id}&redirect_uri=${callback_url}&response_type=code&state=${state}&scope=snsapi_login&#wechat_redirect` + }); + }, + need: needWechat + } + ]; + + const AuthList = () => { + return ( + + {authList + .filter((item) => item.need) + .map((item, index) => ( + + ))} + + ); + }; + + return { + AuthList + }; +}; + +export default useAuthList; diff --git a/frontend/desktop/src/components/signin/auth/useCustomError.tsx b/frontend/desktop/src/components/signin/auth/useCustomError.tsx new file mode 100644 index 00000000000..6dc5b7ad578 --- /dev/null +++ b/frontend/desktop/src/components/signin/auth/useCustomError.tsx @@ -0,0 +1,62 @@ +import { Flex, Img, Button, Text } from '@chakra-ui/react'; +import React, { useState, useEffect } from 'react'; +import warnIcon from 'public/icons/warning.svg'; +import closeIcon from 'public/icons/close_white.svg'; +import { useTranslation } from 'next-i18next'; + +const useCustomError = () => { + const { t } = useTranslation(); + const [error, setError] = useState(''); + + const showError = (errorMessage: string, duration = 5000) => { + setError(errorMessage); + setTimeout(() => { + setError(''); + }, duration); + }; + + const closeError = () => { + setError(''); + }; + + const ErrorComponent = () => { + useEffect(() => { + if (error) { + const timeout = setTimeout(() => { + closeError(); + }, 5000); // 默认 5000 毫秒(5秒)后自动关闭错误消息 + return () => clearTimeout(timeout); + } + }, [error]); + + return error ? ( + + + {t(error)} + + + ) : null; + }; + + return { showError, ErrorComponent }; +}; + +export default useCustomError; diff --git a/frontend/desktop/src/components/signin/auth/useLanguage.tsx b/frontend/desktop/src/components/signin/auth/useLanguage.tsx new file mode 100644 index 00000000000..910bcbab7f4 --- /dev/null +++ b/frontend/desktop/src/components/signin/auth/useLanguage.tsx @@ -0,0 +1,34 @@ +import LangSelect from '@/components/LangSelect'; +import { Box, Flex, UseDisclosureReturn } from '@chakra-ui/react'; +import { I18n } from 'next-i18next'; + +type LanguageType = { disclosure: UseDisclosureReturn; i18n: I18n | null }; + +const Language = ({ disclosure, i18n }: LanguageType) => { + return ( + + + disclosure.onOpen()}>{i18n?.language === 'en' ? 'en' : '中'} + + + + ); +}; + +export default Language; diff --git a/frontend/desktop/src/components/signin/auth/usePassword.tsx b/frontend/desktop/src/components/signin/auth/usePassword.tsx new file mode 100644 index 00000000000..56c4848f7b8 --- /dev/null +++ b/frontend/desktop/src/components/signin/auth/usePassword.tsx @@ -0,0 +1,241 @@ +import request from '@/services/request'; +import useSessionStore from '@/stores/session'; +import { ApiResp, Session } from '@/types'; +import { TUserExist } from '@/types/user'; +import { Flex, Image, Img, Input, InputGroup, InputLeftAddon, Text } from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; +import { useRouter } from 'next/router'; +import lockIcon from 'public/images/lock.svg'; +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; + +export default function usePassword({ + showError +}: { + showError: (errorMessage: string, duration?: number) => void; +}) { + const { t } = useTranslation(); + const router = useRouter(); + const [userExist, setUserExist] = useState(true); + const [isLoading, setIsLoading] = useState(false); + // 对于注册的用户,需要先验证密码 0 默认页面;1为验证密码页面 + const [pageState, setPageState] = useState(0); + + const { updateUser, setSession } = useSessionStore(); + + const { register, handleSubmit, watch, trigger, getValues } = useForm<{ + username: string; + password: string; + confimPassword: string; + }>(); + + const login = async () => { + const deepSearch = (obj: any): string => { + if (!obj || typeof obj !== 'object') return t('Submit Error'); + if (!!obj.message) { + return obj.message; + } + return deepSearch(Object.values(obj)[0]); + }; + + handleSubmit( + async (data) => { + if (data?.username && data?.password) { + try { + setIsLoading(true); + const result = await request.post, { user: string }>( + '/api/auth/password/exist', + { + user: data.username + } + ); + if (result?.code === 200) { + const result = await request.post>('/api/auth/password', { + user: data.username, + password: data.password + }); + setSession(result.data!); + updateUser(); + router.replace('/'); + return; + } + if (result?.code === 201) { + setUserExist(!!result?.data?.exist); + setPageState(1); + if (!!data?.confimPassword) { + if (data?.password !== data?.confimPassword) { + showError('password not match'); + } else { + const result = await request.post>('/api/auth/password', { + user: data.username, + password: data.password + }); + setSession(result.data!); + updateUser(); + router.replace('/'); + } + } + } + } catch (error: any) { + showError(t('Invalid username or password')); + } finally { + setIsLoading(false); + } + } + }, + (err) => { + console.log(err); + showError(deepSearch(err)); + } + )(); + }; + + const PasswordComponent = () => { + if (pageState === 0) { + return ; + } else { + return ; + } + }; + + const PasswordModal = () => { + return ( + <> + + + person setUserExist(true)} /> + + + + + + person + + + + + ); + }; + + const ConfirmPasswordModal = () => { + return ( + <> + + Vector setPageState(0)} + /> + {t('Verify password')} + + + + + + + + + ); + }; + + return { + PasswordComponent, + login, + userExist, + pageState, + isLoading + }; +} diff --git a/frontend/desktop/src/components/signin/auth/useProtocol.tsx b/frontend/desktop/src/components/signin/auth/useProtocol.tsx new file mode 100644 index 00000000000..d34f5ca560f --- /dev/null +++ b/frontend/desktop/src/components/signin/auth/useProtocol.tsx @@ -0,0 +1,70 @@ +import { Checkbox, Flex, Link, Text } from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; +import { useState } from 'react'; + +const useProtocol = ({ + service_protocol, + private_protocol +}: { + service_protocol: string; + private_protocol: string; +}) => { + const { t, i18n } = useTranslation(); + const [isAgree, setIsAgree] = useState(false); + const [isInvalid, setIsInvalid] = useState(false); + + const Protocol = () => ( + + { + setIsInvalid(false); + setIsAgree(e.target.checked); + }} + /> + + {t('agree policy')} + + {t('Service Agreement')} + + {t('and')} + + {t('Privacy Policy')} + + + + ); + return { + Protocol, + isAgree, + setIsInvalid + }; +}; + +export default useProtocol; diff --git a/frontend/desktop/src/components/signin/auth/useSms.tsx b/frontend/desktop/src/components/signin/auth/useSms.tsx new file mode 100644 index 00000000000..c6a979f86d6 --- /dev/null +++ b/frontend/desktop/src/components/signin/auth/useSms.tsx @@ -0,0 +1,206 @@ +import request from '@/services/request'; +import useSessionStore from '@/stores/session'; +import { ApiResp, Session } from '@/types'; +import { + Image, + Input, + InputGroup, + InputLeftAddon, + InputRightAddon, + Link, + Text +} from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; +import NextLink from 'next/link'; +import { useRouter } from 'next/router'; +import { MouseEventHandler, useEffect, useRef, useState } from 'react'; +import { useForm } from 'react-hook-form'; + +export default function useSms({ + showError +}: { + showError: (errorMessage: string, duration?: number) => void; +}) { + const { t } = useTranslation(); + const _remainTime = useRef(0); + const router = useRouter(); + const { updateUser, setSession } = useSessionStore(); + const [isLoading, setIsLoading] = useState(false); + + const { register, handleSubmit, trigger, getValues } = useForm<{ + phoneNumber: string; + verifyCode: string; + }>(); + + const login = async () => { + const deepSearch = (obj: any): string => { + if (!obj || typeof obj !== 'object') return t('Submit Error'); + if (!!obj.message) { + return obj.message; + } + return deepSearch(Object.values(obj)[0]); + }; + + handleSubmit( + async (data) => { + try { + setIsLoading(true); + const result = await request.post>('/api/auth/phone/verify', { + phoneNumbers: data.phoneNumber, + code: data.verifyCode + }); + setSession(result.data!); + updateUser(); + router.replace('/'); + } catch (error) { + showError(t('Invalid verification code') || 'Invalid verification code'); + } finally { + setIsLoading(false); + } + }, + (err) => { + showError(deepSearch(err)); + } + )(); + }; + + const SmsModal = () => { + const [remainTime, setRemainTime] = useState(_remainTime.current); + + useEffect(() => { + if (remainTime <= 0) return; + const interval = setInterval(() => { + setRemainTime(remainTime - 1); + }, 1000); + return () => clearInterval(interval); + }, [remainTime]); + + const getCode: MouseEventHandler = async (e) => { + e.preventDefault(); + + if (!(await trigger('phoneNumber'))) { + showError(t('Invalid phone number') || 'Invalid phone number'); + return; + } + setRemainTime(60); + _remainTime.current = 60; + + try { + const res = await request.post>('/api/auth/phone/sms', { + phoneNumbers: getValues('phoneNumber') + }); + if (res.code !== 200 || res.message !== 'successfully') { + throw new Error('Get code failed'); + } + } catch (err) { + showError(t('Get code failed') || 'Get code failed'); + setRemainTime(0); + _remainTime.current = 0; + } + }; + + return ( + <> + + + + +86 + + + + + + {remainTime <= 0 ? ( + + {t('Get Code')} + + ) : ( + {remainTime} s + )} + + + + + + safety + + + + {getValues('verifyCode')?.length === 6 && ( + material-symbols_update + )} + + + + ); + }; + + return { + SmsModal, + login, + isLoading + }; +} diff --git a/frontend/desktop/src/components/signin/index.tsx b/frontend/desktop/src/components/signin/index.tsx new file mode 100644 index 00000000000..f92ee75c28a --- /dev/null +++ b/frontend/desktop/src/components/signin/index.tsx @@ -0,0 +1,194 @@ +import useAuthList from '@/components/signin/auth/useAuthList'; +import useCustomError from '@/components/signin/auth/useCustomError'; +import Language from '@/components/signin/auth/useLanguage'; +import usePassword from '@/components/signin/auth/usePassword'; +import useProtocol from '@/components/signin/auth/useProtocol'; +import useSms from '@/components/signin/auth/useSms'; +import request from '@/services/request'; +import { ApiResp, LoginType, SystemEnv } from '@/types'; +import { + Box, + Button, + Flex, + Img, + Tab, + TabIndicator, + TabList, + Tabs, + useDisclosure +} from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; +import { debounce } from 'lodash'; +import { useTranslation } from 'next-i18next'; +import Head from 'next/head'; +import sealosTitle from 'public/images/sealos-title.png'; +import { useEffect, useMemo, useState } from 'react'; + +export default function SigninComponent() { + const { data: platformEnv } = useQuery(['getPlatformEnv'], () => + request>('/api/platform/getEnv') + ); + + const { + wechat_client_id = '', + github_client_id = '', + callback_url = '', + service_protocol = '', + private_protocol = '', + needPassword = false, + needSms = false, + needGithub = false, + needWechat = false + } = platformEnv?.data || {}; + + const needTabs = needPassword && needSms; + + const disclosure = useDisclosure(); + const { t, i18n } = useTranslation(); + const [tabIndex, setTabIndex] = useState(LoginType.NONE); + + const { ErrorComponent, showError } = useCustomError(); + + const { Protocol, isAgree, setIsInvalid } = useProtocol({ service_protocol, private_protocol }); + const { SmsModal, login: smsSubmit, isLoading: smsLoading } = useSms({ showError }); + const { + PasswordComponent, + userExist, + pageState, + login: passwordSubmit, + isLoading: passwordLoading + } = usePassword({ showError }); + const isLoading = useMemo(() => passwordLoading || smsLoading, [passwordLoading, smsLoading]); + + const { AuthList } = useAuthList({ + needGithub, + needWechat, + wechat_client_id, + github_client_id, + callback_url + }); + + const loginConfig = useMemo(() => { + return { + [LoginType.SMS]: { + login: smsSubmit, + component: + }, + [LoginType.PASSWORD]: { + login: passwordSubmit, + component: + }, + [LoginType.NONE]: null + }; + }, [PasswordComponent, SmsModal, passwordSubmit, smsSubmit]); + + useEffect(() => { + setTabIndex(needSms ? LoginType.SMS : needPassword ? LoginType.PASSWORD : LoginType.NONE); + }, [needPassword, needSms]); + + const LoginComponent = useMemo( + () => (tabIndex !== LoginType.NONE ? loginConfig[tabIndex].component : null), + [loginConfig, tabIndex] + ); + + const handleLogin = debounce(() => { + const selectedConfig = loginConfig[tabIndex]; + if (isAgree && selectedConfig) { + const { login } = selectedConfig; + login(); + } else { + setIsInvalid(true); + showError(t('Read and agree')); + } + }, 500); + + return ( + + + sealos Cloud + + + + + + + + + + {pageState === 0 && needTabs && ( + setTabIndex(idx)} + variant="unstyled" + p={'0'} + width={'full'} + > + + + {t('Verification Code Login')} + + + {t('Password Login')} + + + + + )} + + {LoginComponent} + + + + + + + + + + ); +} diff --git a/frontend/desktop/src/components/user_menu/index.tsx b/frontend/desktop/src/components/user_menu/index.tsx index a3a88231fc4..c353c2b7f84 100644 --- a/frontend/desktop/src/components/user_menu/index.tsx +++ b/frontend/desktop/src/components/user_menu/index.tsx @@ -3,10 +3,9 @@ import Notification from '@/components/notification'; import useSessionStore from '@/stores/session'; import { Box, Flex, Image, useDisclosure } from '@chakra-ui/react'; import { i18n } from 'next-i18next'; -import { useRef, useState } from 'react'; +import { useState } from 'react'; import LangSelect from '../LangSelect'; import Iconfont from '../iconfont'; -import styles from './index.module.scss'; enum UserMenuKeys { LangSelect, @@ -29,16 +28,7 @@ export default function Index() { }[] = [ { key: UserMenuKeys.LangSelect, - button: ( - user avator - ), + button: {i18n?.language === 'en' ? 'en' : '中'}, click: () => switchLangDisclosure.onOpen(), content: }, diff --git a/frontend/desktop/src/components/withThemeFallback/index.tsx b/frontend/desktop/src/components/withThemeFallback/index.tsx new file mode 100644 index 00000000000..f4a789f07e2 --- /dev/null +++ b/frontend/desktop/src/components/withThemeFallback/index.tsx @@ -0,0 +1,33 @@ +import React, { Suspense } from 'react'; +import dynamic from 'next/dynamic'; + +interface WithThemeFallbackProps { + componentName: string; + isDisplayed?: boolean; + cache?: boolean; +} + +const cachedComponents: { [key: string]: React.ComponentType } = {}; + +export default function withThemeFallback(options: WithThemeFallbackProps) { + const { componentName, cache = false, isDisplayed = true } = options; + + const ComponentWithThemeFallback = (props: any) => { + if (!componentName || !isDisplayed) return null; + + let themePath = `components/${componentName}`; + let defaultPath = `${componentName}`; + const loadComponent = () => + import(`@/theme/${themePath}`).catch(() => import(`@/components/${defaultPath}`)); + + const Component = dynamic(() => loadComponent(), { ssr: false }); + + return ( + + + + ); + }; + + return ComponentWithThemeFallback; +} diff --git a/frontend/desktop/src/pages/404.tsx b/frontend/desktop/src/pages/404.tsx new file mode 100644 index 00000000000..701f2b365af --- /dev/null +++ b/frontend/desktop/src/pages/404.tsx @@ -0,0 +1,13 @@ +import React, { useEffect } from 'react'; +import { useRouter } from 'next/router'; + +const NonePage = () => { + const router = useRouter(); + useEffect(() => { + router.push('/'); + }, [router]); + + return
; +}; + +export default NonePage; diff --git a/frontend/desktop/src/pages/api/auth/password/exist.ts b/frontend/desktop/src/pages/api/auth/password/exist.ts index 8782491a180..d832891379f 100644 --- a/frontend/desktop/src/pages/api/auth/password/exist.ts +++ b/frontend/desktop/src/pages/api/auth/password/exist.ts @@ -15,8 +15,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const result = await queryUser({ id: user, provider: 'password_user' }); if (!result || !result.password || result.password === hashPassword('')) { return jsonRes(res, { - message: 'not found', - code: 404, + message: 'user not found', + code: 201, data: { user, exist: false diff --git a/frontend/desktop/src/pages/api/platform/getEnv.ts b/frontend/desktop/src/pages/api/platform/getEnv.ts index bf823369202..9aee885c942 100644 --- a/frontend/desktop/src/pages/api/platform/getEnv.ts +++ b/frontend/desktop/src/pages/api/platform/getEnv.ts @@ -1,10 +1,30 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/services/backend/response'; +import { enableGithub, enableWechat, enablePassword, enableSms } from '@/services/enable'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const wechat_client_id = process.env.WECHAT_CLIENT_ID || ''; + const github_client_id = process.env.GITHUB_CLIENT_ID || ''; + const service_protocol = process.env.SERVICE_PROTOCOL || ''; + const private_protocol = process.env.PRIVATE_PROTOCOL || ''; + const needGithub = enableGithub(); + const needWechat = enableWechat(); + const needPassword = enablePassword(); + const needSms = enableSms(); + const callback_url = process.env.CALLBACK_URL; + jsonRes(res, { data: { - SEALOS_CLOUD_DOMAIN: process.env.SEALOS_CLOUD_DOMAIN || 'cloud.sealos.io' + SEALOS_CLOUD_DOMAIN: process.env.SEALOS_CLOUD_DOMAIN || 'cloud.sealos.io', + wechat_client_id, + github_client_id, + callback_url, + service_protocol, + private_protocol, + needPassword, + needSms, + needGithub, + needWechat } }); } diff --git a/frontend/desktop/src/pages/index.tsx b/frontend/desktop/src/pages/index.tsx index 242a084a817..f22eada6ea8 100644 --- a/frontend/desktop/src/pages/index.tsx +++ b/frontend/desktop/src/pages/index.tsx @@ -1,6 +1,5 @@ import DesktopContent from '@/components/desktop_content'; import FloatButton from '@/components/floating_button'; -import Layout from '@/components/layout'; import MoreApps from '@/components/more_apps'; import { enableRecharge } from '@/services/enable'; import request from '@/services/request'; @@ -9,12 +8,14 @@ import useSessionStore from '@/stores/session'; import { ApiResp } from '@/types'; import { SystemConfigType } from '@/types/system'; import { parseOpenappQuery } from '@/utils/format'; -import { useColorMode } from '@chakra-ui/react'; +import { Box, useColorMode } from '@chakra-ui/react'; import { useQuery } from '@tanstack/react-query'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +import Head from 'next/head'; import { useRouter } from 'next/router'; import Script from 'next/script'; import { createContext, useEffect, useState } from 'react'; + const destination = '/signin'; interface IMoreAppsContext { showMoreApps: boolean; @@ -31,9 +32,8 @@ export default function Home({ sealos_cloud_domain: string; }) { const router = useRouter(); - const isUpdate = useSessionStore((s) => s.newUser); + const { isUserLogin, newUser: isUpdate, setSession } = useSessionStore(); const { colorMode, toggleColorMode } = useColorMode(); - const isUserLogin = useSessionStore((s) => s.isUserLogin); const init = useAppStore((state) => state.init); const setAutoLaunch = useAppStore((state) => state.setAutoLaunch); const cancelAutoLaunch = useAppStore((state) => state.cancelAutoLaunch); @@ -82,10 +82,14 @@ export default function Home({ }); }); } - }, [router, isUserLogin, init, isUpdate, setAutoLaunch]); + }, [router, isUserLogin, init, isUpdate, setAutoLaunch, sealos_cloud_domain]); return ( - + + + sealos Cloud + + {systemConfig?.data?.scripts?.map((item, i) => { return