feat: 多镜像版本管理与网关化环境编排 (#14)

This commit is contained in:
2026-08-28 19:46:08 +08:00
parent 442a6604fb
commit 199f7b348a
26 changed files with 3243 additions and 328 deletions
+50 -26
View File
@@ -1,7 +1,7 @@
import { HttpError } from 'ra-core'
async function request(path = '', options) {
const response = await fetch(`/api/browsers${path}`, options)
const response = await fetch(`/api${path}`, options)
if (!response.ok) {
const body = await response.json().catch(() => ({}))
throw new HttpError(body.error || `请求失败 (${response.status})`, response.status, body)
@@ -9,38 +9,62 @@ async function request(path = '', options) {
return response.status === 204 ? null : response.json()
}
const unsupported = operation => Promise.reject(new Error(`browsers 不支持 ${operation}`))
const jsonOptions = (method, data) => ({
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
const unsupported = (resource, operation) => Promise.reject(new Error(`${resource} 不支持 ${operation}`))
const resourcePaths = {
browsers: '/browsers',
'browser-images': '/browser-images',
gateways: '/gateways',
}
export const dataProvider = {
async getList(resource) {
if (resource !== 'browsers') return unsupported('getList')
const browsers = await request()
return {
data: browsers.map(browser => ({ ...browser, id: browser.id || browser.name })),
total: browsers.length,
}
const path = resourcePaths[resource]
if (!path) return unsupported(resource, 'getList')
const records = await request(path)
return { data: records.map(record => ({ ...record, id: record.alias ?? record.version ?? record.name })), total: records.length }
},
async create(resource, { data }) {
if (resource !== 'browsers') return unsupported('create')
const created = await request('', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
return { data: { ...data, ...created, id: created.id || created.name } }
const path = resourcePaths[resource]
if (!path) return unsupported(resource, 'create')
const created = await request(path, jsonOptions('POST', data))
return { data: { ...data, ...created, id: created?.alias ?? created?.version ?? created?.name ?? data.alias } }
},
getOne: () => unsupported('getOne'),
getMany: () => unsupported('getMany'),
getManyReference: () => unsupported('getManyReference'),
update: () => unsupported('update'),
updateMany: () => unsupported('updateMany'),
delete: () => unsupported('delete'),
deleteMany: () => unsupported('deleteMany'),
async browserAction(name, action) {
async update(resource, { id, data }) {
const path = resourcePaths[resource]
if (!path || resource === 'browsers') return unsupported(resource, 'update')
// browser-images 的 PUT 不接受 version 字段(路径已携带),透传其余字段。
const { version: _ignored, ...rest } = data ?? {}
await request(`${path}/${encodeURIComponent(id)}`, jsonOptions('PUT', rest))
return { data: { ...rest, id } }
},
async delete(resource, { id }) {
const path = resourcePaths[resource]
if (!path) return unsupported(resource, 'delete')
await request(`${path}/${encodeURIComponent(id)}`, { method: 'DELETE' })
return { data: { id } }
},
getOne: resource => unsupported(resource, 'getOne'),
getMany: resource => unsupported(resource, 'getMany'),
getManyReference: resource => unsupported(resource, 'getManyReference'),
updateMany: resource => unsupported(resource, 'updateMany'),
deleteMany: resource => unsupported(resource, 'deleteMany'),
// 环境的领域动作保持显式动词,不伪装成 CRUD update。
async browserAction(alias, action, data) {
if (action === 'upgrade') {
await request(`/browsers/${encodeURIComponent(alias)}/upgrade`, jsonOptions('POST', data))
return
}
const paths = {
start: [`/${encodeURIComponent(name)}/start`, 'POST'],
stop: [`/${encodeURIComponent(name)}/stop`, 'POST'],
recycle: [`/${encodeURIComponent(name)}`, 'DELETE'],
start: [`/browsers/${encodeURIComponent(alias)}/start`, 'POST'],
stop: [`/browsers/${encodeURIComponent(alias)}/stop`, 'POST'],
recycle: [`/browsers/${encodeURIComponent(alias)}`, 'DELETE'],
}
const target = paths[action]
if (!target) throw new Error(`未知运行环境操作: ${action}`)