Compare commits
2
Commits
19099e5267
...
7c9be4b44b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c9be4b44b | ||
|
|
1bf1f72901 |
@@ -102,4 +102,7 @@ $RECYCLE.BIN/
|
||||
#logs
|
||||
logs/*
|
||||
|
||||
# 运行时生成的统计/日志(避免自动提交把运行产物入库)
|
||||
stats/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/code,go,linux,macos,windows
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,7 @@ function card(r) {
|
||||
${r.conflicts && r.conflicts.length ? `<span class="badge b-conflict">冲突 ${r.conflicts.length}</span>` : ''}
|
||||
</div>
|
||||
<div class="last">上次同步:${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">
|
||||
<button class="btn" data-act="sync" ${r.running ? '' : 'disabled'}>立即同步</button>
|
||||
@@ -106,6 +107,7 @@ function connect() {
|
||||
if (i >= 0) repos[i] = msg.data; else repos.push(msg.data);
|
||||
render();
|
||||
} else if (msg.type === 'log') {
|
||||
appendLiveLine(msg);
|
||||
if (current === msg.repo && !$('#tab-log').hidden) appendLogLine(`[${msg.time}] ${msg.msg}`);
|
||||
}
|
||||
};
|
||||
@@ -115,8 +117,52 @@ function setConn(ok) {
|
||||
const el = $('#conn');
|
||||
el.textContent = ok ? '● 已连接' : '● 未连接';
|
||||
el.className = 'conn ' + (ok ? 'on' : 'off');
|
||||
const live = $('#live-conn');
|
||||
if (live) {
|
||||
live.textContent = ok ? '● 已连接' : '● 未连接';
|
||||
live.className = 'conn ' + (ok ? 'on' : 'off');
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 全局实时活动面板 ----------
|
||||
const LIVE_MAX = 400;
|
||||
function appendLiveLine(msg) {
|
||||
const box = $('#live-log');
|
||||
if (!box) return;
|
||||
let level = 'info';
|
||||
if (msg.level === 'error') level = 'err';
|
||||
else if (msg.level === 'warn') level = 'warn';
|
||||
const line = document.createElement('div');
|
||||
line.className = 'live-line lv-' + level;
|
||||
line.innerHTML = `<span class="ll-time">${esc(msg.time)}</span><span class="ll-repo">${esc(msg.repo)}</span><span class="ll-msg">${esc(msg.msg)}</span>`;
|
||||
box.appendChild(line);
|
||||
while (box.childElementCount > LIVE_MAX) box.removeChild(box.firstChild);
|
||||
box.scrollTop = box.scrollHeight;
|
||||
}
|
||||
|
||||
function clearLive() {
|
||||
const box = $('#live-log');
|
||||
if (box) box.innerHTML = '';
|
||||
}
|
||||
|
||||
// 载入各仓库最近日志作为初始上下文,避免面板一开始空白
|
||||
async function seedLive() {
|
||||
const names = repos.map(r => r.name);
|
||||
await Promise.all(names.map(async (name) => {
|
||||
try {
|
||||
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 });
|
||||
});
|
||||
}
|
||||
} catch (_) { /* 忽略 */ }
|
||||
}));
|
||||
}
|
||||
|
||||
$('#btn-live-clear').addEventListener('click', clearLive);
|
||||
|
||||
// ---------- 登录 ----------
|
||||
function showLogin() {
|
||||
const el = $('#login');
|
||||
@@ -411,6 +457,7 @@ async function refresh() {
|
||||
const d = await api('/api/status');
|
||||
repos = d.repos || [];
|
||||
render();
|
||||
seedLive();
|
||||
} catch (e) { /* 忽略,SSE 会重试 */ }
|
||||
}
|
||||
|
||||
|
||||
+17
-6
@@ -17,12 +17,23 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div id="grid" class="grid"></div>
|
||||
<div id="empty" class="empty" hidden>
|
||||
<p>还没有任何仓库配置。</p>
|
||||
<button class="btn btn-primary" id="btn-empty-new">创建第一个配置</button>
|
||||
</div>
|
||||
<main class="layout">
|
||||
<section class="main-col">
|
||||
<div id="grid" class="grid"></div>
|
||||
<div id="empty" class="empty" hidden>
|
||||
<p>还没有任何仓库配置。</p>
|
||||
<button class="btn btn-primary" id="btn-empty-new">创建第一个配置</button>
|
||||
</div>
|
||||
</section>
|
||||
<aside id="live" class="live">
|
||||
<div class="live-head">
|
||||
<h2>实时活动</h2>
|
||||
<span id="live-conn" class="conn">连接中…</span>
|
||||
<button class="btn btn-sm" id="btn-live-clear" title="清空本面板">清空</button>
|
||||
</div>
|
||||
<div class="live-hint">所有仓库的同步(手动与自动)实时显示在此,无需点进详情</div>
|
||||
<div id="live-log" class="live-log"></div>
|
||||
</aside>
|
||||
</main>
|
||||
|
||||
<div id="modal" class="modal" hidden>
|
||||
|
||||
+28
-2
@@ -23,10 +23,36 @@ header { display: flex; justify-content: space-between; align-items: center; pad
|
||||
.btn-danger:hover { background: var(--red-bg); }
|
||||
.btn-icon { border: none; background: none; font-size: 18px; cursor: pointer; color: var(--muted); }
|
||||
|
||||
main { max-width: 1200px; margin: 24px auto; padding: 0 24px; }
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
|
||||
main { max-width: 1320px; margin: 24px auto; padding: 0 24px; }
|
||||
.layout { display: flex; gap: 20px; align-items: flex-start; }
|
||||
.main-col { flex: 1; min-width: 0; }
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; }
|
||||
.empty { text-align: center; color: var(--muted); padding: 80px 0; }
|
||||
|
||||
/* 全局实时活动面板 */
|
||||
.live { width: 360px; flex: none; position: sticky; top: 80px; background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); display: flex; flex-direction: column; max-height: calc(100vh - 104px); }
|
||||
.live-head { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--border); }
|
||||
.live-head h2 { margin: 0; font-size: 15px; font-weight: 500; flex: 1; }
|
||||
.live-head .conn { margin: 0; }
|
||||
.btn-sm { border: 1px solid var(--border); background: #fff; border-radius: 7px; padding: 3px 10px; font-size: 12px; cursor: pointer; color: var(--muted); }
|
||||
.btn-sm:hover { border-color: #b6bcc4; background: #f3f4f6; }
|
||||
.live-hint { font-size: 11px; color: var(--muted); padding: 8px 14px; border-bottom: 1px solid var(--border); }
|
||||
.live-log { flex: 1; overflow-y: auto; padding: 8px 10px; font-family: Consolas, "Cascadia Code", monospace; font-size: 11.5px; line-height: 1.5; }
|
||||
.live-line { display: flex; gap: 6px; padding: 2px 0; border-bottom: 1px solid #f2f2f2; }
|
||||
.live-line .ll-time { color: #9aa0a6; flex: none; }
|
||||
.live-line .ll-repo { color: var(--blue); flex: none; font-weight: 600; max-width: 96px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.live-line .ll-msg { color: var(--text); min-width: 0; word-break: break-word; }
|
||||
.live-line.lv-err .ll-msg { color: var(--red); }
|
||||
.live-line.lv-warn .ll-msg { color: var(--amber); }
|
||||
.live-line.lv-err .ll-repo, .live-line.lv-warn .ll-repo { color: #b06a00; }
|
||||
|
||||
.last-detail { font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.layout { flex-direction: column; }
|
||||
.live { width: 100%; position: static; max-height: 360px; }
|
||||
}
|
||||
|
||||
.card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; display: flex; flex-direction: column; gap: 8px; }
|
||||
.card.st-conflict { border-color: #e8c98a; background: #fffdf6; }
|
||||
.card.st-error { border-color: #eec9c9; }
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
timestamp,action,result,commits
|
||||
2026-09-08 17:12:28,push,failed,1
|
||||
2026-09-08 17:19:24,push,failed,2
|
||||
2026-09-08 17:20:00,push,failed,3
|
||||
2026-09-08 17:20:42,push,failed,4
|
||||
2026-09-08 17:21:58,push,failed,5
|
||||
2026-09-08 17:23:16,push,failed,6
|
||||
2026-09-08 17:24:33,push,failed,7
|
||||
2026-09-08 17:25:49,push,failed,8
|
||||
2026-09-08 17:27:05,push,failed,9
|
||||
2026-09-08 17:28:21,push,failed,10
|
||||
2026-09-08 17:30:42,push,failed,11
|
||||
2026-09-08 17:31:58,push,failed,12
|
||||
2026-09-08 17:33:14,push,failed,13
|
||||
2026-09-08 17:34:34,push,failed,14
|
||||
2026-09-08 17:35:48,push,failed,15
|
||||
2026-09-08 17:52:48,push,failed,17
|
||||
2026-09-08 17:54:49,push,failed,18
|
||||
2026-09-08 17:57:07,push,failed,19
|
||||
2026-09-08 18:01:06,push,failed,20
|
||||
2026-09-08 18:07:32,push,failed,21
|
||||
2026-09-08 18:13:59,push,failed,22
|
||||
2026-09-08 18:20:25,push,failed,23
|
||||
2026-09-08 18:26:59,push,failed,24
|
||||
2026-09-08 18:33:24,push,failed,25
|
||||
2026-09-08 18:39:49,push,failed,26
|
||||
2026-09-08 18:45:45,push,failed,27
|
||||
2026-09-08 18:47:20,push,failed,28
|
||||
2026-09-08 18:47:38,push,failed,29
|
||||
2026-09-08 18:47:55,push,failed,30
|
||||
2026-09-08 18:48:39,push,failed,31
|
||||
2026-09-08 18:50:15,push,failed,32
|
||||
2026-09-08 18:50:30,push,failed,33
|
||||
2026-09-08 18:51:42,push,failed,35
|
||||
2026-09-08 18:52:56,push,failed,36
|
||||
2026-09-08 18:54:09,push,failed,37
|
||||
2026-09-08 18:55:22,push,failed,38
|
||||
2026-09-08 19:13:34,push,success,1
|
||||
2026-09-08 19:14:30,push,success,2
|
||||
2026-09-08 19:14:45,push,success,1
|
||||
2026-09-08 19:15:10,push,success,1
|
||||
2026-09-08 19:18:15,push,success,1
|
||||
2026-09-08 19:20:39,push,success,1
|
||||
2026-09-08 19:21:26,push,success,1
|
||||
2026-09-08 19:27:43,push,success,1
|
||||
2026-09-08 19:33:57,push,success,1
|
||||
2026-09-08 19:40:15,push,success,1
|
||||
2026-09-08 19:46:30,push,success,1
|
||||
2026-09-08 19:52:44,push,success,1
|
||||
2026-09-08 19:59:00,push,success,1
|
||||
2026-09-08 20:05:17,push,success,1
|
||||
2026-09-08 20:11:33,push,success,1
|
||||
2026-09-08 20:17:49,push,success,1
|
||||
2026-09-08 20:24:03,push,success,1
|
||||
2026-09-08 20:30:19,push,success,1
|
||||
2026-09-08 20:36:33,push,success,1
|
||||
2026-09-08 20:42:50,push,success,1
|
||||
2026-09-08 20:49:05,push,success,1
|
||||
2026-09-08 20:55:25,push,success,1
|
||||
2026-09-08 21:01:40,push,success,1
|
||||
2026-09-08 21:07:56,push,success,1
|
||||
2026-09-08 21:14:10,push,success,1
|
||||
2026-09-08 21:20:26,push,success,1
|
||||
2026-09-08 21:26:51,push,success,1
|
||||
2026-09-08 21:33:08,push,success,1
|
||||
2026-09-08 21:39:24,push,success,1
|
||||
2026-09-08 21:45:40,push,success,1
|
||||
2026-09-08 21:51:56,push,success,1
|
||||
2026-09-08 21:58:26,push,success,1
|
||||
2026-09-08 22:04:42,push,success,1
|
||||
2026-09-08 22:10:59,push,success,1
|
||||
2026-09-08 22:17:19,push,success,1
|
||||
2026-09-08 22:23:34,push,success,1
|
||||
2026-09-08 22:29:54,push,success,1
|
||||
2026-09-08 22:36:10,push,success,1
|
||||
2026-09-08 22:42:29,push,success,1
|
||||
2026-09-08 22:48:45,push,success,1
|
||||
2026-09-08 22:55:01,push,success,1
|
||||
2026-09-08 23:01:16,push,success,1
|
||||
2026-09-08 23:07:31,push,success,1
|
||||
2026-09-08 23:13:46,push,success,1
|
||||
2026-09-08 23:20:05,push,success,1
|
||||
2026-09-08 23:26:20,push,success,1
|
||||
2026-09-08 23:32:35,push,success,1
|
||||
2026-09-08 23:38:53,push,success,1
|
||||
2026-09-08 23:45:09,push,success,1
|
||||
2026-09-08 23:51:28,push,success,1
|
||||
2026-09-08 23:57:44,push,success,1
|
||||
2026-09-09 00:03:59,push,success,1
|
||||
2026-09-09 00:10:16,push,success,1
|
||||
2026-09-09 00:16:31,push,success,1
|
||||
2026-09-09 00:22:46,push,success,1
|
||||
2026-09-09 00:29:03,push,success,1
|
||||
2026-09-09 00:35:32,push,success,1
|
||||
2026-09-09 00:41:49,push,success,1
|
||||
2026-09-09 00:48:06,push,success,1
|
||||
2026-09-09 00:54:23,push,success,1
|
||||
2026-09-09 01:00:39,push,success,1
|
||||
2026-09-09 01:06:54,push,success,1
|
||||
2026-09-09 01:13:11,push,success,1
|
||||
2026-09-09 01:19:26,push,success,1
|
||||
2026-09-09 01:25:44,push,success,1
|
||||
2026-09-09 01:31:58,push,success,1
|
||||
2026-09-09 01:38:15,push,success,1
|
||||
2026-09-09 01:44:29,push,success,1
|
||||
2026-09-09 01:50:43,push,success,1
|
||||
2026-09-09 01:57:00,push,success,1
|
||||
2026-09-09 02:03:14,push,success,1
|
||||
2026-09-09 02:09:29,push,success,1
|
||||
2026-09-09 02:15:58,push,success,1
|
||||
2026-09-09 02:22:15,push,success,1
|
||||
2026-09-09 02:28:31,push,success,1
|
||||
2026-09-09 02:34:47,push,success,1
|
||||
2026-09-09 02:41:01,push,success,1
|
||||
2026-09-09 02:53:22,push,success,1
|
||||
2026-09-09 02:59:36,push,success,1
|
||||
2026-09-09 03:05:51,push,success,1
|
||||
2026-09-09 03:12:04,push,success,1
|
||||
2026-09-09 03:18:20,push,success,1
|
||||
2026-09-09 03:24:34,push,success,1
|
||||
2026-09-09 03:30:47,push,success,1
|
||||
2026-09-09 03:37:01,push,success,1
|
||||
2026-09-09 03:43:15,push,success,1
|
||||
2026-09-09 03:49:31,push,success,1
|
||||
2026-09-09 03:55:45,push,success,1
|
||||
2026-09-09 04:02:00,push,success,1
|
||||
2026-09-09 04:08:14,push,success,1
|
||||
2026-09-09 04:14:29,push,success,1
|
||||
2026-09-09 04:20:45,push,success,1
|
||||
2026-09-09 04:27:00,push,success,1
|
||||
2026-09-09 04:33:14,push,success,1
|
||||
2026-09-09 04:39:28,push,success,1
|
||||
2026-09-09 04:45:43,push,success,1
|
||||
2026-09-09 04:51:57,push,success,1
|
||||
2026-09-09 04:58:11,push,success,1
|
||||
2026-09-09 05:04:24,push,success,1
|
||||
2026-09-09 05:10:38,push,success,1
|
||||
2026-09-09 05:16:51,push,success,1
|
||||
2026-09-09 05:23:06,push,success,1
|
||||
2026-09-09 05:29:20,push,success,1
|
||||
2026-09-09 05:35:33,push,success,1
|
||||
2026-09-09 05:41:47,push,success,1
|
||||
2026-09-09 05:48:01,push,success,1
|
||||
2026-09-09 05:54:16,push,success,1
|
||||
2026-09-09 06:00:30,push,success,1
|
||||
2026-09-09 06:06:44,push,success,1
|
||||
2026-09-09 06:12:59,push,success,1
|
||||
2026-09-09 06:19:13,push,success,1
|
||||
2026-09-09 06:25:26,push,success,1
|
||||
2026-09-09 06:31:39,push,success,1
|
||||
2026-09-09 06:37:53,push,success,1
|
||||
2026-09-09 06:44:07,push,success,1
|
||||
2026-09-09 06:50:21,push,success,1
|
||||
2026-09-09 06:56:37,push,success,1
|
||||
2026-09-09 07:02:51,push,success,1
|
||||
2026-09-09 07:09:06,push,success,1
|
||||
2026-09-09 07:15:20,push,success,1
|
||||
2026-09-09 07:21:33,push,success,1
|
||||
2026-09-09 07:27:48,push,success,1
|
||||
2026-09-09 07:34:01,push,success,1
|
||||
2026-09-09 07:40:15,push,success,1
|
||||
2026-09-09 07:46:28,push,success,1
|
||||
2026-09-09 07:52:42,push,success,1
|
||||
2026-09-09 07:58:55,push,success,1
|
||||
2026-09-09 08:05:11,push,success,1
|
||||
2026-09-09 08:11:26,push,success,1
|
||||
2026-09-09 08:17:40,push,success,1
|
||||
2026-09-09 08:23:53,push,success,1
|
||||
2026-09-09 08:30:07,push,success,1
|
||||
2026-09-09 08:36:21,push,success,1
|
||||
2026-09-09 08:42:35,push,success,1
|
||||
2026-09-09 08:49:00,push,success,1
|
||||
2026-09-09 08:55:14,push,success,1
|
||||
2026-09-09 09:01:26,push,success,1
|
||||
2026-09-09 09:07:40,push,success,1
|
||||
2026-09-09 09:13:54,push,success,1
|
||||
2026-09-09 09:20:08,push,success,1
|
||||
2026-09-09 09:26:22,push,success,1
|
||||
2026-09-09 09:32:46,push,success,1
|
||||
2026-09-09 09:38:59,push,success,1
|
||||
2026-09-09 09:45:16,push,success,1
|
||||
2026-09-09 09:51:29,push,success,1
|
||||
2026-09-09 09:57:46,push,success,1
|
||||
2026-09-09 10:04:00,push,success,1
|
||||
|
Reference in New Issue
Block a user