Matthias Nott
2026-02-21 15c6d258137464aad5bbcc53f6044ad628849048
fix: trailing slash routes, static file serving
2 files modified
changed files
app/main.py patch | view | blame | history
static/js/app.js patch | view | blame | history
app/main.py
....@@ -51,10 +51,11 @@
5151 app.include_router(system.router, prefix="/api/system", tags=["system"])
5252
5353 # ---------------------------------------------------------------------------
54
-# Static files – serve the frontend SPA at /
55
-# Mount last so API routes take precedence
54
+# Static files – serve CSS/JS at /static and HTML at /
55
+# Mount /static first for explicit asset paths, then / for SPA fallback
5656 # ---------------------------------------------------------------------------
5757 if _STATIC_DIR.exists():
58
+ app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static-assets")
5859 app.mount("/", StaticFiles(directory=str(_STATIC_DIR), html=True), name="static")
5960 else:
6061 logger.warning("Static directory not found at %s – frontend will not be served", _STATIC_DIR)
static/js/app.js
....@@ -89,7 +89,7 @@
8989 }
9090 this.loading = true;
9191 try {
92
- const res = await fetch('/api/status', {
92
+ const res = await fetch('/api/status/', {
9393 headers: { 'Authorization': 'Bearer ' + this.loginInput.trim() }
9494 });
9595 if (res.ok || res.status === 200) {
....@@ -245,7 +245,7 @@
245245 this.loading = true;
246246 this.error = null;
247247 try {
248
- const res = await api('/api/status');
248
+ const res = await api('/api/status/');
249249 if (!res.ok) throw new Error('HTTP ' + res.status);
250250 const data = await res.json();
251251 this.projects = this.groupByProject(data);
....@@ -333,7 +333,7 @@
333333 this.loading = true;
334334 this.error = null;
335335 try {
336
- const res = await api('/api/backups');
336
+ const res = await api('/api/backups/');
337337 if (!res.ok) throw new Error('HTTP ' + res.status);
338338 const data = await res.json();
339339 this.local = Array.isArray(data) ? data : Object.values(data).flat();
....@@ -448,7 +448,7 @@
448448 async loadProjectList() {
449449 this.loadingProjects = true;
450450 try {
451
- const endpoint = this.source === 'offsite' ? '/api/backups/offsite' : '/api/backups';
451
+ const endpoint = this.source === 'offsite' ? '/api/backups/offsite' : '/api/backups/';
452452 const res = await api(endpoint);
453453 if (!res.ok) throw new Error('HTTP ' + res.status);
454454 const data = await res.json();
....@@ -587,7 +587,7 @@
587587 this.loading = true;
588588 this.error = null;
589589 try {
590
- const res = await api('/api/status');
590
+ const res = await api('/api/status/');
591591 if (!res.ok) throw new Error('HTTP ' + res.status);
592592 const data = await res.json();
593593 this.projects = this.groupByProject(data);
....@@ -691,7 +691,7 @@
691691 async fetchDisk() {
692692 this.loading.disk = true;
693693 try {
694
- const res = await api('/api/system/disk');
694
+ const res = await api('/api/system//disk');
695695 if (!res.ok) throw new Error('HTTP ' + res.status);
696696 const data = await res.json();
697697 this.disk = Array.isArray(data) ? data : (data.filesystems || []);
....@@ -705,7 +705,7 @@
705705 async fetchHealth() {
706706 this.loading.health = true;
707707 try {
708
- const res = await api('/api/system/health');
708
+ const res = await api('/api/system//health');
709709 if (!res.ok) throw new Error('HTTP ' + res.status);
710710 const data = await res.json();
711711 this.health = Array.isArray(data) ? data : (data.checks || []);
....@@ -719,7 +719,7 @@
719719 async fetchTimers() {
720720 this.loading.timers = true;
721721 try {
722
- const res = await api('/api/system/timers');
722
+ const res = await api('/api/system//timers');
723723 if (!res.ok) throw new Error('HTTP ' + res.status);
724724 const data = await res.json();
725725 this.timers = Array.isArray(data) ? data : (data.timers || []);
....@@ -733,7 +733,7 @@
733733 async fetchInfo() {
734734 this.loading.info = true;
735735 try {
736
- const res = await api('/api/system/info');
736
+ const res = await api('/api/system//info');
737737 if (!res.ok) throw new Error('HTTP ' + res.status);
738738 this.info = await res.json();
739739 } catch (e) {