Skip to content

Commit

Permalink
feat(frontend): update for instance entry
Browse files Browse the repository at this point in the history
  • Loading branch information
GZTimeWalker committed Aug 15, 2023
1 parent 2210a75 commit 531887e
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 86 deletions.
52 changes: 8 additions & 44 deletions src/GZCTF/ClientApp/src/components/ChallengeDetailModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
Box,
Button,
Card,
Code,
Divider,
Group,
LoadingOverlay,
Expand All @@ -18,19 +17,18 @@ import {
Text,
TextInput,
Title,
Tooltip,
} from '@mantine/core'
import { useClipboard, useDisclosure, useInputState } from '@mantine/hooks'
import { useDisclosure, useInputState } from '@mantine/hooks'
import { notifications, showNotification, updateNotification } from '@mantine/notifications'
import { mdiCheck, mdiClose, mdiDownload, mdiLightbulbOnOutline, mdiLoading } from '@mdi/js'
import { Icon } from '@mdi/react'
import MarkdownRender, { InlineMarkdownRender } from '@Components/MarkdownRender'
import { showErrorNotification } from '@Utils/ApiErrorHandler'
import { ChallengeTagItemProps } from '@Utils/Shared'
import { useTooltipStyles } from '@Utils/ThemeOverride'
import { OnceSWRConfig } from '@Utils/useConfig'
import { useTypographyStyles } from '@Utils/useTypographyStyles'
import api, { AnswerResult, ChallengeType } from '@Api'
import InstanceEntry from './InstanceEntry'

interface ChallengeDetailModalProps extends ModalProps {
gameId: number
Expand Down Expand Up @@ -130,15 +128,10 @@ const ChallengeDetailModal: FC<ChallengeDetailModalProps> = (props) => {
setPlaceholder(FlagPlaceholders[Math.floor(Math.random() * FlagPlaceholders.length)])
}, [challengeId])

const instanceCloseTime = dayjs(challenge?.context?.closeTime ?? 0)
const instanceLeft = instanceCloseTime.diff(dayjs(), 'minute')

const isDynamic =
challenge?.type === ChallengeType.StaticContainer ||
challenge?.type === ChallengeType.DynamicContainer
const { classes, theme } = useTypographyStyles()
const { classes: tooltipClasses } = useTooltipStyles()
const clipBoard = useClipboard()

const [disabled, setDisabled] = useState(false)
const [onSubmitting, setOnSubmitting] = useState(false)
Expand Down Expand Up @@ -404,41 +397,12 @@ const ChallengeDetailModal: FC<ChallengeDetailModalProps> = (props) => {
</Group>
)}
{isDynamic && challenge?.context?.instanceEntry && (
<Stack align="center">
<Group>
<Text size="sm" fw={600}>
实例访问入口:
<Tooltip label="点击复制" withArrow classNames={tooltipClasses}>
<Code
style={{
backgroundColor: 'transparent',
fontSize: theme.fontSizes.sm,
cursor: 'pointer',
}}
onClick={() => {
clipBoard.copy(challenge.context?.instanceEntry ?? '')
showNotification({
color: 'teal',
message: '实例入口已复制到剪贴板',
icon: <Icon path={mdiCheck} size={1} />,
})
}}
>
{challenge?.context?.instanceEntry}
</Code>
</Tooltip>
</Text>
<Countdown time={challenge?.context?.closeTime ?? '0'} />
</Group>
<Group position="center">
<Button color="orange" onClick={onProlongContainer} disabled={instanceLeft > 10}>
延长时间
</Button>
<Button color="red" onClick={onDestroyContainer} disabled={disabled}>
销毁实例
</Button>
</Group>
</Stack>
<InstanceEntry
context={challenge.context}
onProlong={onProlongContainer}
onDestroy={onDestroyContainer}
disabled={disabled}
/>
)}
</Stack>
<Divider />
Expand Down
85 changes: 85 additions & 0 deletions src/GZCTF/ClientApp/src/components/InstanceEntry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import dayjs from 'dayjs'
import { FC } from 'react'
import { Stack, Text, Button, Group, Code, Tooltip, Anchor } from '@mantine/core'
import { useClipboard } from '@mantine/hooks'
import { showNotification } from '@mantine/notifications'
import { mdiCheck } from '@mdi/js'
import { Icon } from '@mdi/react'
import { getProxyUrl } from '@Utils/Shared'
import { useTooltipStyles } from '@Utils/ThemeOverride'
import { ClientFlagContext } from '@Api'
import { Countdown } from './ChallengeDetailModal'

