Skip to content

Commit ce49d41

Browse files
committed
Centralize MIME type allowlists and update usage
Introduces src/lib/constants/mime.ts to centralize text and image MIME allowlists. Updates components and server endpoint logic to use these constants for file type filtering, improving consistency and maintainability. Also fixes a minor label and MIME string typo.
1 parent e9854ee commit ce49d41

6 files changed

Lines changed: 38 additions & 20 deletions

File tree

src/lib/components/chat/ChatInput.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import CarbonLink from "~icons/carbon/link";
1212
import CarbonChevronRight from "~icons/carbon/chevron-right";
1313
import UrlFetchModal from "./UrlFetchModal.svelte";
14+
import { TEXT_MIME_ALLOWLIST, IMAGE_MIME_ALLOWLIST_DEFAULT } from "$lib/constants/mime";
1415
1516
import { isVirtualKeyboard } from "$lib/utils/isVirtualKeyboard";
1617
import { requireAuthUser } from "$lib/utils/auth";
@@ -73,13 +74,14 @@
7374
function openFilePickerText() {
7475
const textAccept =
7576
mimeTypes.filter((m) => !(m === "image/*" || m.startsWith("image/"))).join(",") ||
76-
"text/*,application/json,application/xml,application/csv";
77+
TEXT_MIME_ALLOWLIST.join(",");
7778
openPickerWithAccept(textAccept);
7879
}
7980
8081
function openFilePickerImage() {
8182
const imageAccept =
82-
mimeTypes.filter((m) => m === "image/*" || m.startsWith("image/")).join(",") || "image/*";
83+
mimeTypes.filter((m) => m === "image/*" || m.startsWith("image/")).join(",") ||
84+
IMAGE_MIME_ALLOWLIST_DEFAULT.join(",");
8385
openPickerWithAccept(imageAccept);
8486
}
8587

src/lib/components/chat/ChatWindow.svelte

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -232,18 +232,15 @@
232232
let modelIsMultimodal = $derived((modelIsMultimodalOverride ?? currentModel.multimodal) === true);
233233
234234
// Always allow common text-like files; add images only when model is multimodal
235-
const ALWAYS_TEXT_MIME_TYPES = [
236-
"text/*",
237-
"application/json",
238-
"application/xml",
239-
"application/csv",
240-
];
235+
import { TEXT_MIME_ALLOWLIST, IMAGE_MIME_ALLOWLIST_DEFAULT } from "$lib/constants/mime";
241236
242237
let activeMimeTypes = $derived(
243238
Array.from(
244239
new Set([
245-
...ALWAYS_TEXT_MIME_TYPES,
246-
...(modelIsMultimodal ? (currentModel.multimodalAcceptedMimetypes ?? ["image/*"]) : []),
240+
...TEXT_MIME_ALLOWLIST,
241+
...(modelIsMultimodal
242+
? currentModel.multimodalAcceptedMimetypes ?? [...IMAGE_MIME_ALLOWLIST_DEFAULT]
243+
: []),
247244
])
248245
)
249246
);

src/lib/components/chat/UploadedFile.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@
182182
{/if}
183183
</dl>
184184
</div>
185-
{:else if file.mime === "octet-stream"}
185+
{:else if file.mime === "application/octet-stream"}
186186
<div
187187
class="flex h-14 w-72 items-center gap-2 overflow-hidden rounded-xl border border-gray-200 bg-white p-2 dark:border-gray-800 dark:bg-gray-900"
188188
class:file-hoverable={isClickable}

src/lib/components/chat/UrlFetchModal.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@
136136

137137
<div class="flex flex-col gap-2">
138138
<label class="text-sm text-gray-600 dark:text-gray-400" for="fetch-url-input"
139-
>HTTPS URL</label
139+
>Enter URL</label
140140
>
141141
<input
142142
id="fetch-url-input"

src/lib/constants/mime.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Centralized MIME allowlists used across client and server
2+
// Keep these lists minimal and consistent with server processing.
3+
4+
export const TEXT_MIME_ALLOWLIST = [
5+
"text/*",
6+
"application/json",
7+
"application/xml",
8+
"application/csv",
9+
] as const;
10+
11+
export const IMAGE_MIME_ALLOWLIST_DEFAULT = [
12+
"image/jpeg",
13+
"image/png",
14+
] as const;
15+

src/lib/server/endpoints/openai/endpointOai.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { config } from "$lib/server/config";
1414
import type { Endpoint } from "../endpoints";
1515
import type OpenAI from "openai";
1616
import { createImageProcessorOptionsValidator, makeImageProcessor } from "../images";
17+
import { TEXT_MIME_ALLOWLIST } from "$lib/constants/mime";
1718
import type { MessageFile } from "$lib/types/Message";
1819
import type { EndpointMessage } from "../endpoints";
1920
// uuid import removed (no tool call ids)
@@ -282,14 +283,17 @@ async function prepareFiles(
282283
textContent: string;
283284
}> {
284285
// Separate image and text files
285-
const imageFiles = files.filter((file) => file.mime.startsWith("image/"));
286-
const textFiles = files.filter(
287-
(file) =>
288-
file.mime.startsWith("text/") ||
289-
file.mime === "application/json" ||
290-
file.mime === "application/xml" ||
291-
file.mime === "application/csv"
292-
);
286+
const imageFiles = files.filter((file) => file.mime.startsWith("image/"));
287+
const textFiles = files.filter((file) => {
288+
const mime = (file.mime || "").toLowerCase();
289+
const [fileType, fileSubtype] = mime.split("/");
290+
return TEXT_MIME_ALLOWLIST.some((allowed) => {
291+
const [type, subtype] = allowed.toLowerCase().split("/");
292+
const typeOk = type === "*" || type === fileType;
293+
const subOk = subtype === "*" || subtype === fileSubtype;
294+
return typeOk && subOk;
295+
});
296+
});
293297

294298
// Process images if multimodal is enabled
295299
let imageParts: OpenAI.Chat.Completions.ChatCompletionContentPartImage[] = [];

0 commit comments

Comments
 (0)