authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-27 20:52:38-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-27 21:30:46-08:00
log8b538e28021f73da137c8d83117f9cb391a6ec0b
tree5e635fa5ad81ce32ae978590756d3131ee0e43e4
parenta6c57e169aa8e06189d2a8e88672549731b305c0

dedupe


3 files changed, 34 insertions(+), 33 deletions(-)

apps/server/src/orchestration/Layers/ProjectionPipeline.ts+7-30
......@@ -38,6 +38,11 @@ import {
3838 OrchestrationProjectionPipeline,
3939 type OrchestrationProjectionPipelineShape,
4040} from "../Services/ProjectionPipeline.ts";
41import {
42 ATTACHMENTS_ROUTE_PREFIX,
43 IMAGE_EXTENSION_BY_MIME_TYPE,
44 SAFE_IMAGE_FILE_EXTENSIONS,
45} from "../../projectFaviconRoute.ts";
4146
4247export const ORCHESTRATION_PROJECTOR_NAMES = {
4348 projects: "projection.projects",
......@@ -61,31 +66,6 @@ interface ProjectorDefinition {
6166 ) => Effect.Effect<void, ProjectionRepositoryError>;
6267}
6368
64const ATTACHMENTS_ROUTE_PREFIX = "/attachments";
65const ATTACHMENTS_STATE_SUBDIRECTORY = "attachments";
66const 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};
77const 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
8969interface PendingAttachmentWrite {
9070 readonly absolutePath: string;
9171 readonly bytes: Uint8Array;
......@@ -133,7 +113,7 @@ const materializeAttachmentsForProjection = Effect.fn(function* (input: {
133113 if (Option.isNone(serverConfig)) return input.attachments;
134114
135115 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");
137117 const threadSegment = encodeURIComponent(input.threadId);
138118 const messageSegment = encodeURIComponent(input.messageId);
139119
......@@ -332,10 +312,7 @@ const runAttachmentSideEffects = Effect.fn(function* (input: {
332312 return;
333313 }
334314
335 const attachmentsRootDir = input.path.join(
336 serverConfig.value.stateDir,
337 ATTACHMENTS_STATE_SUBDIRECTORY,
338 );
315 const attachmentsRootDir = input.path.join(serverConfig.value.stateDir, "attachments");
339316
340317 yield* Effect.forEach(
341318 input.sideEffects.deletedThreadIds,
apps/server/src/projectFaviconRoute.ts+26
......@@ -2,6 +2,32 @@ import fs from "node:fs";
22import http from "node:http";
33import path from "node:path";
44
5export const ATTACHMENTS_ROUTE_PREFIX = "/attachments";
6
7export const IMAGE_EXTENSION_BY_MIME_TYPE: Record<string, string> = {
8 "image/png": ".png",
9 "image/jpeg": ".jpg",
10 "image/jpg": ".jpg",
11 "image/gif": ".gif",
12 "image/webp": ".webp",
13 "image/svg+xml": ".svg",
14 "image/bmp": ".bmp",
15 "image/tiff": ".tiff",
16 "image/heic": ".heic",
17};
18
19export const SAFE_IMAGE_FILE_EXTENSIONS = new Set([
20 ".jpg",
21 ".jpeg",
22 ".png",
23 ".gif",
24 ".webp",
25 ".bmp",
26 ".tiff",
27 ".svg",
28 ".ico",
29]);
30
531const FAVICON_MIME_TYPES: Record<string, string> = {
632 ".png": "image/png",
733 ".jpg": "image/jpeg",
apps/server/src/wsServer.ts+1-3
......@@ -55,9 +55,7 @@ import { clamp } from "effect/Number";
5555import { Open } from "./open";
5656import { ServerConfig } from "./config";
5757import { GitCore } from "./git/Services/GitCore.ts";
58import { tryHandleProjectFaviconRequest } from "./projectFaviconRoute";
59
60const ATTACHMENTS_ROUTE_PREFIX = "/attachments/";
58import { ATTACHMENTS_ROUTE_PREFIX, tryHandleProjectFaviconRequest } from "./projectFaviconRoute";
6159
6260/**
6361 * ServerShape - Service API for server lifecycle control.