แยกข้อมูลพนักงานออกจากการยืนยันตัวตน
addtel ยังคงเป็นต้นทางข้อมูลพนักงาน ส่วน Employee Import ทำหน้าที่เชื่อมข้อมูลและควบคุมงาน provisioning; Authentik ดูแลบัญชี การเข้าสู่ระบบ และ token โดยไม่ใช้เป็นฐานข้อมูล HR หลัก
ระบบนี้รับผิดชอบ
- อ่าน snapshot ผ่าน export API โดยไม่แก้โปรแกรม HR เดิม
- ประเมินสถานะและ rollout ก่อนสร้างหรือปิดบัญชี
- เชื่อมแผนกกับกลุ่ม และส่งลิงก์ตั้งรหัสผ่าน
- ให้ IT ตรวจงานค้าง ข้อขัดแย้ง และประวัติ
ยังไม่อยู่ในขอบเขต
- สร้างหรือลบ mailbox ขององค์กร
- ย้ายฐานข้อมูล HR มาเป็น employee service ใหม่
- สร้างบัญชี Odoo หรือ E-Learning อัตโนมัติ
- ให้กลุ่มแผนกมีสิทธิ์เข้าแอปทุกตัวโดยอัตโนมัติ
สองเส้นทางที่ทำงานร่วมกัน
ก. นำเข้าข้อมูลและจัดการบัญชี
user2 + dept
MariaDB 10.6.20
DirectAdmin · 61.91.251.20
Fetch → Import → Reconcile → Execute
MySQL 8.0.21 เก็บ snapshot สถานะ คิว และ auditUsers · Groups
Sessions · OAuth tokens
Worker ดึง HTTPS GET จาก /_export-api/export.php พร้อม bearer token แล้วเรียก Authentik REST API แยกด้วย Service Account token
Worker ขอ recovery link จาก Authentik แล้วส่งคำเชิญผ่าน SMTP
Local ส่งเข้า Mailpit; อีเมลจาก Authentik flow ใช้ SMTP ของ Authentik แยกต่างหากตั้งรหัสผ่านผ่าน recovery flow แล้วเข้าสู่ระบบ SSO
การทดสอบใช้ Mailpit เพื่อเก็บอีเมลภายในเครื่องข. เข้าใช้งานหน้า Employee Import
เข้าสู่ระบบผ่าน popup
ต่ออายุ token อัตโนมัติ
Authorization Code + PKCE
scope offline_access
employee-viewers /
employee-admins
employee-viewers และ employee-admins เป็นสิทธิ์ของแอปนี้ ต้องกำหนดแยกกันจากข้อมูล HR ไปเป็นบัญชี SSO
- HR เพิ่มพนักงานและ IT กำหนดอีเมลต้นทางใช้รหัสแถว user2.id เป็น identity ถาวรของการนำเข้า; exmail เก็บชื่อหน้า @
- Worker ดึง snapshotดึงประมาณทุก 5 นาทีหรือเมื่อ IT ขอ sync; รอบประมวลผลพัก 15 วินาที โดยใช้ล็อก MySQL ป้องกันการแก้ข้อมูลชนกัน
- ตรวจและนำเข้าข้อมูลตรวจความครบถ้วน จำนวนรายการ รหัสซ้ำ อีเมล active ซ้ำ และแผนก; บันทึกใน transaction และเพิ่ม Version เมื่อข้อมูลเปลี่ยน
- เชื่อมแผนกกับกลุ่มสร้างหรือค้นหากลุ่มจาก source marker แล้วบันทึก UUID ที่ Departments.AuthentikId; ใช้ชื่อแผนกตรง ๆ ไม่มี MDT นำหน้า
- ประเมินนโยบายและสร้างงานอ่าน rollout, IT hold, สถานะ, วันเริ่มงาน วันลาออก และความสดของข้อมูล; จัดคิว Enable หรือ Disable พร้อมเวอร์ชัน
- ประมวลผลคิวไม่เกิน 25 งานต่อชุด ให้ Disable ก่อน ข้ามงานเก่า และเรียก Authentik เฉพาะบัญชีที่ ownership ตรงกัน
- แจ้งผลและติดตามบันทึกงานและ audit; สร้างคำเชิญที่มี key ป้องกันซ้ำ แล้วส่งผ่าน Notifications ไม่เกิน 20 รายการต่อชุด
สัญญาณปิดบัญชีมีลำดับก่อนการเปิดบัญชี
| เงื่อนไข | ผล | รายละเอียด |
|---|---|---|
| IT hold หรือ statuss = w / n / t | Disable | w ลาออกแต่รอลบเมล; n ลาออก; t temporary ซึ่งโค้ดปัจจุบันปิด SSO |
| ครบกำหนด quitDate | Disable | เที่ยงคืนเวลาไทยของวันถัดจากวันทำงานสุดท้าย ไม่ต้องรอสถานะเปลี่ยน |
| ข้อมูลผิด ข้อมูลเก่า หรือแถวหายจาก snapshot | Review | หากไม่มีสัญญาณปิดบัญชีที่ทราบแน่ชัด จะไม่ถือว่าแถวหายเท่ากับลาออก |
| ไม่ใช่ y และไม่อยู่ในกลุ่มสถานะปิด | Review | unknown-status ต้องตรวจสอบ |
| นอก rollout | Skip สำหรับการเปิด | ไม่ใช้ rollout เพื่อข้ามสัญญาณปิดบัญชีที่มีอยู่ |
| ยังไม่ถึงวันเริ่มงาน | Wait | ประเมินใหม่ในรอบถัดไป |
| y + ถึงวันเริ่มงาน + ข้อมูลครบ + อยู่ใน rollout | Enable | สร้างใหม่หรืออัปเดตบัญชีที่ผูกกับ source identity เดิม |
| n → y แต่มี manual groups / roles นอกการดูแล | Review | ไม่เปิดกลับอัตโนมัติจนตรวจสิทธิ์เดิม |
หาก addtel สร้าง id ใหม่ถือเป็น identity ใหม่ แม้ชื่อหรืออีเมลเหมือนเดิม ส่วน id เดิมกลับมาทำงานใช้บัญชีเดิมได้เมื่อผ่านเงื่อนไข; อีเมลชนกับบัญชีอื่นจะเข้าตรวจสอบ ไม่รวมบัญชีด้วยอีเมล
เมื่อปิดบัญชี Authentik จะยกเลิก session และ refresh token แต่ API ยังรับ access JWT ที่ออกแล้วจนหมดอายุ รุ่นที่ทดสอบมีอายุ 300 วินาที ผู้ใช้ยอมรับข้อจำกัดนี้แล้ว ระยะจริงยังรวมเวลารอ sync และค่า clock skew ของ API ซึ่งยังไม่ได้วัดขอบเขตเวลาสูงสุด
ใช้รหัสให้ตรงหน้าที่
| ข้อมูล | หน้าที่ |
|---|---|
user2.id / EmployeeRow.Id | รหัสรายการจากต้นทาง ใช้ผูก identity และคิวงาน |
meditop_source_key | internal-user2:{id} ใช้ตรวจ ownership ใน Authentik |
employee_code | จาก idno เป็นรหัสพนักงานทางธุรกิจ ไม่ใช้แทน primary key |
Departments.AuthentikId | UUID กลุ่ม Authentik ของแผนก; ยังไม่สร้างจะเป็น null |
| User attributes | meditop_source_key, employee_code, department, avatar_url; ลบ employee_id, department_id, short_name และ picture_path แบบเก่าออก |
| Group attributes | meditop_department_key = internal-dept:{id} และ short_name |
avatar_url | origin internal.meditopthailand.com + path /staff/ ที่ผ่าน validation; เป็น URL ไม่ใช่การคัดลอกไฟล์ภาพ |
ฐานข้อมูลที่ Employee Import ดูแล
Employees เก็บ payload/hash/version/decision, Departments เก็บ mapping กลุ่ม, Jobs เก็บคิวและ retry, Runs เก็บผลดึงต้นทาง, Notifications เก็บคิวอีเมล, Audits เก็บประวัติ และ Settings เก็บ rollout / sync-request / execution-mode
API สำหรับหน้า IT
GET /api/config เป็นค่าหน้าเว็บที่ไม่รวม secrets; ข้อมูล /api/overview, /employees, /departments, /jobs, /runs, /notifications, /audit, /rollout ต้องมี Viewer หรือ Admin
Admin ใช้ POST /api/sync, /api/employees/{id}/hold, /api/jobs/{id}/retry และ /api/rollout เพื่อควบคุมงาน
Snapshot ต้นทาง
Source ต้องเป็น internal-user2, Complete ต้องเป็น true และ EmployeeCount/DepartmentCount ต้องตรงกับรายการจริง ใช้ snapshot ครบชุด เพราะ updatex ของระบบเดิมไม่ใช่ฐานที่เชื่อถือได้สำหรับ incremental sync
เฉพาะข้อมูลที่ต้องใช้ในการ provisioning ถูกนำมาใช้ ไม่ย้าย passwd หรือข้อมูลติดต่อฉุกเฉินเข้า Authentik
เก็บสถานะก่อน retry และตรวจสอบย้อนหลังได้
ความซ้ำและความล้มเหลว
- Jobs มี unique key: EmployeeId + Version + Action
- งานที่เวอร์ชันไม่ตรงเป็น Superseded
- API error เก็บ Pending และ backoff สูงสุด 300 วินาที
- ReviewException ต้องตรวจสอบ ไม่ retry อัตโนมัติ
- ส่งอีเมลผิดพลาดรอ 5 นาทีแล้วลองใหม่
- Notifications มี unique key แต่ SMTP ไม่รับประกัน exactly-once เมื่อส่งสำเร็จแล้วบันทึกฐานข้อมูลล้มเหลว
สิทธิ์และ secrets
- Service Account แยกจากบัญชีพนักงาน ใช้สิทธิ์ที่จำเป็น
- การทดสอบยืนยัน change_user แบบรายบัญชีที่สร้างผ่าน API
- API ตรวจ JWT และกลุ่ม Viewer/Admin
- เก็บ credential ใน .env; backup ของ DB/config มี secrets ต้องจัดเก็บอย่างจำกัดสิทธิ์
- ไม่บันทึก recovery link ใน audit หรือฐานข้อมูลแอป
- แผน retention และเจ้าของการตรวจ backup ยังต้องกำหนดก่อนใช้งานจริง
หลักฐานทดสอบวันที่ 25 ตุลาคม 2026
| รายการ | ผลและขอบเขต |
|---|---|
| Worker lifecycle | ผ่าน: สร้างบัญชี ย้ายแผนก w/n ปิด y เปิดกลับ และ manual privileges ส่ง Review โดยเรียก core Engine กับคิวจริง ไม่ใช่ hosted timer loop ทั้งระบบ |
| API / SMTP ขัดข้อง | ผ่าน: connection refused แล้ว retry สำเร็จ; Mailpit ทดสอบมีอีเมลหนึ่งฉบับ ไม่ครอบคลุมทุก HTTP error หรือ timeout |
| Restore | กู้ MySQL, PostgreSQL และ data/templates/certs ลง Docker แยก; hash ไฟล์ตรง เปิด Authentik server/worker และ Employee API/Web ได้ |
| SSO บนชุด restore | ผ่าน token exchange, refresh และ protected API ด้วยบัญชีจำลองที่ seed session/code; ไม่ใช่ browser login ครบ flow |
| ข้อจำกัด restore | ทดสอบเครื่องเดิมและ image ที่มีอยู่ ไม่ใช่กู้บนเครื่องใหม่หรือ snapshot แบบ atomic; ชุดทดสอบหยุดแล้วและเก็บข้อมูลไว้ |
สัญญาณปฏิบัติการที่มีแล้ว: ข้อมูลต้นทางเก่าเกิน 10 นาที และงาน Disable ค้างเกิน 10 นาที ยังไม่มี SLO production ที่อนุมัติอย่างเป็นทางการ
sso.meditopthailand.com
HTTPS :443
sso.meditopthailand.comTLS termination
HTTP reverse proxy
รองรับ WebSocket
192.168.0.3:9001
→ 192.168.10.8:9000
Authentik Docker :9000
- ยืนยัน NAT port และจำกัดแหล่งเชื่อมต่อให้เป็น DirectAdmin proxy
- ติดตั้ง conf ใน HTTPS virtual host, ตรวจ Apache config และ HTTP → HTTPS redirect
- กำหนด Authentik Brand และ trusted proxy ให้ตรงเส้นทางจริง
- ตั้ง OIDC redirect/logout URL ของ Employee Import เป็นโดเมน production ที่ตกลงไว้
- ตรวจ SMTP ของ Worker และ Authentik แยกกัน รวมลิงก์ recovery ต้องเป็นโดเมนจริง
- ทดสอบ login/logout, token refresh, กลุ่มสิทธิ์, รูปภาพ และ offboarding ด้วยบัญชีนำร่องก่อนขยาย
- จัด backup นอกเครื่อง พร้อม config/secrets และกำหนดรอบตรวจ restore กับผู้รับผิดชอบ
ใช้ Docker Compose ตามโครงสร้างปัจจุบัน แยก base config สำหรับ production และ override สำหรับ local; รุ่นนี้ยังไม่เพิ่ม API gateway หรือ Kubernetes และยังคง HR เดิมเป็น source of truth
ฐานของเอกสารฉบับนี้
สถานะ ณ 25 ตุลาคม 2026 จากโค้ดและผลทดสอบในโปรเจค ไม่ใช่การรับรองว่า production เปิดใช้งานแล้ว
meditop-employee-import/src/Meditop.EmployeeImport.Core/— Lifecycle, Engine, AuthentikClient, Notifications, Store และ Servicesmeditop-employee-import/src/Meditop.EmployeeImport.Api/Program.cs— JWT, policies และ APImeditop-employee-import/src/Meditop.EmployeeImport.Worker/Program.cs— timer loop และ orchestrationmeditop-employee-import/docs/local-worker-recovery-2026-10-25.md— หลักฐาน lifecycle และ restoremeditop-employee-import/docs/local-backup-revocation-2026-10-25.md— token revocation และ object permissionsmeditop-sso/reverse-proxy/direct-admin-httpd.conf— proxy snippet ที่เตรียมไว้