Fix worker logs not appearing + align settings form
Docker Build and Push / check-secrets (push) Successful in 5s
Docker Build and Push / build-and-push (cpu, latest) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.6) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 11.8) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.8) (push) Has been skipped
Docker Build and Push / build-and-push (directml, latest) (push) Has been skipped
Build Windows CPU / build (push) Has been cancelled
Build Windows CUDA 11.8 / build (push) Has been cancelled
Build Windows CUDA 12.6 / build (push) Has been cancelled
Build Windows CUDA 12.8 / build (push) Has been cancelled
Build Windows DirectML / build (push) Has been cancelled
Docker Build and Push / check-secrets (push) Successful in 5s
Docker Build and Push / build-and-push (cpu, latest) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.6) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 11.8) (push) Has been skipped
Docker Build and Push / build-and-push (cuda, 12.8) (push) Has been skipped
Docker Build and Push / build-and-push (directml, latest) (push) Has been skipped
Build Windows CPU / build (push) Has been cancelled
Build Windows CUDA 11.8 / build (push) Has been cancelled
Build Windows CUDA 12.6 / build (push) Has been cancelled
Build Windows CUDA 12.8 / build (push) Has been cancelled
Build Windows DirectML / build (push) Has been cancelled
- Add _WorkerLogHandler that bridges Python logging → WorkerLogBuffer (previously only StreamHandler was used, logs went to stdout but not the buffer the frontend polls) - Rewrite settings form: proper form-grid layout, form submit handler, App Secret show/hide toggle, hints below number fields, footer save button - workerStatus shows configured/unconfigured text instead of badge Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 4.5
parent
e512a842e4
commit
abd5d0ce47
+53
-43
@@ -561,41 +561,56 @@
|
||||
|
||||
<!-- Page: Settings -->
|
||||
<div class="page" id="page-settings">
|
||||
<div class="panel" style="margin-bottom:20px">
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>平台 Worker 配置</h2>
|
||||
<div id="workerStatus" class="badge neutral"><span class="bdot"></span>未运行</div>
|
||||
<span id="workerStatus" class="subtle">○ 未配置</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-grid">
|
||||
<div class="field full">
|
||||
<label>平台地址 (Base URL)</label>
|
||||
<input type="url" id="s-base-url" placeholder="https://your-platform.com" />
|
||||
</div>
|
||||
<form id="settings-form" onsubmit="saveSettings(event)">
|
||||
<div class="panel-body" style="display:grid;gap:16px">
|
||||
<div class="field">
|
||||
<label>App ID</label>
|
||||
<input type="text" id="s-app-id" placeholder="your_app_id" autocomplete="off" />
|
||||
<label for="s-base-url">平台地址(Base URL)</label>
|
||||
<input type="url" id="s-base-url" name="platform_base_url"
|
||||
placeholder="https://your-platform.com" autocomplete="off" />
|
||||
<span class="hint">留空则不启动 Worker 轮询,仅保留手动提交任务的能力。</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>App Secret</label>
|
||||
<input type="text" id="s-app-secret" placeholder="your_app_secret" autocomplete="new-password" />
|
||||
<div class="form-grid">
|
||||
<div class="field">
|
||||
<label for="s-app-id">App ID</label>
|
||||
<input type="text" id="s-app-id" name="platform_app_id"
|
||||
placeholder="your_app_id" autocomplete="off" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="s-app-secret">App Secret</label>
|
||||
<div style="display:flex;gap:8px">
|
||||
<input type="password" id="s-app-secret" name="platform_app_secret"
|
||||
placeholder="your_app_secret" autocomplete="new-password" style="flex:1" />
|
||||
<button type="button" id="toggle-secret" class="button secondary"
|
||||
style="min-height:40px;padding:0 12px;font-size:12px;flex-shrink:0"
|
||||
onclick="toggleSecret()">显示</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>并发槽数 (Concurrency)</label>
|
||||
<input type="number" id="s-concurrency" min="0" max="8" value="1" />
|
||||
<span class="hint">0 = 关闭平台拉取,仅保留手动提交</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>轮询间隔(秒)</label>
|
||||
<input type="number" id="s-poll-interval" min="1" max="60" value="5" />
|
||||
<div class="form-grid">
|
||||
<div class="field">
|
||||
<label for="s-concurrency">并发槽数(Concurrency)</label>
|
||||
<input type="number" id="s-concurrency" name="worker_concurrency"
|
||||
min="0" max="8" value="1" />
|
||||
<span class="hint">同时认领的任务数。设为 0 表示不启动 Worker 轮询。</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="s-poll-interval">轮询间隔(秒)</label>
|
||||
<input type="number" id="s-poll-interval" name="worker_poll_interval"
|
||||
min="1" max="60" value="5" />
|
||||
<span class="hint">每隔多少秒向平台认领一次任务。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top:16px;display:flex;gap:10px">
|
||||
<button class="button" onclick="saveSettings()">保存并重启 Worker</button>
|
||||
<div style="padding:14px 18px;border-top:1px solid var(--line);display:flex;justify-content:flex-end">
|
||||
<button type="submit" class="button">保存并重启 Worker</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 页面:Worker 日志 -->
|
||||
@@ -1089,20 +1104,15 @@ async function loadSettings() {
|
||||
document.getElementById('s-app-secret').value = d.platform_app_secret || '';
|
||||
document.getElementById('s-concurrency').value = d.worker_concurrency ?? 1;
|
||||
document.getElementById('s-poll-interval').value = d.worker_poll_interval ?? 5;
|
||||
|
||||
const status = await api('/api/status');
|
||||
const configured = !!(d.platform_base_url && d.platform_app_id && d.platform_app_secret);
|
||||
const el = document.getElementById('workerStatus');
|
||||
if (status.worker_running) {
|
||||
el.className = 'badge good';
|
||||
el.innerHTML = '<span class="bdot"></span>运行中';
|
||||
} else {
|
||||
el.className = 'badge neutral';
|
||||
el.innerHTML = '<span class="bdot"></span>未运行';
|
||||
}
|
||||
el.textContent = configured ? '● Worker 已配置' : '○ 未配置';
|
||||
el.style.color = configured ? 'var(--good)' : 'var(--muted)';
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
async function saveSettings() {
|
||||
async function saveSettings(event) {
|
||||
if (event) event.preventDefault();
|
||||
const body = {
|
||||
platform_base_url: document.getElementById('s-base-url').value.trim(),
|
||||
platform_app_id: document.getElementById('s-app-id').value.trim(),
|
||||
@@ -1112,18 +1122,18 @@ async function saveSettings() {
|
||||
};
|
||||
try {
|
||||
const r = await api('/api/settings', { method: 'PUT', body: JSON.stringify(body) });
|
||||
const el = document.getElementById('workerStatus');
|
||||
if (r.worker_running) {
|
||||
el.className = 'badge good';
|
||||
el.innerHTML = '<span class="bdot"></span>运行中';
|
||||
} else {
|
||||
el.className = 'badge neutral';
|
||||
el.innerHTML = '<span class="bdot"></span>未运行';
|
||||
}
|
||||
showToast(r.worker_running ? 'Worker 已启动' : '设置已保存(Worker 未启动)');
|
||||
await loadSettings();
|
||||
} catch(e) { showToast('保存失败: ' + e.message); }
|
||||
}
|
||||
|
||||
function toggleSecret() {
|
||||
const inp = document.getElementById('s-app-secret');
|
||||
const show = inp.type === 'password';
|
||||
inp.type = show ? 'text' : 'password';
|
||||
document.getElementById('toggle-secret').textContent = show ? '隐藏' : '显示';
|
||||
}
|
||||
|
||||
// ── Worker 日志轮询(同 df-autotest)──────────────────────────
|
||||
const LEVEL_CLS = { WARNING: 'll-WARNING', ERROR: 'll-ERROR', CRITICAL: 'll-CRITICAL', DEBUG: 'll-DEBUG' };
|
||||
|
||||
|
||||
+15
-2
@@ -609,6 +609,19 @@ class WorkerLogBuffer:
|
||||
return lines, (lines[-1]["seq"] if lines else seq)
|
||||
|
||||
|
||||
class _WorkerLogHandler(logging.Handler):
|
||||
"""把 Python logging 桥接到 WorkerLogBuffer,供前端轮询。"""
|
||||
def __init__(self, buf: WorkerLogBuffer) -> None:
|
||||
super().__init__()
|
||||
self._buf = buf
|
||||
|
||||
def emit(self, record: logging.LogRecord) -> None:
|
||||
try:
|
||||
self._buf.push(record.levelname, self.format(record))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class VSRTaskWorker:
|
||||
"""轮询外部平台认领 VSR 任务,执行后上报结果。"""
|
||||
|
||||
@@ -773,8 +786,8 @@ class WorkerManager:
|
||||
def __init__(self) -> None:
|
||||
self._tasks: list[asyncio.Task] = []
|
||||
self.log_buffer = WorkerLogBuffer()
|
||||
_handler = logging.StreamHandler()
|
||||
_handler.setFormatter(logging.Formatter("%(asctime)s [vsr_worker] %(message)s", datefmt="%H:%M:%S"))
|
||||
_handler = _WorkerLogHandler(self.log_buffer)
|
||||
_handler.setFormatter(logging.Formatter("%(asctime)s %(message)s", datefmt="%H:%M:%S"))
|
||||
_wl = logging.getLogger(__name__)
|
||||
_wl.setLevel(logging.DEBUG)
|
||||
_wl.addHandler(_handler)
|
||||
|
||||
Reference in New Issue
Block a user