Use relative asset and API URLs so the UI works under a path prefix
index.html and app.js requested /static/... and /api/... from the site root, so serving the app behind a reverse proxy at a sub-path (e.g. https://host/uj/ with the prefix stripped) 404'd every asset and call. Relative URLs resolve against the page, which is the same at / and at /uj/; direct access is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UXA39Sh9coAx4FhRkd8R2
This commit is contained in:
+8
-8
@@ -100,7 +100,7 @@ async function api(path, options) {
|
||||
}
|
||||
|
||||
async function loadMetrics() {
|
||||
const data = await api('/api/metrics');
|
||||
const data = await api('api/metrics');
|
||||
state.metrics = data.metrics;
|
||||
state.metric = data.default;
|
||||
const select = el('metric-select');
|
||||
@@ -134,7 +134,7 @@ function renderFileList() {
|
||||
check.addEventListener('click', (ev) => ev.stopPropagation());
|
||||
check.addEventListener('change', async () => {
|
||||
const ids = collectOverlayFromDOM();
|
||||
await api('/api/overlay', {
|
||||
await api('api/overlay', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ file_ids: ids }),
|
||||
@@ -159,7 +159,7 @@ function renderFileList() {
|
||||
remove.title = 'Forget this track (drops the analysis and removes it from browser storage)';
|
||||
remove.addEventListener('click', async (ev) => {
|
||||
ev.stopPropagation();
|
||||
await api(`/api/file?file_id=${encodeURIComponent(f.file_id)}`, { method: 'DELETE' });
|
||||
await api(`api/file?file_id=${encodeURIComponent(f.file_id)}`, { method: 'DELETE' });
|
||||
// The server only ever held numbers; the audio is ours to delete.
|
||||
await store.remove(f.file_id);
|
||||
releaseObjectUrl(f.file_id);
|
||||
@@ -871,7 +871,7 @@ async function refreshPlot() {
|
||||
if (state.yLog !== null) params.set('y_log', String(state.yLog));
|
||||
if (state.xLog !== null) params.set('x_log', String(state.xLog));
|
||||
|
||||
const payload = await api(`/api/plot?${params.toString()}`);
|
||||
const payload = await api(`api/plot?${params.toString()}`);
|
||||
state.payload = payload;
|
||||
el('plot-title').textContent = payload.datasets.length
|
||||
? `${payload.metric_name}${payload.datasets.length > 1 ? ` — ${payload.datasets.length} tracks` : ` — ${payload.datasets[0].label}`}`
|
||||
@@ -933,7 +933,7 @@ async function ingest(fileList) {
|
||||
}
|
||||
}
|
||||
|
||||
const s = await api('/api/session');
|
||||
const s = await api('api/session');
|
||||
state.files = s.files;
|
||||
state.overlay = s.overlay;
|
||||
if (!state.selected && state.files.length) state.selected = state.files[0].file_id;
|
||||
@@ -952,7 +952,7 @@ async function ingest(fileList) {
|
||||
* second round trip) and is recorded in `state.analysed`.
|
||||
*/
|
||||
async function analyseOne(fileId, name, metric = state.metric) {
|
||||
const url = `/api/analyse?` + new URLSearchParams({
|
||||
const url = `api/analyse?` + new URLSearchParams({
|
||||
file_id: fileId,
|
||||
name,
|
||||
metric: metric || state.metric || '',
|
||||
@@ -972,7 +972,7 @@ async function analyseOne(fileId, name, metric = state.metric) {
|
||||
function ensureOverlay(fileId) {
|
||||
if (!state.overlay.includes(fileId)) {
|
||||
state.overlay = [...state.overlay, fileId];
|
||||
api('/api/overlay', {
|
||||
api('api/overlay', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ file_ids: state.overlay }),
|
||||
@@ -1129,7 +1129,7 @@ async function boot() {
|
||||
}
|
||||
|
||||
await loadMetrics();
|
||||
const s = await api('/api/session');
|
||||
const s = await api('api/session');
|
||||
state.files = s.files;
|
||||
state.overlay = s.overlay;
|
||||
state.selected = state.files.length ? state.files[0].file_id : null;
|
||||
|
||||
Reference in New Issue
Block a user