/* 域名视图:域名列表 + 子域名管理 + 站点 SSL 状态 * 交互:每行可展开「子域名」面板;子域名可添加/编辑/删除、一键探测 SSL 证书; * 域名行显示该域名下最近的证书到期徽章。 */ const DomainsView = { template: `
🌐 域名资产下管理子域名与站点 SSL 证书:展开任意域名添加子域名(如 www/api),点「探测SSL」即可监控该站点证书到期情况。
域名 注册商 DNS 到期 站点SSL 使用中 操作
暂无域名资产
`, setup() { const domains = Vue.computed(() => store.assets.filter(a => a.asset_type === 'domain' && a.domain_detail)); const expanded = Vue.reactive({}); // asset_id -> 是否展开子域名面板 const forms = Vue.reactive({}); // asset_id -> 新增/编辑表单 const subsByAsset = Vue.computed(() => { const m = {}; for (const s of store.subdomains) { (m[s.asset_id] = m[s.asset_id] || []).push(s); } return m; }); // 每个域名资产下最近到期的一条证书(用于主行徽章) const sslByAsset = Vue.computed(() => { const m = {}; for (const c of store.siteCerts) { if (!c.asset_id) continue; if (!m[c.asset_id] || (c.days_to_expiry ?? 9999) < (m[c.asset_id].days_to_expiry ?? 9999)) m[c.asset_id] = c; } return m; }); function domainName(a) { return a.domain_detail.domain_name; } function fullHost(s, a) { return s.host === '@' ? domainName(a) : s.host + '.' + domainName(a); } function toggle(a) { expanded[a.id] = !expanded[a.id]; if (expanded[a.id] && !forms[a.id]) resetForm(a); } function resetForm(a) { forms[a.id] = { host: '', record_type: 'A', record_value: '', note: '', is_active: true, editing: null }; } function startEdit(a, s) { expanded[a.id] = true; forms[a.id] = { host: s.host, record_type: s.record_type || 'A', record_value: s.record_value || '', note: s.note || '', is_active: !!s.is_active, editing: s.id }; } async function saveSub(a) { const f = forms[a.id]; if (!f.host.trim()) { alert('请填写主机名'); return; } const payload = { host: f.host.trim(), record_type: f.record_type, record_value: f.record_value, note: f.note, is_active: !!f.is_active }; try { if (f.editing) await Api.put('/subdomains/' + f.editing, payload); else await Api.post('/subdomains', { ...payload, asset_id: a.id }); await loadSubdomains(); resetForm(a); } catch (e) { alert('保存失败:' + e.message); } } async function delSub(s) { if (!confirm('删除子域名「' + s.host + '」?')) return; try { await Api.del('/subdomains/' + s.id); await loadSubdomains(); } catch (e) { alert('删除失败:' + e.message); } } async function probeSsl(defaultHost, a) { const target = prompt('探测 SSL 证书的站点(留空取消):', defaultHost); if (!target) return; try { await Api.post('/site-certs', { hostname: target, asset_id: a.id }); await loadSiteCerts(); alert('已探测:' + target); } catch (e) { alert('探测失败:' + e.message); } } function certText(c) { const d = c.days_to_expiry; if (d === null || d === undefined) return '未知'; if (d < 0) return '已过期' + Math.abs(d) + '天'; if (d === 0) return '今天到期'; return d + '天'; } function certBadge(d) { if (d === null || d === undefined) return 'bg-slate-500/10 text-slate-500'; if (d < 0 || d === 0 || d <= 7) return 'bg-red-500/10 text-red-600 dark:text-red-400'; if (d <= 30) return 'bg-amber-500/10 text-amber-600 dark:text-amber-400'; if (d <= 90) return 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'; return 'bg-slate-500/10 text-slate-500'; } return { store, Fmt, domains, subsByAsset, sslByAsset, expanded, forms, domainName, fullHost, toggle, resetForm, startEdit, saveSub, delSub, probeSsl, certText, certBadge, edit: openAssetEdit }; }, };