feat(ui): add backend meta/version status and refresh check

This commit is contained in:
Carl Niklas Rydberg 2026-02-14 21:36:51 +01:00
parent 912f8ebc56
commit cff60dcf3e
4 changed files with 41 additions and 0 deletions

17
app.py
View file

@ -26,6 +26,8 @@ from gremlin_python.driver.serializer import GraphSONSerializersV3d0
from dotenv import load_dotenv from dotenv import load_dotenv
APP_NAME = "concept-api" APP_NAME = "concept-api"
APP_VERSION = os.getenv("APP_VERSION", "dev-local")
APP_STARTED_AT_UTC = datetime.now(timezone.utc).isoformat()
# Keep env loading behavior aligned with connectivity_check.py. # Keep env loading behavior aligned with connectivity_check.py.
load_dotenv() load_dotenv()
@ -1881,6 +1883,21 @@ async def startup():
await es_ensure_index() await es_ensure_index()
@app.get("/meta")
async def meta():
return {
"app_name": APP_NAME,
"version": APP_VERSION,
"started_at_utc": APP_STARTED_AT_UTC,
"features": {
"assistant_chat": True,
"assistant_learn": True,
"assistant_tasks_ai": True,
"assistant_ui": True,
},
}
@app.get("/ui", include_in_schema=False) @app.get("/ui", include_in_schema=False)
async def assistant_ui(): async def assistant_ui():
index_html = UI_DIR / "index.html" index_html = UI_DIR / "index.html"

View file

@ -49,6 +49,17 @@ async function apiPost(path, payload) {
return r.json(); return r.json();
} }
async function loadMeta() {
const status = document.getElementById("metaStatus");
status.textContent = "backend: checking...";
try {
const data = await apiGet("/meta", {});
status.textContent = `backend: ${data.version} @ ${new Date(data.started_at_utc).toLocaleTimeString()}`;
} catch (e) {
status.textContent = "backend: unreachable/old";
}
}
function renderRows(target, rows, formatter) { function renderRows(target, rows, formatter) {
target.innerHTML = ""; target.innerHTML = "";
if (!rows || rows.length === 0) { if (!rows || rows.length === 0) {
@ -206,6 +217,7 @@ async function sendChat() {
} }
document.getElementById("saveConfig").addEventListener("click", saveConfig); document.getElementById("saveConfig").addEventListener("click", saveConfig);
document.getElementById("refreshMeta").addEventListener("click", loadMeta);
document.getElementById("loadInbox").addEventListener("click", loadInbox); document.getElementById("loadInbox").addEventListener("click", loadInbox);
document.getElementById("loadTasks").addEventListener("click", loadTasks); document.getElementById("loadTasks").addEventListener("click", loadTasks);
document.getElementById("makeDraft").addEventListener("click", makeDraft); document.getElementById("makeDraft").addEventListener("click", makeDraft);
@ -213,3 +225,4 @@ document.getElementById("saveLearn").addEventListener("click", saveLearn);
document.getElementById("sendChat").addEventListener("click", sendChat); document.getElementById("sendChat").addEventListener("click", sendChat);
loadConfig(); loadConfig();
loadMeta();

View file

@ -65,6 +65,15 @@ body {
flex-wrap: wrap; flex-wrap: wrap;
} }
.badge {
font-size: 12px;
color: #0f3f3b;
background: #e7f4f2;
border: 1px solid #b8dfd9;
border-radius: 999px;
padding: 5px 9px;
}
input, input,
textarea, textarea,
button { button {

View file

@ -11,8 +11,10 @@
<header class="topbar"> <header class="topbar">
<h1>Assistant Console</h1> <h1>Assistant Console</h1>
<div class="controls"> <div class="controls">
<span id="metaStatus" class="badge">backend: unknown</span>
<input id="apiKey" type="password" placeholder="X-Admin-Api-Key" /> <input id="apiKey" type="password" placeholder="X-Admin-Api-Key" />
<input id="releaseName" type="text" placeholder="release_name (optional)" /> <input id="releaseName" type="text" placeholder="release_name (optional)" />
<button id="refreshMeta">Check</button>
<button id="saveConfig">Save</button> <button id="saveConfig">Save</button>
</div> </div>
</header> </header>