Kaynağa Gözat

feat: xxo enhancement. (#5671)

Garfield Dai 9 ay önce
ebeveyn
işleme
f0ea540b34

+ 1 - 0
.gitignore

@@ -139,6 +139,7 @@ web/.vscode/settings.json
 !.idea/icon.png
 .ideaDataSources/
 *.iml
+api/.idea
 
 api/.env
 api/storage/*

+ 2 - 77
web/app/(shareLayout)/chatbot/[token]/page.tsx

@@ -1,85 +1,10 @@
 'use client'
-import React, { useEffect } from 'react'
-import cn from 'classnames'
+import React from 'react'
 import EmbeddedChatbot from '@/app/components/base/chat/embedded-chatbot'
-import Loading from '@/app/components/base/loading'
-import { fetchSystemFeatures } from '@/service/share'
-import LogoSite from '@/app/components/base/logo/logo-site'
 
 const Chatbot = () => {
-  const [isSSOEnforced, setIsSSOEnforced] = React.useState(true)
-  const [loading, setLoading] = React.useState(true)
-
-  useEffect(() => {
-    fetchSystemFeatures().then((res) => {
-      setIsSSOEnforced(res.sso_enforced_for_web)
-      setLoading(false)
-    })
-  }, [])
-
   return (
-    <>
-      {
-        loading
-          ? (
-            <div className="flex items-center justify-center h-full" >
-              <div className={
-                cn(
-                  'flex flex-col items-center w-full grow items-center justify-center',
-                  'px-6',
-                  'md:px-[108px]',
-                )
-              }>
-                <Loading type='area' />
-              </div>
-            </div >
-          )
-          : (
-            <>
-              {isSSOEnforced
-                ? (
-                  <div className={cn(
-                    'flex w-full min-h-screen',
-                    'sm:p-4 lg:p-8',
-                    'gap-x-20',
-                    'justify-center lg:justify-start',
-                  )}>
-                    <div className={
-                      cn(
-                        'flex w-full flex-col bg-white shadow rounded-2xl shrink-0',
-                        'space-between',
-                      )
-                    }>
-                      <div className='flex items-center justify-between p-6 w-full'>
-                        <LogoSite />
-                      </div>
-
-                      <div className={
-                        cn(
-                          'flex flex-col items-center w-full grow items-center justify-center',
-                          'px-6',
-                          'md:px-[108px]',
-                        )
-                      }>
-                        <div className='flex flex-col md:w-[400px]'>
-                          <div className="w-full mx-auto">
-                            <h2 className="text-[16px] font-bold text-gray-900">
-                          Warning: Chatbot is not available
-                            </h2>
-                            <p className="text-[16px] text-gray-600 mt-2">
-                          Because SSO is enforced. Please contact your administrator.
-                            </p>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                )
-                : <EmbeddedChatbot />
-              }
-            </>
-          )}
-    </>
+    <EmbeddedChatbot />
   )
 }
 

+ 68 - 119
web/app/(shareLayout)/webapp-signin/page.tsx

@@ -2,150 +2,99 @@
 import cn from 'classnames'
 import { useRouter, useSearchParams } from 'next/navigation'
 import type { FC } from 'react'
-import React, { useEffect, useState } from 'react'
-import { useTranslation } from 'react-i18next'
+import React, { useEffect } from 'react'
 import Toast from '@/app/components/base/toast'
-import Button from '@/app/components/base/button'
 import { fetchSystemFeatures, fetchWebOAuth2SSOUrl, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/service/share'
-import LogoSite from '@/app/components/base/logo/logo-site'
 import { setAccessToken } from '@/app/components/share/utils'
+import Loading from '@/app/components/base/loading'
 
 const WebSSOForm: FC = () => {
   const searchParams = useSearchParams()
+  const router = useRouter()
 
   const redirectUrl = searchParams.get('redirect_url')
   const tokenFromUrl = searchParams.get('web_sso_token')
   const message = searchParams.get('message')
 
-  const router = useRouter()
-  const { t } = useTranslation()
-
-  const [isLoading, setIsLoading] = useState(false)
-  const [protocol, setProtocol] = useState('')
+  const showErrorToast = (message: string) => {
+    Toast.notify({
+      type: 'error',
+      message,
+    })
+  }
 
-  useEffect(() => {
-    const fetchFeaturesAndSetToken = async () => {
-      await fetchSystemFeatures().then((res) => {
-        setProtocol(res.sso_enforced_for_web_protocol)
-      })
-
-      // Callback from SSO, process token and redirect
-      if (tokenFromUrl && redirectUrl) {
-        const appCode = redirectUrl.split('/').pop()
-        if (!appCode) {
-          Toast.notify({
-            type: 'error',
-            message: 'redirect url is invalid. App code is not found.',
-          })
-          return
-        }
-
-        await setAccessToken(appCode, tokenFromUrl)
-        router.push(redirectUrl)
-      }
-    }
+  const getAppCodeFromRedirectUrl = () => {
+    const appCode = redirectUrl?.split('/').pop()
+    if (!appCode)
+      return null
 
-    fetchFeaturesAndSetToken()
+    return appCode
+  }
 
-    if (message) {
-      Toast.notify({
-        type: 'error',
-        message,
-      })
+  const processTokenAndRedirect = async () => {
+    const appCode = getAppCodeFromRedirectUrl()
+    if (!appCode || !tokenFromUrl || !redirectUrl) {
+      showErrorToast('redirect url or app code or token is invalid.')
+      return
     }
-  }, [])
 
-  const handleSSOLogin = () => {
-    setIsLoading(true)
+    await setAccessToken(appCode, tokenFromUrl)
+    router.push(redirectUrl)
+  }
 
-    if (!redirectUrl) {
-      Toast.notify({
-        type: 'error',
-        message: 'redirect url is not found.',
-      })
-      setIsLoading(false)
+  const handleSSOLogin = async (protocol: string) => {
+    const appCode = getAppCodeFromRedirectUrl()
+    if (!appCode || !redirectUrl) {
+      showErrorToast('redirect url or app code is invalid.')
       return
     }
 
-    const appCode = redirectUrl.split('/').pop()
-    if (!appCode) {
-      Toast.notify({
-        type: 'error',
-        message: 'redirect url is invalid. App code is not found.',
-      })
-      return
+    switch (protocol) {
+      case 'saml': {
+        const samlRes = await fetchWebSAMLSSOUrl(appCode, redirectUrl)
+        router.push(samlRes.url)
+        break
+      }
+      case 'oidc': {
+        const oidcRes = await fetchWebOIDCSSOUrl(appCode, redirectUrl)
+        router.push(oidcRes.url)
+        break
+      }
+      case 'oauth2': {
+        const oauth2Res = await fetchWebOAuth2SSOUrl(appCode, redirectUrl)
+        router.push(oauth2Res.url)
+        break
+      }
+      default:
+        showErrorToast('SSO protocol is not supported.')
     }
+  }
 
-    if (protocol === 'saml') {
-      fetchWebSAMLSSOUrl(appCode, redirectUrl).then((res) => {
-        router.push(res.url)
-      }).finally(() => {
-        setIsLoading(false)
-      })
-    }
-    else if (protocol === 'oidc') {
-      fetchWebOIDCSSOUrl(appCode, redirectUrl).then((res) => {
-        router.push(res.url)
-      }).finally(() => {
-        setIsLoading(false)
-      })
-    }
-    else if (protocol === 'oauth2') {
-      fetchWebOAuth2SSOUrl(appCode, redirectUrl).then((res) => {
-        router.push(res.url)
-      }).finally(() => {
-        setIsLoading(false)
-      })
-    }
-    else {
-      Toast.notify({
-        type: 'error',
-        message: 'sso protocol is not supported.',
-      })
-      setIsLoading(false)
+  useEffect(() => {
+    const init = async () => {
+      const res = await fetchSystemFeatures()
+      const protocol = res.sso_enforced_for_web_protocol
+
+      if (message) {
+        showErrorToast(message)
+        return
+      }
+
+      if (!tokenFromUrl) {
+        await handleSSOLogin(protocol)
+        return
+      }
+
+      await processTokenAndRedirect()
     }
-  }
+
+    init()
+  }, [message, tokenFromUrl]) // Added dependencies to useEffect
 
   return (
-    <div className={cn(
-      'flex w-full min-h-screen',
-      'sm:p-4 lg:p-8',
-      'gap-x-20',
-      'justify-center lg:justify-start',
-    )}>
-      <div className={
-        cn(
-          'flex w-full flex-col bg-white shadow rounded-2xl shrink-0',
-          'space-between',
-        )
-      }>
-        <div className='flex items-center justify-between p-6 w-full'>
-          <LogoSite />
-        </div>
-
-        <div className={
-          cn(
-            'flex flex-col items-center w-full grow items-center justify-center',
-            'px-6',
-            'md:px-[108px]',
-          )
-        }>
-          <div className='flex flex-col md:w-[400px]'>
-            <div className="w-full mx-auto">
-              <h2 className="text-[32px] font-bold text-gray-900">{t('login.pageTitle')}</h2>
-            </div>
-            <div className="w-full mx-auto mt-10">
-              <Button
-                tabIndex={0}
-                variant='primary'
-                onClick={() => { handleSSOLogin() }}
-                disabled={isLoading}
-                className="w-full !text-sm"
-              >{t('login.sso')}
-              </Button>
-            </div>
-          </div>
-        </div>
+    <div className="flex items-center justify-center h-full">
+      <div className={cn('flex flex-col items-center w-full grow justify-center', 'px-6', 'md:px-[108px]')}>
+        <Loading type='area' />
       </div>
     </div>
   )