interface InstanceEntryProps {
context: ClientFlagContext
disabled: boolean
onProlong: () => void
onDestroy: () => void
}

export const InstanceEntry: FC<InstanceEntryProps> = (props) => {
const { context, onProlong, disabled, onDestroy } = props
const clipBoard = useClipboard()
const instanceCloseTime = dayjs(context.closeTime ?? 0)
const instanceLeft = instanceCloseTime.diff(dayjs(), 'minute')
const { classes: tooltipClasses, theme } = useTooltipStyles()

const instanceEntry = context.instanceEntry ?? ''
const isPlatfromProxy = instanceEntry.length === 36 && !instanceEntry.includes(':')
const copyEntry = isPlatfromProxy ? getProxyUrl(instanceEntry) : instanceEntry

return (
<Stack align="flex-start" spacing={2}>
<Group noWrap spacing={0} w="100%" position="apart">
<Text size="sm" fw={600}>
实例入口:
<Tooltip label="点击复制" withArrow classNames={tooltipClasses}>
<Code
bg="transparent"
style={{
fontSize: theme.fontSizes.sm,
cursor: 'pointer',
}}
onClick={() => {
clipBoard.copy(copyEntry)
showNotification({
color: 'teal',
title: isPlatfromProxy ? '实例入口已复制到剪贴板' : undefined,
message: isPlatfromProxy ? '请使用客户端进行访问' : '实例入口已复制到剪贴板',
icon: <Icon path={mdiCheck} size={1} />,
})
}}
>
{instanceEntry}
</Code>
</Tooltip>
</Text>
<Countdown time={context.closeTime ?? '0'} />
</Group>
{isPlatfromProxy && (
<Group noWrap spacing={0}>
<Text size="sm" fw={600}>
获取客户端:
<Anchor
href="https://github.com/XDSEC/WebSocketReflectorX"
target="_blank"
rel="noreferrer"
>
WebSocketReflectorX
</Anchor>
</Text>
</Group>
)}
<Group position="center" pt="md" w="100%" align="center">
<Button color="orange" onClick={onProlong} disabled={instanceLeft > 10}>
延长时间
</Button>
<Button color="red" onClick={onDestroy} disabled={disabled}>
销毁实例
</Button>
</Group>
</Stack>
)
}

export default InstanceEntry
45 changes: 12 additions & 33 deletions src/GZCTF/ClientApp/src/components/admin/ChallengePreviewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
ActionIcon,
Box,
Button,
Code,
Divider,
Group,
LoadingOverlay,
Expand All @@ -16,16 +15,15 @@ import {
Text,
TextInput,
Title,
Tooltip,
} from '@mantine/core'
import { useDisclosure, useInputState } from '@mantine/hooks'
import { showNotification } from '@mantine/notifications'
import { mdiCheck, mdiDownload, mdiLightbulbOnOutline } from '@mdi/js'
import { Icon } from '@mdi/react'
import { Countdown, FlagPlaceholders } from '@Components/ChallengeDetailModal'
import { FlagPlaceholders } from '@Components/ChallengeDetailModal'
import InstanceEntry from '@Components/InstanceEntry'
import MarkdownRender, { InlineMarkdownRender } from '@Components/MarkdownRender'
import { ChallengeTagItemProps } from '@Utils/Shared'
import { useTooltipStyles } from '@Utils/ThemeOverride'
import { useTypographyStyles } from '@Utils/useTypographyStyles'
import { ChallengeType, ChallengeUpdateModel, FileType } from '@Api'

