| ... | @@ -38,6 +38,11 @@ import { | ... | @@ -38,6 +38,11 @@ import { |
| 38 | OrchestrationProjectionPipeline, | 38 | OrchestrationProjectionPipeline, |
| 39 | type OrchestrationProjectionPipelineShape, | 39 | type OrchestrationProjectionPipelineShape, |
| 40 | } from "../Services/ProjectionPipeline.ts"; | 40 | } from "../Services/ProjectionPipeline.ts"; |
| | 41 | import { |
| | 42 | ATTACHMENTS_ROUTE_PREFIX, |
| | 43 | IMAGE_EXTENSION_BY_MIME_TYPE, |
| | 44 | SAFE_IMAGE_FILE_EXTENSIONS, |
| | 45 | } from "../../projectFaviconRoute.ts"; |
| 41 | | 46 | |
| 42 | export const ORCHESTRATION_PROJECTOR_NAMES = { | 47 | export const ORCHESTRATION_PROJECTOR_NAMES = { |
| 43 | projects: "projection.projects", | 48 | projects: "projection.projects", |
| ... | @@ -61,31 +66,6 @@ interface ProjectorDefinition { | ... | @@ -61,31 +66,6 @@ interface ProjectorDefinition { |
| 61 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 66 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 62 | } | 67 | } |
| 63 | | 68 | |
| 64 | const ATTACHMENTS_ROUTE_PREFIX = "/attachments"; | | |
| 65 | const ATTACHMENTS_STATE_SUBDIRECTORY = "attachments"; | | |
| 66 | const IMAGE_EXTENSION_BY_MIME_TYPE: Record<string, string> = { | | |
| 67 | "image/png": ".png", | | |
| 68 | "image/jpeg": ".jpg", | | |
| 69 | "image/jpg": ".jpg", | | |
| 70 | "image/gif": ".gif", | | |
| 71 | "image/webp": ".webp", | | |
| 72 | "image/svg+xml": ".svg", | | |
| 73 | "image/bmp": ".bmp", | | |
| 74 | "image/tiff": ".tiff", | | |
| 75 | "image/heic": ".heic", | | |
| 76 | }; | | |
| 77 | const SAFE_IMAGE_FILE_EXTENSIONS = new Set([ | | |
| 78 | ".jpg", | | |
| 79 | ".jpeg", | | |
| 80 | ".png", | | |
| 81 | ".gif", | | |
| 82 | ".webp", | | |
| 83 | ".bmp", | | |
| 84 | ".tiff", | | |
| 85 | ".svg", | | |
| 86 | ".ico", | | |
| 87 | ]); | | |
| 88 | | | |
| 89 | interface PendingAttachmentWrite { | 69 | interface PendingAttachmentWrite { |
| 90 | readonly absolutePath: string; | 70 | readonly absolutePath: string; |
| 91 | readonly bytes: Uint8Array; | 71 | readonly bytes: Uint8Array; |
| ... | @@ -133,7 +113,7 @@ const materializeAttachmentsForProjection = Effect.fn(function* (input: { | ... | @@ -133,7 +113,7 @@ const materializeAttachmentsForProjection = Effect.fn(function* (input: { |
| 133 | if (Option.isNone(serverConfig)) return input.attachments; | 113 | if (Option.isNone(serverConfig)) return input.attachments; |
| 134 | | 114 | |
| 135 | const path = yield* Path.Path; | 115 | const path = yield* Path.Path; |
| 136 | const attachmentsRootDir = path.join(serverConfig.value.stateDir, ATTACHMENTS_STATE_SUBDIRECTORY); | 116 | const attachmentsRootDir = path.join(serverConfig.value.stateDir, "attachments"); |
| 137 | const threadSegment = encodeURIComponent(input.threadId); | 117 | const threadSegment = encodeURIComponent(input.threadId); |
| 138 | const messageSegment = encodeURIComponent(input.messageId); | 118 | const messageSegment = encodeURIComponent(input.messageId); |
| 139 | | 119 | |
| ... | @@ -332,10 +312,7 @@ const runAttachmentSideEffects = Effect.fn(function* (input: { | ... | @@ -332,10 +312,7 @@ const runAttachmentSideEffects = Effect.fn(function* (input: { |
| 332 | return; | 312 | return; |
| 333 | } | 313 | } |
| 334 | | 314 | |
| 335 | const attachmentsRootDir = input.path.join( | 315 | const attachmentsRootDir = input.path.join(serverConfig.value.stateDir, "attachments"); |
| 336 | serverConfig.value.stateDir, | | |
| 337 | ATTACHMENTS_STATE_SUBDIRECTORY, | | |
| 338 | ); | | |
| 339 | | 316 | |
| 340 | yield* Effect.forEach( | 317 | yield* Effect.forEach( |
| 341 | input.sideEffects.deletedThreadIds, | 318 | input.sideEffects.deletedThreadIds, |