feat(adapters): 接入 CloudCone/Zeabur/Dynadot/新网/Agnes 五个平台
线上平台列表里 cloudcone、zeabur、xinwang、dynadot、agnes 的 sdk_type 仍为空, 平台卡片只能显示「未对接」,用户无法同步。按各自真实 API 能力补齐: - CloudCone:实例清单 + 开机/关机/重启;无公开账单接口,不声明余额与续费 - Zeabur:服务器清单 + 重启;毫核/MB 归一化为核/GB,未采集时不伪造规格 - Dynadot:域名清单(含到期日)+ 账户余额;set_dns2 位置参数无法安全 CRUD - 新网:代理 API 按域名逐个查到期日;无列表接口,需在账号里填 domains 清单 - Agnes:/models 校验 Key;无余额接口,如实标记不支持 配套改动: - _align_preset_providers 为库中为空的 sdk_type 回填种子值(启动即生效), 避免新适配器上线后线上仍停留在「未对接」 - 适配器可声明 plain_config,代理号/域名等非密钥字段前端按明文输入 - PRESET_PROVIDERS 五条记录填上对应 sdk_type 测试:445 passed(新增 tests/test_platform_adapters.py),vitest 62 passed。
This commit is contained in:
+9
-6
@@ -226,7 +226,7 @@ const AccountModal = {
|
||||
<div v-if="currentFields.length" class="border border-slate-200 dark:border-slate-700 rounded-lg p-3 space-y-2">
|
||||
<div class="text-xs font-medium text-slate-600 dark:text-slate-300">API 配置(供 SDK 同步,加密存储)</div>
|
||||
<label v-for="field in currentFields" :key="field" class="block"><span class="text-xs text-slate-400">{{ field }}</span>
|
||||
<input v-model="apiFields[field]" type="password" :placeholder="store.accountModal.editing ? '留空不修改' : '填写 ' + field" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
|
||||
<input v-model="apiFields[field]" :type="isPlain(field) ? 'text' : 'password'" :placeholder="store.accountModal.editing ? '留空不修改' : '填写 ' + field" class="mt-1 w-full px-2 py-1.5 rounded border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-sm"></label>
|
||||
</div>
|
||||
<p v-if="store.accountModal.editing" class="text-xs text-slate-400">修改账号标识后,引用该账号的资产会自动同步更新。</p>
|
||||
<div class="flex justify-end gap-2">
|
||||
@@ -244,12 +244,15 @@ const AccountModal = {
|
||||
try { const r = await Api.get('/providers/adapters'); adapterMeta.value = r.adapters || []; } catch (e) { /* ignore */ }
|
||||
});
|
||||
// 按账号所属平台的 sdk_type 动态渲染所需凭证字段
|
||||
const currentFields = Vue.computed(() => {
|
||||
const currentAdapter = Vue.computed(() => {
|
||||
const p = store.providers.find(x => x.slug === (f.value && f.value.platform));
|
||||
if (!p || !p.sdk_type) return [];
|
||||
const m = adapterMeta.value.find(x => (x.sdk_types || []).includes(p.sdk_type));
|
||||
return m ? (m.required_config || []) : [];
|
||||
if (!p || !p.sdk_type) return null;
|
||||
return adapterMeta.value.find(x => (x.sdk_types || []).includes(p.sdk_type)) || null;
|
||||
});
|
||||
const currentFields = Vue.computed(() => (currentAdapter.value ? (currentAdapter.value.required_config || []) : []));
|
||||
// 代理号、域名清单等非密钥字段按明文显示,否则用户无法核对输入
|
||||
const plainFields = Vue.computed(() => (currentAdapter.value ? (currentAdapter.value.plain_config || []) : []));
|
||||
function isPlain(field) { return plainFields.value.includes(field); }
|
||||
// 每次打开清空残留输入,避免串号
|
||||
Vue.watch(() => store.accountModal.show, (show) => {
|
||||
if (show) { for (const k of Object.keys(apiFields)) delete apiFields[k]; }
|
||||
@@ -260,7 +263,7 @@ const AccountModal = {
|
||||
f.value.api_config = Object.keys(cfg).length ? JSON.stringify(cfg) : '';
|
||||
saveAccount();
|
||||
}
|
||||
return { store, f, currentFields, apiFields, save };
|
||||
return { store, f, currentFields, apiFields, isPlain, save };
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user