authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-27 20:48:48-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-27 21:30:46-08:00
log6f92f0a66855ef6745814b0a91cffc2d588e0777
tree4401cddfa575cdc34f5baaaa7785229bb11d13b9
parent35817601ff3f3d4f2e27ea8c5c8a08a8b0926dc3

kewl


2 files changed, 21 insertions(+), 29 deletions(-)

apps/server/src/orchestration/Layers/ProjectionPipeline.ts+20-26
......@@ -1,3 +1,4 @@
1import Mime from "@effect/platform-node/Mime";
12import {
23 ApprovalRequestId,
34 type ChatAttachment,
......@@ -112,20 +113,13 @@ function parseBase64DataUrl(
112113}
113114
114115function inferImageExtension(attachment: Extract<ChatAttachment, { type: "image" }>): string {
115 const normalizedMimeType = attachment.mimeType.trim().toLowerCase();
116 const normalizedMimeType = attachment.mimeType.toLowerCase();
116117 if (normalizedMimeType.startsWith("image/")) {
117118 const fromMime = IMAGE_EXTENSION_BY_MIME_TYPE[normalizedMimeType];
118 if (fromMime) {
119 return fromMime;
120 }
121 }
122
123 const fromName = attachment.name.includes(".")
124 ? `.${attachment.name.split(".").slice(-1)[0]?.toLowerCase() ?? ""}`
125 : "";
126 if (/^\.[a-z0-9]{1,12}$/.test(fromName) && SAFE_IMAGE_FILE_EXTENSIONS.has(fromName)) {
127 return fromName;
119 if (fromMime) return fromMime;
128120 }
121 const ext = Mime.getExtension(attachment.mimeType);
122 if (ext && SAFE_IMAGE_FILE_EXTENSIONS.has(ext)) return ext;
129123 return ".bin";
130124}
131125
......@@ -135,14 +129,10 @@ const materializeAttachmentsForProjection = Effect.fn(function* (input: {
135129 readonly attachments: ReadonlyArray<ChatAttachment>;
136130 readonly stageFileWrite: (pendingWrite: PendingAttachmentWrite) => void;
137131}) {
138 if (input.attachments.length === 0) {
139 return [] as ReadonlyArray<ChatAttachment>;
140 }
132 if (input.attachments.length === 0) return [];
141133
142134 const serverConfig = yield* Effect.serviceOption(ServerConfig);
143 if (Option.isNone(serverConfig)) {
144 return input.attachments;
145 }
135 if (Option.isNone(serverConfig)) return input.attachments;
146136
147137 const path = yield* Path.Path;
148138 const attachmentsRootDir = path.join(serverConfig.value.stateDir, ATTACHMENTS_STATE_SUBDIRECTORY);
......@@ -166,9 +156,7 @@ const materializeAttachmentsForProjection = Effect.fn(function* (input: {
166156 }
167157
168158 const bytes = Buffer.from(parsed.base64, "base64");
169 if (bytes.byteLength === 0) {
170 return attachment;
171 }
159 if (bytes.byteLength === 0) return attachment;
172160
173161 const fileName = `${index}${inferImageExtension({
174162 ...attachment,
......@@ -375,9 +363,7 @@ const runAttachmentSideEffects = Effect.fn(function* (input: {
375363 entries,
376364 (entry) =>
377365 Effect.gen(function* () {
378 const threadRelativePath = entry
379 .replace(/^[/\\]+/, "")
380 .replace(/\\/g, "/");
366 const threadRelativePath = entry.replace(/^[/\\]+/, "").replace(/\\/g, "/");
381367 if (threadRelativePath.length === 0 || threadRelativePath.startsWith("..")) {
382368 return;
383369 }
......@@ -411,7 +397,9 @@ const runAttachmentSideEffects = Effect.fn(function* (input: {
411397 writesByPath.entries(),
412398 ([absolutePath, bytes]) =>
413399 Effect.gen(function* () {
414 yield* input.fileSystem.makeDirectory(input.path.dirname(absolutePath), { recursive: true });
400 yield* input.fileSystem.makeDirectory(input.path.dirname(absolutePath), {
401 recursive: true,
402 });
415403 const alreadyExists = yield* input.fileSystem
416404 .exists(absolutePath)
417405 .pipe(Effect.catch(() => Effect.succeed(false)));
......@@ -615,7 +603,10 @@ const makeOrchestrationProjectionPipeline = Effect.gen(function* () {
615603 }
616604 });
617605
618 const applyThreadMessagesProjection: ProjectorDefinition["apply"] = (event, attachmentSideEffects) =>
606 const applyThreadMessagesProjection: ProjectorDefinition["apply"] = (
607 event,
608 attachmentSideEffects,
609 ) =>
619610 Effect.gen(function* () {
620611 switch (event.type) {
621612 case "thread.message-sent": {
......@@ -778,7 +769,10 @@ const makeOrchestrationProjectionPipeline = Effect.gen(function* () {
778769 });
779770 });
780771
781 const applyThreadTurnsProjection: ProjectorDefinition["apply"] = (event, _attachmentSideEffects) =>
772 const applyThreadTurnsProjection: ProjectorDefinition["apply"] = (
773 event,
774 _attachmentSideEffects,
775 ) =>
782776 Effect.gen(function* () {
783777 switch (event.type) {
784778 case "thread.turn-start-requested": {
apps/server/src/wsServer.ts+1-3
......@@ -330,9 +330,7 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
330330 const staticRequestPath = url.pathname === "/" ? "/index.html" : url.pathname;
331331 const rawStaticRelativePath = staticRequestPath.replace(/^[/\\]+/, "");
332332 const hasRawLeadingParentSegment = rawStaticRelativePath.startsWith("..");
333 const staticRelativePath = path
334 .normalize(rawStaticRelativePath)
335 .replace(/^[/\\]+/, "");
333 const staticRelativePath = path.normalize(rawStaticRelativePath).replace(/^[/\\]+/, "");
336334 const hasPathTraversalSegment = staticRelativePath.startsWith("..");
337335 if (
338336 staticRelativePath.length === 0 ||