Storage Service¶
Service กลางสำหรับจัดการไฟล์ของทุกแอป · serviceKey: storage
storage ไม่รับตัวไฟล์ผ่านตัวเองตอนอัปโหลด แต่ออก presigned URL ให้หน้าบ้านอัปโหลด / ดาวน์โหลดกับ object storage (S3 หรือ S3-compatible) โดยตรง แล้วเก็บข้อมูลไฟล์ (metadata) ไว้อ้างอิงด้วย fileKey
ภาพรวมการใช้งาน¶
อัปโหลดไฟล์¶
- หน้าบ้านเรียก Put Signed Upload File (login แล้ว) หรือ Put Public Signed Upload File (ยังไม่ login) พร้อม
fileName,contentType,path,acl - ได้
signedUrl(อายุ 1 ชั่วโมง) และfileKeyกลับมา - หน้าบ้าน
PUTตัวไฟล์ไปที่signedUrlโดยตรง ใส่ headerContent-Typeให้ตรงกับcontentTypeที่ขอไว้
js
await fetch(signedUrl, { method: "PUT", headers: { "Content-Type": file.type }, body: file });
- เก็บ
fileKeyไว้ในข้อมูลของ service ตัวเอง (เช่น รูปโปรไฟล์ เอกสารแนบ) ใช้อ้างถึงไฟล์นี้ต่อไป - storage ตั้งเวลากับ Schedule Service ไว้ตอนหมดอายุ signedUrl แล้วตรวจว่าไฟล์ถูกอัปโหลดจริงหรือไม่ · สถานะไฟล์จะเปลี่ยนจาก
UNKNOWNเป็นSUCCESS(พร้อมขนาดไฟล์) หรือFAILED
File key¶
รูปแบบ fileKey = <appKey>/<path>/<id><นามสกุลไฟล์> เช่น myApp/profile/6650f0c2a1b2c3d4e5f60718.png
pathคือหมวดที่ใช้จัดกลุ่มไฟล์ในแอป (ค่าเริ่มต้นdefault)- ไฟล์ทุกไฟล์อยู่ใต้
appKeyของแอปตัวเอง
ดาวน์โหลด / แสดงไฟล์¶
acl: PUBLICใช้publicUrlได้ตรง ๆacl: PRIVATEต้องเรียก Get Signed Url ด้วยfileKeyเพื่อขอ URL ชั่วคราว (อายุ 1 ชั่วโมง) ทุกครั้งที่จะแสดงไฟล์ · ต้อง loginpublicUrlอาจชี้ไปที่ object storage โดยตรง หรือชี้มาที่GET /file/<fileKey>ของ storage service (ส่งไฟล์แบบ stream,Content-Disposition: inline) ขึ้นกับการตั้งค่าของระบบ ให้ใช้ URL ตามที่ได้รับ
ไฟล์ที่ service อื่นสร้างเอง¶
service ที่สร้างไฟล์ลง object storage เอง (เช่น ผลการแปลงวิดีโอ) ลงทะเบียนไฟล์นั้นกับ storage ผ่าน Kafka topic sync-file-upload เพื่อให้เรียกดูผ่าน API เดียวกันได้
API Reference¶
GraphQL endpoint /graphql · API ที่ต้อง login ใช้ header authorization และต้องมี permission ตามที่ระบุ · API ที่ระบุว่า "app credential" เรียกได้ทั้งแบบ login หรือแบบไม่ login โดยใช้ client ของแอป (header X-APP-CLIENT-ID และสำหรับระบบภายนอกเพิ่ม X-APP-CLIENT-SECRET)
สรุป API ทั้งหมด 8 รายการ — กดชื่อเพื่อไปที่รายละเอียด
| กลุ่ม | API | ทำอะไร |
|---|---|---|
| Query | Get Signed Url | ขอ URL สำหรับเปิดไฟล์จาก fileKey (ได้หลายไฟล์พร้อมกัน) · app credential |
| Query | Get File Uploads | ดึงรายการไฟล์ของแอปที่ login (แบ่งหน้า) · permission: getFileUploads |
| Query | Get File Upload By ID | ดึงข้อมูลไฟล์ตาม _id · permission: getFileUploadById |
| Query | Get File Upload By File Key | ดึงข้อมูลไฟล์ตาม fileKey · permission: getFileUploadByFileKey |
| Mutation | Put Signed Upload File | ขอ presigned URL สำหรับอัปโหลดไฟล์ (อายุ 1 ชั่วโมง) แล้วบันทึกข้อมูลไฟล์สถานะ UNKNOWN… |
| Mutation | Put Public Signed Upload File | ขอ presigned URL สำหรับอัปโหลดไฟล์โดยไม่ต้อง login (เช่น ฟอร์มสาธารณะ) · app credential… |
| Mutation | Delete File Upload | ลบข้อมูลไฟล์และลบไฟล์ใน object storage ตาม fileKey (ได้หลายไฟล์) · permission: `delet… |
| REST | Get File | ส่งไฟล์แบบ stream ผ่าน storage service (Content-Disposition: inline พร้อม `Content-Ty… |
Query¶
เป็น API ที่ใช้สำหรับการ Query ข้อมูลออกมา ไม่มีการแก้ไข Data
Get Signed Url¶
ขอ URL สำหรับเปิดไฟล์จาก fileKey (ได้หลายไฟล์พร้อมกัน) · app credential
- ไฟล์
PUBLICคืนpublicUrl - ไฟล์
PRIVATEคืนsignedUrlอายุ 1 ชั่วโมง และexpired· ต้อง login ถ้าไม่ login จะได้ error Forbidden - ทุก
fileKeyต้องมีอยู่จริงในแอป ไม่อย่างนั้นได้ error Not Found
query {
getSignedUrl(fileKeys: ["myApp/profile/6650f0c2a1b2c3d4e5f60718.png"]) {
_id fileKey fileName acl publicUrl signedUrl expired contentType
}
}
Response : [FileUploadsUrl]
FileUploadsUrl
| key | Type | คำอธิบาย |
|---|---|---|
| _id | ID | id ที่ใช้อ้างอิง |
| acl | ENUM | PUBLIC, PRIVATE |
| signedUrl | String | URL ชั่วคราว (อัปโหลด: ใช้ PUT ไฟล์ · ดาวน์โหลดไฟล์ PRIVATE: ใช้เปิดไฟล์) |
| publicUrl | String | URL ของไฟล์ (ไฟล์ PRIVATE ต้องใช้ signedUrl แทน) |
| fileKey | String | key อ้างอิงไฟล์ |
| fileName | String | ชื่อไฟล์ |
| contentType | String | MIME type |
| expired | Date | เวลาหมดอายุของ signedUrl |
| fileType | ENUM | INTERNAL (อัปโหลดผ่าน storage) · EXTERNAL (ลงทะเบียนจากภายนอก) |
Get File Uploads¶
ดึงรายการไฟล์ของแอปที่ login (แบ่งหน้า) · permission: getFileUploads
query {
getFileUploads(input: {
filter: { acl: PRIVATE, path: "myApp/profile" }
search: { fileName: "invoice" }
sort: { sortBy: createdAt, sortOrder: DESC }
pagination: { limit: 20, page: 1 }
}) {
fileUploads { _id fileKey fileName contentType acl authId createdAt }
pagination { totalItems totalPages hasNextPage }
}
}
Input GetFileUploadsInput
| key | Type | คำอธิบาย |
|---|---|---|
| filter | GetFileUploadsFilterInput | acl, signedUrl, publicUrl, path, fileKey, fileName, contentType, authId, fileType |
| search | GetFileUploadsSearchInput | ค้นหาบางส่วน: path, fileKey, fileName, contentType |
| sort | GetFileUploadsSortInput | sortBy (ค่าเริ่มต้น createdAt), sortOrder ASC / DESC |
| pagination | CustomPaginateInput | limit (ค่าเริ่มต้น 10), page (ค่าเริ่มต้น 1) |
Response : FileUploadsPagination = { fileUploads: [FileUploads], pagination: CustomPaginate }
FileUploads
| key | Type | คำอธิบาย |
|---|---|---|
| _id | ID | id ที่ใช้อ้างอิง |
| acl | ENUM | PUBLIC, PRIVATE |
| signedUrl | String | URL ที่ใช้อัปโหลดตอนสร้าง |
| publicUrl | String | URL ของไฟล์ |
| path | String | หมวดของไฟล์ (รวม appKey นำหน้า) |
| fileKey | String | key อ้างอิงไฟล์ |
| fileName | String | ชื่อไฟล์ |
| contentType | String | MIME type |
| authId | String | user ที่อัปโหลด |
| expired | Date | เวลาหมดอายุของ signedUrl ตอนอัปโหลด |
| fileType | ENUM | INTERNAL, EXTERNAL |
| createdBy, updatedBy | String | ผู้สร้าง / ผู้แก้ไข |
| createdAt, updatedAt | Date | เวลาสร้าง / แก้ไขล่าสุด |
Get File Upload By ID¶
ดึงข้อมูลไฟล์ตาม _id · permission: getFileUploadById
query {
getFileUploadById(fileUploadId: "6650f0c2a1b2c3d4e5f60718") { _id fileKey fileName acl publicUrl }
}
Response : FileUploads
Get File Upload By File Key¶
ดึงข้อมูลไฟล์ตาม fileKey · permission: getFileUploadByFileKey
query {
getFileUploadByFileKey(fileKey: "myApp/profile/6650f0c2a1b2c3d4e5f60718.png") { _id fileName acl publicUrl }
}
Response : FileUploads
Mutation¶
เป็น API ที่ใช้สำหรับการแก้ไขข้อมูล
Put Signed Upload File¶
ขอ presigned URL สำหรับอัปโหลดไฟล์ (อายุ 1 ชั่วโมง) แล้วบันทึกข้อมูลไฟล์สถานะ UNKNOWN และตั้งเวลาตรวจผลการอัปโหลดกับ schedule · ต้อง login · permission: putSignedUploadFille
mutation {
putSignedUploadFile(input: {
acl: PRIVATE
path: "documents"
fileName: "invoice-2026-10.pdf"
contentType: "application/pdf"
}) {
_id fileKey signedUrl publicUrl expired
}
}
Input CreateFileUploadsInput
| key | Type | คำอธิบาย |
|---|---|---|
| acl | ENUM | PUBLIC (ค่าเริ่มต้น) หรือ PRIVATE |
| path | String | หมวดของไฟล์ (ค่าเริ่มต้น default) |
| fileName | String! | ชื่อไฟล์พร้อมนามสกุล (นามสกุลใช้ต่อท้าย fileKey) |
| contentType | String! | MIME type ของไฟล์ ต้องตรงกับ header Content-Type ตอน PUT |
Response : FileUploadsUrl
Put Public Signed Upload File¶
ขอ presigned URL สำหรับอัปโหลดไฟล์โดยไม่ต้อง login (เช่น ฟอร์มสาธารณะ) · app credential · ไฟล์เป็น acl: PUBLIC เสมอ และบันทึกผู้อัปโหลดเป็น SYSTEM · ชื่อ API สะกด Singed ตามโค้ด
mutation {
putPublicSingedUploadFile(input: {
path: "public-form"
fileName: "photo.jpg"
contentType: "image/jpeg"
}) {
_id fileKey signedUrl publicUrl expired
}
}
Input CreatePublicFileUploadsInput: path (ค่าเริ่มต้น default), fileName: String!, contentType: String!
Response : FileUploadsUrl
Delete File Upload¶
ลบข้อมูลไฟล์และลบไฟล์ใน object storage ตาม fileKey (ได้หลายไฟล์) · permission: deleteFileUpload
mutation {
deleteFileUpload(fileKeys: ["myApp/documents/6650f0c2a1b2c3d4e5f60718.pdf"]) { _id fileKey }
}
Response : [FileUploads] (ข้อมูลที่ถูกลบ)
REST¶
Get File¶
GET /file/<fileKey>
ส่งไฟล์แบบ stream ผ่าน storage service (Content-Disposition: inline พร้อม Content-Type ของไฟล์) · ใช้เป็น publicUrl เมื่อระบบตั้งค่าให้เปิดไฟล์ผ่าน storage · ไม่พบไฟล์ได้ HTTP 404
kafka consume Reference¶
topic ส่วนใหญ่ตรวจว่า storage มีสิทธิ์ในแอปตาม appKey (มี appCertificate ของแอปนั้น) ก่อนทำงาน
Standard topics¶
| topic | คำอธิบาย |
|---|---|
init-system |
ตั้งระบบจากศูนย์ (เฉพาะ core set) |
refresh-data |
ส่งข้อมูลที่ตัวเองถือขึ้นไปใหม่ (เช่น permission) และล้าง cache |
sync-app-certificate |
รับ appCertificate ของแอปที่ storage มีสิทธิ์ |
sync-app-credential |
รับข้อมูลการเข้าใช้ของแอป (ใช้ตรวจ token และ app credential) |
sync-application |
รับข้อมูลแอป |
sync-user-policy |
รับ UserPolicy สำหรับตรวจ permission |
sync-service-setting |
รับค่าตั้งค่าเพิ่มเติมของ storage ระดับระบบ (header serviceKey: storage) |
sync-app-service-setting |
รับค่าตั้งค่าเพิ่มเติมของ storage เฉพาะแอป (header serviceKey: storage) |
Sync File Upload¶
ลงทะเบียนไฟล์ที่ service อื่นสร้างไว้ใน object storage แล้ว ให้ storage รู้จักและเรียกดูผ่าน API ได้
topic: sync-file-upload
header: appKey, serviceKey = storage
Action: ADD
{
"action": "ADD",
"fileUpload": {
"appKey": "myApp",
"acl": "PRIVATE",
"publicUrl": null,
"path": "myApp/videos",
"fileKey": "myApp/videos/6650f0c2a1b2c3d4e5f60718/index.m3u8",
"fileName": "index.m3u8",
"fileSize": 1024,
"fileType": "EXTERNAL",
"contentType": "application/vnd.apple.mpegurl",
"authId": "665000000000000000000030",
"status": "SUCCESS",
"createdBy": "665000000000000000000030",
"updatedBy": "665000000000000000000030"
}
}
| key | Type | คำอธิบาย |
|---|---|---|
| appKey | string | แอปเจ้าของไฟล์ |
| acl | ENUM | PUBLIC, PRIVATE |
| publicUrl | string | null | URL ของไฟล์ (ถ้ามี) |
| path | string | หมวดของไฟล์ |
| fileKey | string | key ของไฟล์ใน object storage |
| fileName | string | ชื่อไฟล์ |
| fileSize | number | null | ขนาดไฟล์ (byte) |
| fileType | string | รูปแบบการจัดการไฟล์ เช่น EXTERNAL |
| contentType | string | MIME type |
| authId | string | user เจ้าของไฟล์ |
| status | ENUM | UNKNOWN, SUCCESS, FAILED (ค่าเริ่มต้น SUCCESS) |
| createdBy, updatedBy | string | ผู้สร้าง / ผู้แก้ไข |
Schedule Alarm¶
รับ alarm จาก Schedule Service เมื่อ signedUrl ของการอัปโหลดหมดอายุ · รับเฉพาะรายการที่ scheduleAlarm.serviceKey = storage แล้วใช้ scheduleRefKey (= _id ของไฟล์) ตรวจกับ object storage ว่ามีไฟล์จริงหรือไม่ จากนั้นอัปเดตสถานะเป็น SUCCESS (พร้อมขนาดไฟล์) หรือ FAILED
topic: schedule-alarm
Action: ADD
| key | Type | คำอธิบาย |
|---|---|---|
| scheduleAlarm.serviceKey | string | storage |
| scheduleAlarm.scheduleRefKey | string | _id ของไฟล์ |
| scheduleAlarm.scheduleRefType | string | fileUploads |
| scheduleAlarm.alarmData | object | _id, description, timeStamp, expiredTime |
Kafka Produce Reference¶
Set Schedule¶
ลงทะเบียนเวลาตรวจผลการอัปโหลดกับ schedule ทุกครั้งที่ออก presigned URL สำหรับอัปโหลด (ครั้งเดียว ไม่วนซ้ำ ที่เวลาหมดอายุของ signedUrl)
topic: set-schedule
header: appKey, serviceKey = schedule
Action: ADD
| key | Type | คำอธิบาย |
|---|---|---|
| schedule.appKey | string | แอปของไฟล์ |
| schedule.serviceKey | string | storage (ผู้รับ alarm) |
| schedule.scheduleRefKey | string | _id ของไฟล์ |
| schedule.scheduleRefType | string | fileUploads |
| schedule.alarmData | object | _id, description, timeStamp, expiredTime |
| schedule.title / description | string | file upload / storage upload file: <fileName> |
| schedule.isRecurring | boolean | false |
| schedule.notificationAt | Date | เวลาหมดอายุของ signedUrl |
Sync Permission¶
ส่ง permission ของ storage ให้ access-control เมื่อได้รับ refresh-data เพื่อนำไปผูกกับ role (putSignedUploadFille, getSignedUrl, getFileUploads, getFileUploadById, getFileUploadByFileKey, deleteFileUpload)
topic: sync-permission
Action: ADD
อัปเดตจากโค้ด gumon-storage-service@3f02969 · 2026-10-05