Expand All @@ -42,9 +40,8 @@ const ChallengePreviewModal: FC<ChallengePreviewModalProps> = (props) => {

const [placeholder, setPlaceholder] = useState('')
const [flag, setFlag] = useInputState('')
const [withContainer, setWithContainer] = useState(false)
const [startTime, setStartTime] = useState(dayjs())
const { classes: tooltipClasses } = useTooltipStyles()
const [withContainer, setWithContainer] = useState(false)

const onSubmit = (event: React.FormEvent) => {
event.preventDefault()
Expand Down Expand Up @@ -183,33 +180,15 @@ const ChallengePreviewModal: FC<ChallengePreviewModalProps> = (props) => {
</Group>
)}
{isDynamic && withContainer && (
<Stack align="center">
<Group>
<Text size="sm" fw={600}>
实例访问入口:
<Tooltip label="点击复制" withArrow classNames={tooltipClasses}>
<Code
style={{
backgroundColor: 'transparent',
fontSize: theme.fontSizes.sm,
cursor: 'pointer',
}}
>
localhost:2333
</Code>
</Tooltip>
</Text>
<Countdown time={startTime.add(2, 'h').toJSON()} />
</Group>
<Group position="center">
<Button color="orange" disabled>
延长时间
</Button>
<Button color="red" onClick={() => setWithContainer(false)}>
销毁实例
</Button>
</Group>
</Stack>
<InstanceEntry
context={{
closeTime: startTime.add(2, 'hour').toJSON(),
instanceEntry: 'localhost:2333',
}}
disabled={false}
onProlong={() => {}}
onDestroy={() => setWithContainer(false)}
/>
)}
</Stack>
<Divider />
Expand Down
46 changes: 37 additions & 9 deletions src/GZCTF/ClientApp/src/pages/admin/Instances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { Icon } from '@mdi/react'
import { ActionIconWithConfirm } from '@Components/ActionIconWithConfirm'
import AdminPage from '@Components/admin/AdminPage'
import { showErrorNotification } from '@Utils/ApiErrorHandler'
import { ChallengeTagLabelMap } from '@Utils/Shared'
import { ChallengeTagLabelMap, getProxyUrl } from '@Utils/Shared'
import { useTableStyles, useTooltipStyles } from '@Utils/ThemeOverride'
import api, { ChallengeModel, ChallengeTag, TeamModel } from '@Api'

Expand Down Expand Up @@ -205,8 +205,8 @@ const Instances: FC = () => {
<tr>
<th>队伍</th>
<th>题目</th>
<th>容器 Id</th>
<th>生命周期</th>
<th>容器 Id</th>
<th>访问入口</th>
<th />
</tr>
Expand All @@ -233,11 +233,6 @@ const Instances: FC = () => {
</Text>
</Box>
</td>
<td>
<Text size="sm" ff={theme.fontFamilyMonospace} lineClamp={1}>
{inst.containerId?.substring(0, 20)}
</Text>
</td>
<td>
<Group noWrap spacing="xs">
<Badge size="xs" color={color} variant="dot">
Expand All @@ -249,6 +244,39 @@ const Instances: FC = () => {
</Badge>
</Group>
</td>
<td>
<Text size="sm" ff={theme.fontFamilyMonospace} lineClamp={1}>
<Tooltip
label="复制 URL"
withArrow
position="left"
classNames={tooltipClasses}
>
<Text
size="sm"
ff={theme.fontFamilyMonospace}
style={{
backgroundColor: 'transparent',
fontSize: theme.fontSizes.sm,
cursor: 'pointer',
}}
onClick={() => {
clipBoard.copy(
inst.containerGuid && getProxyUrl(inst.containerGuid)
)
showNotification({
color: 'teal',
title: '代理 URL 已复制到剪贴板',
message: '请使用客户端进行访问',
icon: <Icon path={mdiCheck} size={1} />,
})
}}
>
{inst.containerGuid}
</Text>
</Tooltip>
</Text>
</td>
<td>
<Tooltip
label="点击复制"
Expand All @@ -269,7 +297,7 @@ const Instances: FC = () => {
clipBoard.copy(`${inst.ip ?? ''}:${inst.port ?? ''}`)
showNotification({
color: 'teal',
message: '实例入口已复制到剪贴板',
message: '访问入口已复制到剪贴板',
icon: <Icon path={mdiCheck} size={1} />,
})
}}
Expand All @@ -286,7 +314,7 @@ const Instances: FC = () => {
<ActionIconWithConfirm
iconPath={mdiPackageVariantClosedRemove}
color="alert"
message={`确定销毁容器:\n${inst.containerId} `}
message={`确定销毁容器:${inst.containerGuid?.substring(0, 8)}?`}
disabled={disabled}
onClick={() => onDelete(inst.containerGuid)}
/>
Expand Down
5 changes: 5 additions & 0 deletions src/GZCTF/ClientApp/src/utils/Shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,8 @@ export const TaskStatusColorMap = new Map<TaskStatus | null, string>([
[TaskStatus.Duplicate, 'lime'],
[null, 'gray'],
])

export const getProxyUrl = (guid: string) => {
const protocol = window.location.protocol.replace('http', 'ws')
return `${protocol}//${window.location.host}/api/proxy/${guid}`
}

0 comments on commit 531887e

Please sign in to comment.