แทรกแถวพร้อมรูปภาพ (POST /blob/insert)
endpoint สำหรับ INSERT 1 แถวที่บางคอลัมน์เป็นรูป: client ส่ง multipart (ชื่อตาราง ชื่อคอลัมน์ ค่าธรรมดา ไฟล์รูป); backend เก็บรูปลง MinIO (S3) แล้วเขียน URL ของรูป ลงคอลัมน์ ไม่ใช่ไบต์รูป. ตัดสินใจไว้ใน ADR-0010 /blob/insert เก็บรูปเป็น bytea (ล้าสมัย: โค้ดใช้ MinIO แล้ว) (ADR เดิมพูดถึง bytea — โค้ดปัจจุบันใช้ MinIO).
ทำงานอย่างไร
- route:
POST /blob/insertต้องAuth+queryWriteGuard(สิทธิ์เมนูWrite) (router.go:105-111) (สิทธิ์เมนูและ middleware RequireRight (Menu rights / permission)). Handler.Insert(blob/handler.go:60): จำกัดขนาด body 20MB (maxRequestBytes,:32) และParseMultipartFormใช้หน่วยความจำ 10MB (:33,68).- field ของฟอร์ม:
table,fieldlist(JSON array ชื่อคอลัมน์),valuelist(JSON array ค่า — ข้ามคอลัมน์ที่เป็นไฟล์),returning(ชื่อคอลัมน์ที่ต้องการคืนค่า, ต้องระบุ) และไฟล์ที่ ชื่อ part = ชื่อคอลัมน์. - ตรวจชื่อ:
NewIdentifier— ขึ้นต้นด้วยตัวอักษร/_ตามด้วยตัวอักษร ตัวเลข_เท่านั้น (domain/blob/blob.go:58-66); แล้วValidateClientInsert(table, fieldlist, level)กันตารางเงิน/ระบบ (403) (นโยบายล็อกการเขียนผ่าน Query Gateway (Query write policy / lockdown)). zipValuesToMinIO(handler.go:137) วนตามfieldlist:- คอลัมน์ที่มีไฟล์ →
readImagePartอ่านไม่เกิน 5MB (MaxImageBytes,blob.go:56) และNewImageValueตรวจ magic bytes จริง ว่าเป็น JPEG/PNG/WEBP (ไม่เชื่อ Content-Type ที่ client ส่ง) (blob.go:125-148). - ตั้ง key
"<dbname>/<table>/<yyyymmdd_hhmmss>_<sha256 16 ตัวแรก>.<ext>"(พิมพ์เล็ก; db ว่าง →smldemo, table ว่าง →images) แล้วPutObjectลง MinIO และสร้าง thumbnail JPEG (ด้านยาวสุด 320px,crm/minio.go:329) เก็บเป็น<key>.thumb.jpg(ล้มเหลวได้ ไม่ทำให้ insert ล้ม) (handler.go:155-175). - ค่าที่เขียนลงคอลัมน์ =
"/api/crm/image?key=<key>"(handler.go:177). - คอลัมน์ที่ไม่มีไฟล์ → หยิบจาก
valuelistตามลำดับ; จำนวนไม่ตรง → error.
- คอลัมน์ที่มีไฟล์ →
appblob.Service.Insert(application/blob/service.go:32):NewInsertตรวจ table/fields (ไม่ว่าง, จำนวนตรง, ไม่ซ้ำ, ชื่อถูกต้อง,returningถูกต้อง) → timeout 30 วินาที →ResolveConnection→blobsql.Gateway.Insert.blobsql.Gateway.Insert(outbound/blobsql/gateway.go:37): ประกอบINSERT INTO t (cols) VALUES ($1..) RETURNING colโดยครอบทุก identifier ด้วยpgx.Identifier.Sanitize()(:79-92) และส่งค่าเป็น parameter จริง → คืนค่าคอลัมน์returningแถวแรก; ตอบ{"id": <ค่า>}(insertResponse).
การตั้งค่า MinIO
ตัวแปร env S3_ENDPOINT, S3_BUCKET_NAME (default smlaiaccount), S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_REGION (default us-east-1) หรือชื่อ R2_* ที่เทียบเท่า (crm/minio.go:41-60) — ตั้งผ่าน env เท่านั้น (ตัวแปรสภาพแวดล้อมและการโหลด config ของ backend (Environment config)). ไคลเอนต์เซ็น AWS SigV4 เองด้วย stdlib (signedRequest, :207) และ NewHandler เรียก EnsureBucket ตอนเริ่ม (blob/handler.go:44-48).
ข้อควรระวัง
- URL ที่เก็บคือ
/api/crm/image?...แต่ route จริงที่ลงทะเบียนคือ/api/v1/crm/image(router.go:262) — น่าจะมี proxy/rewrite คั่น (ยังไม่ยืนยัน). - ไม่มีสิทธิ์รายตารางนอกจาก
ValidateClientInsert+ สิทธิ์Writeของเมนู; ตารางที่ level < 9 เขียนได้คือ allow-list เดียวกับ exec. - ถ้า INSERT ล้มหลังอัปโหลดรูปแล้ว ไฟล์ใน MinIO ค้าง (ไม่มีขั้นตอนลบ — ไม่พบในโค้ด).
- ข้อความ error ของ DB ถูกส่งกลับเป็น 400 (รูปแบบ error และ response ของ API (apperr + response helper)).
CONTEXT.mdยังเขียนว่าเก็บรูปเป็นbytea; โค้ดจริงเปลี่ยนเป็น MinIO แล้ว ให้ยึดโค้ด.
ที่มาในโค้ด
backend/internal/adapter/inbound/http/blob/handler.go:32-135— ขีดจำกัด, ฟอร์ม, ตรวจชื่อ/นโยบาย.backend/internal/adapter/inbound/http/blob/handler.go:137-205— รูป → MinIO → URL.backend/internal/domain/blob/blob.go:22-148— identifier,NewInsert,NewImageValue.backend/internal/application/blob/service.go:13-50— use case + timeout.backend/internal/adapter/outbound/blobsql/gateway.go:37-95— INSERT parameterized.backend/internal/adapter/inbound/http/crm/minio.go:41-130,317-345— ไคลเอนต์ S3 และ thumbnail.
ดูเพิ่ม
ภาพรวม Backend Go ของ SML Ai Account (Backend overview) · นโยบายล็อกการเขียนผ่าน Query Gateway (Query write policy / lockdown) · ตัวแปรสภาพแวดล้อมและการโหลด config ของ backend (Environment config) · ตัวจัดการ connection pool ต่อฐานข้อมูล (PoolManager) · ADR-0010 /blob/insert เก็บรูปเป็น bytea (ล้าสมัย: โค้ดใช้ MinIO แล้ว) · สารบัญ ADR ของ backend (Architecture Decision Records)