import { useEffect, useState } from 'react' import { Link, useNavigate, useParams } from 'react-router' import { useDataProvider, useList, useOne } from '@refinedev/core' import { Alert, Button, Card, CardContent, Copyable, DetailList, Field, Input, Modal, PageHeader, PageState, Select, StatusPill, Table, } from './lib/ui.jsx' import { useTitle } from './lib/hooks.js' const aliasPattern = /^[a-z0-9][a-z0-9-]{0,31}$/ const platformOptions = ['windows', 'linux', 'macos'].map(value => ({ value, label: value })) const brandOptions = ['Chrome', 'Edge', 'Opera', 'Vivaldi'].map(value => ({ value, label: value })) const spoofingFields = ['font', 'audio', 'canvas', 'clientrects', 'gpu'] const stateText = { created: '已创建', running: '运行中', exited: '已停止', restarting: '重启中', paused: '已暂停', missing: '未部署' } const blockText = { account_revoked: '授权已撤销', account_paused: '账号已暂停', binding_missing: '绑定缺失', network_exit_missing: '固定出口缺失', network_exit_unhealthy: '固定出口不健康', runtime_stop_pending: '停止结果待确认', runtime_missing: '运行实例缺失', } const scheduleBlock = runtime => runtime.schedule_status === 'ready' ? '' : blockText[runtime.schedule_block_reason] || runtime.schedule_block_reason || '不可调度' const createInitial = { name: '', alias: '', gateway: '', image_version: '', account_id: '', network_exit_id: '', seed: '1000', platform: '', platform_version: '', brand: '', brand_version: '', hardware_concurrency: '', lang: '', accept_lang: '', timezone: '', disable_spoofing: [], } // 高级字段空值不提交,收敛到 seed 驱动。 function fingerprintPayload(form) { const payload = { seed: Number(form.seed) } const advanced = { platform: form.platform, platform_version: form.platform_version, brand: form.brand, brand_version: form.brand_version, lang: form.lang, accept_lang: form.accept_lang, timezone: form.timezone } for (const [key, value] of Object.entries(advanced)) if (value !== '') payload[key] = value if (form.hardware_concurrency !== '') payload.hardware_concurrency = Number(form.hardware_concurrency) if (form.disable_spoofing.length > 0) payload.disable_spoofing = form.disable_spoofing.join(',') return payload } function StatePill({ state }) { const known = Object.hasOwn(stateText, state) return } function RuntimeActions({ runtime, busy, onAction }) { const running = runtime.state === 'running' const block = runtime.schedule_block_reason === 'runtime_missing' ? '' : scheduleBlock(runtime) const startLabel = `启动 ${runtime.name}${block ? `(${block})` : ''}` return (
) } function SchedulePill({ runtime }) { const block = scheduleBlock(runtime) return } function UpgradeDialog({ target, images, busy, onClose, onConfirm }) { const [version, setVersion] = useState('') const enabled = images.filter(image => image.enabled) useEffect(() => { setVersion('') }, [target]) return ( }>

容器将按新镜像重建,Profile 数据卷与指纹参数保持不变。

update('name', event.target.value)} /> update('alias', event.target.value)} placeholder="如:shop-01" /> update('image_version', event.target.value)} options={enabledImages.map(item => ({ value: item.version, label: item.version }))} placeholder="选择版本" disabled={enabledImages.length === 0} /> update('seed', event.target.value)} /> update('network_exit_id', event.target.value)} options={healthyExits.map(item => ({ value: item.id, label: `${item.id} · ${item.protocol}://${item.host}:${item.port}` }))} placeholder="当前机器直连" emptyOption={{ value: '', label: '当前机器直连' }} />