Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(console): support global auth from system features #2328

Merged
merged 2 commits into from
Jun 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion console/src/api/ApiHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import { useFetchProject } from '@/domain/project/hooks/useFetchProject'
import qs from 'qs'
import { useProjectRole } from '@/domain/project/hooks/useProjectRole'
import { useFetchProjectRole } from '@/domain/project/hooks/useFetchProjectRole'
import { useFetchSystemFeatures } from '@/domain/setting/hooks/useSettings'
import { useSystemFeatures } from '@/domain/setting/hooks/useSystemFeatures'

export default function ApiHeader() {
function ApiHeader() {
const location = useLocation()
const errMsgExpireTimeSeconds = 5
const lastErrMsgRef = useRef<Record<string, number>>({})
Expand All @@ -21,7 +23,9 @@ export default function ApiHeader() {
const projectInfo = useFetchProject(projectId)
const { setProject } = useProject()
const { role: projectRole } = useFetchProjectRole(projectId as string)
const systemFeaturesInfo = useFetchSystemFeatures()
const { setRole } = useProjectRole()
const { setSystemFeatures } = useSystemFeatures()

useFirstRender(() => {
// @ts-ignore
Expand Down Expand Up @@ -133,5 +137,13 @@ export default function ApiHeader() {
}
}, [projectRole, setRole])

useEffect(() => {
if (systemFeaturesInfo.data) {
setSystemFeatures(systemFeaturesInfo?.data)
}
}, [systemFeaturesInfo?.data, setSystemFeatures])

return <></>
}

export default ApiHeader
14 changes: 10 additions & 4 deletions console/src/api/WithAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useCurrentUser } from '@/hooks/useCurrentUser'
import React from 'react'
import { IUserSchema } from '@user/schemas/user'
import { IPrivileges, Privileges, Role, RolePrivilege } from './const'
import { useSystemFeatures } from '@/domain/setting/hooks/useSystemFeatures'

const hasPrivilege = (role: Role, id: string) => {
return RolePrivilege[role]?.[id] ?? false
Expand All @@ -27,13 +28,17 @@ export default function WithAuth({
let isPrivileged = false
// eslint-disable-next-line react-hooks/exhaustive-deps
const { currentUser } = useCurrentUser()
const { systemFeatures } = useSystemFeatures()

if (!currentUser) return <Empty />
if (isWrongKey(id)) return <Empty str='wrong key' />
if (isAdmin(currentUser)) isPrivileged = true
else {
isPrivileged = hasPrivilege(role, id)
}

// prirority: system global > admin > role
if (systemFeatures.disabled?.includes(id)) isPrivileged = false
else if (isAdmin(currentUser)) isPrivileged = true
else isPrivileged = hasPrivilege(role, id)

// render
if (typeof children === 'function') {
return children(isPrivileged)
}
Expand All @@ -43,6 +48,7 @@ export default function WithAuth({

export function WithCurrentAuth({ id, children }: { id: keyof IPrivileges; children: React.ReactElement | any }) {
const { role } = useProjectRole()

return (
<WithAuth role={role} id={id}>
{children}
Expand Down
4 changes: 4 additions & 0 deletions console/src/api/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export const Privileges = {
'dataset.version.revert': true,
'evaluation.panel.save': true,
'runtime.image.build': true,
// global
'online-eval': true,
'job-pause': true,
'job-resume': true,
}
export type IPrivileges = typeof Privileges

Expand Down
20 changes: 4 additions & 16 deletions console/src/domain/project/hooks/useFetchProjectRole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,10 @@ import { listProjectRole } from '../services/project'
export function useFetchProjectRole(projectId: string) {
// eslint-disable-next-line react-hooks/exhaustive-deps
const { currentUser } = useCurrentUser()
const members = useQuery(
['fetchProjectMembers', projectId],
() => {
if (!projectId) return
// eslint-disable-next-line consistent-return
return listProjectRole(projectId)
},
{ refetchOnWindowFocus: true, enabled: true }
)

React.useEffect(() => {
if (projectId) {
members.refetch()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [projectId])
const members = useQuery(['fetchProjectMembers', projectId], () => listProjectRole(projectId), {
refetchOnWindowFocus: true,
enabled: !!projectId,
})

const role = React.useMemo(() => {
if (!members.data) return Role.NONE
Expand Down
13 changes: 12 additions & 1 deletion console/src/domain/setting/hooks/useSettings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { IListQuerySchema } from '@/domain/base/schemas/list'
import { useQuery } from 'react-query'
import qs from 'qs'
import { listAgents, fetchSystemVersion, fetchSystemSetting, fetchSystemResourcePool } from '../services/system'
import {
listAgents,
fetchSystemVersion,
fetchSystemSetting,
fetchSystemResourcePool,
fetchSystemFeatures,
} from '../services/system'

export function useFetchAgents(query: IListQuerySchema) {
const info = useQuery(`fetchAgents:${qs.stringify(query)}`, () => listAgents(query))
Expand All @@ -18,6 +24,11 @@ export function useFetchSystemSetting() {
return info
}

export function useFetchSystemFeatures() {
const info = useQuery('fetchSystemFeatures', () => fetchSystemFeatures())
return info
}

export function useFetchSystemResourcePool() {
const info = useQuery('fetchSystemResourcePool', () => fetchSystemResourcePool())
return info
Expand Down
9 changes: 9 additions & 0 deletions console/src/domain/setting/hooks/useSystemFeatures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import useGlobalState from '@/hooks/global'

export const useSystemFeatures = () => {
const [systemFeatures, setSystemFeatures] = useGlobalState('systemFeatures')
return {
systemFeatures,
setSystemFeatures,
}
}
5 changes: 5 additions & 0 deletions console/src/domain/setting/schemas/system.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { IPrivileges } from '@/api/const'
import { IResourceSchema } from '@/domain/base/schemas/resource'

export type IBaseImageSchema = IResourceSchema
Expand Down Expand Up @@ -34,3 +35,7 @@ export type ISystemResourcePool = {
nodeSelector: Record<string, string>
resources: ISystemResource[]
}

export type ISystemFeaturesSchema = {
disabled?: Array<keyof IPrivileges>
}
6 changes: 6 additions & 0 deletions console/src/domain/setting/services/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import axios from 'axios'
import {
IAgentSchema,
IDeviceSchema,
ISystemFeaturesSchema,
ISystemResourcePool,
ISystemSettingSchema,
ISystemVersionSchema,
Expand Down Expand Up @@ -50,3 +51,8 @@ export async function fetchSystemResourcePool(): Promise<ISystemResourcePool[]>
const resp = await axios.get('/api/v1/system/resourcePool')
return resp.data
}

export async function fetchSystemFeatures(): Promise<ISystemFeaturesSchema> {
const resp = await axios.get('/api/v1/system/features')
return resp.data
}
2 changes: 2 additions & 0 deletions console/src/hooks/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ITaskDetailSchema } from '@/domain/job/schemas/task'
import { Role } from '@/api/const'
import { IRuntimeDetailSchema } from '@/domain/runtime/schemas/runtime'
import { ThemeType } from '@starwhale/ui/theme'
import { ISystemFeaturesSchema } from '@/domain/setting/schemas/system'

const initialState = {
token: undefined as string | undefined,
Expand Down Expand Up @@ -38,6 +39,7 @@ const initialState = {
drawerExpanded: false,
role: Role.NONE as Role,
vscodeTheme: 'light' as string,
systemFeatures: { disabled: [] } as ISystemFeaturesSchema,
}

const { useGlobalState } = createGlobalState(initialState)
Expand Down
56 changes: 32 additions & 24 deletions console/src/pages/Job/JobListCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,14 @@ export default function JobListCard() {
{t('Cancel')}
</Button>
&nbsp;&nbsp;
<Button
kind='tertiary'
onClick={() => handleAction(job.id, JobActionType.PAUSE)}
>
{t('Pause')}
</Button>
<WithCurrentAuth id='job-pause'>
<Button
kind='tertiary'
onClick={() => handleAction(job.id, JobActionType.PAUSE)}
>
{t('Pause')}
</Button>
</WithCurrentAuth>
</>
),
[JobStatusType.RUNNING]: (
Expand All @@ -103,12 +105,14 @@ export default function JobListCard() {
{t('Cancel')}
</Button>
&nbsp;&nbsp;
<Button
kind='tertiary'
onClick={() => handleAction(job.id, JobActionType.PAUSE)}
>
{t('Pause')}
</Button>
<WithCurrentAuth id='job-pause'>
<Button
kind='tertiary'
onClick={() => handleAction(job.id, JobActionType.PAUSE)}
>
{t('Pause')}
</Button>
</WithCurrentAuth>
</>
),
[JobStatusType.PAUSED]: (
Expand All @@ -120,22 +124,26 @@ export default function JobListCard() {
{t('Cancel')}
</Button>
&nbsp;&nbsp;
<Button
kind='tertiary'
onClick={() => handleAction(job.id, JobActionType.RESUME)}
>
{t('Resume')}
</Button>
<WithCurrentAuth id='job-resume'>
<Button
kind='tertiary'
onClick={() => handleAction(job.id, JobActionType.RESUME)}
>
{t('Resume')}
</Button>
</WithCurrentAuth>
</>
),
[JobStatusType.FAIL]: (
<>
<Button
kind='tertiary'
onClick={() => handleAction(job.id, JobActionType.RESUME)}
>
{t('Resume')}
</Button>
<WithCurrentAuth id='job-resume'>
<Button
kind='tertiary'
onClick={() => handleAction(job.id, JobActionType.RESUME)}
>
{t('Resume')}
</Button>
</WithCurrentAuth>
</>
),
[JobStatusType.SUCCESS]: (
Expand Down
15 changes: 9 additions & 6 deletions console/src/pages/Model/ModelListCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useHistory, useParams } from 'react-router-dom'
import { useFetchModels } from '@model/hooks/useFetchModels'
import { TextLink } from '@/components/Link'
import { Button } from '@starwhale/ui'
import { WithCurrentAuth } from '@/api/WithAuth'

export default function ModelListCard() {
const [page] = usePage()
Expand Down Expand Up @@ -52,12 +53,14 @@ export default function ModelListCard() {
{t('Version History')}
</Button>
&nbsp;&nbsp;
<Button
kind='tertiary'
onClick={() => history.push(`/projects/${projectId}/online_eval/${model.id}`)}
>
{t('online eval')}
</Button>
<WithCurrentAuth id='online-eval'>
<Button
kind='tertiary'
onClick={() => history.push(`/projects/${projectId}/online_eval/${model.id}`)}
>
{t('online eval')}
</Button>
</WithCurrentAuth>
</>,
]
}) ?? []
Expand Down
18 changes: 10 additions & 8 deletions console/src/pages/Model/ModelVersionListCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,16 @@ export default function ModelVersionListCard() {
content={`${window.location.protocol}//${window.location.host}/projects/${projectId}/models/${modelId}/versions/${model.id}/`}
/>
&nbsp;&nbsp;
<Button
kind='tertiary'
onClick={() =>
history.push(`/projects/${projectId}/online_eval/${modelId}/${model.id}`)
}
>
{t('online eval')}
</Button>
<WithCurrentAuth id='online-eval'>
<Button
kind='tertiary'
onClick={() =>
history.push(`/projects/${projectId}/online_eval/${modelId}/${model.id}`)
}
>
{t('online eval')}
</Button>
</WithCurrentAuth>
&nbsp;&nbsp;
{i ? (
<WithCurrentAuth id='model.version.revert'>
Expand Down
5 changes: 4 additions & 1 deletion console/src/pages/Project/ProjectLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface IProjectLayoutProps {
children: React.ReactNode
}

export default function ProjectLayout({ children }: IProjectLayoutProps) {
function ProjectLayout({ children }: IProjectLayoutProps) {
const { projectId } = useParams<{ projectId: string }>()
const projectInfo = useQuery(`fetchProject:${projectId}`, () => fetchProject(projectId))
const { project, setProject } = useProject()
Expand All @@ -38,3 +38,6 @@ export default function ProjectLayout({ children }: IProjectLayoutProps) {

return <BaseSubLayout>{children}</BaseSubLayout>
}

ProjectLayout.displayName = 'ProjectLayout'
export default ProjectLayout
15 changes: 8 additions & 7 deletions console/src/wdyr.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import * as React from 'react'
/// <reference types="@welldone-software/why-did-you-render" />

if (import.meta.env.DEV) {
import('@welldone-software/why-did-you-render').then((m) => {
if (window.location.search.indexOf('why-render') === -1) return
import React from 'react'

m.default(React, {
if (import.meta.env.DEV) {
if (window.location.search.indexOf('why-render') !== -1) {
const whyDidYouRender = await import('@welldone-software/why-did-you-render')
whyDidYouRender.default(React, {
include: [/.*/],
exclude: [/^BrowserRouter/, /^Link/, /^Route/],
exclude: [/^BrowserRouter/, /^Router/, /^Link/, /^Styled/, /^Unknown/, /^WithTheme/, /^Popover/],
trackHooks: true,
trackAllPureComponents: true,
})
})
}
}