ย้ายระบบจาก Google Apps Script มาโฮสต์เองบน Railway (Node.js/Express) เพื่อแก้ปัญหา กล้อง/GPS ถูกบล็อกจาก iframe sandbox ของ GAS และเพิ่มโหมด "สแกนต่อเนื่อง" แบบห้างสรรพสินค้า — เปิดกล้องค้างไว้ เล็งสแกนทีละขวดได้ต่อเนื่อง ระบบหาเองอัตโนมัติว่าตรงกับสารตัวไหน พร้อมบี๊บ/สั่น/นับให้อัตโนมัติ
server.js Express app + REST endpoint /api/rpc
src/sheetsClient.js wrapper เรียก Google Sheets API v4 (แทน SpreadsheetApp)
src/drive.js อัปโหลดรูปลายเซ็นเข้า Google Drive (แทน DriveApp)
src/functions.js พอร์ต business logic ทั้งหมดจาก GS ไฟล์เดิม (1:1)
src/mutex.js กันเขียนชนกัน (แทน LockService)
public/index.html หน้าเว็บเดิม + เพิ่มโหมดสแกนต่อเนื่อง
public/gas-compat.js shim จำลอง google.script.run ให้ยิงเข้า /api/rpc แทน
- ไปที่ Google Cloud Console → สร้างโปรเจกต์ (หรือใช้โปรเจกต์เดิม)
- เปิดใช้งาน Google Sheets API และ Google Drive API
- ไปที่ IAM & Admin → Service Accounts → สร้าง Service Account ใหม่
- สร้างคีย์แบบ JSON (Keys → Add Key → Create new key → JSON) แล้วดาวน์โหลดไฟล์เก็บไว้
- เปิดไฟล์ JSON คัดลอกค่า
client_emailและprivate_key
- เปิด Google Sheet เดิม (F-WWT-28) → กด "แชร์" → วางอีเมลของ Service Account (ลงท้าย
.iam.gserviceaccount.com) → ให้สิทธิ์ แก้ไขได้ (Editor) - ไม่ต้องแชร์ Drive folder ล่วงหน้า ระบบจะสร้างโฟลเดอร์ "F-WWT-28 ลายเซ็นตรวจนับสารเคมี" ให้เองในไดรฟ์ของ Service Account (ถ้าต้องการให้ไฟล์ไปอยู่ใน Shared Drive ของทีมแทน แจ้งได้ จะปรับโค้ดให้)
คัดลอก .env.example เป็น .env แล้วกรอก:
[email protected]
GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
SPREADSHEET_ID=<เอาจาก URL ของชีท>
REVIEWER_PIN=1212312121
ADMIN_PIN=1212312121
GOOGLE_PRIVATE_KEYให้คงเครื่องหมาย\nไว้ในบรรทัดเดียว (ไม่ต้องขึ้นบรรทัดจริง) โค้ดจะแปลงให้เองตอนรัน
npm install
npm start
# เปิด http://localhost:3000- Push โค้ดนี้ขึ้น GitHub repo ใหม่
- Railway → New Project → Deploy from GitHub repo
- ใส่ Environment Variables ชุดเดียวกับ
.envในหน้า Variables ของ Railway - Railway จะ build/deploy อัตโนมัติ (ตรวจพบ
npm startจาก package.json) - ตั้ง Custom Domain (Settings → Networking → Custom Domain) — จำเป็นมาก เพื่อให้เป็น top-level origin จริง กล้อง/GPS จึงจะขอ permission ได้ปกติเหมือนที่ทำกับ F-WWT-025
โค้ด public/index.html แทบไม่ได้แก้ logic เดิมเลย — ทุกจุดที่เรียก
google.script.run.withSuccessHandler(...).withFailureHandler(...).ฟังก์ชัน(...)
ยังทำงานเหมือนเดิมทุกอย่าง เพราะ gas-compat.js จำลอง API ตัวนี้ให้ยิง REST ไปที่ /api/rpc แทน
ดังนั้นฟีเจอร์เดิมทั้งหมด (Draft, ส่งใบตรวจนับ, อนุมัติ, Archive รายเดือน, คำขอแก้ไข, Admin CRUD) ใช้งานได้ปกติ
- ปุ่ม "📷 สแกนต่อเนื่อง (โหมดยิงเร็ว)" อยู่เหนือรายการเช็คของ
- เปิดกล้องเต็มจอค้างไว้ ไม่ต้องเลือกรายการก่อน — สแกนเจอ QR ตรงกับสารตัวไหน ระบบหาให้เอง
- บี๊บเสียง + สั่นมือถือ + toast ทุกครั้งที่สแกนสำเร็จ พร้อมนับจำนวนขวดอัตโนมัติ (
นับได้/เอกสารเช่น 2/3) - กันสแกนซ้ำขวดเดียวกัน (เตือนสีเหลือง ไม่นับซ้ำ)
- สแกนไม่พบรหัสในระบบ → เตือนสีแดง ไม่ทำให้แอปค้าง เล็งขวดถัดไปต่อได้เลย
- มีช่องพิมพ์รหัสเองสำรองไว้เผื่อ QR เสียหาย/ลบเลือน
- ค่าที่ scan ได้จะ auto-fill ช่อง "จำนวนนับได้จริง" ให้ แต่ การกดยืนยันรายการ (checked) ยังคงต้องทำโดยผู้ตรวจนับเอง เหมือนเดิม เผื่อกรณีจำนวนไม่ตรงเอกสารต้องเลือกเหตุผลก่อน — เป็นการเร่งขั้นตอน ไม่ใช่ตัดขั้นตอนตรวจสอบทิ้ง
- Endpoint
/api/rpcเปิด whitelist เฉพาะฟังก์ชันที่ export จากsrc/functions.jsเท่านั้น เรียกชื่ออื่นจะถูกปฏิเสธทันที - PIN ยังคงเช็คฝั่ง backend เหมือนเดิม (ไม่ได้ผ่อนสิทธิ์ใดๆ)
- แนะนำเปลี่ยน
REVIEWER_PIN/ADMIN_PINให้ไม่ซ้ำกับ PIN สาธารณะอื่น ๆ ก่อนใช้งานจริง