.
This commit is contained in:
11
Dockerfile
11
Dockerfile
@@ -15,6 +15,17 @@ COPY help_processor.py /app/help_processor.py
|
|||||||
COPY help_codes.py /app/help_codes.py
|
COPY help_codes.py /app/help_codes.py
|
||||||
COPY Bairaci.png /app/Bairaci.png
|
COPY Bairaci.png /app/Bairaci.png
|
||||||
|
|
||||||
|
# Git SHA after COPY so pip layer stays cached.
|
||||||
|
# Coolify: Advanced → Include Source Commit in Build = ON
|
||||||
|
# (или Build ARG GIT_SHA). Иначе се ползва hash на копираните файлове.
|
||||||
|
ARG SOURCE_COMMIT=
|
||||||
|
ARG GIT_SHA=
|
||||||
|
RUN GIT_SHA="$GIT_SHA" SOURCE_COMMIT="$SOURCE_COMMIT" python -c 'import hashlib,os,pathlib,sys; sha=(os.environ.get("GIT_SHA") or os.environ.get("SOURCE_COMMIT") or "").strip(); h=hashlib.sha1(); \
|
||||||
|
[h.update(p.as_posix().encode()+p.read_bytes()) for p in sorted(pathlib.Path("/app").rglob("*")) if p.is_file() and "/__pycache__/" not in p.as_posix() and p.name!="BUILD_ID"]; \
|
||||||
|
pathlib.Path("/app/BUILD_ID").write_text(sha or h.hexdigest()[:12], encoding="utf-8"); \
|
||||||
|
print("BUILD_ID", pathlib.Path("/app/BUILD_ID").read_text(), file=sys.stderr)'
|
||||||
|
ENV APP_GIT_SHA=${GIT_SHA}
|
||||||
|
|
||||||
ENV OUTPUT_DIR=/mnt/mssql/share/RIP/RIP_Help_Source/Output
|
ENV OUTPUT_DIR=/mnt/mssql/share/RIP/RIP_Help_Source/Output
|
||||||
ENV HOME_IMAGE=/app/Bairaci.png
|
ENV HOME_IMAGE=/app/Bairaci.png
|
||||||
ENV HELP_REMOTE_ROOT=/mnt/mssql/share/RIP/RIP_Help_Source/Output
|
ENV HELP_REMOTE_ROOT=/mnt/mssql/share/RIP/RIP_Help_Source/Output
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ Endpoint-и:
|
|||||||
GET /api/search — търсене (?q=&prefix=)
|
GET /api/search — търсене (?q=&prefix=)
|
||||||
GET /api/section/{code} — една секция с пълен текст
|
GET /api/section/{code} — една секция с пълен текст
|
||||||
POST /api/keywords/{code} — обновяване на keywords
|
POST /api/keywords/{code} — обновяване на keywords
|
||||||
GET /healthz — health + OUTPUT_DIR статус
|
GET /healthz — health + OUTPUT_DIR статус + version/git
|
||||||
|
|
||||||
Env:
|
Env:
|
||||||
HELP_DB_CONN — libpq Postgres
|
HELP_DB_CONN — libpq Postgres
|
||||||
OUTPUT_DIR — локален/mounted път към секциите (default: share)
|
OUTPUT_DIR — локален/mounted път към секциите (default: share)
|
||||||
HOME_IMAGE — път към home картинка
|
HOME_IMAGE — път към home картинка
|
||||||
|
GIT_SHA / SOURCE_COMMIT / COMMIT_SHA — git SHA (Coolify / Docker build-arg)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -22,6 +23,7 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -56,9 +58,56 @@ HOME_IMAGE = os.getenv("HOME_IMAGE") # absolute path or None
|
|||||||
_IMG_PLACEHOLDER_RE = re.compile(r"\[IMG:\s*([^\]]+?)\s*\]")
|
_IMG_PLACEHOLDER_RE = re.compile(r"\[IMG:\s*([^\]]+?)\s*\]")
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).parent
|
BASE_DIR = Path(__file__).parent
|
||||||
|
APP_VERSION = "0.3.0"
|
||||||
|
_NO_CACHE = {"Cache-Control": "no-store, no-cache, max-age=0"}
|
||||||
|
|
||||||
|
|
||||||
|
def _short_id(value: str, n: int = 7) -> str:
|
||||||
|
value = value.strip()
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return value[:n] if len(value) > n else value
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_git() -> str:
|
||||||
|
"""Git SHA / build id that changes on each image rebuild.
|
||||||
|
|
||||||
|
Prefers Coolify/runtime env, then /app/BUILD_ID (baked in Docker),
|
||||||
|
then local ``git rev-parse``.
|
||||||
|
"""
|
||||||
|
for key in ("GIT_SHA", "APP_GIT_SHA", "SOURCE_COMMIT", "COMMIT_SHA"):
|
||||||
|
raw = (os.getenv(key) or "").strip()
|
||||||
|
if raw and raw.lower() not in {"unknown", "none", "null"}:
|
||||||
|
return _short_id(raw)
|
||||||
|
build_id = Path("/app/BUILD_ID")
|
||||||
|
if build_id.is_file():
|
||||||
|
try:
|
||||||
|
text = build_id.read_text(encoding="utf-8").strip()
|
||||||
|
if text:
|
||||||
|
return _short_id(text, 7 if len(text) >= 40 else 12)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
repo = BASE_DIR.parent
|
||||||
|
r = subprocess.run(
|
||||||
|
["git", "rev-parse", "--short=7", "HEAD"],
|
||||||
|
cwd=str(repo),
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=1,
|
||||||
|
)
|
||||||
|
if r.returncode == 0 and r.stdout.strip():
|
||||||
|
return r.stdout.strip()
|
||||||
|
except (OSError, subprocess.SubprocessError):
|
||||||
|
pass
|
||||||
|
return "dev"
|
||||||
|
|
||||||
|
|
||||||
|
GIT_SHA = _resolve_git()
|
||||||
|
|
||||||
from webapp import rescan as rescan_mod
|
from webapp import rescan as rescan_mod
|
||||||
|
|
||||||
app = FastAPI(title="RIP Help System", version="0.3.0")
|
app = FastAPI(title="RIP Help System", version=APP_VERSION)
|
||||||
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
|
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
|
||||||
app.include_router(rescan_mod.router)
|
app.include_router(rescan_mod.router)
|
||||||
|
|
||||||
@@ -502,7 +551,7 @@ def api_delete_file_extractions(
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
@app.get("/healthz", summary="Статус на услугата и БД; output_dir_ok, txt_count.")
|
@app.get("/healthz", summary="Статус на услугата и БД; version/git, output_dir_ok, txt_count.")
|
||||||
def healthz():
|
def healthz():
|
||||||
db_status = "ok"
|
db_status = "ok"
|
||||||
try:
|
try:
|
||||||
@@ -513,8 +562,16 @@ def healthz():
|
|||||||
conn.close()
|
conn.close()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
{"status": "error", "db": str(e), "output_dir": str(OUTPUT_DIR)},
|
{
|
||||||
|
"ok": False,
|
||||||
|
"status": "error",
|
||||||
|
"version": f"{APP_VERSION}+{GIT_SHA}",
|
||||||
|
"git": GIT_SHA,
|
||||||
|
"db": str(e),
|
||||||
|
"output_dir": str(OUTPUT_DIR),
|
||||||
|
},
|
||||||
status_code=503,
|
status_code=503,
|
||||||
|
headers=_NO_CACHE,
|
||||||
)
|
)
|
||||||
|
|
||||||
out_ok = OUTPUT_DIR.is_dir()
|
out_ok = OUTPUT_DIR.is_dir()
|
||||||
@@ -528,11 +585,17 @@ def healthz():
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return {
|
return JSONResponse(
|
||||||
|
{
|
||||||
|
"ok": True,
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
|
"version": f"{APP_VERSION}+{GIT_SHA}",
|
||||||
|
"git": GIT_SHA,
|
||||||
"db": db_status,
|
"db": db_status,
|
||||||
"output_dir": str(OUTPUT_DIR),
|
"output_dir": str(OUTPUT_DIR),
|
||||||
"output_dir_ok": out_ok,
|
"output_dir_ok": out_ok,
|
||||||
"txt_count": txt_count,
|
"txt_count": txt_count,
|
||||||
"sample": sample,
|
"sample": sample,
|
||||||
}
|
},
|
||||||
|
headers=_NO_CACHE,
|
||||||
|
)
|
||||||
|
|||||||
@@ -476,7 +476,7 @@
|
|||||||
<p>JSON API за четене на help секции, търсене и (опционално) rescan. Готово за връзка с външни системи.</p>
|
<p>JSON API за четене на help секции, търсене и (опционално) rescan. Готово за връзка с външни системи.</p>
|
||||||
|
|
||||||
<div class="api-ep"><span class="method">GET</span><code>/healthz</code>
|
<div class="api-ep"><span class="method">GET</span><code>/healthz</code>
|
||||||
<div class="desc">Статус на услугата и БД; <code>output_dir_ok</code>, <code>txt_count</code>.</div></div>
|
<div class="desc">Статус на услугата и БД; <code>version</code>, <code>git</code>, <code>output_dir_ok</code>, <code>txt_count</code>.</div></div>
|
||||||
<div class="api-ep"><span class="method">GET</span><code>/api/sections?prefix=RIP</code>
|
<div class="api-ep"><span class="method">GET</span><code>/api/sections?prefix=RIP</code>
|
||||||
<div class="desc">Списък секции (метаданни + preview текст). Филтър по <code>prefix</code>.</div></div>
|
<div class="desc">Списък секции (метаданни + preview текст). Филтър по <code>prefix</code>.</div></div>
|
||||||
<div class="api-ep"><span class="method">GET</span><code>/api/search?q=…&prefix=RIP</code>
|
<div class="api-ep"><span class="method">GET</span><code>/api/search?q=…&prefix=RIP</code>
|
||||||
|
|||||||
Reference in New Issue
Block a user