added subpath-fix
authorPhiTux <redacted>
Sat, 11 Oct 2025 20:00:27 +0000 (22:00 +0200)
committerPhiTux <redacted>
Sat, 11 Oct 2025 20:00:27 +0000 (22:00 +0200)
17 files changed:
README.md
backend/handlers/admin.go
backend/utils/helpers.go
docker-compose.yml
docker-entrypoint.sh
frontend/src/i18n/en.json
frontend/src/lib/settings/Statistics.svelte
frontend/src/routes/(authed)/+layout.svelte
frontend/src/routes/(authed)/read/+page.js
frontend/src/routes/(authed)/read/+page.svelte
frontend/src/routes/(authed)/write/+page.js
frontend/src/routes/+layout.svelte
frontend/src/routes/+page.js
frontend/src/routes/[...missing]/+page.js
frontend/src/routes/login/+page.js
frontend/src/routes/login/+page.svelte
frontend/src/routes/reauth/+page.svelte

index 004da265dfe335a4eb5748a86b4b3fa2a64ad071..f345567d131765b6121607f83d026df9d7d9e460 100644 (file)
--- a/README.md
+++ b/README.md
@@ -103,6 +103,9 @@ services:
 
       # After how many days shall the login-cookie expire?
       - LOGOUT_AFTER_DAYS=40
+
+      # Set the BASE_PATH if you are running DailyTxT under a subpath (e.g. /dailytxt).
+      # - BASE_PATH=/dailytxt
     ports:
       # Change the left port to your needs.
       # You often would only see 8000:80. But this way, port 8000 is publicly accessible (without TLS!).
