Add server-side /api/rescan upload pipeline and Home UI.
Accept ZIP/single help files, process with help_processor into OUTPUT_DIR+Postgres, pollable jobs, optional RESCAN_TOKEN. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -195,12 +195,23 @@
|
||||
|
||||
.sep { color: var(--border); margin: 0 4px; }
|
||||
|
||||
.home-wrap { display: flex; justify-content: center; align-items: flex-start; padding: 24px; }
|
||||
.home-wrap { display: flex; flex-direction: column; align-items: center; gap: 20px; padding: 24px; }
|
||||
.home-wrap img {
|
||||
max-width: 100%; max-height: calc(100vh - 200px);
|
||||
border: 1px solid var(--border); border-radius: var(--radius-lg);
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,.06);
|
||||
max-width: min(920px, 100%); height: auto; border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border); box-shadow: 0 8px 24px rgba(0,0,0,.08);
|
||||
}
|
||||
.rescan-box {
|
||||
width: min(640px, 100%);
|
||||
background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius-lg);
|
||||
padding: 16px 18px;
|
||||
}
|
||||
.rescan-box h3 {
|
||||
font-family: var(--mono); font-size: 12px; color: var(--muted);
|
||||
letter-spacing: .06em; text-transform: uppercase; margin-bottom: 12px;
|
||||
}
|
||||
.rescan-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 10px; }
|
||||
.rescan-box input[type=file] { font-size: 12px; }
|
||||
.rescan-status { font-family: var(--mono); font-size: 12px; color: var(--muted); min-height: 1.2em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -222,7 +233,18 @@
|
||||
|
||||
{% if home_url %}
|
||||
<div id="tab-home" class="panel active">
|
||||
<div class="home-wrap"><img src="{{ home_url }}" alt="Home"></div>
|
||||
<div class="home-wrap">
|
||||
<img src="{{ home_url }}" alt="Home">
|
||||
<div class="rescan-box">
|
||||
<h3>Rescan — качи help файлове</h3>
|
||||
<div class="rescan-row">
|
||||
<input type="file" id="rescan-file" accept=".zip,.docx,.doc,.html,.htm,.pdf,.txt">
|
||||
<label class="stats"><input type="checkbox" id="rescan-force"> force</label>
|
||||
<button class="btn-primary" id="rescan-btn" onclick="startRescan()">Старт</button>
|
||||
</div>
|
||||
<div class="rescan-status" id="rescan-status">ZIP или един файл → обработка на сървъра → БД + Output</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -307,6 +329,58 @@ function switchTab(name) {
|
||||
if (name === 'generator') renderGenerator();
|
||||
}
|
||||
|
||||
async function startRescan() {
|
||||
const inp = document.getElementById('rescan-file');
|
||||
const st = document.getElementById('rescan-status');
|
||||
const btn = document.getElementById('rescan-btn');
|
||||
if (!inp.files || !inp.files[0]) {
|
||||
st.textContent = 'Избери файл първо.';
|
||||
return;
|
||||
}
|
||||
const fd = new FormData();
|
||||
fd.append('file', inp.files[0]);
|
||||
fd.append('prefix', {{ (prefix or 'RIP') | tojson }});
|
||||
fd.append('force', document.getElementById('rescan-force').checked ? 'true' : 'false');
|
||||
const token = localStorage.getItem('RESCAN_TOKEN') || '';
|
||||
if (token) fd.append('token', token);
|
||||
|
||||
btn.disabled = true;
|
||||
st.textContent = 'Качване...';
|
||||
try {
|
||||
const headers = {};
|
||||
if (token) headers['X-Rescan-Token'] = token;
|
||||
const res = await fetch('/api/rescan', { method: 'POST', body: fd, headers });
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok) throw new Error(data.detail || ('HTTP ' + res.status));
|
||||
st.textContent = 'Job ' + data.job_id + ' — обработка...';
|
||||
await pollRescan(data.job_id);
|
||||
} catch (e) {
|
||||
st.textContent = 'Грешка: ' + e.message;
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function pollRescan(jobId) {
|
||||
const st = document.getElementById('rescan-status');
|
||||
for (let i = 0; i < 180; i++) {
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
const res = await fetch('/api/rescan/' + encodeURIComponent(jobId));
|
||||
const data = await res.json();
|
||||
st.textContent = data.status + (data.message ? ' — ' + data.message : '');
|
||||
if (data.status === 'done') {
|
||||
st.textContent = 'Готово. Презареждане...';
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
if (data.status === 'error') {
|
||||
st.textContent = 'Грешка: ' + (data.message || 'unknown');
|
||||
return;
|
||||
}
|
||||
}
|
||||
st.textContent = 'Timeout — провери /api/rescan/' + jobId;
|
||||
}
|
||||
|
||||
// ── EDITOR ────────────────────────────
|
||||
function renderEditor(rows) {
|
||||
const tbody = document.getElementById('editor-body');
|
||||
|
||||
Reference in New Issue
Block a user