diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/iaop/registry.ts b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/iaop/registry.ts new file mode 100644 index 0000000..4e4c8c4 --- /dev/null +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/api/iaop/registry.ts @@ -0,0 +1,106 @@ +/** + * 模型注册表 API 客户端(issue #183 [E2]) + * + * 封装 E1(#182)四接口(nginx 反代 /api/v1/registry/ → registry 服务 :8002): + * GET /api/v1/registry/models 列表(?stage= 过滤) + * POST /api/v1/registry/models 注册新版本 + * POST /api/v1/registry/models/{name}/{ver}/promote 阶段提升 + * POST /api/v1/registry/models/{name}/rollback 回滚 + * + * 鉴权:携带 FBA access token(useAccessStore,与 requestClient 同源)。 + * 统一错误:抛 Error(msg 来自服务端 / HTTP 状态),由调用方降级或提示。 + */ +import { useAccessStore } from '@vben/stores'; + +export interface RegistryModel { + name: string; + version: string; + backbone: string; + stage: 'dev' | 'staging' | 'prod'; + description?: string; + registered_at?: number; + [k: string]: unknown; +} + +export interface RegistryListResp { + models: RegistryModel[]; + stage_pointers?: Record>; +} + +const REG_BASE = '/api/v1/registry'; + +function bearer(): string { + const accessStore = useAccessStore(); + const token = accessStore.accessToken as string | undefined; + return token ? `Bearer ${token}` : ''; +} + +async function regFetch(path: string, init?: RequestInit): Promise { + let resp: Response; + try { + resp = await fetch(`${REG_BASE}${path}`, { + ...init, + headers: { + 'Content-Type': 'application/json', + Authorization: bearer(), + ...(init?.headers || {}), + }, + }); + } catch { + throw new Error('注册表服务不可达'); + } + const raw = await resp.text(); + const data = (raw ? JSON.parse(raw) : {}) as { msg?: string; [k: string]: unknown }; + if (!resp.ok) { + throw new Error(data.msg || `注册表 HTTP ${resp.status}`); + } + return data as T; +} + +/** 模型列表(可按 stage 过滤) */ +export async function fetchRegistryModels(stage?: string): Promise { + return regFetch(stage ? `/models?stage=${stage}` : '/models'); +} + +/** 注册新版本(stage 默认 dev) */ +export async function registerModel(body: { + name: string; + version: string; + backbone?: string; + description?: string; + stage?: string; +}): Promise<{ ok: boolean; model: RegistryModel }> { + return regFetch('/models', { method: 'POST', body: JSON.stringify(body) }); +} + +/** 阶段提升 */ +export async function promoteModel( + name: string, + version: string, +): Promise<{ ok: boolean; model: RegistryModel }> { + return regFetch(`/models/${encodeURIComponent(name)}/${encodeURIComponent(version)}/promote`, { + method: 'POST', + }); +} + +/** 回滚某 stage 指针到指定版本 */ +export async function rollbackModel( + name: string, + stage: string, + version: string, +): Promise<{ ok: boolean; model: RegistryModel }> { + return regFetch(`/models/${encodeURIComponent(name)}/rollback`, { + method: 'POST', + body: JSON.stringify({ stage, version }), + }); +} + +/** 注册表可用性探测(离线时调用方降级 localStorage) */ +export async function registryAvailable(): Promise { + try { + await fetchRegistryModels(); + return true; + } catch { + return false; + } +} diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/iaop/admin/models.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/iaop/admin/models.vue index 1279f25..c90172e 100644 --- a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/iaop/admin/models.vue +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/iaop/admin/models.vue @@ -1,117 +1,194 @@ diff --git a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/iaop/studio/version.vue b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/iaop/studio/version.vue index 657baa0..3ecc756 100644 --- a/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/iaop/studio/version.vue +++ b/deploy/fba/fba-ui-src/apps/web-antdv-next/src/views/iaop/studio/version.vue @@ -1,13 +1,28 @@