feat: add /sub/clash/latest and /sub/singbox/latest public subscription APIs and display them on the web dashboard UI

This commit is contained in:
jocayn
2026-06-13 17:54:46 +08:00
parent 7d561e0c6b
commit 807b765e2c
3 changed files with 181 additions and 0 deletions
+56
View File
@@ -262,6 +262,32 @@
<div class="muted" id="summary">正在加载...</div>
</div>
</header>
<div style="padding: 20px 24px; border-bottom: 1px solid var(--line); background: #fafbfc; display: flex; flex-direction: column; gap: 12px;">
<h3 style="margin: 0; font-size: 0.95rem; font-weight: 700; color: #374151;">最新订阅地址 (无Token/持久链接)</h3>
<div style="display: flex; flex-wrap: wrap; gap: 16px;">
<div style="flex: 1; min-width: 280px; display: flex; align-items: center; justify-content: space-between; background: #fff; border: 1px solid var(--line); border-radius: 6px; padding: 10px 14px;">
<div style="display: flex; flex-direction: column; gap: 4px;">
<span style="font-size: 0.78rem; font-weight: 750; color: var(--primary); text-transform: uppercase;">Clash (YAML)</span>
<span style="font-size: 0.85rem; font-family: monospace; color: var(--muted); word-break: break-all;" id="latest-clash-link">加载中...</span>
</div>
<div style="display: flex; gap: 8px; align-items: center;">
<button type="button" class="btn btn-secondary" onclick="copyLatestLink('clash')">复制</button>
<a class="btn btn-green" id="latest-clash-import" href="#">导入 Clash</a>
</div>
</div>
<div style="flex: 1; min-width: 280px; display: flex; align-items: center; justify-content: space-between; background: #fff; border: 1px solid var(--line); border-radius: 6px; padding: 10px 14px;">
<div style="display: flex; flex-direction: column; gap: 4px;">
<span style="font-size: 0.78rem; font-weight: 750; color: var(--primary); text-transform: uppercase;">Sing-box (JSON)</span>
<span style="font-size: 0.85rem; font-family: monospace; color: var(--muted); word-break: break-all;" id="latest-singbox-link">加载中...</span>
</div>
<div style="display: flex; gap: 8px; align-items: center;">
<button type="button" class="btn btn-secondary" onclick="copyLatestLink('singbox')">复制</button>
<a class="btn btn-primary" id="latest-singbox-import" href="#">导入 Sing-box</a>
</div>
</div>
</div>
</div>
<div class="table-wrap">
<table>
<thead>
@@ -339,6 +365,35 @@
}
}
let latestClashUrl = '';
let latestSingboxUrl = '';
function updateLatestUrls() {
const clientOrigin = getClientImportOrigin();
latestClashUrl = `${clientOrigin}/sub/clash/latest`;
latestSingboxUrl = `${clientOrigin}/sub/singbox/latest`;
const clashLinkEl = document.getElementById('latest-clash-link');
const singboxLinkEl = document.getElementById('latest-singbox-link');
if (clashLinkEl) clashLinkEl.textContent = latestClashUrl;
if (singboxLinkEl) singboxLinkEl.textContent = latestSingboxUrl;
const clashImportEl = document.getElementById('latest-clash-import');
const singboxImportEl = document.getElementById('latest-singbox-import');
if (clashImportEl) {
clashImportEl.href = `clash://install-config?url=${encodeURIComponent(latestClashUrl)}&name=${encodeURIComponent('CatMata-Latest')}`;
}
if (singboxImportEl) {
singboxImportEl.href = `sing-box://import-remote-profile?url=${encodeURIComponent(latestSingboxUrl)}#${encodeURIComponent('CatMata-Latest')}`;
}
}
function copyLatestLink(type) {
const link = type === 'clash' ? latestClashUrl : latestSingboxUrl;
copyLinkToClipboard(link);
}
async function loadAppConfig() {
try {
const res = await fetch('/api/app-config', { credentials: 'same-origin' });
@@ -469,6 +524,7 @@
(async function init() {
await loadAppConfig();
updateLatestUrls();
if (await checkSession()) {
showHistory();
await loadHistory();