mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: align chat attachment picker with allowed file types (#24917)
The agent chat composer only advertised image uploads to the OS file picker and filtered drag-and-drop and paste events to `image/*`, even though the backend accepts text, CSV, JSON, PDF, and a narrower set of image types. Move the allowed chat attachment media types into `codersdk` so the frontend picker and backend enforcement share one source of truth. Use the generated TypeScript list to drive the file input `accept` attribute and the drag-and-drop and paste filters, while adding common text extensions so platforms without MIME registrations still surface those files in the picker.
This commit is contained in:
+19
-15
@@ -13,6 +13,8 @@ import (
|
||||
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
const MaxStoredFileNameBytes = 255
|
||||
@@ -28,17 +30,17 @@ var (
|
||||
|
||||
utf8BOM = []byte{0xEF, 0xBB, 0xBF}
|
||||
|
||||
allowedStoredMediaTypes = map[string]struct{}{
|
||||
"image/png": {},
|
||||
"image/jpeg": {},
|
||||
"image/gif": {},
|
||||
"image/webp": {},
|
||||
"text/plain": {},
|
||||
"text/markdown": {},
|
||||
"text/csv": {},
|
||||
"application/json": {},
|
||||
"application/pdf": {},
|
||||
}
|
||||
// allowedStoredMediaTypes is derived from codersdk.AllChatAttachmentMediaTypes
|
||||
// so the frontend file picker and the server enforcement share a single
|
||||
// source of truth. Do not edit this map directly; add new entries to the
|
||||
// codersdk const block instead.
|
||||
allowedStoredMediaTypes = func() map[string]struct{} {
|
||||
m := make(map[string]struct{}, len(codersdk.AllChatAttachmentMediaTypes))
|
||||
for _, t := range codersdk.AllChatAttachmentMediaTypes {
|
||||
m[string(t)] = struct{}{}
|
||||
}
|
||||
return m
|
||||
}()
|
||||
|
||||
recordingArtifactMediaTypes = map[string]struct{}{
|
||||
"video/mp4": {},
|
||||
@@ -139,14 +141,16 @@ func PrepareRecordingArtifact(name, expectedMediaType string, data []byte) (stor
|
||||
|
||||
// IsCompatibleUploadMediaType reports whether an upload request that declared
|
||||
// declaredMediaType may be stored as storedMediaType after byte
|
||||
// classification. Exact matches are always compatible; the compatibility
|
||||
// table only covers explicit refinements like text/plain uploads that safely
|
||||
// store as richer text subtypes.
|
||||
// classification. Exact matches are always compatible. Clients that declare
|
||||
// application/octet-stream are treated as "unknown", so the classified bytes
|
||||
// decide the stored type. The compatibility table also covers explicit
|
||||
// refinements like text/plain uploads that safely store as richer text
|
||||
// subtypes.
|
||||
func IsCompatibleUploadMediaType(declaredMediaType, storedMediaType string) bool {
|
||||
declaredMediaType = BaseMediaType(declaredMediaType)
|
||||
storedMediaType = BaseMediaType(storedMediaType)
|
||||
|
||||
if declaredMediaType == storedMediaType {
|
||||
if declaredMediaType == storedMediaType || declaredMediaType == "application/octet-stream" {
|
||||
return true
|
||||
}
|
||||
if declaredMediaType != "text/plain" {
|
||||
|
||||
@@ -270,6 +270,18 @@ func TestIsCompatibleUploadMediaType(t *testing.T) {
|
||||
stored: "text/plain",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "OctetStreamMatchesPNG",
|
||||
declared: "application/octet-stream",
|
||||
stored: "image/png",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "OctetStreamMatchesJSON",
|
||||
declared: "application/octet-stream",
|
||||
stored: "application/json",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "TextPlainRefinesToMarkdown",
|
||||
declared: "text/plain",
|
||||
|
||||
Reference in New Issue
Block a user