สมอง SML

คลังความรู้ / API & Process actions

#api#query

API /query/exec และ /query/execBatch

ประตูเขียนข้อมูลทั่วไป (INSERT/UPDATE/DELETE) สำหรับหน้าจอที่ยังไม่มี process endpoint ของตัวเอง. ผ่านสามชั้นตรวจ: (1) สิทธิ์ตามชนิดคำสั่งที่ router, (2) รูปแบบ SQL ที่ domain, (3) "write lockdown" ที่ห้ามแตะตารางเงิน/สต็อก/GL จากเบราว์เซอร์. เหตุผลอ่าน ADR-0006 /query/exec รับเฉพาะ DML + ตารางระบบต้อง level>=9 (ถูกแทนด้วย ADR-0011), ADR-0009 /query/execBatch เขียนหลาย statement ใน transaction เดียว (atomic), ADR-0011 ล็อก SQL เขียนจาก browser: ตารางเงิน/สต๊อก/GL ผ่าน /api/process เท่านั้น; ตารางเงินให้ใช้ API เครื่องยนต์ process /process/* — เส้นทาง สิทธิ์ ขนาดบอดี้ (Process engine run API) แทน; ฝั่งอ่านดู API ประตูอ่านข้อมูล /query — list, limit, batch (Query gateway read endpoints).

ชั้นที่ 1: router และ right (router.go:85-102)

  • Auth (Bearer access token) แล้วตามด้วย queryWriteGuard = RequireRight(ex.Rights, processhttp.QueryRequiredRight) — ตั้งเมื่อมี extra[0].Rights (router.go:85-87) ไม่งั้น queryWriteGuard เป็น nil และไม่ตรวจ right
  • QueryRequiredRight (process/permission.go:135-161) ตัดสิน right จากคำกริยาแรกของ SQL: insert -> Add, update -> Edit, delete -> Delete, อื่น ๆ -> Write. สำหรับ execBatch: ทุกคำสั่งเป็น delete -> Delete; คำสั่งแรก insert -> Add; update -> Edit. คำสั่งเขียน sml_audit_log แยกชนิดจาก values (1|2|3,. ผู้ใช้ที่มีสิทธิ์เมนูระดับ Admin (rights.Admin) ผ่านทุกชนิด; Write = Add หรือ Edit (middleware/permission.go:43-60) (ดู การตรวจสิทธิ์เมนูของ route process (Process route rights), สิทธิ์เมนูและ middleware RequireRight (Menu rights / permission))
  • ไม่ผ่านสิทธิ์ = 403 พร้อมข้อความไทยจาก deniedMessages (middleware/permission.go:27); peekJSON อ่านบอดี้สูงสุด 1 MB เพื่อดูคำสั่งแล้วคืนบอดี้ให้ handler (permission.go:231)

ชั้นที่ 2: รูปแบบ SQL (domain/query/query.go)

  • NewWriteStatement (query.go:335): รับคำสั่งเดียวที่ขึ้นต้น INSERT/UPDATE/DELETE หรือ WITH ... <DML>; ปฏิเสธ DDL/คำสั่งผู้ดูแล (ErrNotWriteOnly = 400)
  • ValidateUserLevel (query.go:361): level < 9 ห้ามเขียน sml_user_list, sml_database_list*, erp_option (ErrForbiddenTable -> 403)
  • ชุด execBatch: NewWriteBatchStatements (query.go:394) ไม่เกิน 1000 คำสั่ง (MaxExecBatchStatements, query.go:77) และรวมไม่เกิน 2 MB (MaxExecBatchBytes, query.go:81)

ชั้นที่ 3: write lockdown (เฉพาะคำขอจากเบราว์เซอร์ ClientRequest:true)

ValidateClientPolicy (domain/query/write_policy.go:77-103):

  1. SQL ที่ใช้ identifier แบบ U&"..." หรือหาตารางเป้าหมายไม่ได้ = ErrAdminOnlyWrite
  2. ตารางเงิน/สต็อก/GL (ตระกูล ic_trans*, ap_ar_trans*, as_trans*, cb_trans*, cb_chq_list, gl_journal* และอื่น ๆ ตาม regex moneyTablePattern ที่ write_policy.go:24) หรือ ic_inventory ที่แก้คอลัมน์ยอดคงเหลือ (balance_qty, average_cost ฯลฯ) = ErrMoneyTableViaExec ข้อความ "…only be changed through /api/process"
  3. level < 9 เขียนได้เฉพาะตารางใน allow-list เดิม (write_policy_legacy.go:9) นอกนั้น ErrAdminOnlyWrite
  4. ทั้งสองกรณีแมปเป็น KindForbidden = 403 (service.go:104-112)

POST /query/exec

  • Request: {"sql":"UPDATE ic_inventory SET name_1 = '<ชื่อ>' WHERE code = '<itemCode>' RETURNING code"} (handler.go:227)
  • Response 200: {"rowsAffected":1,"rows":[{...}]} (rows คืนเมื่อมี RETURNING)
  • Errors: 400 SQL ไม่ใช่ DML เดี่ยว/ว่าง/DB error (gateway error ถูกแมปเป็น validation, service.go:123), 401 token, 403 right/ตารางต้องห้าม
  • timeout 30 วินาที (service.go:115); log เก็บแค่ keyword, ตาราง, hash ของ SQL (ไม่เก็บ SQL ดิบ) handler.go:243-260

POST /query/execBatch

  • Request: {"sql":["INSERT ...","UPDATE ..."]} (handler.go:281)
  • Response 200: [{"rowsAffected":n,"rows":[...]},...] ตามลำดับ
  • ทำใน transaction เดียว: คำสั่งไหนพัง ย้อน (rollback) ทั้งชุด ไม่มีผลบางส่วน (service.go:137-171, ADR-0009 /query/execBatch เขียนหลาย statement ใน transaction เดียว (atomic)); ข้อความ error ของ lockdown ระบุ statement N
  • ตัวอย่าง:
POST /query/execBatch
{"sql":["INSERT INTO ic_color (code, name_1) VALUES ('<code>','<name>')","DELETE FROM ic_color WHERE code = '<oldCode>'"]}

ผู้เรียกฝั่งหน้าเว็บ

ที่มาในโค้ด

  • backend/internal/adapter/inbound/http/router.go:85-102
  • backend/internal/adapter/inbound/http/process/permission.go:135-161,231
  • backend/internal/adapter/inbound/http/query/handler.go:218-340
  • backend/internal/application/query/service.go:95-171
  • backend/internal/domain/query/query.go:61-81,335-425, write_policy.go:12-103, write_policy_legacy.go:5-41
  • frontend/src/app/api/query/exec/route.ts, execBatch/route.ts; frontend/src/lib/query-client.ts:44,63