This commit is contained in:
2026-09-11 14:49:03 +03:00
parent 695fca0d84
commit 1d06e8c7a4
4 changed files with 131 additions and 3 deletions

View File

@@ -81,7 +81,7 @@ def _safe_extract_zip(data: bytes, dest: Path) -> int:
return count
def _run_rescan(job_id: str, staging: Path, prefix: str, force: bool):
def _run_rescan(job_id: str, staging: Path, prefix: str, force: bool, seed_keywords: str = ""):
cfg = _cfg()
try:
if not cfg["conn"]:
@@ -114,6 +114,7 @@ def _run_rescan(job_id: str, staging: Path, prefix: str, force: bool):
force=force,
purge_missing=False,
remote_root=cfg["remote_root"],
seed_keywords=seed_keywords,
)
files = []
if isinstance(summary, dict):
@@ -208,12 +209,14 @@ async def rescan(
file: list[UploadFile] = File(..., description="ZIP, one help file, or several files from a folder"),
prefix: str = Form("RIP"),
force: str = Form("false"),
keywords: str = Form(""),
x_rescan_token: Optional[str] = Header(None, alias="X-Rescan-Token"),
token: Optional[str] = Form(None),
):
"""
Upload a .zip, one .docx/.html/.pdf/.txt/.doc, or several such files
(folder pick). Process on server, write sections to OUTPUT_DIR and Postgres.
Optional form field ``keywords``: comma-separated terms prepended to every section.
"""
_check_token(x_rescan_token or token)
@@ -232,6 +235,7 @@ async def rescan(
job_id = uuid.uuid4().hex[:12]
force_flag = str(force).lower() in ("1", "true", "yes", "on")
seed_keywords = (keywords or "").strip()
_set_job(
job_id,
status="queued",
@@ -241,7 +245,7 @@ async def rescan(
force=force_flag,
created_at=datetime.now(timezone.utc).isoformat(),
)
background_tasks.add_task(_run_rescan, job_id, staging, prefix, force_flag)
background_tasks.add_task(_run_rescan, job_id, staging, prefix, force_flag, seed_keywords)
return JSONResponse(
{
"ok": True,

View File

@@ -276,6 +276,8 @@
.rescan-filemeta.warn { color: var(--danger); }
.wipe-box { margin-top: 16px; }
.wipe-box input[type=text] { width: 100%; font-family: var(--mono); }
.rescan-kw-input { width: 100%; }
.rescan-hint { margin-bottom: 10px; line-height: 1.45; }
.src-file { cursor: pointer; }
.src-file:hover { color: var(--accent); text-decoration: underline; }
@@ -361,6 +363,15 @@
onchange="onScanFolderPicked()">
<div class="rescan-selection" id="rescan-selection"></div>
<div class="rescan-filemeta" id="rescan-filemeta"></div>
<p class="stats rescan-hint">
Ръчни ключови думи (раздели със запетая) — записват се <strong>първи</strong> във всяка секция
от това сканиране. Ако файлът вече е сканиран, включи force.
</p>
<div class="rescan-row">
<input type="text" id="rescan-keywords" class="rescan-kw-input"
placeholder="напр. лимитни карти, справка, ATR"
autocomplete="off">
</div>
<div class="rescan-row">
<label class="stats"><input type="checkbox" id="rescan-force"> force</label>
<button type="button" class="btn-primary" id="rescan-btn" onclick="startRescan()">Старт</button>
@@ -486,7 +497,7 @@
<div class="api-ep"><span class="method post">POST</span><code>/api/keywords/{code}</code>
<div class="desc">Body JSON: <code>{"keywords":"а, б, в"}</code> — обновява ключови думи.</div></div>
<div class="api-ep"><span class="method post">POST</span><code>/api/rescan</code>
<div class="desc">multipart: <code>file</code> (един или повече: zip/docx/…), <code>prefix</code>, <code>force</code>. Header опционално: <code>X-Rescan-Token</code>.</div></div>
<div class="desc">multipart: <code>file</code> (един или повече: zip/docx/…), <code>prefix</code>, <code>force</code>, по избор <code>keywords</code> (запетая; първи във всяка секция). Header опционално: <code>X-Rescan-Token</code>.</div></div>
<div class="api-ep"><span class="method">GET</span><code>/api/rescan/{job_id}</code>
<div class="desc">Статус на rescan задача (<code>queued</code> / <code>processing</code> / <code>done</code> / <code>error</code>).</div></div>
<div class="api-ep"><span class="method">GET</span><code>/api/file-extractions?file=…&amp;prefix=RIP</code>
@@ -866,6 +877,7 @@ async function startRescan() {
scanFiles.forEach(f => fd.append('file', f));
fd.append('prefix', PAGE_PREFIX);
fd.append('force', document.getElementById('rescan-force').checked ? 'true' : 'false');
fd.append('keywords', (document.getElementById('rescan-keywords').value || '').trim());
const token = localStorage.getItem('RESCAN_TOKEN') || '';
if (token) fd.append('token', token);