From 15c6d258137464aad5bbcc53f6044ad628849048 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Sat, 21 Feb 2026 10:36:55 +0100
Subject: [PATCH] fix: trailing slash routes, static file serving

---
 static/js/app.js |   18 +++++++++---------
 app/main.py      |    5 +++--
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/app/main.py b/app/main.py
index 7088a4b..5a6e260 100644
--- a/app/main.py
+++ b/app/main.py
@@ -51,10 +51,11 @@
 app.include_router(system.router, prefix="/api/system", tags=["system"])
 
 # ---------------------------------------------------------------------------
-# Static files – serve the frontend SPA at /
-# Mount last so API routes take precedence
+# Static files – serve CSS/JS at /static and HTML at /
+# Mount /static first for explicit asset paths, then / for SPA fallback
 # ---------------------------------------------------------------------------
 if _STATIC_DIR.exists():
+    app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static-assets")
     app.mount("/", StaticFiles(directory=str(_STATIC_DIR), html=True), name="static")
 else:
     logger.warning("Static directory not found at %s – frontend will not be served", _STATIC_DIR)
diff --git a/static/js/app.js b/static/js/app.js
index d34f5d4..256b139 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -89,7 +89,7 @@
       }
       this.loading = true;
       try {
-        const res = await fetch('/api/status', {
+        const res = await fetch('/api/status/', {
           headers: { 'Authorization': 'Bearer ' + this.loginInput.trim() }
         });
         if (res.ok || res.status === 200) {
@@ -245,7 +245,7 @@
       this.loading = true;
       this.error = null;
       try {
-        const res = await api('/api/status');
+        const res = await api('/api/status/');
         if (!res.ok) throw new Error('HTTP ' + res.status);
         const data = await res.json();
         this.projects = this.groupByProject(data);
@@ -333,7 +333,7 @@
       this.loading = true;
       this.error = null;
       try {
-        const res = await api('/api/backups');
+        const res = await api('/api/backups/');
         if (!res.ok) throw new Error('HTTP ' + res.status);
         const data = await res.json();
         this.local = Array.isArray(data) ? data : Object.values(data).flat();
@@ -448,7 +448,7 @@
     async loadProjectList() {
       this.loadingProjects = true;
       try {
-        const endpoint = this.source === 'offsite' ? '/api/backups/offsite' : '/api/backups';
+        const endpoint = this.source === 'offsite' ? '/api/backups/offsite' : '/api/backups/';
         const res = await api(endpoint);
         if (!res.ok) throw new Error('HTTP ' + res.status);
         const data = await res.json();
@@ -587,7 +587,7 @@
       this.loading = true;
       this.error = null;
       try {
-        const res = await api('/api/status');
+        const res = await api('/api/status/');
         if (!res.ok) throw new Error('HTTP ' + res.status);
         const data = await res.json();
         this.projects = this.groupByProject(data);
@@ -691,7 +691,7 @@
     async fetchDisk() {
       this.loading.disk = true;
       try {
-        const res = await api('/api/system/disk');
+        const res = await api('/api/system//disk');
         if (!res.ok) throw new Error('HTTP ' + res.status);
         const data = await res.json();
         this.disk = Array.isArray(data) ? data : (data.filesystems || []);
@@ -705,7 +705,7 @@
     async fetchHealth() {
       this.loading.health = true;
       try {
-        const res = await api('/api/system/health');
+        const res = await api('/api/system//health');
         if (!res.ok) throw new Error('HTTP ' + res.status);
         const data = await res.json();
         this.health = Array.isArray(data) ? data : (data.checks || []);
@@ -719,7 +719,7 @@
     async fetchTimers() {
       this.loading.timers = true;
       try {
-        const res = await api('/api/system/timers');
+        const res = await api('/api/system//timers');
         if (!res.ok) throw new Error('HTTP ' + res.status);
         const data = await res.json();
         this.timers = Array.isArray(data) ? data : (data.timers || []);
@@ -733,7 +733,7 @@
     async fetchInfo() {
       this.loading.info = true;
       try {
-        const res = await api('/api/system/info');
+        const res = await api('/api/system//info');
         if (!res.ok) throw new Error('HTTP ' + res.status);
         this.info = await res.json();
       } catch (e) {

--
Gitblit v1.3.1