Add configurable upstream sync proxy and schedule settings
This commit is contained in:
@@ -269,6 +269,35 @@
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
.sync-schedule-card {
|
||||
margin: 14px 0;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
background: #fbfcff;
|
||||
}
|
||||
.sync-schedule-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 280px) minmax(180px, 240px);
|
||||
gap: 12px;
|
||||
align-items: end;
|
||||
}
|
||||
.sync-schedule-grid .full-row {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.check-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
min-height: 42px;
|
||||
}
|
||||
.check-row input {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.hidden { display: none; }
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
@@ -397,6 +426,28 @@
|
||||
<section id="syncTabPanel" class="manage-panel hidden">
|
||||
<h3 class="title">原始数据同步</h3>
|
||||
<p class="sub">从上游 `KHwang9883/MobileModels` 拉取原始 markdown 数据,并重建 `dist/device_index.json`。如已开启 MySQL 自动装载,也会同步刷新 MySQL。请先启动完整服务。</p>
|
||||
<div class="sync-schedule-card">
|
||||
<h4 class="title">每日自动同步</h4>
|
||||
<p class="sub">在项目容器内按固定时间自动拉取上游原始数据,并重建索引与 MySQL Seed。时间按容器时区执行,设置会持久化到运行期数据目录。</p>
|
||||
<div class="sync-schedule-grid">
|
||||
<label class="check-row">
|
||||
<input id="scheduleEnabled" type="checkbox" />
|
||||
<span>启用每日自动同步</span>
|
||||
</label>
|
||||
<div>
|
||||
<label for="scheduleTimeInput">每日同步时间</label>
|
||||
<input id="scheduleTimeInput" type="time" step="60" value="03:00" />
|
||||
</div>
|
||||
<div class="full-row">
|
||||
<label for="githubProxyPrefixInput">GitHub 加速前缀</label>
|
||||
<input id="githubProxyPrefixInput" type="text" placeholder="例如 https://ghfast.top/" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button id="saveSyncScheduleBtn" type="button" class="primary">保存同步设置</button>
|
||||
</div>
|
||||
<div id="scheduleStatus" class="sub">正在读取自动同步设置。</div>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button id="syncUpstreamBtn" type="button" class="primary">开始同步原始数据</button>
|
||||
<button id="refreshSyncStatusBtn" type="button">刷新同步状态</button>
|
||||
@@ -470,12 +521,18 @@
|
||||
const syncLogEl = document.getElementById("syncLog");
|
||||
const syncUpstreamBtnEl = document.getElementById("syncUpstreamBtn");
|
||||
const refreshSyncStatusBtnEl = document.getElementById("refreshSyncStatusBtn");
|
||||
const scheduleEnabledEl = document.getElementById("scheduleEnabled");
|
||||
const scheduleTimeInputEl = document.getElementById("scheduleTimeInput");
|
||||
const githubProxyPrefixInputEl = document.getElementById("githubProxyPrefixInput");
|
||||
const saveSyncScheduleBtnEl = document.getElementById("saveSyncScheduleBtn");
|
||||
const scheduleStatusEl = document.getElementById("scheduleStatus");
|
||||
const reloadIndexBtnEl = document.getElementById("reloadIndexBtn");
|
||||
const indexStatusEl = document.getElementById("indexStatus");
|
||||
const indexSummaryEl = document.getElementById("indexSummary");
|
||||
|
||||
let syncSupported = false;
|
||||
let syncRunning = false;
|
||||
let scheduleSaving = false;
|
||||
|
||||
function normalizeText(text) {
|
||||
return (text || "").toLowerCase().replace(/[^0-9a-z\u4e00-\u9fff]+/g, "");
|
||||
@@ -502,6 +559,7 @@
|
||||
function updateSyncButtons() {
|
||||
syncUpstreamBtnEl.disabled = syncRunning || !syncSupported;
|
||||
refreshSyncStatusBtnEl.disabled = syncRunning;
|
||||
saveSyncScheduleBtnEl.disabled = syncRunning || scheduleSaving;
|
||||
}
|
||||
|
||||
function renderIndexStatus(message, details) {
|
||||
@@ -522,6 +580,8 @@
|
||||
if (data.workspace_root) lines.push(`工作空间目录: ${data.workspace_root}`);
|
||||
if (data.storage_mode) lines.push(`存储模式: ${data.storage_mode}`);
|
||||
if (data.upstream_repo_url) lines.push(`上游仓库: ${data.upstream_repo_url}`);
|
||||
if (data.github_proxy_prefix) lines.push(`GitHub 加速前缀: ${data.github_proxy_prefix}`);
|
||||
if (data.effective_upstream_repo_url) lines.push(`实际同步地址: ${data.effective_upstream_repo_url}`);
|
||||
if (data.upstream_branch) lines.push(`上游分支: ${data.upstream_branch}`);
|
||||
if (data.last_sync_time) lines.push(`最近同步时间: ${data.last_sync_time}`);
|
||||
if (data.last_upstream_commit) lines.push(`最近同步提交: ${data.last_upstream_commit}`);
|
||||
@@ -543,8 +603,33 @@
|
||||
syncLogEl.textContent = lines.join("\n").trim() || "暂无同步记录";
|
||||
}
|
||||
|
||||
function renderScheduleStatus(data, options = {}) {
|
||||
const preserveMessage = !!options.preserveMessage;
|
||||
const enabled = !!(data && data.sync_schedule_enabled);
|
||||
const dailyTime = (data && data.sync_schedule_time) || "03:00";
|
||||
const githubProxyPrefix = (data && data.github_proxy_prefix) || "";
|
||||
scheduleEnabledEl.checked = enabled;
|
||||
scheduleTimeInputEl.value = dailyTime;
|
||||
githubProxyPrefixInputEl.value = githubProxyPrefix;
|
||||
if (preserveMessage) return;
|
||||
|
||||
const lines = [
|
||||
`每日自动同步: ${enabled ? "已启用" : "未启用"}`,
|
||||
`同步时间: ${dailyTime}`,
|
||||
];
|
||||
if (data && data.sync_schedule_timezone) lines.push(`容器时区: ${data.sync_schedule_timezone}`);
|
||||
if (githubProxyPrefix) lines.push(`GitHub 加速前缀: ${githubProxyPrefix}`);
|
||||
if (data && data.effective_upstream_repo_url) lines.push(`实际同步地址: ${data.effective_upstream_repo_url}`);
|
||||
if (data && data.sync_schedule_next_run) lines.push(`下次执行: ${data.sync_schedule_next_run}`);
|
||||
if (data && data.sync_schedule_last_run_time) lines.push(`最近自动执行: ${data.sync_schedule_last_run_time}`);
|
||||
if (data && data.sync_schedule_last_run_status) lines.push(`最近执行结果: ${data.sync_schedule_last_run_status}`);
|
||||
if (data && data.sync_schedule_last_run_message) lines.push(`结果详情: ${data.sync_schedule_last_run_message}`);
|
||||
scheduleStatusEl.textContent = lines.join(";");
|
||||
}
|
||||
|
||||
async function loadSyncStatus(options = {}) {
|
||||
const preserveLog = !!options.preserveLog;
|
||||
const preserveScheduleMessage = !!options.preserveScheduleMessage;
|
||||
syncStatusEl.textContent = "正在检测同步能力。";
|
||||
try {
|
||||
const data = await fetchJson("/api/status", { cache: "no-store" });
|
||||
@@ -552,12 +637,16 @@
|
||||
syncStatusEl.textContent = syncSupported
|
||||
? "已连接 Docker Compose 服务,可以直接从页面同步原始数据、索引和 MySQL。"
|
||||
: "当前服务不支持原始数据同步。";
|
||||
renderScheduleStatus(data, { preserveMessage: preserveScheduleMessage });
|
||||
if (!preserveLog) {
|
||||
renderSyncLog(data, "服务状态");
|
||||
}
|
||||
} catch (err) {
|
||||
syncSupported = false;
|
||||
syncStatusEl.textContent = `当前页面未连接支持同步的 Docker Compose 服务:${err.message}`;
|
||||
if (!preserveScheduleMessage) {
|
||||
scheduleStatusEl.textContent = `自动同步设置读取失败: ${err.message}`;
|
||||
}
|
||||
if (!preserveLog) {
|
||||
syncLogEl.textContent = "请使用 `docker compose up --build -d` 启动完整服务后,再使用这个功能。";
|
||||
}
|
||||
@@ -593,6 +682,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSyncSchedule() {
|
||||
scheduleSaving = true;
|
||||
updateSyncButtons();
|
||||
scheduleStatusEl.textContent = "正在保存每日自动同步设置...";
|
||||
try {
|
||||
const payload = await fetchJson("/api/sync-schedule", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
enabled: !!scheduleEnabledEl.checked,
|
||||
daily_time: scheduleTimeInputEl.value || "03:00",
|
||||
github_proxy_prefix: githubProxyPrefixInputEl.value || "",
|
||||
}),
|
||||
});
|
||||
scheduleStatusEl.textContent = payload.message || "每日自动同步设置已保存。";
|
||||
await loadSyncStatus({ preserveLog: true, preserveScheduleMessage: true });
|
||||
renderScheduleStatus(payload);
|
||||
} catch (err) {
|
||||
scheduleStatusEl.textContent = `保存失败: ${err.message}`;
|
||||
} finally {
|
||||
scheduleSaving = false;
|
||||
updateSyncButtons();
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeAliasList(name, aliases) {
|
||||
const out = [];
|
||||
const seen = new Set();
|
||||
@@ -1235,6 +1349,7 @@
|
||||
manufacturerCountBtnEl.addEventListener("click", openManufacturerListModal);
|
||||
syncUpstreamBtnEl.addEventListener("click", runUpstreamSync);
|
||||
refreshSyncStatusBtnEl.addEventListener("click", loadSyncStatus);
|
||||
saveSyncScheduleBtnEl.addEventListener("click", saveSyncSchedule);
|
||||
reloadIndexBtnEl.addEventListener("click", loadIndexFromPath);
|
||||
|
||||
brandModalCancelBtnEl.addEventListener("click", closeBrandModal);
|
||||
|
||||
Reference in New Issue
Block a user