diff --git a/app/share/client.tsx b/app/share/client.tsx index 8e5eaaa..e34014b 100644 --- a/app/share/client.tsx +++ b/app/share/client.tsx @@ -33,6 +33,21 @@ import { } from "@/lib/share/limits"; const IMAGE_TYPES = "image/png,image/jpeg,image/webp,image/gif"; +const TEXT_FILE_TYPES = [ + "application/json", + "application/xml", + "application/yaml", + "application/toml", + "application/javascript", + "application/x-javascript", +]; +const TEXT_FILE_ACCEPT = `text/*,${TEXT_FILE_TYPES.join(",")}`; + +function isTextFile(file: File) { + return !file.type || + file.type.startsWith("text/") || + TEXT_FILE_TYPES.includes(file.type); +} function formatBytes(bytes: number) { if (bytes < 1024) return `${bytes} B`; @@ -57,20 +72,33 @@ export function ShareUploadClient() { return () => URL.revokeObjectURL(imagePreview); }, [imagePreview]); + function showError(message: string) { + setError(message); + toast.error(message); + } + async function loadTextFile(file: File) { setError(null); + if (!isTextFile(file)) { + showError("รองรับเฉพาะไฟล์ข้อความเท่านั้น"); + return; + } if (file.size > MAX_SHARE_TEXT_BYTES) { - setError("ไฟล์ต้องมีขนาดไม่เกิน 5 MiB"); + showError("ไฟล์ต้องมีขนาดไม่เกิน 5 MiB"); return; } try { const content = new TextDecoder("utf-8", { fatal: true }).decode( await file.arrayBuffer() ); + if (content.includes("\0")) { + showError("ไฟล์ข้อความต้องไม่มีอักขระ NUL"); + return; + } setText(content); toast.success(`เปิด ${file.name} แล้ว`); } catch { - setError("ไฟล์ต้องเป็นข้อความ UTF-8 ที่ถูกต้อง"); + showError("ไฟล์ต้องเป็นข้อความ UTF-8 ที่ถูกต้อง"); } } @@ -78,11 +106,11 @@ export function ShareUploadClient() { setError(null); if (!file) return; if (!IMAGE_TYPES.split(",").includes(file.type)) { - setError("รูปภาพต้องเป็น PNG, JPEG, WebP หรือ GIF"); + showError("รูปภาพต้องเป็น PNG, JPEG, WebP หรือ GIF"); return; } if (file.size > MAX_SHARE_IMAGE_BYTES) { - setError("รูปภาพต้องมีขนาดไม่เกิน 50 MiB"); + showError("รูปภาพต้องมีขนาดไม่เกิน 50 MiB"); return; } setImage(file); @@ -93,11 +121,11 @@ export function ShareUploadClient() { event.preventDefault(); setError(null); if (!text.trim()) { - setError("วางข้อความก่อนสร้างลิงก์"); + showError("วางข้อความก่อนสร้างลิงก์"); return; } if (byteCount > MAX_SHARE_TEXT_BYTES) { - setError("ข้อความต้องมีขนาดไม่เกิน 5 MiB"); + showError("ข้อความต้องมีขนาดไม่เกิน 5 MiB"); return; } @@ -110,12 +138,12 @@ export function ShareUploadClient() { try { const result = await createShareAction(formData); if (!result.success || !result.id) { - setError(result.success ? "สร้างลิงก์ไม่สำเร็จ" : result.error); + showError(result.success ? "สร้างลิงก์ไม่สำเร็จ" : result.error); return; } router.push(`/share/${result.id}`); } catch { - setError("เชื่อมต่อเซิร์ฟเวอร์ไม่สำเร็จ ลองใหม่อีกครั้ง"); + showError("เชื่อมต่อเซิร์ฟเวอร์ไม่สำเร็จ ลองใหม่อีกครั้ง"); } finally { setPending(false); } @@ -196,6 +224,7 @@ export function ShareUploadClient() { { const file = event.target.files?.[0];