authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-27 21:12:01-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-27 21:30:46-08:00
logb52518f85fa3f11a6a405e3bebaf908ebdc5add7
treee80b24ccd8d5421fc4a2717e2cda06192b50b7a7
parent3e99019af85e67cdbc5be0ef95b71662ebe683e7

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

2 files changed, 12 insertions(+), 18 deletions(-)

apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts+9-16
......@@ -230,7 +230,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
230230 name: "example.png",
231231 mimeType: "image/png",
232232 sizeBytes: 5,
233 dataUrl: "/attachments/thread-attachments/message-attachments/0.png",
233 dataUrl: "/attachments/thread-attachments/message-attachments-0.png",
234234 },
235235 ]);
236236
......@@ -238,8 +238,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
238238 stateDir,
239239 "attachments",
240240 "thread-attachments",
241 "message-attachments",
242 "0.png",
241 "message-attachments-0.png",
243242 );
244243 assert.equal(fs.existsSync(attachmentPath), true);
245244 assert.deepEqual(fs.readFileSync(attachmentPath), Buffer.from("SGVsbG8=", "base64"));
......@@ -325,7 +324,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
325324 name: "untrusted.exe",
326325 mimeType: "image/x-unknown",
327326 sizeBytes: 5,
328 dataUrl: "/attachments/thread-attachments-safe/message-attachments-safe/0.bin",
327 dataUrl: "/attachments/thread-attachments-safe/message-attachments-safe-0.bin",
329328 },
330329 {
331330 type: "image",
......@@ -340,8 +339,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
340339 stateDir,
341340 "attachments",
342341 "thread-attachments-safe",
343 "message-attachments-safe",
344 "0.bin",
342 "message-attachments-safe-0.bin",
345343 );
346344 assert.equal(fs.existsSync(firstAttachmentPath), true);
347345 assert.deepEqual(fs.readFileSync(firstAttachmentPath), Buffer.from("SGVsbG8=", "base64"));
......@@ -350,8 +348,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
350348 stateDir,
351349 "attachments",
352350 "thread-attachments-safe",
353 "message-attachments-safe",
354 "1.png",
351 "message-attachments-safe-1.png",
355352 );
356353 assert.equal(fs.existsSync(secondAttachmentPath), false);
357354 fs.rmSync(stateDir, { recursive: true, force: true });
......@@ -614,8 +611,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
614611 stateDir,
615612 "attachments",
616613 "thread-overwrite",
617 "message-overwrite",
618 "0.png",
614 "message-overwrite-0.png",
619615 );
620616 assert.equal(fs.existsSync(attachmentPath), true);
621617 assert.deepEqual(fs.readFileSync(attachmentPath), Buffer.from("V29ybGQ=", "base64"));
......@@ -753,8 +749,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
753749 stateDir,
754750 "attachments",
755751 "thread-rollback",
756 "message-rollback",
757 "0.png",
752 "message-rollback-0.png",
758753 );
759754 assert.equal(fs.existsSync(attachmentPath), false);
760755 yield* sql`DROP TRIGGER IF EXISTS fail_thread_messages_projection_state_update`;
......@@ -945,15 +940,13 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
945940 stateDir,
946941 "attachments",
947942 "thread-revert-files",
948 "message-keep",
949 "0.png",
943 "message-keep-0.png",
950944 );
951945 const removePath = path.join(
952946 stateDir,
953947 "attachments",
954948 "thread-revert-files",
955 "message-remove",
956 "0.png",
949 "message-remove-0.png",
957950 );
958951 assert.equal(fs.existsSync(keepPath), true);
959952 assert.equal(fs.existsSync(removePath), true);
apps/server/src/orchestration/Layers/ProjectionPipeline.ts+3-2
......@@ -140,8 +140,9 @@ const materializeAttachmentsForProjection = Effect.fn(function* (input: {
140140 ...attachment,
141141 mimeType: parsed.mimeType,
142142 })}`;
143 const relativePath = `${threadSegment}/${messageSegment}/${fileName}`;
144 const absolutePath = path.join(attachmentsRootDir, threadSegment, messageSegment, fileName);
143 const uniqueFileName = `${messageSegment}-${fileName}`;
144 const relativePath = `${threadSegment}/${uniqueFileName}`;
145 const absolutePath = path.join(attachmentsRootDir, threadSegment, uniqueFileName);
145146 input.stageFileWrite({ absolutePath, bytes });
146147
147148 return {