2way-meeting-backoffice — Scorecard
Legend:
- 🔴 Critical = active security/data-loss/availability risk
- 🟡 High = correctness/maintainability hazard, real impact
- 🟢 Medium = friction/hygiene, ยังไม่เป็นอันตรายทันที
ทุกแถวอ้างโค้ดบน branch prod
Health table
| ID | Severity | คำอธิบาย | หลักฐาน (file:line) | การแก้ไขที่เสนอ |
|---|---|---|---|---|
| SEC-2WAYMTBO-01 | 🟡 High | JWT เก็บใน localStorage (ไม่ใช่ httpOnly cookie) — เสี่ยงขโมยผ่าน XSS | utils/auth.tsx:7-11, fetchApiWithAxios.tsx:8, withAuthVerify.tsx:19 | httpOnly cookie + CSRF หรืออย่างน้อย CSP ที่เข้มขึ้น (ปัจจุบัน connect-src *) |
| SEC-2WAYMTBO-02 | 🟡 High | Route guard เป็น client-side หลัง mount — หน้า L2 render ได้ก่อน verify เสร็จ; ความปลอดภัยจริงอยู่ที่ backend | withAuthVerify.tsx:16-63, _app.tsx:152 | Next middleware หรือ SSR session check ก่อนส่ง HTML/data |
| SEC-2WAYMTBO-03 | 🟡 High | Sentry DSN ถูก hardcode เป็น fallback ใน client และ server config — commit ใน git | sentry.client.config.js:7-10, sentry.server.config.js:7-10 | ลบ fallback; บังคับอ่านจาก env เท่านั้น; rotate DSN ถ้าเคยหลุด |
| SEC-2WAYMTBO-04 | 🟡 High | Login พึ่ง 2way-api /api/v1/auth ซึ่งมี backdoor credential ฝั่ง backend (SEC-2WAYAPI-01) | Login/components/Form.tsx:97-99 | แก้ที่ 2way-api เป็นหลัก; ฝั่ง UI อย่าพึ่ง "security by obscurity" |
| SEC-2WAYMTBO-05 | 🟢 Medium | Bypass login รับ Bearer จาก query path แล้วแลก token — ถ้า token หลุดจาก email/link จะเข้า ChatCenter ได้ | bypassLogin/[token].tsx:34-57 | TTL สั้น + one-time use ฝั่ง API; อย่า log token |
| CORR-2WAYMTBO-01 | 🟡 High | fetchAPI ไม่มี branch สำหรับ HTTP DELETE แต่ deleteAnnouncesContentId เรียก "DELETE" — คำสั่งลบประกาศผ่าน wrapper นี้จะไม่ยิง request | fetchApiWithAxios.tsx:17-65, services/announces.tsx:73-78 | เพิ่ม DELETE ใน wrapper หรือเปลี่ยนไปใช้ method ที่รองรับ |
| CORR-2WAYMTBO-02 | 🟡 High | withAuth เช็ค statusCode === 201 แต่ withAuthVerify (ตัวที่ active) เช็ค !== 200 — ถ้าสลับ HOC หรือ backend เปลี่ยน code จะได้พฤติกรรมคนละแบบ | withAuth.tsx:41, withAuthVerify.tsx:38 | รวมเกณฑ์เป็นค่าเดียวที่ตรงกับ 2way-api จริง |
| CORR-2WAYMTBO-03 | 🟢 Medium | Problem/Detail เรียก path /createQuestion มีช่องว่างท้ายสตริง | features/2way/Problem/Detail/index.tsx:147 | ตัด whitespace; เพิ่ม regression test |
| CORR-2WAYMTBO-04 | 🟢 Medium | getChatDetail hardcode roomEmpCode ค่าคงที่ — ใช้ดูห้องอื่นไม่ได้ | services/chat-center.tsx:6 | รับ parameter จริง; หรือลบ dead code ถ้าไม่ถูกเรียก |
| QUAL-2WAYMTBO-01 | 🟢 Medium | ชื่อ repo มีคำว่า meeting แต่ไม่มีฟีเจอร์ประชุม / ไม่เรียก meeting-vtrc-api | feature folders + next.config.js:38-39 basePath: "/2way-backoffice" | เปลี่ยนชื่อหรือ README สั้น ๆ ที่ root ชี้แจง |
| QUAL-2WAYMTBO-02 | 🟢 Medium | API error ถูก resolve(error) + console.log ใน services — UI มักเงียบเมื่อ backend ล่ม | fetchApiWithAxios.tsx:35-40, surveys.tsx:8-10,116-118 | reject จริง + toast/error boundary |
| QUAL-2WAYMTBO-03 | 🟢 Medium | Material-UI v4 + MUI v5 ปนในโปรเจกต์เดียว | package.json:23-27 | migrate เป็น MUI v5 อย่างเดียว |
| QUAL-2WAYMTBO-04 | 🟢 Medium | ignoreDuringBuilds (ESLint) + ignoreBuildErrors (TypeScript) — production build ผ่านได้ทั้งที่ type/lint พัง | next.config.js:25-36 | เปิดกลับทีละส่วน; กัน regression ใน CI |
| QUAL-2WAYMTBO-05 | 🟢 Medium | ไฟล์ backup จำนวนมาก (* copy*, *_old*, .bk, ChatCenter2) ทำให้ค้นหา source of truth ยาก | เช่น HOC/withAuth copy.tsx, ChatCenter/index copy.tsx | ย้ายออกจาก src/ หรือลบหลังยืนยันไม่ถูก import |
| QUAL-2WAYMTBO-06 | 🟢 Medium | services/feedback.tsx ว่าง — logic ฝังใน component ยาวมาก | services/feedback.tsx (0 bytes), Feedback/Edit/components/Form.tsx | สกัดเป็น service module ให้สอดคล้อง announces/surveys |
| QUAL-2WAYMTBO-07 | 🟢 Medium | withAuthSuperAdmin ไม่ได้เช็ค level === 1 ทั้งที่ชื่อบอก super admin | HOC/withAuthSuperAdmin.tsx:8-14 vs auth.tsx:71 | ใช้ getUserInfo().isSuperAdmin หรือลบ HOC ที่ทำให้เข้าใจผิด |
| LEG-2WAYMTBO-01 | 🟢 Medium | Next.js 12 + React 17 — EOL ด้าน security patch ของสาย major | package.json:44,47 | วางแผน upgrade Next 14+/React 18 เมื่อมี capacity |
| LEG-2WAYMTBO-02 | 🟢 Medium | enzyme + jest setup สำหรับ React 17 — ecosystem หยุดนิ่ง | package.json devDeps, __test__/ | ย้ายไป React Testing Library เมื่อ upgrade React |
| OBS-2WAYMTBO-01 | 🟢 Medium | ชื่อ env appConnectChatAPI ชี้ 2way-vtrc-api ไม่ใช่ chat-center — debug ข้าม repo สับสน | next.config.js:57, .env.production:9 | เปลี่ยนชื่อเป็น appConnectVtrcApiLegacy (breaking — ต้อง sync ทุก call site) |
| OBS-2WAYMTBO-02 | 🟢 Medium | Sentry webpack plugin ปิด → source map จาก pipeline นี้อาจไม่ขึ้น; แต่ runtime init ยังส่ง event ที่ tracesSampleRate: 1.0 | next.config.js:79-80, sentry.client.config.js:12 | เปิด withSentryConfig อย่างถูกต้อง หรือปิด init ถ้าไม่ใช้; ลด sample rate |
| OBS-2WAYMTBO-03 | 🟢 Medium | CSP ใน _app อนุญาต connect-src * และ wss host ไม่ตรง env websocket (vtrcapi vs vtrc) | _app.tsx:126-129, .env.production:11 | จำกัด allowlist ตาม env จริง |
| OBS-2WAYMTBO-04 | 🟢 Medium | Production mute console.log/error/warn ทั้งก้อน — ทำให้นักปฏิบัติการ debug จาก browser ยาก และอาจกลบสัญญาณ error | _app.tsx:55-58 | mute เฉพาะ log ระดับ debug; คง console.error หรือส่งเข้า Sentry |
สรุปเชิงภาพรวม
repo นี้ไม่มี business logic สำคัญฝั่ง server — debt Critical ส่วนใหญ่จึง อยู่ที่ backend ที่เรียก (2way-api, 2way-vtrc-api, chat-center-api) ไม่ใช่ที่ Next process เอง
ข้อค้นพบที่ควรยึดเป็น canonical truth:
- ชื่อ "meeting" ใน repo ไม่เกี่ยวกับ Meeting Platform
- LIFF พนักงาน →
2way-vtrc-api; แอดมิน CRUD หลัก →2way-api; ChatCenter →chat-center-api+ WS - Group/approver admin ใน UI นี้ = teamAdmin (chat) + survey adminApprove + complaint multi-step — ไม่ใช่ meeting approver
- Sentry มีไฟล์ config และ
_errorcapture แต่ webpack plugin ถูกปิด และ DSN hardcode - Build artifact ที่ deploy จริงคือ
.nextใน Docker (Dockerfile) ไม่ใช่โฟลเดอร์out/จากnext export(แม้ script จะมี)
Checklist ก่อนเปลี่ยนโค้ดใน repo นี้
- ยืนยันว่าเรียก backend ตัวไหน (
appConnectAPIvsappConnectChatAPIvsappConnectChatCenterAPI) - ถ้าแตะ auth — อ่านทั้ง
withAuthVerifyและ responsestatusCodeจริงจาก 2way-api - ถ้าแตะ ChatCenter — ตรวจทั้ง REST และ websocket path
/chat-center-ws/socket.io - อย่าลบไฟล์
*_copy*/*.bkโดยไม่grepimport ก่อน - หลังแก้ API wrapper — ตรวจว่า method ที่เรียก (รวม DELETE) ถูก implement จริง