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() {
|
async function loadMetrics() {
|
||||||
const data = await api('/api/metrics');
|
const data = await api('api/metrics');
|
||||||
state.metrics = data.metrics;
|
state.metrics = data.metrics;
|
||||||
state.metric = data.default;
|
state.metric = data.default;
|
||||||
const select = el('metric-select');
|
const select = el('metric-select');
|
||||||
@@ -134,7 +134,7 @@ function renderFileList() {
|
|||||||
check.addEventListener('click', (ev) => ev.stopPropagation());
|
check.addEventListener('click', (ev) => ev.stopPropagation());
|
||||||
check.addEventListener('change', async () => {
|
check.addEventListener('change', async () => {
|
||||||
const ids = collectOverlayFromDOM();
|
const ids = collectOverlayFromDOM();
|
||||||
await api('/api/overlay', {
|
await api('api/overlay', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ file_ids: ids }),
|
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.title = 'Forget this track (drops the analysis and removes it from browser storage)';
|
||||||
remove.addEventListener('click', async (ev) => {
|
remove.addEventListener('click', async (ev) => {
|
||||||
ev.stopPropagation();
|
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.
|
// The server only ever held numbers; the audio is ours to delete.
|
||||||
await store.remove(f.file_id);
|
await store.remove(f.file_id);
|
||||||
releaseObjectUrl(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.yLog !== null) params.set('y_log', String(state.yLog));
|
||||||
if (state.xLog !== null) params.set('x_log', String(state.xLog));
|
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;
|
state.payload = payload;
|
||||||
el('plot-title').textContent = payload.datasets.length
|
el('plot-title').textContent = payload.datasets.length
|
||||||
? `${payload.metric_name}${payload.datasets.length > 1 ? ` — ${payload.datasets.length} tracks` : ` — ${payload.datasets[0].label}`}`
|
? `${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.files = s.files;
|
||||||
state.overlay = s.overlay;
|
state.overlay = s.overlay;
|
||||||
if (!state.selected && state.files.length) state.selected = state.files[0].file_id;
|
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`.
|
* second round trip) and is recorded in `state.analysed`.
|
||||||
*/
|
*/
|
||||||
async function analyseOne(fileId, name, metric = state.metric) {
|
async function analyseOne(fileId, name, metric = state.metric) {
|
||||||
const url = `/api/analyse?` + new URLSearchParams({
|
const url = `api/analyse?` + new URLSearchParams({
|
||||||
file_id: fileId,
|
file_id: fileId,
|
||||||
name,
|
name,
|
||||||
metric: metric || state.metric || '',
|
metric: metric || state.metric || '',
|
||||||
@@ -972,7 +972,7 @@ async function analyseOne(fileId, name, metric = state.metric) {
|
|||||||
function ensureOverlay(fileId) {
|
function ensureOverlay(fileId) {
|
||||||
if (!state.overlay.includes(fileId)) {
|
if (!state.overlay.includes(fileId)) {
|
||||||
state.overlay = [...state.overlay, fileId];
|
state.overlay = [...state.overlay, fileId];
|
||||||
api('/api/overlay', {
|
api('api/overlay', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ file_ids: state.overlay }),
|
body: JSON.stringify({ file_ids: state.overlay }),
|
||||||
@@ -1129,7 +1129,7 @@ async function boot() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await loadMetrics();
|
await loadMetrics();
|
||||||
const s = await api('/api/session');
|
const s = await api('api/session');
|
||||||
state.files = s.files;
|
state.files = s.files;
|
||||||
state.overlay = s.overlay;
|
state.overlay = s.overlay;
|
||||||
state.selected = state.files.length ? state.files[0].file_id : null;
|
state.selected = state.files.length ? state.files[0].file_id : null;
|
||||||
|
|||||||
+4
-4
@@ -4,8 +4,8 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Audio Mastering Analysis Toolkit</title>
|
<title>Audio Mastering Analysis Toolkit</title>
|
||||||
<link rel="stylesheet" href="/static/vendor/uPlot.min.css">
|
<link rel="stylesheet" href="static/vendor/uPlot.min.css">
|
||||||
<link rel="stylesheet" href="/static/app.css">
|
<link rel="stylesheet" href="static/app.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -89,9 +89,9 @@
|
|||||||
|
|
||||||
<div id="drop-overlay" class="drop-overlay" hidden><span>Drop to analyse</span></div>
|
<div id="drop-overlay" class="drop-overlay" hidden><span>Drop to analyse</span></div>
|
||||||
|
|
||||||
<script src="/static/vendor/uPlot.iife.min.js"></script>
|
<script src="static/vendor/uPlot.iife.min.js"></script>
|
||||||
<!-- app.js is an ES module: it imports ./audio-store.js, which owns the
|
<!-- app.js is an ES module: it imports ./audio-store.js, which owns the
|
||||||
browser-side copy of every uploaded file. -->
|
browser-side copy of every uploaded file. -->
|
||||||
<script type="module" src="/static/app.js"></script>
|
<script type="module" src="static/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user