From b52518f85fa3f11a6a405e3bebaf908ebdc5add7 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 27 Feb 2026 21:12:01 -0800 Subject: [PATCH] Flatten attachment paths with message-prefixed filenames - Write projected attachments under the thread directory using `messageSegment-fileName` - Avoid nested per-message folders while preserving unique, deterministic file paths - Update projection pipeline tests to match new attachment URLs and on-disk paths --- .../Layers/ProjectionPipeline.test.ts | 25 +++++++------------ .../Layers/ProjectionPipeline.ts | 5 ++-- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts b/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts index abb411b724530f3d83fbb03d18afe9f4144fe973..39b5929deb4276ca7bccce8e9b0948f212580028 100644 --- a/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts +++ b/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts @@ -230,7 +230,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => { name: "example.png", mimeType: "image/png", sizeBytes: 5, - dataUrl: "/attachments/thread-attachments/message-attachments/0.png", + dataUrl: "/attachments/thread-attachments/message-attachments-0.png", }, ]); @@ -238,8 +238,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => { stateDir, "attachments", "thread-attachments", - "message-attachments", - "0.png", + "message-attachments-0.png", ); assert.equal(fs.existsSync(attachmentPath), true); assert.deepEqual(fs.readFileSync(attachmentPath), Buffer.from("SGVsbG8=", "base64")); @@ -325,7 +324,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => { name: "untrusted.exe", mimeType: "image/x-unknown", sizeBytes: 5, - dataUrl: "/attachments/thread-attachments-safe/message-attachments-safe/0.bin", + dataUrl: "/attachments/thread-attachments-safe/message-attachments-safe-0.bin", }, { type: "image", @@ -340,8 +339,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => { stateDir, "attachments", "thread-attachments-safe", - "message-attachments-safe", - "0.bin", + "message-attachments-safe-0.bin", ); assert.equal(fs.existsSync(firstAttachmentPath), true); assert.deepEqual(fs.readFileSync(firstAttachmentPath), Buffer.from("SGVsbG8=", "base64")); @@ -350,8 +348,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => { stateDir, "attachments", "thread-attachments-safe", - "message-attachments-safe", - "1.png", + "message-attachments-safe-1.png", ); assert.equal(fs.existsSync(secondAttachmentPath), false); fs.rmSync(stateDir, { recursive: true, force: true }); @@ -614,8 +611,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => { stateDir, "attachments", "thread-overwrite", - "message-overwrite", - "0.png", + "message-overwrite-0.png", ); assert.equal(fs.existsSync(attachmentPath), true); assert.deepEqual(fs.readFileSync(attachmentPath), Buffer.from("V29ybGQ=", "base64")); @@ -753,8 +749,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => { stateDir, "attachments", "thread-rollback", - "message-rollback", - "0.png", + "message-rollback-0.png", ); assert.equal(fs.existsSync(attachmentPath), false); yield* sql`DROP TRIGGER IF EXISTS fail_thread_messages_projection_state_update`; @@ -945,15 +940,13 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => { stateDir, "attachments", "thread-revert-files", - "message-keep", - "0.png", + "message-keep-0.png", ); const removePath = path.join( stateDir, "attachments", "thread-revert-files", - "message-remove", - "0.png", + "message-remove-0.png", ); assert.equal(fs.existsSync(keepPath), true); assert.equal(fs.existsSync(removePath), true); diff --git a/apps/server/src/orchestration/Layers/ProjectionPipeline.ts b/apps/server/src/orchestration/Layers/ProjectionPipeline.ts index 5474c602bb70e40ff378950226e89d60c8ccd1ec..2ded57a17db65fd2699ad03b0b636a67f31c9b90 100644 --- a/apps/server/src/orchestration/Layers/ProjectionPipeline.ts +++ b/apps/server/src/orchestration/Layers/ProjectionPipeline.ts @@ -140,8 +140,9 @@ const materializeAttachmentsForProjection = Effect.fn(function* (input: { ...attachment, mimeType: parsed.mimeType, })}`; - const relativePath = `${threadSegment}/${messageSegment}/${fileName}`; - const absolutePath = path.join(attachmentsRootDir, threadSegment, messageSegment, fileName); + const uniqueFileName = `${messageSegment}-${fileName}`; + const relativePath = `${threadSegment}/${uniqueFileName}`; + const absolutePath = path.join(attachmentsRootDir, threadSegment, uniqueFileName); input.stageFileWrite({ absolutePath, bytes }); return { -- 2.54.0