index 897b6bade00dffb9cfafa70cd08943c1bd718af3..0315c0c89333d8d8c0d2a6996a4891ec43a46b84 100644 (file)
@@ -68,6 +68,7 @@ func validateAdminPasswordInRequest(r *http.Request) bool {
 // - all users with their disk usage
 // - free disk space
 // - migration-info
+// - app settings (env-vars)
 func GetAdminData(w http.ResponseWriter, r *http.Request) {
        if !validateAdminPasswordInRequest(r) {
                http.Error(w, "Invalid admin password", http.StatusUnauthorized)
index 664cdb5754b30e087bfefdad02b9213ca6737ad6..5f9f0315f8cb5ee7bef29ec73f830956957a8ec7 100644 (file)
@@ -43,6 +43,7 @@ type AppSettings struct {
        AllowedHosts      []string `json:"allowed_hosts"`
        Indent            int      `json:"indent"`
        AllowRegistration bool     `json:"allow_registration"`
+       BasePath          string   `json:"base_path"`
 }
 
 // Global settings
@@ -102,6 +103,7 @@ func InitSettings() error {
                AllowedHosts:      []string{},
                Indent:            0,
                AllowRegistration: false,
+               BasePath:          "/",
        }
 
        fmt.Print("\nDetected the following settings:\n================\n")
@@ -160,6 +162,11 @@ func InitSettings() error {
        }
        fmt.Printf("Allow Registration: %t\n", Settings.AllowRegistration)
 
+       if basePath := os.Getenv("BASE_PATH"); basePath != "" {
+               Settings.BasePath = basePath
+       }
+       fmt.Printf("Base Path: %s\n", Settings.BasePath)
+
        fmt.Print("================\n\n")
 
        // Create data directory if it doesn't exist
index d4cdb3bda629621e84d193ec71379024a1b0a003..2c1cdb71044463fcee726561df915612cc241620 100644 (file)
@@ -24,6 +24,9 @@ services:
 
       # After how many days shall the login-cookie expire?
       - LOGOUT_AFTER_DAYS=40
+
+      # Set the BASE_PATH if you are running DailyTxT under a subpath (e.g. /dailytxt).
+      # - BASE_PATH=/dailytxt
     ports:
       # Change the left port to your needs.
       # You often would only see 8000:80. But this way, port 8000 is publicly accessible (without TLS!).
index 68ca972f00be0006737f88f25cb1635ce586fdb0..f53569defe9e4283f832050fbaa7b5a2fdf24fbe 100644 (file)
@@ -1,18 +1,38 @@
 #!/bin/sh
 set -e
 
-# Start the Go backend in background
-# Environment variables consumed by backend (see utils.InitSettings):
-#   DATA_PATH, DEVELOPMENT, SECRET_TOKEN, LOGOUT_AFTER_DAYS, ALLOWED_HOSTS, INDENT, ALLOW_REGISTRATION, ADMIN_PASSWORD
-
 # Ensure data directory exists
 mkdir -p "${DATA_PATH:-/data}"
 
-# Run backend
+# Run backend in background
 /usr/local/bin/dailytxt &
 BACKEND_PID=$!
 
 echo "Started backend (PID $BACKEND_PID)"
 
+# Edit some files to make dailytxt work on a subpath provided by BASE_PATH 
+if [ -n "${BASE_PATH:-}" ]; then
+    echo "Configuring frontend for BASE_PATH: $BASE_PATH"
+
+    # remove leading and trailing slash if exists
+    BASE_PATH="${BASE_PATH#/}"
+    BASE_PATH="${BASE_PATH%/}"
+    # print the BASE_PATH being used
+    echo "Using BASE_PATH: '$BASE_PATH'"
+
+    # Update base href in app.html
+    sed -i "s|href=\"/|href=\"|g" /usr/share/nginx/html/index.html
+    
+    # Update base path in app.html
+    sed -i "s|base: \"|base: \"/$BASE_PATH|g" /usr/share/nginx/html/index.html
+
+    # Update import-paths in app.html
+    sed -i "s|import(\"|import(\"/$BASE_PATH|g" /usr/share/nginx/html/index.html
+
+    # Update manifest.webmanifest base
+    sed -i "s|start_url\":\"/|start_url\":\"/$BASE_PATH|g" /usr/share/nginx/html/manifest.webmanifest
+    sed -i "s|src\":\"|src\":\"/$BASE_PATH|g" /usr/share/nginx/html/manifest.webmanifest
+fi
+
 # Start nginx in foreground
 exec nginx -g 'daemon off;'
index badf74aefaa9dfb85983bad35fb307b51b477849..3ab6c52f339658cb01b8e4de80c7ae50ee37cbd3 100644 (file)
@@ -67,7 +67,7 @@
       "error_reordering_files": "Error changing the order of files",
       "error_saving": "Error saving the text!"
     },
-    "written_on": "Written on:"
+    "written_on": "Posted on:"
   },
   "login": {
     "alert": {
index 2a3463f73a4503d6eda9c3a0373c607678668752..3fad1143d2cc00153a57b4fe1b7aa7b52a925a2d 100644 (file)
@@ -11,6 +11,7 @@
        import { goto } from '$app/navigation';
        import { selectedDate } from '$lib/calendarStore.js';
        import { formatBytes } from '$lib/helpers';
+       import { resolve } from '$app/paths';
 
        const { t } = getTranslate();
        const tolgee = getTolgee(['language']);
                }
 
                // Navigate to write page
-               goto('/write');
+               goto(resolve('/write'));
        }
 
        function initTooltips() {
index 55de698678ef6398b58a43cce5cdedd0013b5335..5f8f83b9bc356fdbb9303980e4385fcb48b116d1 100644 (file)
@@ -3,6 +3,7 @@
        import Fa, { FaLayers } from 'svelte-fa';
        import { goto } from '$app/navigation';
        import { onDestroy, onMount } from 'svelte';
+       import { resolve } from '$app/paths';
        import {
                readingMode,
                settings,
        });
 
        $effect(() => {
-               if ($readingMode === true && page.url.pathname !== '/read') {
-                       goto('/read');
+               if ($readingMode === true && !page.url.pathname.endsWith('/read')) {
+                       goto(resolve('/read'));
                } else if ($readingMode === false) {
-                       goto('/write');
+                       goto(resolve('/write'));
                }
        });
 
                if (!$isAuthenticated && needsReauth) {
                        // Save current route for return after reauth
                        localStorage.setItem('returnAfterReauth', window.location.pathname);
-                       goto('/reauth');
+                       goto(resolve('/reauth'));
                        return; // Stop further initialization
                }
 
                getVersionInfo();
                loadTags();
 
-               if (page.url.pathname === '/read') {
+               if (page.url.pathname.endsWith('/read')) {
                        $readingMode = true;
-               } else if (page.url.pathname === '/write') {
+               } else if (page.url.pathname.endsWith('/write')) {
                        $readingMode = false;
                }
 
                        .then(() => {
                                localStorage.removeItem('user');
                                if (errorCode) {
-                                       goto(`/login?error=${errorCode}`);
+                                       goto(resolve(`/login?error=${errorCode}`));
                                } else {
-                                       goto('/login');
+                                       goto(resolve('/login'));
                                }
                        })
                        .catch((error) => {
index 68c6d4df025b922910d3fa08ce29ebc7dc3176f0..345f985231aa8ec295c72e3b8a8256c3d172f503 100644 (file)
@@ -1,8 +1,9 @@
 import {redirect} from '@sveltejs/kit'
+import { resolve } from '$app/paths';
 
 export const load = () => {
   const user = localStorage.getItem('user');
                if (!user) {
-                       throw redirect(307, '/login');
+                       throw redirect(307, resolve('/login'));
                }
 }
\ No newline at end of file
index 877ecd358dafa2656f137e51d818ce0917f3a0fd..18af7a0dc648cd82d35db854ac2036712013e2e7 100644 (file)
 
        .log {
                border-radius: 15px;
-               margin-left: 1rem;
-               margin-right: 1rem;
+       }
+
+       @media screen and (min-width: 576px) {
+               .log {
+                       margin-left: 1rem;
+                       margin-right: 1rem;
+               }
        }
 
        :global(body[data-bs-theme='dark']) .log {
index 68c6d4df025b922910d3fa08ce29ebc7dc3176f0..345f985231aa8ec295c72e3b8a8256c3d172f503 100644 (file)
@@ -1,8 +1,9 @@
 import {redirect} from '@sveltejs/kit'
+import { resolve } from '$app/paths';
 
 export const load = () => {
   const user = localStorage.getItem('user');
                if (!user) {
-                       throw redirect(307, '/login');
+                       throw redirect(307, resolve('/login'));
                }
 }
\ No newline at end of file
index d26df27f50b2a1265c9c651432d9e56829356b16..3809d876f86ef4965e944daf938a67deb927f185 100644 (file)
@@ -12,6 +12,7 @@
        import { FormatIcu } from '@tolgee/format-icu';
        import { darkMode } from '$lib/settingsStore.js';
        import { registerSW } from 'virtual:pwa-register';
+       import { resolve } from '$app/paths';
 
        const tolgee = Tolgee()
                .use(DevTools())
@@ -74,7 +75,7 @@
                                        .get(API_URL + '/users/logout')
                                        .then(() => {
                                                localStorage.removeItem('user');
-                                               goto(`/login?error=${error.response.status}`);
+                                               goto(resolve(`/login?error=${error.response.status}`));
                                        })
                                        .catch((error) => {
                                                console.error(error);
                calculateResize();
 
                // if on login page, generate neon mesh
-               if (page.url.pathname === '/login') {
+               if (page.url.pathname.endsWith('/login')) {
                        generateNeonMesh($darkMode);
                }
 
                }
        });
 
-       let routeToFromLoginKey = $derived(page.url.pathname === '/login');
+       let routeToFromLoginKey = $derived(page.url.pathname.endsWith('/login'));
 </script>
 
 <main class="d-flex flex-column background" use:focus={generateNeonMesh}>
index 0dde40f21453ac99910aab80f756044e13622717..71eb4a96217760c0f834afefca4a6cb239b3f893 100644 (file)
@@ -1,6 +1,7 @@
 import { redirect } from "@sveltejs/kit";
+import { resolve } from '$app/paths';
 
 export function load() {
   // Redirect to the /write route
-  throw redirect(307, "/write");
+  throw redirect(307, resolve("/write"));
 }
\ No newline at end of file
index edf1cc9363a8fa8cf637204c885c5b547425025b..f487fb0c964a1a72c8cdb33a62ae8efb72ac32f4 100644 (file)
@@ -1,8 +1,9 @@
 import { redirect } from '@sveltejs/kit';
+import { resolve } from '$app/paths';
 
 // Catch-all for unknown routes: redirect to /write (primary app surface)
 // If /write itself handles auth, unauthenticated users will still be bounced to /login there.
 // Adjust here if you later want a different fallback (e.g. redirect to /login when not authenticated).
 export function load() {
-       throw redirect(307, '/write');
+       throw redirect(307, resolve('/write'));
 }
index 9ad951ed04d398c1d9ef8632c318db248ed17254..0cad61ce21187d6339cbf7c879978ddd681b8eee 100644 (file)
@@ -1,8 +1,9 @@
 import {redirect} from '@sveltejs/kit'
+import { resolve } from '$app/paths';
 
 export const load = () => {
   const user = localStorage.getItem('user');
                if (user) {
-                       throw redirect(307, '/write');
+                       throw redirect(307, resolve('/write'));
                }
 }
\ No newline at end of file
index 3ea3496705652c189e984d9e9593148111e8f075..ebded22044f7a0d1ed5fccfe34d2e7911dce4253 100644 (file)
@@ -8,6 +8,7 @@
        import { getTranslate, getTolgee } from '@tolgee/svelte';
        import { isAuthenticated, loadFlagEmoji } from '$lib/helpers.js';
        import { fade } from 'svelte/transition';
+       import { resolve } from '$app/paths';
 
        const { t } = getTranslate();
        const tolgee = getTolgee(['language']);
                                } else {
                                        $isAuthenticated = true;
                                        localStorage.setItem('user', response.data.username);
-                                       goto('/write');
+                                       goto(resolve('/write'));
                                }
                        })
                        .catch((error) => {
index 59bdb6105c50e127f144a53fdce5bde6e6735667..e59e5de7028bae28fcb073989a8b726883f01f15 100644 (file)
@@ -6,6 +6,7 @@
        import { generateNeonMesh, isAuthenticated } from '$lib/helpers';
        import { getTranslate } from '@tolgee/svelte';
        import logo from '$lib/assets/locked_heart_with_keyhole.svg';
+       import { resolve } from '$app/paths';
 
        const { t } = getTranslate();
 
@@ -40,7 +41,7 @@
                                $isAuthenticated = true;
 
                                // Authentication successful - return to original route
-                               const returnPath = localStorage.getItem('returnAfterReauth') || '/write';
+                               const returnPath = localStorage.getItem('returnAfterReauth') || resolve('/write');
                                localStorage.removeItem('returnAfterReauth');
                                goto(returnPath);
                        } else {
git clone https://git.99rst.org/PROJECT