From 71ec576e7ccca5f53556e993703bb57785352d09 Mon Sep 17 00:00:00 2001 From: yy Date: Thu, 25 Apr 2024 20:30:23 +0800 Subject: [PATCH 01/11] change desktop to use config file. --- frontend/desktop/.gitignore | 1 + frontend/desktop/data/config.yaml | 61 ++++++ frontend/desktop/data/config.yaml.local | 64 ++++++ .../desktop/deploy/manifests/deploy.yaml.tmpl | 58 ++++- frontend/desktop/src/api/platform.ts | 29 ++- .../desktop/src/components/account/index.tsx | 15 +- .../src/components/signin/auth/AuthList.tsx | 65 +++--- .../desktop/src/components/signin/index.tsx | 43 ++-- .../src/components/user_menu/index.tsx | 8 +- frontend/desktop/src/hooks/useDriver.tsx | 10 +- frontend/desktop/src/pages/_app.tsx | 11 +- .../src/pages/api/platform/getAppConfig.ts | 51 +++++ .../src/pages/api/platform/getAuthConfig.ts | 66 ++++++ .../src/pages/api/platform/getCloudConfig.ts | 34 +++ .../src/pages/api/platform/getCommonConfig.ts | 27 +++ .../desktop/src/pages/api/platform/getEnv.ts | 70 ------ .../src/pages/api/platform/getLayoutConfig.ts | 27 +++ .../src/pages/api/platform/getSystemConfig.ts | 35 --- frontend/desktop/src/pages/index.tsx | 19 +- frontend/desktop/src/pages/proxyOAuth.tsx | 13 +- frontend/desktop/src/services/enable.ts | 6 +- frontend/desktop/src/stores/config.ts | 41 ++-- frontend/desktop/src/stores/global.ts | 49 ----- frontend/desktop/src/types/index.ts | 3 + frontend/desktop/src/types/system.ts | 205 ++++++++++++++---- 25 files changed, 697 insertions(+), 314 deletions(-) create mode 100644 frontend/desktop/data/config.yaml create mode 100644 frontend/desktop/data/config.yaml.local create mode 100644 frontend/desktop/src/pages/api/platform/getAppConfig.ts create mode 100644 frontend/desktop/src/pages/api/platform/getAuthConfig.ts create mode 100644 frontend/desktop/src/pages/api/platform/getCloudConfig.ts create mode 100644 frontend/desktop/src/pages/api/platform/getCommonConfig.ts delete mode 100644 frontend/desktop/src/pages/api/platform/getEnv.ts create mode 100644 frontend/desktop/src/pages/api/platform/getLayoutConfig.ts delete mode 100644 frontend/desktop/src/pages/api/platform/getSystemConfig.ts delete mode 100644 frontend/desktop/src/stores/global.ts diff --git a/frontend/desktop/.gitignore b/frontend/desktop/.gitignore index 9e88043fc96..9683369c1dd 100644 --- a/frontend/desktop/.gitignore +++ b/frontend/desktop/.gitignore @@ -27,6 +27,7 @@ yarn-error.log* # local env files .env*.local +*.local data/config.local.json # vercel .vercel diff --git a/frontend/desktop/data/config.yaml b/frontend/desktop/data/config.yaml new file mode 100644 index 00000000000..dbea93aa64b --- /dev/null +++ b/frontend/desktop/data/config.yaml @@ -0,0 +1,61 @@ +cloud: + domain: "{{ .cloudDomain }}" + port: "{{ .cloudPort }}" + regionUID: "{{ .regionUID }}" + certSecretName: "{{ .certSecretName }}" +common: + guideEnabled: "{{ .guideEnabled }}" + apiEnabled: "{{ .apiEnabled }}" + rechargeEnabled: "{{ .rechargeEnabled }}" +database: + mongodbUri: "{{ .mongodbUri }}" + globalCockroachdbURI: "{{ .globalCockroachdbURI }}" + regionalCockroachdbURI: "{{ .regionalCockroachdbURI }}" +desktop: + layout: + title: "Sealos Cloud" + logo: "" + backgroundImage: "" + cfSiteKey: "" + meta: + title: "Sealos Cloud" + description: "Sealos Cloud" + keywords: "Sealos Cloud" + common: + githubStarEnabled: "false" + auth: + callbackURL: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/callback" + jwt: + internal: "{{ .jwtInternal }}" + regional: "{{ .jwtRegional }}" + global: "{{ .jwtGlobal }}" + idp: + password: + enabled: "{{ .passwordEnabled }}" + salt: "{{ .passwordSalt }}" + github: + enabled: "{{ .githubEnabled }}" + clientId: "{{ .githubClientID }}" + clientSecret: "{{ .githubClientSecret }}" + wechat: + enabled: "{{ .wechatEnabled }}" + clientId: "{{ .wechatClientID }}" + clientSecret: "{{ .wechatClientSecret }}" + sms: + ali: + enabled: "{{ .smsEnabled }}" + endpoint: "{{ .aliSmsEndpoint }}" + signName: "{{ .aliSmsSignName }}" + accessKeyId: "{{ .aliAccessKeyID }}" + accessKeySecret: "{{ .aliAccessKeySecret }}" + oauth2: + enabled: "{{ .oauth2Enabled }}" + callbackURL: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/callback" + clientId: "{{ .oauth2ClientId }}" + clientSecret: "{{ .oauth2ClientSecret }}" + authURL: "{{ .oauth2AuthURL }}" + tokenURL: "{{ .oauth2TokenURL }}" + userInfoURL: "{{ .oauth2UserInfoURL }}" + components: + billingService: + uri: "{{ .billingURI }}" \ No newline at end of file diff --git a/frontend/desktop/data/config.yaml.local b/frontend/desktop/data/config.yaml.local new file mode 100644 index 00000000000..7d65365cb97 --- /dev/null +++ b/frontend/desktop/data/config.yaml.local @@ -0,0 +1,64 @@ +cloud: + domain: "{{ .cloudDomain }}" + port: "{{ .cloudPort }}" + regionUID: "{{ .regionUID }}" + certSecretName: "{{ .certSecretName }}" +common: + guideEnabled: "{{ .guideEnabled }}" + apiEnabled: "{{ .apiEnabled }}" + rechargeEnabled: "{{ .rechargeEnabled }}" +database: + mongodbUri: "{{ .mongodbUri }}" + globalCockroachdbURI: "{{ .globalCockroachdbURI }}" + regionalCockroachdbURI: "{{ .regionalCockroachdbURI }}" +desktop: + layout: + title: "Sealos Cloud" + logo: "" + backgroundImage: "" + meta: + title: "Sealos Cloud" + description: "Sealos Cloud" + keywords: "Sealos Cloud" + common: + githubStarEnabled: "false" + auth: + callbackURL: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/callback" + jwt: + internal: "{{ .jwtInternal }}" + regional: "{{ .jwtRegional }}" + global: "{{ .jwtGlobal }}" + idp: + password: + enabled: "{{ .passwordEnabled }}" + salt: "{{ .passwordSalt }}" + github: + enabled: "{{ .githubEnabled }}" + clientId: "{{ .githubClientID }}" + clientSecret: "{{ .githubClientSecret }}" + wechat: + enabled: "{{ .wechatEnabled }}" + clientId: "{{ .wechatClientID }}" + clientSecret: "{{ .wechatClientSecret }}" + sms: + ali: + enabled: "{{ .smsEnabled }}" + endpoint: "{{ .aliSmsEndpoint }}" + signName: "{{ .aliSmsSignName }}" + accessKeyId: "{{ .aliAccessKeyID }}" + accessKeySecret: "{{ .aliAccessKeySecret }}" + oauth2: + enabled: "{{ .oauth2Enabled }}" + callbackURL: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/callback" + clientId: "{{ .oauth2ClientId }}" + clientSecret: "{{ .oauth2ClientSecret }}" + authURL: "{{ .oauth2AuthURL }}" + tokenURL: "{{ .oauth2TokenURL }}" + userInfoURL: "{{ .oauth2UserInfoURL }}" + jwt: + internal: "{{ .jwtInternal }}" + regional: "{{ .jwtRegional }}" + global: "{{ .jwtGlobal }}" + components: + billingService: + uri: "{{ .billingURI }}" \ No newline at end of file diff --git a/frontend/desktop/deploy/manifests/deploy.yaml.tmpl b/frontend/desktop/deploy/manifests/deploy.yaml.tmpl index 30c17dd63fa..ebcf7e1b459 100644 --- a/frontend/desktop/deploy/manifests/deploy.yaml.tmpl +++ b/frontend/desktop/deploy/manifests/deploy.yaml.tmpl @@ -36,7 +36,63 @@ metadata: namespace: sealos data: config.yaml: |- - addr: :3000 + cloud: + domain: "{{ .cloudDomain }}" + port: "{{ .cloudPort }}" + regionUID: "{{ .regionUID }}" + certSecretName: "{{ .certSecretName }}" + common: + guideEnabled: "{{ .guideEnabled }}" + apiEnabled: "{{ .apiEnabled }}" + database: + mongodbURI: "{{ .mongodbURI }}" + globalCockroachdbURI: "{{ .globalCockroachdbURI }}" + regionalCockroachdbURI: "{{ .regionalCockroachdbURI }}" + desktop: + layout: + title: "Sealos Cloud" + logo: "" + backgroundImage: "" + meta: + title: "Sealos Cloud" + description: "Sealos Cloud" + keywords: "Sealos Cloud" + common: + githubStarEnabled: "false" + login: # or auth? + password: + enabled: "{{ .passwordEnabled }}" + salt: "{{ .passwordSalt }}" + github: + enabled: "{{ .githubEnabled }}" + clientId: "{{ .githubClientID }}" + clientSecret: "{{ .githubClientSecret }}" + wechat: + enabled: "{{ .wechatEnabled }}" + clientId: "{{ .wechatClientID }}" + clientSecret: "{{ .wechatClientSecret }}" + sms: + ali: + enabled: "{{ .smsEnabled }}" + endpoint: "{{ .aliSmsEndpoint }}" + signName: "{{ .aliSmsSignName }}" + accessKeyId: "{{ .aliAccessKeyID }}" + accessKeySecret: "{{ .aliAccessKeySecret }}" + oauth2: + enabled: "{{ .oauth2Enabled }}" + callbackURL: "https://{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/callback" + clientId: "{{ .oauth2ClientId }}" + clientSecret: "{{ .oauth2ClientSecret }}" + authURL: "{{ .oauth2AuthURL }}" + tokenURL: "{{ .oauth2TokenURL }}" + userInfoURL: "{{ .oauth2UserInfoURL }}" + jwt: + internal: "{{ .jwtInternal }}" + regional: "{{ .jwtRegional }}" + global: "{{ .jwtGlobal }}" + components: + billingService: + uri: "{{ .billingURI }}" config.json: |- { "scripts": [], diff --git a/frontend/desktop/src/api/platform.ts b/frontend/desktop/src/api/platform.ts index bdb6da57c50..7dea37c7680 100644 --- a/frontend/desktop/src/api/platform.ts +++ b/frontend/desktop/src/api/platform.ts @@ -1,5 +1,12 @@ import request from '@/services/request'; -import { ApiResp, NotificationItem, Session, SystemConfigType, SystemEnv } from '@/types'; +import { + ApiResp, + NotificationItem, + LayoutConfigType, + AppConfigType, + CloudConfigType, + AuthConfigType +} from '@/types'; import { AccountCRD } from '@/types/user'; // handle baidu @@ -25,12 +32,24 @@ export const getUserAccount = () => { return request.get('/api/account/getAccount'); }; -export const getSystemEnv = () => { - return request.get('/api/platform/getEnv'); +export const getAppConfig = () => { + return request.get('/api/platform/getAppConfig'); }; -export const getSystemConfig = () => { - return request.get('/api/platform/getSystemConfig'); +export const getCloudConfig = () => { + return request.get('/api/platform/getCloudConfig'); +}; + +export const getCommonConfig = () => { + return request.get('/api/platform/getCommonConfig'); +}; + +export const getLayoutConfig = () => { + return request.get('/api/platform/getLayoutConfig'); +}; + +export const getAuthConfig = () => { + return request.get('/api/platform/getAuthConfig'); }; export const getPriceBonus = () => { diff --git a/frontend/desktop/src/components/account/index.tsx b/frontend/desktop/src/components/account/index.tsx index 383526d8ba6..1e449735120 100644 --- a/frontend/desktop/src/components/account/index.tsx +++ b/frontend/desktop/src/components/account/index.tsx @@ -22,18 +22,15 @@ import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import { useMemo, useState } from 'react'; import useAppStore from '@/stores/app'; -import { ApiResp, Region } from '@/types'; +import { ApiResp } from '@/types'; import { formatMoney } from '@/utils/format'; import TeamCenter from '@/components/team/TeamCenter'; import NsList from '@/components/team/NsList'; import { nsListRequest, switchRequest } from '@/api/namespace'; import { NSType } from '@/types/team'; import PasswordModify from './PasswordModify'; -import { useGlobalStore } from '@/stores/global'; import { CopyIcon, DownloadIcon, LogoutIcon, RightArrowIcon } from '@sealos/ui'; -import { ImageFallBackUrl } from '@/stores/config'; -import { jwtDecode } from 'jwt-decode'; -import { AccessTokenPayload } from '@/types/token'; +import { useConfigStore } from '@/stores/config'; import { sessionConfig } from '@/utils/sessionConfig'; const NsMenu = () => { @@ -119,7 +116,9 @@ const NsMenu = () => { }; export default function Account({ disclosure }: { disclosure: UseDisclosureReturn }) { const [showId, setShowId] = useState(true); - const { needPassword, rechargeEnabled } = useGlobalStore((state) => state.systemEnv); + const passwordEnabled = useConfigStore().authConfig?.idp?.password?.enabled; + const rechargeEnabled = useConfigStore().commonConfig?.rechargeEnabled; + const logo = useConfigStore().layoutConfig?.logo; const router = useRouter(); const { copyData } = useCopyData(); const openApp = useAppStore((s) => s.openApp); @@ -191,7 +190,7 @@ export default function Account({ disclosure }: { disclosure: UseDisclosureRetur height={'80px'} borderRadius="full" src={user?.avatar} - fallbackSrc={ImageFallBackUrl} + fallbackSrc={logo} alt="user avator" /> @@ -231,7 +230,7 @@ export default function Account({ disclosure }: { disclosure: UseDisclosureRetur {t('Manage Team')} - {needPassword && ( + {passwordEnabled && ( { - const { systemEnv } = useGlobalStore(); - const { - needGithub = false, - needWechat = false, - needGoogle = false, - wechat_client_id = '', - github_client_id = '', - google_client_id = '', - callback_url = '', - // https://sealos.io/siginIn - oauth_proxy = '', - oauth2_client_id, - oauth2_auth_url, - needOAuth2 = false - } = systemEnv ?? {}; + const conf = useConfigStore().authConfig; const oauthLogin = async ({ url, provider }: { url: string; provider?: OauthProvider }) => { setProvider(provider); window.location.href = url; }; + const router = useRouter(); + const oauthProxyLogin = async ({ state, provider, @@ -36,14 +24,15 @@ const AuthList = () => { id: string; }) => { setProvider(provider); - const target = new URL(oauth_proxy); - const callback = new URL(callback_url); + const target = new URL(conf?.proxyAddress as string); + const callback = new URL(conf?.callbackURL as string); callback.searchParams.append('state', state); target.searchParams.append('oauthProxyState', callback.toString()); target.searchParams.append('oauthProxyClientID', id); target.searchParams.append('oauthProxyProvider', provider); router.replace(target.toString()); }; + const { generateState, setProvider } = useSessionStore(); const authList: { icon: typeof Icon; cb: MouseEventHandler; need: boolean }[] = [ { @@ -51,58 +40,65 @@ const AuthList = () => { cb: (e) => { e.preventDefault(); const state = generateState(); - if (oauth_proxy) + const githubConf = conf?.idp.github; + if (conf?.proxyAddress) oauthProxyLogin({ provider: 'github', state, - id: github_client_id + id: githubConf?.clientID as string }); else 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}` + url: `https://github.com/login/oauth/authorize?client_id=${githubConf?.clientID}&redirect_uri=${conf?.callbackURL}&scope=user:email%20read:user&state=${state}` }); }, - need: needGithub + need: conf?.idp.github?.enabled as boolean }, { icon: WechatIcon, cb: (e) => { + const wechatConf = conf?.idp.wechat; e.preventDefault(); const state = generateState(); - if (oauth_proxy) + if (conf?.proxyAddress) oauthProxyLogin({ provider: 'wechat', state, - id: wechat_client_id + id: conf?.idp.wechat?.clientID as string }); else 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` + url: `https://open.weixin.qq.com/connect/qrconnect?appid=${wechatConf?.clientID}&redirect_uri=${conf?.callbackURL}&response_type=code&state=${state}&scope=snsapi_login&#wechat_redirect` }); }, - need: needWechat + need: conf?.idp.wechat?.enabled as boolean }, { icon: GoogleIcon, cb: (e) => { e.preventDefault(); const state = generateState(); + const googleConf = conf?.idp.google; const scope = encodeURIComponent(`https://www.googleapis.com/auth/userinfo.profile openid`); - if (oauth_proxy) + if (conf?.proxyAddress) oauthProxyLogin({ state, provider: 'google', - id: google_client_id + id: googleConf?.clientID as string }); else oauthLogin({ provider: 'google', - url: `https://accounts.google.com/o/oauth2/v2/auth?client_id=${google_client_id}&redirect_uri=${callback_url}&response_type=code&state=${state}&scope=${scope}&include_granted_scopes=true` + url: `https://accounts.google.com/o/oauth2/v2/auth?client_id=${ + googleConf?.clientID as string + }&redirect_uri=${ + conf?.callbackURL + }&response_type=code&state=${state}&scope=${scope}&include_granted_scopes=true` }); }, - need: needGoogle + need: conf?.idp.google?.enabled as boolean }, { icon: () => ( @@ -113,19 +109,20 @@ const AuthList = () => { cb: (e) => { e.preventDefault(); const state = generateState(); - if (oauth_proxy) + const oauth2Conf = conf?.idp.oauth2; + if (conf?.proxyAddress) oauthProxyLogin({ provider: 'oauth2', state, - id: oauth2_client_id + id: oauth2Conf?.clientID as string }); else oauthLogin({ provider: 'oauth2', - url: `${oauth2_auth_url}?client_id=${oauth2_client_id}&redirect_uri=${callback_url}&response_type=code&state=${state}` + url: `${oauth2Conf?.authURL}?client_id=${oauth2Conf?.clientID}&redirect_uri=${oauth2Conf?.callbackURL}&response_type=code&state=${state}` }); }, - need: needOAuth2 + need: conf?.idp.oauth2?.enabled as boolean } ]; diff --git a/frontend/desktop/src/components/signin/index.tsx b/frontend/desktop/src/components/signin/index.tsx index 974bda63416..26e2d93c9f0 100644 --- a/frontend/desktop/src/components/signin/index.tsx +++ b/frontend/desktop/src/components/signin/index.tsx @@ -5,8 +5,7 @@ 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 { BackgroundImageUrl, useSystemConfigStore } from '@/stores/config'; -import { useGlobalStore } from '@/stores/global'; +import { useConfigStore } from '@/stores/config'; import useSessionStore from '@/stores/session'; import { LoginType } from '@/types'; import { @@ -30,20 +29,10 @@ import useWechat from './auth/useWechat'; import { Turnstile, TurnstileInstance } from '@marsidev/react-turnstile'; export default function SigninComponent() { - const platformEnv = useGlobalStore((state) => state.systemEnv); - - const { systemConfig } = useSystemConfigStore(); - const { - service_protocol_zh = '', - private_protocol_zh = '', - service_protocol_en = '', - private_protocol_en = '', - needPassword = false, - needSms = false, - openWechatEnabled = false, - cf_sitekey - } = platformEnv || {}; - const needTabs = needPassword && needSms; + const conf = useConfigStore(); + const needPassword = conf.authConfig?.idp.password?.enabled; + const needSms = conf.authConfig?.idp.sms?.enabled; + const needTabs = conf.authConfig?.idp.password?.enabled && conf.authConfig?.idp.sms?.enabled; const disclosure = useDisclosure(); const { t, i18n } = useTranslation(); const [tabIndex, setTabIndex] = useState(LoginType.NONE); @@ -52,13 +41,13 @@ export default function SigninComponent() { let protocol_data: Parameters[0]; if (['zh', 'zh-Hans'].includes(i18n.language)) protocol_data = { - service_protocol: service_protocol_zh, - private_protocol: private_protocol_zh + service_protocol: conf.layoutConfig?.protocol.serviceProtocol.zh as string, + private_protocol: conf.layoutConfig?.protocol.privateProtocol.zh as string }; else protocol_data = { - service_protocol: service_protocol_en, - private_protocol: private_protocol_en + service_protocol: conf.layoutConfig?.protocol.serviceProtocol.en as string, + private_protocol: conf.layoutConfig?.protocol.privateProtocol.en as string }; const { Protocol, isAgree, setIsInvalid } = useProtocol(protocol_data!); const { WechatComponent, login: wechatSubmit } = useWechat(); @@ -153,13 +142,13 @@ export default function SigninComponent() { overflow={'hidden'} w="100vw" h="100vh" - backgroundImage={`url(${BackgroundImageUrl})`} + backgroundImage={`url(${conf.layoutConfig?.backgroundImage})`} backgroundRepeat={'no-repeat'} backgroundSize={'cover'} > - {systemConfig?.metaTitle} - + {conf.layoutConfig?.meta.title} + @@ -169,7 +158,7 @@ export default function SigninComponent() { fontWeight={700} textShadow={'0px 2px 6px rgba(0, 0, 0, 0.30)'} > - {systemConfig?.title} + {conf.layoutConfig?.title} {t('Password Login')} - {openWechatEnabled && ( + {conf.authConfig?.idp.wechat?.enabled && ( {t('Official account login')} @@ -227,13 +216,13 @@ export default function SigninComponent() { {tabIndex !== LoginType.WeChat && ( <> - {!!cf_sitekey && ( + {!!conf.layoutConfig?.cfSiteKey && ( )}