git-sync-web 自动提交 2026-09-09 13:46:40
This commit is contained in:
+76
-13
@@ -9,6 +9,31 @@ const STATUS = {
|
||||
};
|
||||
const RESULT_TEXT = { ok: '成功', error: '失败', conflict: '冲突' };
|
||||
|
||||
// ---------- 相对时间(卡片「上次同步」) ----------
|
||||
const STALE_HOURS = 24; // 超过该小时数且仍在运行 → 标记「久未同步」
|
||||
function parseTs(s) {
|
||||
if (!s) return null;
|
||||
// 后端时间格式 "2006-01-02 15:04:05",补齐为 ISO 本地时间
|
||||
const t = new Date(String(s).replace(' ', 'T'));
|
||||
return isNaN(t.getTime()) ? null : t;
|
||||
}
|
||||
function relTime(s) {
|
||||
const t = parseTs(s);
|
||||
if (!t) return String(s || '');
|
||||
const min = Math.floor((Date.now() - t.getTime()) / 60000);
|
||||
if (min < 1) return '刚刚';
|
||||
if (min < 60) return min + ' 分钟前';
|
||||
const hr = Math.floor(min / 60);
|
||||
if (hr < 24) return hr + ' 小时前';
|
||||
const day = Math.floor(hr / 24);
|
||||
if (day < 30) return day + ' 天前';
|
||||
return String(s).slice(0, 10);
|
||||
}
|
||||
function isStale(s) {
|
||||
const t = parseTs(s);
|
||||
return !!t && (Date.now() - t.getTime()) > STALE_HOURS * 3600 * 1000;
|
||||
}
|
||||
|
||||
let repos = [];
|
||||
let current = null;
|
||||
let creating = false;
|
||||
@@ -45,7 +70,7 @@ function card(r) {
|
||||
el.className = 'card ' + st.cls;
|
||||
el.dataset.name = r.name;
|
||||
const last = r.last_sync
|
||||
? `${r.last_sync.time.slice(5, 16)} · ${RESULT_TEXT[r.last_sync.result] || r.last_sync.result}`
|
||||
? `${relTime(r.last_sync.time)} · ${RESULT_TEXT[r.last_sync.result] || r.last_sync.result}`
|
||||
: '从未同步';
|
||||
el.innerHTML = `
|
||||
<div class="card-head">
|
||||
@@ -63,8 +88,9 @@ function card(r) {
|
||||
${r.cron ? `<span class="badge" title="${esc(r.cron)}">cron</span>` : ''}
|
||||
${r.conflicts && r.conflicts.length ? `<span class="badge b-conflict">冲突 ${r.conflicts.length}</span>` : ''}
|
||||
${r.push_conflict ? `<span class="badge b-conflict">推送冲突</span>` : ''}
|
||||
${r.running && r.last_sync && isStale(r.last_sync.time) ? '<span class="badge b-stale">久未同步</span>' : ''}
|
||||
</div>
|
||||
<div class="last">上次同步:${esc(last)}</div>
|
||||
<div class="last" title="${r.last_sync ? esc(r.last_sync.time) : ''}">上次同步:${esc(last)}</div>
|
||||
${r.last_sync && r.last_sync.detail ? `<div class="last-detail" title="${esc(r.last_sync.detail)}">${esc(r.last_sync.detail)}</div>` : ''}
|
||||
${r.status === 'error' && r.last_error ? `<div class="err" title="${esc(r.last_error)}">${esc(r.last_error)}</div>` : ''}
|
||||
<div class="card-btns">
|
||||
@@ -111,7 +137,7 @@ function connect() {
|
||||
if (current === msg.repo && !$('#tab-diff').hidden) loadDiff(msg.repo);
|
||||
} else if (msg.type === 'log') {
|
||||
appendLiveLine(msg);
|
||||
if (current === msg.repo && !$('#tab-log').hidden) appendLogLine(`[${msg.time}] ${msg.msg}`);
|
||||
if (current === msg.repo && !$('#tab-log').hidden) appendLogLine(`[${msg.time}] [${msg.level || 'info'}] ${msg.msg}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -159,8 +185,15 @@ async function seedLive() {
|
||||
const d = await api(`/api/repos/${name}/log?lines=8`);
|
||||
if (d.lines && d.lines.length) {
|
||||
d.lines.forEach(t => {
|
||||
const m = t.match(/^\[(.+?)\] (.*)$/);
|
||||
appendLiveLine({ time: m ? m[1] : '', repo: name, msg: m ? m[2] : t, level: 'info', seed: true });
|
||||
// 兼容新格式 [时间] [级别] 消息 与历史格式 [时间] 消息
|
||||
const m = t.match(/^\[(.+?)\]\s*(?:\[(info|warn|error)\]\s*)?(.*)$/i);
|
||||
appendLiveLine({
|
||||
time: m ? m[1] : '',
|
||||
repo: name,
|
||||
msg: m ? m[3] : t,
|
||||
level: m && m[2] ? m[2].toLowerCase() : 'info',
|
||||
seed: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (_) { /* 忽略 */ }
|
||||
@@ -250,22 +283,52 @@ $('#m-close').addEventListener('click', closeModal);
|
||||
$('#modal').addEventListener('click', (e) => { if (e.target === $('#modal')) closeModal(); });
|
||||
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); });
|
||||
|
||||
// ---------- 日志 ----------
|
||||
// ---------- 日志(级别过滤 + 关键词搜索) ----------
|
||||
let logLines = [];
|
||||
let logLevel = 'all';
|
||||
let logQuery = '';
|
||||
|
||||
// 落盘格式:[时间] [级别] 消息;历史日志无级别前缀时按 info 处理
|
||||
function logLevelOf(line) {
|
||||
const m = /^\[[^\]]+\]\s*\[(info|warn|error)\]/i.exec(line);
|
||||
return m ? m[1].toLowerCase() : 'info';
|
||||
}
|
||||
function renderLog() {
|
||||
const box = $('#logbox');
|
||||
const q = logQuery.trim().toLowerCase();
|
||||
const out = logLines.filter(l =>
|
||||
(logLevel === 'all' || logLevelOf(l) === logLevel) &&
|
||||
(!q || l.toLowerCase().includes(q))
|
||||
);
|
||||
if (!logLines.length) box.textContent = '(暂无日志)';
|
||||
else if (!out.length) box.textContent = '(当前过滤条件下无匹配行)';
|
||||
else box.textContent = out.join('\n');
|
||||
box.scrollTop = box.scrollHeight;
|
||||
}
|
||||
function loadLog(name) {
|
||||
$('#logbox').textContent = '加载中…';
|
||||
api(`/api/repos/${name}/log?lines=500`).then(d => {
|
||||
$('#logbox').textContent = d.lines && d.lines.length ? d.lines.join('\n') : '(暂无日志)';
|
||||
$('#logbox').scrollTop = $('#logbox').scrollHeight;
|
||||
}).catch(err => { $('#logbox').textContent = '日志加载失败:' + err.message; });
|
||||
logLines = d.lines && d.lines.length ? d.lines : [];
|
||||
renderLog();
|
||||
}).catch(err => { logLines = []; $('#logbox').textContent = '日志加载失败:' + err.message; });
|
||||
}
|
||||
|
||||
function appendLogLine(line) {
|
||||
const el = $('#logbox');
|
||||
if (el.textContent === '(暂无日志)') el.textContent = '';
|
||||
el.textContent += (el.textContent ? '\n' : '') + line;
|
||||
el.scrollTop = el.scrollHeight;
|
||||
logLines.push(line);
|
||||
if (logLines.length > 2000) logLines.shift();
|
||||
renderLog();
|
||||
}
|
||||
|
||||
$('#log-level').addEventListener('change', (e) => { logLevel = e.target.value; renderLog(); });
|
||||
$('#log-search').addEventListener('input', (e) => { logQuery = e.target.value; renderLog(); });
|
||||
$('#btn-log-clear').addEventListener('click', () => {
|
||||
logLevel = 'all';
|
||||
logQuery = '';
|
||||
$('#log-level').value = 'all';
|
||||
$('#log-search').value = '';
|
||||
renderLog();
|
||||
});
|
||||
|
||||
// ---------- 提交历史 ----------
|
||||
function loadHistory(name) {
|
||||
const list = $('#commit-list');
|
||||
|
||||
Reference in New Issue
Block a user