diff --git a/server/src/__tests__/plugin-local-folders.test.ts b/server/src/__tests__/plugin-local-folders.test.ts index 89e00eff966..073220c4b7d 100644 --- a/server/src/__tests__/plugin-local-folders.test.ts +++ b/server/src/__tests__/plugin-local-folders.test.ts @@ -10,6 +10,7 @@ import { preparePluginLocalFolder, readPluginLocalFolderText, resolvePluginLocalFolderPath, + deletePluginLocalFolderFile, writePluginLocalFolderTextAtomic, } from "../services/plugin-local-folders.js"; @@ -218,6 +219,16 @@ describe("plugin local folders", () => { expect(leftovers.filter((name) => name.includes(".paperclip-"))).toEqual([]); }); + it("returns the real folder key after deleting a file", async () => { + const root = await makeRoot(); + await fs.writeFile(path.join(root, "stale.md"), "delete me", "utf8"); + + const status = await deletePluginLocalFolderFile(root, "stale.md", "content-root"); + + expect(status.folderKey).toBe("content-root"); + await expect(fs.stat(path.join(root, "stale.md"))).rejects.toMatchObject({ code: "ENOENT" }); + }); + it("lists nested local folder entries without following symlink escapes", async () => { const root = await makeRoot(); const outside = await makeRoot(); diff --git a/server/src/home-paths.ts b/server/src/home-paths.ts index f12464d1fe8..0a5be471a9e 100644 --- a/server/src/home-paths.ts +++ b/server/src/home-paths.ts @@ -25,8 +25,15 @@ export { resolvePaperclipSpaceRoot, }; +const defaultConfigPathCache = new Map(); + export function resolveDefaultConfigPath(): string { - return resolvePaperclipConfigPathForInstance(); + const cacheKey = `${process.env.PAPERCLIP_HOME ?? ""}\0${process.env.PAPERCLIP_INSTANCE_ID ?? ""}`; + const cached = defaultConfigPathCache.get(cacheKey); + if (cached) return cached; + const resolved = resolvePaperclipConfigPathForInstance(); + defaultConfigPathCache.set(cacheKey, resolved); + return resolved; } export function resolveDefaultEmbeddedPostgresDir(): string { diff --git a/server/src/services/plugin-host-services.ts b/server/src/services/plugin-host-services.ts index ba4b7c4c147..3d4c8947f0a 100644 --- a/server/src/services/plugin-host-services.ts +++ b/server/src/services/plugin-host-services.ts @@ -897,7 +897,7 @@ export function buildHostServices( const companyId = ensureCompanyId(params.companyId); const status = await inspectStoredLocalFolder(companyId, params.folderKey); assertWritableConfiguredLocalFolder(status); - await deletePluginLocalFolderFile(status.realPath!, params.relativePath); + await deletePluginLocalFolderFile(status.realPath!, params.relativePath, params.folderKey); return inspectStoredLocalFolder(companyId, params.folderKey); }, }, diff --git a/server/src/services/plugin-local-folders.ts b/server/src/services/plugin-local-folders.ts index 93548baea62..049b6e668d9 100644 --- a/server/src/services/plugin-local-folders.ts +++ b/server/src/services/plugin-local-folders.ts @@ -541,6 +541,7 @@ export async function writePluginLocalFolderTextAtomic( export async function deletePluginLocalFolderFile( rootPath: string, relativePath: string, + folderKey: string, ) { const rootRealPath = await fs.realpath(rootPath); let resolved: Awaited>; @@ -553,7 +554,7 @@ export async function deletePluginLocalFolderFile( const code = typeof error === "object" && error && "code" in error ? String((error as { code?: unknown }).code) : ""; if (code !== "ENOENT") throw error; return inspectPluginLocalFolder({ - folderKey: "delete-result", + folderKey, storedConfig: { path: rootPath, access: "readWrite", @@ -578,7 +579,7 @@ export async function deletePluginLocalFolderFile( } return inspectPluginLocalFolder({ - folderKey: "delete-result", + folderKey, storedConfig: { path: rootPath, access: "readWrite",