feat: support subpath mount via reverse proxy — v0.50.67 (PR #588 by @vcavichini)

Squash-merges feature from PR #588 by @vcavichini. Dynamic <base href> injection + api() helper slash-stripping enables deploying hermes-webui behind a reverse proxy at any subpath without configuration. Also fixes pre-existing bug: api/upload was using location.origin instead of location.href (closes #596). Co-authored-by: vcavichini <vcavichini@users.noreply.github.com>
This commit is contained in:
nesquena-hermes
2026-04-16 11:20:08 -07:00
committed by GitHub
parent 8a1bc134fa
commit 54e83fb8b6
14 changed files with 49 additions and 40 deletions

View File

@@ -65,7 +65,7 @@ async function populateModelDropdown(){
const sel=$('modelSelect');
if(!sel) return;
try{
const data=await fetch(new URL('/api/models',location.origin).href,{credentials:'include'}).then(r=>r.json());
const data=await fetch(new URL('api/models',location.href).href,{credentials:'include'}).then(r=>r.json());
if(!data.groups||!data.groups.length) return; // keep HTML defaults
// Store active provider globally so the send path can warn on mismatch
window._activeProvider=data.active_provider||null;
@@ -108,7 +108,7 @@ async function _fetchLiveModels(provider, sel){
// All providers now supported via agent's provider_model_ids() — no exclusions needed
if(_liveModelCache[provider]) return; // already fetched this session
try{
const url=new URL('/api/models/live',location.origin);
const url=new URL('api/models/live',location.href);
url.searchParams.set('provider',provider);
const data=await fetch(url.href,{credentials:'include'}).then(r=>r.json());
if(!data.models||!data.models.length) return;
@@ -582,7 +582,7 @@ function renderMd(raw){
return `<a href="${esc(ref)}" target="_blank" rel="noopener">${esc(ref)}</a>`;
}
// Local file path
const apiUrl='/api/media?path='+encodeURIComponent(ref);
const apiUrl='api/media?path='+encodeURIComponent(ref);
if(_IMAGE_EXTS.test(ref)){
return `<img class="msg-media-img" src="${esc(apiUrl)}" alt="${esc(ref.split('/').pop())}" loading="lazy" onclick="this.classList.toggle('msg-media-img--full')">`;
}
@@ -1854,7 +1854,7 @@ async function uploadPendingFiles(){
const f=S.pendingFiles[i];const fd=new FormData();
fd.append('session_id',S.session.session_id);fd.append('file',f,f.name);
try{
const res=await fetch(new URL('/api/upload',location.origin).href,{method:'POST',credentials:'include',body:fd});
const res=await fetch(new URL('api/upload',location.href).href,{method:'POST',credentials:'include',body:fd});
if(!res.ok){const err=await res.text();throw new Error(err);}
const data=await res.json();
if(data.error)throw new Error(data.error);