fix: show error message when web app access denied

This commit is contained in:
NFish 2025-04-16 09:09:21 +08:00
parent 480e9ebb82
commit c91398883c
4 changed files with 47 additions and 9 deletions

View File

@ -11,6 +11,7 @@ import { setAccessToken } from '@/app/components/share/utils'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import { useGlobalPublicStore } from '@/context/global-public-context' import { useGlobalPublicStore } from '@/context/global-public-context'
import { SSOProtocol } from '@/types/feature' import { SSOProtocol } from '@/types/feature'
import Loading from '@/app/components/base/loading'
const WebSSOForm: FC = () => { const WebSSOForm: FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
@ -91,6 +92,9 @@ const WebSSOForm: FC = () => {
init() init()
}, [message, processTokenAndRedirect, tokenFromUrl]) }, [message, processTokenAndRedirect, tokenFromUrl])
if (tokenFromUrl)
return <div className='flex items-center justify-center h-full'><Loading /></div>
if (systemFeatures.webapp_auth.enabled) { if (systemFeatures.webapp_auth.enabled) {
if (systemFeatures.webapp_auth.allow_sso) { if (systemFeatures.webapp_auth.allow_sso) {
return ( return (

View File

@ -177,7 +177,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
}) })
} }
setShowConfirmDelete(false) setShowConfirmDelete(false)
}, [appDetail, mutateApps, notify, onPlanInfoChanged, replace, t]) }, [appDetail, mutateApps, notify, onPlanInfoChanged, replace, setAppDetail, t])
const handleClickAccessControl = useCallback(() => { const handleClickAccessControl = useCallback(() => {
if (!appDetail) if (!appDetail)
@ -480,7 +480,9 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
/> />
)} )}
{ {
showAccessControl && <AccessControl app={appDetail} onClose={() => { setShowAccessControl(false) }} /> showAccessControl && <AccessControl app={appDetail}
onConfirm={() => { setShowAccessControl(false) }}
onClose={() => { setShowAccessControl(false) }} />
} }
</div> </div>
</PortalToFollowElem> </PortalToFollowElem>

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import { RiAlertFill, RiCloseCircleFill, RiLockLine, RiOrganizationChart } from '@remixicon/react' import { RiAlertFill, RiCloseCircleFill, RiLockLine, RiOrganizationChart } from '@remixicon/react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useEffect } from 'react' import { useCallback, useEffect } from 'react'
import Avatar from '../../base/avatar' import Avatar from '../../base/avatar'
import Divider from '../../base/divider' import Divider from '../../base/divider'
import Tooltip from '../../base/tooltip' import Tooltip from '../../base/tooltip'
@ -85,7 +85,13 @@ type GroupItemProps = {
group: AccessControlGroup group: AccessControlGroup
} }
function GroupItem({ group }: GroupItemProps) { function GroupItem({ group }: GroupItemProps) {
return <BaseItem icon={<RiOrganizationChart className='w-[14px] h-[14px] text-components-avatar-shape-fill-stop-0' />}> const specificGroups = useAccessControlStore(s => s.specificGroups)
const setSpecificGroups = useAccessControlStore(s => s.setSpecificGroups)
const handleRemoveGroup = useCallback(() => {
setSpecificGroups(specificGroups.filter(g => g.id !== group.id))
}, [group, setSpecificGroups, specificGroups])
return <BaseItem icon={<RiOrganizationChart className='w-[14px] h-[14px] text-components-avatar-shape-fill-stop-0' />}
onRemove={handleRemoveGroup}>
<p className='system-xs-regular text-text-primary'>{group.name}</p> <p className='system-xs-regular text-text-primary'>{group.name}</p>
<p className='system-xs-regular text-text-tertiary'>{group.groupSize}</p> <p className='system-xs-regular text-text-tertiary'>{group.groupSize}</p>
</BaseItem> </BaseItem>
@ -95,7 +101,13 @@ type MemberItemProps = {
member: AccessControlAccount member: AccessControlAccount
} }
function MemberItem({ member }: MemberItemProps) { function MemberItem({ member }: MemberItemProps) {
return <BaseItem icon={<Avatar className='w-[14px] h-[14px]' textClassName='text-[12px]' avatar={null} name={member.name} />}> const specificMembers = useAccessControlStore(s => s.specificMembers)
const setSpecificMembers = useAccessControlStore(s => s.setSpecificMembers)
const handleRemoveMember = useCallback(() => {
setSpecificMembers(specificMembers.filter(m => m.id !== member.id))
}, [member, setSpecificMembers, specificMembers])
return <BaseItem icon={<Avatar className='w-[14px] h-[14px]' textClassName='text-[12px]' avatar={null} name={member.name} />}
onRemove={handleRemoveMember}>
<p className='system-xs-regular text-text-primary'>{member.name}</p> <p className='system-xs-regular text-text-primary'>{member.name}</p>
</BaseItem> </BaseItem>
} }
@ -103,8 +115,9 @@ function MemberItem({ member }: MemberItemProps) {
type BaseItemProps = { type BaseItemProps = {
icon: React.ReactNode icon: React.ReactNode
children: React.ReactNode children: React.ReactNode
onRemove?: () => void
} }
function BaseItem({ icon, children }: BaseItemProps) { function BaseItem({ icon, onRemove, children }: BaseItemProps) {
return <div className='rounded-full border-[0.5px] bg-components-badge-white-to-dark shadow-xs p-1 pr-1.5 group flex items-center flex-row gap-x-1'> return <div className='rounded-full border-[0.5px] bg-components-badge-white-to-dark shadow-xs p-1 pr-1.5 group flex items-center flex-row gap-x-1'>
<div className='w-5 h-5 rounded-full bg-components-icon-bg-blue-solid overflow-hidden'> <div className='w-5 h-5 rounded-full bg-components-icon-bg-blue-solid overflow-hidden'>
<div className='w-full h-full flex items-center justify-center bg-access-app-icon-mask-bg'> <div className='w-full h-full flex items-center justify-center bg-access-app-icon-mask-bg'>
@ -112,7 +125,7 @@ function BaseItem({ icon, children }: BaseItemProps) {
</div> </div>
</div> </div>
{children} {children}
<div className='flex items-center justify-center w-4 h-4 cursor-pointer'> <div className='flex items-center justify-center w-4 h-4 cursor-pointer' onClick={onRemove}>
<RiCloseCircleFill className='w-[14px] h-[14px] text-text-quaternary' /> <RiCloseCircleFill className='w-[14px] h-[14px] text-text-quaternary' />
</div> </div>
</div> </div>

View File

@ -512,7 +512,16 @@ export const ssePost = (
}).catch(() => { }).catch(() => {
res.json().then((data: any) => { res.json().then((data: any) => {
if (isPublicAPI) { if (isPublicAPI) {
if (data.code === 'web_sso_auth_required' || data.code === 'web_app_access_denied') if (data.code === 'web_app_access_denied') {
Toast.notify({
type: 'error',
message: data.message,
})
setTimeout(() => {
requiredWebSSOLogin()
}, 1500)
}
if (data.code === 'web_sso_auth_required')
requiredWebSSOLogin() requiredWebSSOLogin()
if (data.code === 'unauthorized') { if (data.code === 'unauthorized') {
@ -566,7 +575,17 @@ export const request = async<T>(url: string, options = {}, otherOptions?: IOther
// special code // special code
const { code, message } = errRespData const { code, message } = errRespData
// webapp sso // webapp sso
if (code === 'web_sso_auth_required' || code === 'web_app_access_denied') { if (code === 'web_app_access_denied') {
Toast.notify({
type: 'error',
message,
})
setTimeout(() => {
requiredWebSSOLogin()
}, 1500)
return Promise.reject(err)
}
if (code === 'web_sso_auth_required') {
requiredWebSSOLogin() requiredWebSSOLogin()
return Promise.reject(err) return Promise.reject(err)
} }