import { useEffect, useState } from 'react'
import { useDataProvider, useGetList } from 'ra-core'
import {
Alert,
Box,
Button,
CircularProgress,
Paper,
Stack,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Typography,
} from '@mui/material'
import DeleteOutlined from '@mui/icons-material/DeleteOutlined'
import PlayArrowOutlined from '@mui/icons-material/PlayArrowOutlined'
import SecurityOutlined from '@mui/icons-material/SecurityOutlined'
import StopOutlined from '@mui/icons-material/StopOutlined'
const statusLabels = {
created: '已创建',
running: '运行中',
exited: '已停止',
restarting: '重启中',
paused: '已暂停',
}
function Status({ state }) {
const color = state === 'running' ? 'success.main' : state === 'exited' ? 'warning.main' : 'text.secondary'
return {statusLabels[state] || state}
}
function RuntimeActions({ runtime, busy, onAction }) {
const running = runtime.state === 'running'
return (
} onClick={() => onAction(runtime.name, 'start')}>启动
} onClick={() => onAction(runtime.name, 'stop')}>停止
} onClick={() => onAction(runtime.name, 'recycle')}>回收
)
}
function RuntimeTable({ runtimes, busy, onAction }) {
return (
环境状态ProfileCDP操作
{runtimes.map(runtime => (
{runtime.name}{runtime.status}
{runtime.profile}
{runtime.endpoint}
))}
)
}
function RuntimeCards({ runtimes, busy, onAction }) {
return {runtimes.map(runtime => {runtime.name}{runtime.status}{runtime.profile})}
}
export function BrowserList() {
const dataProvider = useDataProvider()
const [name, setName] = useState('')
const [seed, setSeed] = useState('1000')
const [busy, setBusy] = useState('')
const [localError, setLocalError] = useState('')
const { data: runtimes = [], error, isPending, refetch } = useGetList('browsers', {}, { refetchInterval: 3000 })
useEffect(() => {
document.title = 'CreatorHub · 运行环境'
}, [])
async function createRuntime(event) {
event.preventDefault()
setBusy('create')
setLocalError('')
try {
await dataProvider.create('browsers', { data: { name, seed: Number(seed) } })
setName('')
await refetch()
} catch (reason) {
setLocalError(reason.message)
} finally {
setBusy('')
}
}
async function act(runtimeName, action) {
if (action === 'recycle' && !window.confirm(`回收 ${runtimeName}?Profile 数据卷将保留。`)) return
setBusy(runtimeName)
setLocalError('')
try {
await dataProvider.browserAction(runtimeName, action)
await refetch()
} catch (reason) {
setLocalError(reason.message)
} finally {
setBusy('')
}
}
const message = localError || error?.message
return (
<>
运行环境启动、停止并回收隔离的浏览器 Profile
环境名称 *
setName(event.target.value)} placeholder="例如:account-a" />
Fingerprint Seed *
setSeed(event.target.value)} />
{message ? { setLocalError(''); refetch() }}>重试} sx={{ mb: 2.5 }}>{message} : null}
{isPending ? : null}
{!isPending && runtimes.length === 0 ? 暂无运行环境,创建第一个隔离 Profile。 : null}
{!isPending && runtimes.length > 0 ? <>> : null}
Docker socket 仅由受限网关访问
>
)
}