feat: 🎸 Allow multi-segement path

This commit is contained in:
Jin
2025-06-26 23:08:22 +02:00
parent c52d6a6384
commit 923eabd750
6 changed files with 58 additions and 23 deletions

View File

@@ -76,13 +76,6 @@ export default class ShareSettingsOptions extends OptionsWidget {
const DEFAULT_SHAREPATH = "/share";
const sharePathInput = this.$sharePath.val()?.trim() || "";
// TODO: inform user if they try to add more than a single path prefix (i.e. /sharePath/test)
// → this currently is not properly working, as for some reason the assets URL is not correctly rewritten
// and it only includes the first path in the URL, e.g.
// http://localhost:8080/sharePath/assets/v0.93.0/node_modules/normalize.css/normalize.css
// instead of
// http://localhost:8080/sharePath/test/assets/v0.93.0/node_modules/normalize.css/normalize.css
// alternatively/better approach: fix this behaviour :-)
const normalizedSharePath = normalizeSharePathInput(sharePathInput);
const optionValue = (!sharePathInput || !normalizedSharePath || normalizedSharePath === "/")
? DEFAULT_SHAREPATH

View File

@@ -16,7 +16,7 @@ export interface Result {
isEmpty?: boolean;
}
function getContent(note: SNote) {
function getContent(note: SNote, relativePath = '../') {
if (note.isProtected) {
return {
header: "",
@@ -32,7 +32,7 @@ function getContent(note: SNote) {
};
if (note.type === "text") {
renderText(result, note);
renderText(result, note, relativePath);
} else if (note.type === "code") {
renderCode(result);
} else if (note.type === "mermaid") {
@@ -65,7 +65,7 @@ function renderIndex(result: Result) {
result.content += "</ul>";
}
function renderText(result: Result, note: SNote) {
function renderText(result: Result, note: SNote, relativePath: string) {
const document = new JSDOM(result.content || "").window.document;
result.isEmpty = document.body.textContent?.trim().length === 0 && document.querySelectorAll("img").length === 0;
@@ -88,10 +88,10 @@ function renderText(result: Result, note: SNote) {
if (result.content.includes(`<span class="math-tex">`)) {
result.header += `
<script src="../${assetPath}/node_modules/katex/dist/katex.min.js"></script>
<link rel="stylesheet" href="../${assetPath}/node_modules/katex/dist/katex.min.css">
<script src="../${assetPath}/node_modules/katex/dist/contrib/auto-render.min.js"></script>
<script src="../${assetPath}/node_modules/katex/dist/contrib/mhchem.min.js"></script>
<script src="${relativePath}${assetPath}/node_modules/katex/dist/katex.min.js"></script>
<link rel="stylesheet" href="${relativePath}${assetPath}/node_modules/katex/dist/katex.min.css">
<script src="${relativePath}${assetPath}/node_modules/katex/dist/contrib/auto-render.min.js"></script>
<script src="${relativePath}${assetPath}/node_modules/katex/dist/contrib/mhchem.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.getElementById('content'));

View File

@@ -43,3 +43,39 @@ describe("Share API test", () => {
});
});
describe("Share Routes - Asset Path Calculation", () => {
it("should calculate correct relative path depth for different share paths", () => {
// Helper function to simulate the path depth calculation
const calculateRelativePath = (sharePath: string) => {
const pathDepth = sharePath.split('/').filter(segment => segment.length > 0).length;
return '../'.repeat(pathDepth);
};
// Test single level path
expect(calculateRelativePath("/share")).toBe("../");
// Test double level path
expect(calculateRelativePath("/sharePath/test")).toBe("../../");
// Test triple level path
expect(calculateRelativePath("/my/custom/share")).toBe("../../../");
// Test root path
expect(calculateRelativePath("/")).toBe("");
// Test path with trailing slash
expect(calculateRelativePath("/share/")).toBe("../");
});
it("should handle normalized share paths correctly", () => {
const calculateRelativePath = (sharePath: string) => {
const pathDepth = sharePath.split('/').filter(segment => segment.length > 0).length;
return '../'.repeat(pathDepth);
};
// Test the examples from the original TODO comment
expect(calculateRelativePath("/sharePath")).toBe("../");
expect(calculateRelativePath("/sharePath/test")).toBe("../../");
});
});

View File

@@ -138,17 +138,21 @@ function renderImageAttachment(image: SNote, res: Response, attachmentName: stri
}
function register(router: Router) {
function renderNote(note: SNote, req: Request, res: Response) {
function renderNote(note: SNote, req: Request, res: Response) {
// Calculate the correct relative path depth based on the current request path
// We need to go up one level for each path segment in the request URL
const pathSegments = req.path.split('/').filter(segment => segment.length > 0);
const relativePath = '../'.repeat(pathSegments.length);
if (!note) {
console.log("Unable to find note ", note);
res.status(404);
renderDefault(res, "404");
renderDefault(res, "404", { relativePath, t });
return;
}
if (!checkNoteAccess(note.noteId, req, res)) {
requestCredentials(res);
return;
}
@@ -160,18 +164,20 @@ function register(router: Router) {
return;
}
const { header, content, isEmpty } = contentRenderer.getContent(note);
const { header, content, isEmpty } = contentRenderer.getContent(note, relativePath);
const subRoot = getSharedSubTreeRoot(note);
const showLoginInShareTheme = options.getOption("showLoginInShareTheme");
const opts = {
note,
header,
content,
isEmpty,
subRoot,
assetPath: isDev ? assetPath : `../${assetPath}`,
assetPath: isDev ? assetPath : `${relativePath}${assetPath}`,
assetUrlFragment,
appPath: isDev ? appPath : `../${appPath}`,
appPath: isDev ? appPath : `${relativePath}${appPath}`,
relativePath,
showLoginInShareTheme,
t,
isDev

View File

@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="shortcut icon" href="<%= relativePath %>favicon.ico">
<title><%= t("share_404.title") %></title>
</head>
<body>

View File

@@ -5,7 +5,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="<% if (note.hasRelation("shareFavicon")) { %>api/notes/<%= note.getRelation("shareFavicon").value %>/download<% } else { %>../favicon.ico<% } %>">
<link rel="shortcut icon" href="<% if (note.hasRelation("shareFavicon")) { %>api/notes/<%= note.getRelation("shareFavicon").value %>/download<% } else { %><%= relativePath %>favicon.ico<% } %>">
<script src="<%= appPath %>/share.js" type="module"></script>
<% if (!isDev && !note.isLabelTruthy("shareOmitDefaultCss")) { %>
<link href="<%= assetPath %>/src/share.css" rel="stylesheet">
@@ -56,7 +56,7 @@
</head>
<%
const customLogoId = subRoot.note.getRelation("shareLogo")?.value;
const logoUrl = customLogoId ? `api/images/${customLogoId}/image.png` : `../${assetUrlFragment}/images/icon-color.svg`;
const logoUrl = customLogoId ? `api/images/${customLogoId}/image.png` : `${relativePath}${assetUrlFragment}/images/icon-color.svg`;
const logoWidth = subRoot.note.getLabelValue("shareLogoWidth") ?? 53;
const logoHeight = subRoot.note.getLabelValue("shareLogoHeight") ?? 40;
const mobileLogoHeight = logoHeight && logoWidth ? 32 / (logoWidth / logoHeight) : "";