.
This commit is contained in:
@@ -3,6 +3,7 @@ setlocal EnableExtensions EnableDelayedExpansion
|
||||
chcp 65001 > nul
|
||||
rem Същите ключове като GET /healthz: ok, version, git.
|
||||
rem Префиксът е от VERSION; SHA е от git (не може да е в commit-натия файл).
|
||||
rem Двоен клик / без аргументи — pause. От терминал: show-version.bat -q
|
||||
|
||||
set "ROOT=%~dp0"
|
||||
if "%ROOT:~-1%"=="\" set "ROOT=%ROOT:~0,-1%"
|
||||
@@ -14,11 +15,18 @@ if exist "%ROOT%\VERSION" (
|
||||
)
|
||||
|
||||
set "GIT_SHA=dev"
|
||||
for /f "usebackq delims=" %%I in (`git rev-parse --short=7 HEAD 2^>nul`) do set "GIT_SHA=%%I"
|
||||
set "SHA_FILE=%TEMP%\rip-help-git-sha.txt"
|
||||
git rev-parse --short=7 HEAD > "%SHA_FILE%" 2>nul
|
||||
if exist "%SHA_FILE%" (
|
||||
set /p GIT_SHA=<"%SHA_FILE%"
|
||||
del "%SHA_FILE%" >nul 2>&1
|
||||
)
|
||||
if not defined GIT_SHA set "GIT_SHA=dev"
|
||||
if "!GIT_SHA!"=="" set "GIT_SHA=dev"
|
||||
|
||||
echo {"ok": true, "version": "%APP_VERSION%+!GIT_SHA!", "git": "!GIT_SHA!"}
|
||||
echo.
|
||||
echo Сравни "git" с https://rip.inex-project.net/healthz
|
||||
echo Production вижда SHA едва след commit + push + Coolify redeploy.
|
||||
echo.
|
||||
pause
|
||||
if /i not "%~1"=="-q" pause
|
||||
|
||||
@@ -216,9 +216,17 @@ def _rich_html_with_images(html: str) -> str:
|
||||
return _IMG_PLACEHOLDER_RE.sub(sub, html)
|
||||
|
||||
|
||||
def _fmt_ts(value, n: int = 19) -> str:
|
||||
return str(value)[:n] if value else ""
|
||||
|
||||
|
||||
def _row_to_dict(cols, r, *, full_text: bool = False) -> dict:
|
||||
d = dict(zip(cols, r))
|
||||
d["updated_at"] = str(d["updated_at"])[:16] if d["updated_at"] else ""
|
||||
d["updated_at"] = _fmt_ts(d.get("updated_at"), 16)
|
||||
if "created_at" in d:
|
||||
d["created_at"] = _fmt_ts(d.get("created_at"))
|
||||
if "processed_at" in d:
|
||||
d["processed_at"] = _fmt_ts(d.get("processed_at"))
|
||||
try:
|
||||
d["images"] = json.loads(d["images"]) if d.get("images") else []
|
||||
except Exception:
|
||||
@@ -251,9 +259,9 @@ def fetch_sections(
|
||||
conn = db_conn()
|
||||
cur = conn.cursor()
|
||||
sql = """
|
||||
SELECT s.prefix, s.code, s.title, s.keywords, s.char_count,
|
||||
s.source_file, s.output_path, s.updated_at,
|
||||
s.images, s.html_text, f.section_count
|
||||
SELECT s.id, s.prefix, s.code, s.title, s.keywords, s.char_count,
|
||||
s.source_file, s.output_path, s.updated_at, s.created_at,
|
||||
f.processed_at, s.images, s.html_text, f.section_count
|
||||
FROM rip_help_sections s
|
||||
LEFT JOIN rip_help_files f
|
||||
ON f.file_path = s.source_file AND f.prefix = s.prefix
|
||||
@@ -353,9 +361,9 @@ def api_section(code: str):
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT s.prefix, s.code, s.title, s.keywords, s.char_count,
|
||||
s.source_file, s.output_path, s.updated_at,
|
||||
s.images, s.html_text, f.section_count
|
||||
SELECT s.id, s.prefix, s.code, s.title, s.keywords, s.char_count,
|
||||
s.source_file, s.output_path, s.updated_at, s.created_at,
|
||||
f.processed_at, s.images, s.html_text, f.section_count
|
||||
FROM rip_help_sections s
|
||||
LEFT JOIN rip_help_files f
|
||||
ON f.file_path = s.source_file AND f.prefix = s.prefix
|
||||
|
||||
@@ -940,9 +940,42 @@ async function pollRescan(jobId) {
|
||||
}
|
||||
|
||||
// ── EDITOR ────────────────────────────
|
||||
function tsKey(v) {
|
||||
return v ? String(v) : '';
|
||||
}
|
||||
function rowId(r) {
|
||||
const n = Number(r && r.id);
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
}
|
||||
function sortEditorRows(rows) {
|
||||
const fileTs = new Map();
|
||||
const fileId = new Map();
|
||||
(rows || []).forEach(r => {
|
||||
const f = (fileBase(r.source_file) || r.source_file || '').toLowerCase();
|
||||
const t = tsKey(r.processed_at) || tsKey(r.created_at) || tsKey(r.updated_at);
|
||||
if (t > (fileTs.get(f) || '')) fileTs.set(f, t);
|
||||
const id = rowId(r);
|
||||
if (id > (fileId.get(f) || 0)) fileId.set(f, id);
|
||||
});
|
||||
return [...(rows || [])].sort((a, b) => {
|
||||
const fa = (fileBase(a.source_file) || a.source_file || '').toLowerCase();
|
||||
const fb = (fileBase(b.source_file) || b.source_file || '').toLowerCase();
|
||||
const byFileTs = (fileTs.get(fb) || '').localeCompare(fileTs.get(fa) || '');
|
||||
if (byFileTs) return byFileTs;
|
||||
const byFileId = (fileId.get(fb) || 0) - (fileId.get(fa) || 0);
|
||||
if (byFileId) return byFileId;
|
||||
const sa = tsKey(a.created_at) || tsKey(a.updated_at);
|
||||
const sb = tsKey(b.created_at) || tsKey(b.updated_at);
|
||||
const bySec = sb.localeCompare(sa);
|
||||
if (bySec) return bySec;
|
||||
return rowId(b) - rowId(a);
|
||||
});
|
||||
}
|
||||
|
||||
function renderEditor(rows) {
|
||||
const tbody = document.getElementById('editor-body');
|
||||
tbody.innerHTML = rows.map(r => `
|
||||
const sorted = sortEditorRows(rows);
|
||||
tbody.innerHTML = sorted.map(r => `
|
||||
<tr>
|
||||
<td><span class="code-badge">${r.code}</span></td>
|
||||
<td class="title-cell">${esc(r.title)}</td>
|
||||
@@ -967,7 +1000,7 @@ function renderEditor(rows) {
|
||||
<td style="font-size:11px;color:var(--muted);font-family:var(--mono);white-space:nowrap">${r.updated_at}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
document.getElementById('editor-stats').textContent = rows.length + ' реда';
|
||||
document.getElementById('editor-stats').textContent = sorted.length + ' реда';
|
||||
}
|
||||
|
||||
function filterEditor() {
|
||||
|
||||
Reference in New Issue
Block a user