Dashboard: Matrix chat card — self-serve one extra federated MXID below API keys

This commit is contained in:
inference-bot committed 2026-10-03 10:48:18 -06:00
1 parent 98f829b65a
commit 92158da5ea
1 file changed
+71
+71
View File
@@ -159,6 +159,30 @@ async def api_revoke_key(name: str, request: Request):
return JSONResponse({"error": e.detail}, status_code=e.status_code) return JSONResponse({"error": e.detail}, status_code=e.status_code)
@app.get("/api/matrix-mxid")
async def api_get_matrix_mxid(request: Request):
identity = get_user_identity(request)
if not identity:
return JSONResponse({"error": "unauthenticated"}, status_code=401)
try:
return await broker_get(identity, "/broker/matrix-mxid")
except HTTPException as e:
return JSONResponse({"error": e.detail}, status_code=e.status_code)
@app.post("/api/matrix-mxid")
async def api_set_matrix_mxid(request: Request):
identity = get_user_identity(request)
if not identity:
return JSONResponse({"error": "unauthenticated"}, status_code=401)
body = await request.json()
mxid = (body.get("matrix_mxid") or "").strip()
try:
return await broker_post(identity, "/broker/matrix-mxid", {"matrix_mxid": mxid})
except HTTPException as e:
return JSONResponse({"error": e.detail}, status_code=e.status_code)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# OIDC routes — authorization-code flow against Cloudron's OIDC provider. # OIDC routes — authorization-code flow against Cloudron's OIDC provider.
# Active only when the `oidc` addon is configured (CLOUDRON_OIDC_* present). # Active only when the `oidc` addon is configured (CLOUDRON_OIDC_* present).
@@ -532,6 +556,16 @@ PAGE_HTML = """<!DOCTYPE html>
<button class="ghost" onclick="$('key-reveal').style.display='none';">Dismiss</button> <button class="ghost" onclick="$('key-reveal').style.display='none';">Dismiss</button>
</div> </div>
</div> </div>
<div class="card">
<h2>Matrix chat</h2>
<p class="hint">You're invited to the co-op's Matrix space at your co-op account (<code id="local-mxid">@you:inference.coop</code>). Prefer to use an existing account on another server instead? Add it below (one account).</p>
<div class="new-key">
<input id="mxid-input" type="text" placeholder="e.g. @you:matrix.org" maxlength="255" />
<button class="primary" id="mxid-btn" onclick="saveMxid()">Save</button>
</div>
<div class="hint" id="mxid-status"></div>
</div>
</div> </div>
<footer> <footer>
@@ -594,11 +628,48 @@ async function load() {
renderModels(d.models || []); renderModels(d.models || []);
renderBoard(d.coop_models || []); renderBoard(d.coop_models || []);
renderKeys(d.keys || []); renderKeys(d.keys || []);
loadMxid();
} catch (e) { } catch (e) {
flash('Could not load usage: ' + e.message, false); flash('Could not load usage: ' + e.message, false);
} }
} }
async function loadMxid() {
try {
const r = await fetch('/api/matrix-mxid');
if (r.status === 401) return;
const d = await r.json();
if (d.error) return;
if (d.local_mxid) $('local-mxid').textContent = d.local_mxid;
if (d.matrix_mxid) {
$('mxid-input').value = d.matrix_mxid;
$('mxid-status').textContent = 'Using ' + d.matrix_mxid + ' for Matrix invites.';
}
} catch (e) { /* non-critical */ }
}
async function saveMxid() {
const mxid = $('mxid-input').value.trim();
const btn = $('mxid-btn');
btn.disabled = true;
$('mxid-status').textContent = '';
try {
const r = await fetch('/api/matrix-mxid', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ matrix_mxid: mxid })
});
const d = await r.json();
if (d.error) { $('mxid-status').textContent = d.error; }
else if (mxid) {
$('mxid-status').textContent = 'Saved — invitations will go to ' + mxid + '.';
} else {
$('mxid-status').textContent = 'Cleared — your co-op account will be used for Matrix.';
$('mxid-input').value = '';
}
} catch (e) { $('mxid-status').textContent = 'Could not save: ' + e.message; }
btn.disabled = false;
}
function renderModels(models) { function renderModels(models) {
const el = $('models'); const el = $('models');
if (!models.length) { el.innerHTML = ''; return; } if (!models.length) { el.innerHTML = ''; return; }