Add MySQL web controls
This commit is contained in:
+109
-5
@@ -426,6 +426,27 @@
|
||||
<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">MySQL 自动装载</h4>
|
||||
<p class="sub">控制同步任务和容器后续启动时是否自动导入 schema 与 seed。保持关闭更安全;开启后,启动容器和“开始同步原始数据”都可能刷新 MySQL 数据。</p>
|
||||
<div class="sync-schedule-grid">
|
||||
<label class="check-row">
|
||||
<input id="mysqlAutoLoadEnabled" type="checkbox" />
|
||||
<span>启用 MySQL 自动装载</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button id="saveMysqlSettingsBtn" type="button">保存 MySQL 设置</button>
|
||||
</div>
|
||||
<div id="mysqlSettingsStatus" class="sub">正在读取 MySQL 自动装载设置。</div>
|
||||
</div>
|
||||
<div class="sync-schedule-card">
|
||||
<h4 class="title">外部 MySQL 初始化</h4>
|
||||
<p class="sub">面向关闭自动装载的外部 MySQL。点击后会执行 schema 与 seed 导入,自动创建数据库,并重建 `mobilemodels` 相关表与视图。请确认连接参数与账号权限无误后再执行。</p>
|
||||
<div class="btns">
|
||||
<button id="initMysqlBtn" type="button">初始化外部 MySQL</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sync-schedule-card">
|
||||
<h4 class="title">每日自动同步</h4>
|
||||
<p class="sub">在项目容器内按固定时间自动拉取上游原始数据,并重建索引与 MySQL Seed。时间按容器时区执行,设置会持久化到运行期数据目录。</p>
|
||||
@@ -520,7 +541,11 @@
|
||||
const syncStatusEl = document.getElementById("syncStatus");
|
||||
const syncLogEl = document.getElementById("syncLog");
|
||||
const syncUpstreamBtnEl = document.getElementById("syncUpstreamBtn");
|
||||
const initMysqlBtnEl = document.getElementById("initMysqlBtn");
|
||||
const refreshSyncStatusBtnEl = document.getElementById("refreshSyncStatusBtn");
|
||||
const mysqlAutoLoadEnabledEl = document.getElementById("mysqlAutoLoadEnabled");
|
||||
const saveMysqlSettingsBtnEl = document.getElementById("saveMysqlSettingsBtn");
|
||||
const mysqlSettingsStatusEl = document.getElementById("mysqlSettingsStatus");
|
||||
const scheduleEnabledEl = document.getElementById("scheduleEnabled");
|
||||
const scheduleTimeInputEl = document.getElementById("scheduleTimeInput");
|
||||
const githubProxyPrefixInputEl = document.getElementById("githubProxyPrefixInput");
|
||||
@@ -532,6 +557,8 @@
|
||||
|
||||
let syncSupported = false;
|
||||
let syncRunning = false;
|
||||
let mysqlInitRunning = false;
|
||||
let mysqlSettingsSaving = false;
|
||||
let scheduleSaving = false;
|
||||
|
||||
function normalizeText(text) {
|
||||
@@ -557,9 +584,12 @@
|
||||
}
|
||||
|
||||
function updateSyncButtons() {
|
||||
syncUpstreamBtnEl.disabled = syncRunning || !syncSupported;
|
||||
refreshSyncStatusBtnEl.disabled = syncRunning;
|
||||
saveSyncScheduleBtnEl.disabled = syncRunning || scheduleSaving;
|
||||
const busy = syncRunning || mysqlInitRunning;
|
||||
syncUpstreamBtnEl.disabled = busy || !syncSupported;
|
||||
initMysqlBtnEl.disabled = busy || !syncSupported;
|
||||
refreshSyncStatusBtnEl.disabled = busy;
|
||||
saveMysqlSettingsBtnEl.disabled = busy || mysqlSettingsSaving || !syncSupported;
|
||||
saveSyncScheduleBtnEl.disabled = busy || scheduleSaving;
|
||||
}
|
||||
|
||||
function renderIndexStatus(message, details) {
|
||||
@@ -597,7 +627,7 @@
|
||||
if (data.mysql_status) lines.push(`MySQL 详情: ${data.mysql_status}`);
|
||||
if (data.output) {
|
||||
lines.push("");
|
||||
lines.push("同步输出:");
|
||||
lines.push("任务输出:");
|
||||
lines.push(data.output);
|
||||
}
|
||||
syncLogEl.textContent = lines.join("\n").trim() || "暂无同步记录";
|
||||
@@ -627,9 +657,24 @@
|
||||
scheduleStatusEl.textContent = lines.join(";");
|
||||
}
|
||||
|
||||
function renderMysqlSettingsStatus(data, options = {}) {
|
||||
const preserveMessage = !!options.preserveMessage;
|
||||
const enabled = !!(data && data.mysql_auto_load);
|
||||
mysqlAutoLoadEnabledEl.checked = enabled;
|
||||
if (preserveMessage) return;
|
||||
|
||||
const lines = [
|
||||
`自动装载: ${enabled ? "已启用" : "未启用"}`,
|
||||
];
|
||||
if (data && data.mysql_config_updated_at) lines.push(`最近更新: ${data.mysql_config_updated_at}`);
|
||||
if (data && data.mysql_config_file) lines.push(`配置文件: ${data.mysql_config_file}`);
|
||||
mysqlSettingsStatusEl.textContent = lines.join(";");
|
||||
}
|
||||
|
||||
async function loadSyncStatus(options = {}) {
|
||||
const preserveLog = !!options.preserveLog;
|
||||
const preserveScheduleMessage = !!options.preserveScheduleMessage;
|
||||
const preserveMysqlSettingsMessage = !!options.preserveMysqlSettingsMessage;
|
||||
syncStatusEl.textContent = "正在检测同步能力。";
|
||||
try {
|
||||
const data = await fetchJson("/api/status", { cache: "no-store" });
|
||||
@@ -637,6 +682,7 @@
|
||||
syncStatusEl.textContent = syncSupported
|
||||
? "已连接 Docker Compose 服务,可以直接从页面同步原始数据、索引和 MySQL。"
|
||||
: "当前服务不支持原始数据同步。";
|
||||
renderMysqlSettingsStatus(data, { preserveMessage: preserveMysqlSettingsMessage });
|
||||
renderScheduleStatus(data, { preserveMessage: preserveScheduleMessage });
|
||||
if (!preserveLog) {
|
||||
renderSyncLog(data, "服务状态");
|
||||
@@ -644,6 +690,9 @@
|
||||
} catch (err) {
|
||||
syncSupported = false;
|
||||
syncStatusEl.textContent = `当前页面未连接支持同步的 Docker Compose 服务:${err.message}`;
|
||||
if (!preserveMysqlSettingsMessage) {
|
||||
mysqlSettingsStatusEl.textContent = `MySQL 设置读取失败: ${err.message}`;
|
||||
}
|
||||
if (!preserveScheduleMessage) {
|
||||
scheduleStatusEl.textContent = `自动同步设置读取失败: ${err.message}`;
|
||||
}
|
||||
@@ -656,7 +705,7 @@
|
||||
}
|
||||
|
||||
async function runUpstreamSync() {
|
||||
if (syncRunning) return;
|
||||
if (syncRunning || mysqlInitRunning) return;
|
||||
syncRunning = true;
|
||||
updateSyncButtons();
|
||||
syncStatusEl.textContent = "正在同步原始数据、重建索引并刷新 MySQL,请稍候。";
|
||||
@@ -682,6 +731,59 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function runMysqlInit() {
|
||||
if (syncRunning || mysqlInitRunning) return;
|
||||
const confirmed = window.confirm(
|
||||
"初始化外部 MySQL 会创建数据库,并重建 mobilemodels 相关表、视图和 seed 数据。是否继续?"
|
||||
);
|
||||
if (!confirmed) return;
|
||||
|
||||
mysqlInitRunning = true;
|
||||
updateSyncButtons();
|
||||
syncStatusEl.textContent = "正在初始化外部 MySQL,请稍候。";
|
||||
syncLogEl.textContent = "MySQL 初始化进行中...";
|
||||
|
||||
try {
|
||||
const data = await fetchJson("/api/init-mysql", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: "{}",
|
||||
});
|
||||
syncSupported = true;
|
||||
syncStatusEl.textContent = "外部 MySQL 初始化完成。";
|
||||
renderSyncLog(data, "MySQL 初始化完成");
|
||||
} catch (err) {
|
||||
syncStatusEl.textContent = `MySQL 初始化失败: ${err.message}`;
|
||||
syncLogEl.textContent = `MySQL 初始化失败\n${err.message}`;
|
||||
} finally {
|
||||
mysqlInitRunning = false;
|
||||
await loadSyncStatus({ preserveLog: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function saveMysqlSettings() {
|
||||
mysqlSettingsSaving = true;
|
||||
updateSyncButtons();
|
||||
mysqlSettingsStatusEl.textContent = "正在保存 MySQL 自动装载设置...";
|
||||
try {
|
||||
const payload = await fetchJson("/api/mysql-settings", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
auto_load: !!mysqlAutoLoadEnabledEl.checked,
|
||||
}),
|
||||
});
|
||||
mysqlSettingsStatusEl.textContent = payload.message || "MySQL 自动装载设置已保存。";
|
||||
await loadSyncStatus({ preserveLog: true, preserveMysqlSettingsMessage: true });
|
||||
renderMysqlSettingsStatus(payload);
|
||||
} catch (err) {
|
||||
mysqlSettingsStatusEl.textContent = `保存失败: ${err.message}`;
|
||||
} finally {
|
||||
mysqlSettingsSaving = false;
|
||||
updateSyncButtons();
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSyncSchedule() {
|
||||
scheduleSaving = true;
|
||||
updateSyncButtons();
|
||||
@@ -1348,7 +1450,9 @@
|
||||
brandCountBtnEl.addEventListener("click", openBrandListModal);
|
||||
manufacturerCountBtnEl.addEventListener("click", openManufacturerListModal);
|
||||
syncUpstreamBtnEl.addEventListener("click", runUpstreamSync);
|
||||
initMysqlBtnEl.addEventListener("click", runMysqlInit);
|
||||
refreshSyncStatusBtnEl.addEventListener("click", loadSyncStatus);
|
||||
saveMysqlSettingsBtnEl.addEventListener("click", saveMysqlSettings);
|
||||
saveSyncScheduleBtnEl.addEventListener("click", saveSyncSchedule);
|
||||
reloadIndexBtnEl.addEventListener("click", loadIndexFromPath);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user