From: Adam Dullage Date: Fri, 14 Jun 2024 07:25:51 +0000 (+0100) Subject: Add custom path prefix support X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=a43b9cf592e9e88299b04bf252165a55d17641c8;p=flatnotes.git Add custom path prefix support --- diff --git a/client/api.js b/client/api.js index 11a3731..4657f98 100644 --- a/client/api.js +++ b/client/api.js @@ -12,7 +12,7 @@ const api = axios.create(); api.interceptors.request.use( // If the request is not for the token endpoint, add the token to the headers. function (config) { - if (config.url !== "/api/token") { + if (config.url !== "api/token") { const token = getStoredToken(); config.headers.Authorization = `Bearer ${token}`; } @@ -44,7 +44,7 @@ export function apiErrorHandler(error, toast) { export async function getConfig() { try { - const response = await api.get("/api/config"); + const response = await api.get("api/config"); return response.data; } catch (error) { return Promise.reject(error); @@ -53,7 +53,7 @@ export async function getConfig() { export async function getToken(username, password, totp) { try { - const response = await api.post("/api/token", { + const response = await api.post("api/token", { username: username, password: totp ? password + totp : password, }); @@ -65,7 +65,7 @@ export async function getToken(username, password, totp) { export async function getNotes(term, sort, order, limit) { try { - const response = await api.get("/api/search", { + const response = await api.get("api/search", { params: { term: term, sort: sort, @@ -81,7 +81,7 @@ export async function getNotes(term, sort, order, limit) { export async function createNote(title, content) { try { - const response = await api.post("/api/notes", { + const response = await api.post("api/notes", { title: title, content: content, }); @@ -93,7 +93,7 @@ export async function createNote(title, content) { export async function getNote(title) { try { - const response = await api.get(`/api/notes/${title}`); + const response = await api.get(`api/notes/${title}`); return new Note(response.data); } catch (response) { return Promise.reject(response); @@ -102,7 +102,7 @@ export async function getNote(title) { export async function updateNote(title, newTitle, newContent) { try { - const response = await api.patch(`/api/notes/${title}`, { + const response = await api.patch(`api/notes/${title}`, { newTitle: newTitle, newContent: newContent, }); @@ -114,7 +114,7 @@ export async function updateNote(title, newTitle, newContent) { export async function deleteNote(title) { try { - await api.delete(`/api/notes/${title}`); + await api.delete(`api/notes/${title}`); } catch (response) { return Promise.reject(response); } @@ -122,7 +122,7 @@ export async function deleteNote(title) { export async function getTags() { try { - const response = await api.get("/api/tags"); + const response = await api.get("api/tags"); return response.data; } catch (response) { return Promise.reject(response); @@ -133,7 +133,7 @@ export async function createAttachment(file) { try { const formData = new FormData(); formData.append("file", file); - const response = await api.post("/api/attachments", formData, { + const response = await api.post("api/attachments", formData, { headers: { "Content-Type": "multipart/form-data", }, diff --git a/client/index.html b/client/index.html index 59f9d04..9bf1065 100644 --- a/client/index.html +++ b/client/index.html @@ -8,6 +8,7 @@ content="width=device-width, initial-scale=1, shrink-to-fit=no" /> + - + - + flatnotes
- + diff --git a/client/router.js b/client/router.js index 07b6c9d..81b2314 100644 --- a/client/router.js +++ b/client/router.js @@ -3,7 +3,7 @@ import * as constants from "./constants.js"; import { createRouter, createWebHistory } from "vue-router"; const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), + history: createWebHistory(""), routes: [ { path: "/", diff --git a/client/site.webmanifest b/client/site.webmanifest index 692075a..62faf14 100644 --- a/client/site.webmanifest +++ b/client/site.webmanifest @@ -4,13 +4,13 @@ "start_url": "/", "icons": [ { - "src": "/android-chrome-192x192.png", + "src": "android-chrome-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" }, { - "src": "/android-chrome-512x512.png", + "src": "android-chrome-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" diff --git a/client/style.css b/client/style.css index 713c238..7876857 100644 --- a/client/style.css +++ b/client/style.css @@ -8,7 +8,7 @@ font-style: normal; font-weight: 400; font-display: swap; - src: url("/assets/fonts/Poppins/Poppins-Regular.ttf"); + src: url("assets/fonts/Poppins/Poppins-Regular.ttf"); } body { diff --git a/client/tokenStorage.js b/client/tokenStorage.js index 2c4dc13..959af4d 100644 --- a/client/tokenStorage.js +++ b/client/tokenStorage.js @@ -1,7 +1,7 @@ const tokenStorageKey = "token"; function getCookieString(token) { - return `${tokenStorageKey}=${token}; path=/attachments; SameSite=Strict`; + return `${tokenStorageKey}=${token}; SameSite=Strict`; } export function storeToken(token, persist = false) { diff --git a/server/attachments/file_system/file_system.py b/server/attachments/file_system/file_system.py index e168c1f..9a5f35f 100644 --- a/server/attachments/file_system/file_system.py +++ b/server/attachments/file_system/file_system.py @@ -55,4 +55,4 @@ class FileSystemAttachments(BaseAttachments): def _url_for_filename(self, filename: str) -> str: """Return the URL for the given filename.""" - return f"/attachments/{urllib.parse.quote(filename)}" + return f"attachments/{urllib.parse.quote(filename)}" diff --git a/server/global_config.py b/server/global_config.py index a620d48..4f4f9ee 100644 --- a/server/global_config.py +++ b/server/global_config.py @@ -10,6 +10,7 @@ class GlobalConfig: logger.debug("Loading global config...") self.auth_type: AuthType = self._load_auth_type() self.hide_recently_modified: bool = self._load_hide_recently_modified() + self.path_prefix: str = self._load_path_prefix() def load_auth(self): if self.auth_type in (AuthType.NONE, AuthType.READ_ONLY): @@ -50,6 +51,14 @@ class GlobalConfig: key = "FLATNOTES_HIDE_RECENTLY_MODIFIED" return get_env(key, mandatory=False, default=False, cast_bool=True) + def _load_path_prefix(self): + key = "FLATNOTES_PATH_PREFIX" + value = get_env(key, mandatory=False, default="") + value = value.rstrip("/") + if value and not value.startswith("/"): + value = "/" + value + return value + class AuthType(str, Enum): NONE = "none" diff --git a/server/helpers.py b/server/helpers.py index 977ef82..367830b 100644 --- a/server/helpers.py +++ b/server/helpers.py @@ -1,4 +1,5 @@ import os +import re import sys from pydantic import BaseModel @@ -59,6 +60,22 @@ def get_env( return value +def replace_base_href(html_file, path_prefix): + """Replace the href value for the base element in an HTML file.""" + base_path = path_prefix + "/" + logger.info( + f"Replacing href value for base element in '{html_file}' " + + f"with '{base_path}'." + ) + with open(html_file, "r", encoding="utf-8") as f: + html = f.read() + pattern = r'( str: """A lightweight endpoint that simply returns 'OK' to indicate the server is running.""" @@ -241,4 +247,9 @@ def healthcheck() -> str: # endregion -app.mount("/", StaticFiles(directory="client/dist"), name="dist") +app.include_router(router, prefix=global_config.path_prefix) +app.mount( + global_config.path_prefix, + StaticFiles(directory="client/dist"), + name="dist", +) diff --git a/vite.config.js b/vite.config.js index 34a0c65..3faaad3 100644 --- a/vite.config.js +++ b/vite.config.js @@ -6,7 +6,9 @@ const devApiUrl = "http://127.0.0.1:8000"; export default defineConfig({ plugins: [vue()], root: "client", + base: "", server: { + // Note: The FLATNOTES_PATH_PREFIX environment variable is not supported by the dev server port: 8080, proxy: { "/api/": {