feat(ai-admin-console): complete implementation and resolve lint compilation errors

This commit is contained in:
2026-05-21 21:42:25 +07:00
parent 1580ab2c18
commit 91e9c714df
39 changed files with 3724 additions and 72 deletions
@@ -110,6 +110,43 @@ CREATE TABLE refresh_tokens (
FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บ Refresh Tokens สำหรับ Authentication';
-- ตารางเก็บข้อมูลการตั้งค่าระบบไดนามิก (ADR-027)
CREATE TABLE system_settings (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
setting_key VARCHAR(100) NOT NULL UNIQUE COMMENT 'คีย์การตั้งค่าระบบ (เช่น AI_FEATURES_ENABLED, MAX_UPLOAD_SIZE)',
setting_value TEXT NOT NULL COMMENT 'ค่าที่บันทึก (stringified)',
data_type ENUM('string', 'number', 'boolean', 'json') NOT NULL DEFAULT 'string' COMMENT 'ประเภทข้อมูลสำหรับ validation',
category VARCHAR(50) COMMENT 'หมวดหมู่ (เช่น ai, security, storage, notification)',
is_encrypted TINYINT(1) DEFAULT 0 COMMENT 'เข้ารหัสค่า sensitive',
validation_rules JSON COMMENT 'กฎ validation (min, max, allowed_values)',
description TEXT COMMENT 'คำอธิบายข้อมูลการตั้งค่า',
is_public TINYINT(1) DEFAULT 0 COMMENT 'เผยแพร่ให้ frontend อ่านได้หรือ admin only',
updated_by INT COMMENT 'ผู้แก้ไขล่าสุด',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL,
INDEX idx_system_settings_category (category),
INDEX idx_system_settings_is_public (is_public)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บข้อมูลการตั้งค่าระบบไดนามิก';
INSERT INTO system_settings (
setting_key,
setting_value,
data_type,
category,
description,
is_public
)
VALUES (
'AI_FEATURES_ENABLED',
'true',
'boolean',
'ai',
'สถานะเปิด/ปิดการใช้งานฟีเจอร์ AI ทั้งระบบ สำหรับผู้ใช้ทั่วไป',
1
)
ON DUPLICATE KEY UPDATE setting_key = setting_key;
-- ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ
CREATE TABLE roles (
role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
@@ -0,0 +1,181 @@
# ADR-027: AI Admin Panel and Dynamic Control Architecture
**Status:** Accepted
**Date:** 2026-05-20
**Decision Makers:** Development Team, System Architect, DevOps Engineer
**Related Documents:**
- [CONTEXT-ADR-027: AI Admin Panel Development Plan](./CONTEXT-ADR-027.md)
- [ADR-023A: Unified AI Architecture — Model Revision](./ADR-023A-unified-ai-architecture.md)
- [ADR-019: Hybrid Identifier Strategy](./ADR-019-hybrid-identifier-strategy.md)
- [ADR-016: Security & Authentication](./ADR-016-security-authentication.md)
- [ADR-009: Database Migration Strategy](./ADR-009-database-migration-strategy.md)
- [ADR-008: Email & Notification Strategy (BullMQ)](./ADR-008-email-notification-strategy.md)
> **หมายเหตุ:** ADR นี้กำหนดสถาปัตยกรรมการพัฒนาแผงควบคุมระบบ AI (AI Admin Panel) สำหรับสิทธิ์ **Superadmin** เท่านั้น เพื่อใช้ในการควบคุมความพร้อมใช้งานของบริการ AI แบบ Dynamic, ตรวจสอบสุขภาพระบบโครงสร้างพื้นฐาน (Ollama/Qdrant/BullMQ) และการรัน Sandbox ทดสอบภายใต้สภาพแวดล้อมที่ควบคุมความปลอดภัยสูงสุด
---
## บริบทและปัญหา (Context and Problem Statement)
เนื่องจากระบบปัญญาประดิษฐ์ของโครงการ LCBP3 DMS (Ollama & Qdrant) รันอยู่บนสภาพแวดล้อมแบบ On-premises บนเครื่อง AI Host (`Desk-5439`) ซึ่งมีความเสี่ยงที่จะเกิดเหตุสุดวิสัย เช่น เครื่องล่ม, Latency สูงขึ้นอย่างผิดปกติจากการประมวลผลงานชุดใหญ่ หรือมีความจำเป็นต้องปิดปรับปรุง Prompt หรือตัวโมเดลชั่วคราว
ปัญหากลุ่มนี้ทำให้ระบบต้องการกลไกควบคุมและติดตามดังนี้:
1. **Dynamic Switch:** แอดมินจำเป็นต้องสั่งปิดการให้บริการ AI แก่ผู้ใช้ปกติได้ทันทีโดยไม่ต้องรัน Build หรือ Restart เซิร์ฟเวอร์
2. **Graceful Degradation:** เมื่อปิดระบบ AI, หน้าจอของผู้ใช้ปกติและ API จะต้องปิดตัวลงอย่างสง่างาม ไม่โยนข้อผิดพลาดแปลกๆ ที่ไม่เป็นมิตรต่อผู้ใช้
3. **Isolated Test Laboratory:** ในขณะที่ AI ถูกปิดปรับปรุง แอดมินยังคงต้องการพื้นที่ Sandbox ในการทดสอบประมวลผลจริงเพื่อปรับปรุงความถูกต้อง โดยงานประมวลผลของแอดมินจะต้องไม่ถูกรบกวนจากงานตกค้างของผู้ใช้ทั่วไป หรือทำตัวโมเดลล่ม
---
## ปัจจัยขับเคลื่อนการตัดสินใจ (Decision Drivers)
- **Security Isolation (Tier 1):** แผงควบคุมและ Sandbox ทั้งหมดต้องควบคุมสิทธิ์อย่างเหนียวแน่นสำหรับสิทธิ์ Superadmin เท่านั้น (`system.manage_all`)
- **Latency-free Status Check:** การตรวจสอบสวิตช์เปิด/ปิด AI ใน API ผู้ใช้ภายนอกต้องไม่มี Overhead ในการคิวรีฐานข้อมูลตลอดเวลา
- **User Experience (UX):** หน้าจอผู้ใช้ปกติในฟอร์มเอกสารต้องตอบสนองได้อย่างนุ่มนวล (Soft Fallback) เมื่อ AI ถูกปิด แทนการกดปุ่มแล้วแจ้งเตือนข้อผิดพลาดสีแดง
- **Resource Protection:** การรัน Playground Sandbox ของแอดมินจะต้องไม่ก่อให้เกิด Race Condition หรือโหลดกระแทกบน VRAM ของ GPU RTX 2060 Super (8GB) บนเครื่อง `Desk-5439`
---
## ทางเลือกที่ถูกพิจารณา (Considered Options)
### Option A: Synchronous Direct Sandbox & API Hard Block
- สั่งรัน RAG และ OCR Sandbox ของแอดมินตรงเข้าสู่ API Controller แบบ Synchronous โดยตรง (ไม่ผ่านคิว BullMQ) และเมื่อสวิตช์เปิด/ปิด AI ถูกตั้งค่าเป็นปิดใช้งาน จะทำการซ่อนปุ่มสกัดข้อมูลทั้งหมดในหน้าผู้ใช้ทั่วไปทันที
### Option B: Shared BullMQ Queue & Soft Fallback (ตัวเลือกที่ได้รับเลือก)
- สั่งรัน Sandbox ของแอดมินผ่านคิว `ai-batch` ที่มีอยู่แล้ว (ตาม ADR-023A) โดยใช้ job type `sandbox-rag` และ `sandbox-extract` พร้อม priority สูงกว่างาน batch ปกติ
- จัดทำตาราง `system_settings` โดยเพิ่มลงใน schema file หลัก (ตาม ADR-009) ร่วมกับ Redis Cache และใช้กลไก Polling (ทุก 30 วินาที) ของ Frontend เพื่ออัปเดตสถานะปุ่ม AI Suggestion บนฟอร์มเป็นสถานะ **Disabled (ใช้งานไม่ได้)** พร้อมแสดงข้อความอธิบายความจำเป็นเมื่อชี้เมาส์ (Hover Tooltip)
---
## ผลการตัดสินใจ (Decision Outcome)
**ทางเลือกที่ได้รับเลือก:** **Option B**
เนื่องจากเหตุผลความเสถียรของระบบ VRAM และประสบการณ์การใช้งานที่ดียิ่งขึ้นของผู้ใช้งานทั่วไป (UX) โดยมีตารางวิเคราะห์เปรียบเทียบดังนี้:
| เกณฑ์การประเมิน | Option A (Direct) | Option B (Shared Queue) |
| :--- | :--- | :--- |
| **ความเสถียรของ VRAM บน Desk-5439** | ❌ เสี่ยงล่มหากแอดมินรันโหลดหนักชนกับ Queue ปกติ | ✅ ปลอดภัยสูงสุด ควบคุม Concurrency ของ ai-batch queue ตาม ADR-023A (concurrency=1) |
| **ประสบการณ์การใช้งานทั่วไป (UX)** | ❌ ปุ่มหายกะทันหัน สร้างความสับสนว่าฟีเจอร์หายไปไหน | ✅ แสดงปุ่ม disabled + Tooltip ชี้แจง ทำให้เกิดความเข้าใจและเป็นมิตร |
| **การจำลองโหลดการทำงานจริง** | ❌ ไม่มีการเข้าคิว ไม่สะท้อนความเร็วจริงในสถานการณ์จริง | ✅ สะท้อนพฤติกรรมความเร็วจริงของคิวและ VRAM ได้แม่นยำ 100% |
| **ประสิทธิภาพของ Backend API** | ❌ เช็ค DB ทุกครั้งสร้าง Overhead | ✅ เช็คผ่าน Redis Cache คืนสถานะภายใน <1ms |
| **ความสอดคล้องกับ ADR-023A** | ❌ ไม่สอดคล้องกับ 2-Queue Architecture | ✅ สอดคล้องกับ ADR-023A (ใช้ ai-batch queue ร่วมกัน) |
---
## รายละเอียดเชิงสถาปัตยกรรม (Implementation Details)
### 1. โครงสร้างข้อมูลตาราง `system_settings` (Refined)
ระบบจะนำเสนอตารางเก็บข้อมูลการตั้งค่าระบบแบบรวมศูนย์ (generic) เพื่อรองรับ settings อื่นๆ ในอนาคต (ตามมาตรฐาน ADR-009) ดังนี้:
- **Persistence Layer:** เพิ่มตาราง `system_settings` ใน `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` โดยตรง (ไม่ใช้ migration file แยก)
- **Caching Layer:** จัดเก็บค่าแยกเป็น Redis Key ต่อ setting (เช่น `system_settings:AI_FEATURES_ENABLED`, `system_settings:MAX_UPLOAD_SIZE`) เพื่อให้อ่านค่าได้เร็วในระดับไมโครวินาที (Microseconds) เมื่อ API Guard เรียกตรวจสอบ
```
[Client App] ---> [API Guard] ---> [Redis Cache (Key: system_settings:AI_FEATURES_ENABLED)]
|
+--(Miss)--> [MariaDB (system_settings)]
```
**Schema Design (Generic):**
```sql
CREATE TABLE system_settings (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
setting_key VARCHAR(100) NOT NULL UNIQUE COMMENT 'คีย์การตั้งค่าระบบ (เช่น AI_FEATURES_ENABLED, MAX_UPLOAD_SIZE)',
setting_value TEXT NOT NULL COMMENT 'ค่าที่บันทึก (stringified)',
data_type ENUM('string', 'number', 'boolean', 'json') NOT NULL DEFAULT 'string' COMMENT 'ประเภทข้อมูลสำหรับ validation',
category VARCHAR(50) COMMENT 'หมวดหมู่ (เช่น ai, security, storage, notification)',
is_encrypted TINYINT(1) DEFAULT 0 COMMENT 'เข้ารหัสค่า sensitive (เช่น API keys)',
validation_rules JSON COMMENT 'กฎ validation (min, max, allowed_values)',
description TEXT COMMENT 'คำอธิบายข้อมูลการตั้งค่า',
is_public TINYINT(1) DEFAULT 0 COMMENT 'เผยแพร่ให้ frontend อ่านได้ (หรือ admin only)',
updated_by INT COMMENT 'ผู้แก้ไขล่าสุด',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL,
INDEX idx_category (category),
INDEX idx_is_public (is_public)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บข้อมูลการตั้งค่าระบบไดนามิก';
```
### 2. ระบบคิว Sandbox ร่วมกัน (Shared Queue)
ระบบจะใช้คิว `ai-batch` ที่มีอยู่แล้ว (ตาม ADR-023A) สำหรับงาน Sandbox ของแอดมิน โดย:
- เพิ่ม job type `sandbox-rag` สำหรับคำถาม RAG ใน Playground
- เพิ่ม job type `sandbox-extract` สำหรับ OCR/Extraction ใน Sandbox
- ใช้ priority **SUPERADMIN** (ระดับใหม่ higher than HIGH) สำหรับงาน Sandbox เพื่อให้ได้รับการประมวลผลก่อนงาน batch ปกติโดยไม่ jump queue
- Processor ใน `ai-batch.processor.ts` จะจัดการ job types เหล่านี้เพิ่มเติม
- Concurrency คงที่ที่ 1 ตาม ADR-023A เพื่อป้องกัน VRAM overload
- **Dynamic Rate Limiting:** ตรวจสอบความยาวคิว `ai-batch` ก่อน allow request (queue length < 3 → no limit, queue length ≥ 3 → 10 requests/hour)
### 3. มาตรการควบคุมสิทธิ์ (Security Controls)
- การสลับสวิตช์ AI และการยิง Sandbox Endpoints ทั้งหมดจะถูกปิดกั้นอย่างเข้มงวดด้วยการเช็ค JWT Token และการใช้ `@RequirePermission('system.manage_all')` (CASL Guard)
- **AiEnabledGuard Layered Check:** Superadmin ต้องมีทั้ง `system.manage_all` **และ** `ai.suggest`/`ai.rag_query` เพื่อ bypass เมื่อ AI disabled
- **Admin Endpoints:** ไม่ใช้ AiEnabledGuard (ใช้ permission guard `system.manage_all` เพียงพอ)
- **Job Polling:** ไม่ block job status requests (audit trail ไม่ใช่ AI inference)
- ห้ามระบุ ID หลักเป็น Integer PK ในการทำงาน (เช่น การทดสอบ RAG หรือ Sandbox ประมวลผล) แต่จะใช้ UUIDv7 `publicId` ในการระบุโครงการและจัดกลุ่มเสมอตามข้อตกลง **ADR-019 (Hybrid Identifier Strategy)**
---
## Grilling Session Decisions (2026-05-21)
การตัดสินใจต่อไปนี้ได้รับการ refine ผ่าน grilling session เพื่อความชัดเจนและความพร้อมในการ implement:
| # | ประเด็น | การตัดสินใจ |
|---|---------|--------------|
| 1 | Infrastructure Dependency | ADR-023A infrastructure มีอยู่แล้ว (ai-realtime, ai-batch, permissions) ✅ |
| 2 | system_settings Schema | Generic พร้อม `data_type`, `category`, `is_encrypted`, `validation_rules`, `is_public` |
| 3 | Redis Cache Strategy | Cache แยก key ต่อ setting (เช่น `system_settings:AI_FEATURES_ENABLED`) |
| 4 | Security Controls | Dynamic rate limiting ขึ้นกับ queue length (queue < 3 → no limit, queue ≥ 3 → 10 req/hr) |
| 5 | Frontend Polling | Poll เฉพาะ users ที่มี AI permissions (ทุก 30 วินาที) |
| 6 | AiEnabledGuard | Layered check (system.manage_all + ai.suggest/ai.rag_query) |
| 7 | Error Handling | HTTP 503 + rate-limited warn logs (10 req/user/min) + custom banner debounce 5s |
| 8 | Cache Invalidation | Invalid หลัง DB success (TypeORM transaction) + single key + ยอมรับ 30s latency |
| 9 | Sandbox Priority | Priority ระดับใหม่ `SUPERADMIN` (higher than HIGH) |
| 10 | Health Check | 5s timeout per service + 30s cache + basic queue metrics (waiting, active, failed, rate) |
| 11 | UI/UX | Single page layout + 5s job polling + inline error (red box) + toast |
| 12 | Implementation Priority | Phased (backend → frontend) |
---
## Refined Implementation Details
### 4. AiEnabledGuard Implementation
**Logic:**
```typescript
const aiEnabled = await this.getAiFeaturesEnabled(); // from Redis/DB
const isSuperadmin = user.permissions.includes('system.manage_all');
const hasAiPermission = user.permissions.includes('ai.suggest') || user.permissions.includes('ai.rag_query');
if (!aiEnabled && !(isSuperadmin && hasAiPermission)) {
throw new ServiceUnavailableException({
message: 'AI features are temporarily unavailable',
userMessage: 'ระบบ AI ไม่พร้อมใช้งานชั่วคราว กรุณากรอกข้อมูลด้วยตนเอง',
recoveryAction: 'ติดต่อผู้ดูแลระบบหากต้องการความช่วยเหลือ'
});
}
```
**Error Response (ADR-007):**
- HTTP Status: `503 Service Unavailable`
- Logging: `warn` level แต่ rate limit (log ทุก 10 ครั้งต่อ user ต่อนาที)
- Frontend: Custom Global Banner + debounce 5 วินาที
### 5. Cache Invalidation Strategy
**Timing:** Invalid Redis cache หลัง DB update success (TypeORM transaction)
**Scope:** Invalid เฉพาะ key `system_settings:AI_FEATURES_ENABLED` (efficient)
**Frontend Sync:** ยอมรับ latency 30 วินาที (polling strategy เพียงพอสำหรับ use case นี้)
### 6. Health Check Service
**Timeout:** 5 วินาที per service → timeout return `DEGRADED` (not `DOWN`)
**Frequency:** Cache 30 วินาที (synchronized กับ AI status polling)
**Queue Metrics:** Basic metrics (waiting, active, failed) + processing rate (jobs/second)
**Services:** Ollama (Desk-5439), Qdrant (Desk-5439), BullMQ (ai-realtime, ai-batch)
### 7. Frontend Polling Strategy
**Condition:** Poll เฉพาะ users ที่มี `ai.suggest` หรือ `ai.rag_query` permission
**Frequency:** ทุก 30 วินาที
**Cache:** React Context + refresh on mount
**Implementation:** `useAiStatus()` hook ใน `SessionProvider`
### 8. Admin Console UI/UX
**Layout:** Single page พร้อม tabs (RAG Playground / OCR Sandbox)
**Job Polling:** 5 วินาที (reasonable balance ระหว่าง real-time และ performance)
**Error Display:** Inline error ใน output area (red box) + toast notification
**Style:** Glassmorphism + Health Indicators + Header Switch
@@ -0,0 +1,252 @@
# แผนการพัฒนา: AI Admin Panel (สำหรับสิทธิ์ Superadmin เท่านั้น)
แผนงานนี้จัดทำขึ้นเพื่อแสดงแนวทางการพัฒนาและติดตั้งระบบ **AI Admin Panel** เพื่อให้ผู้ดูแลระบบสูงสุด (Superadmin) สามารถตรวจสอบสถานะการทำงานของเครื่อง AI Host (`Desk-5439`), เปิด/ปิดการใช้งานฟีเจอร์ AI สำหรับผู้ใช้ทั่วไปได้แบบไดนามิก, ตรวจสอบคิวงานของ BullMQ และเวกเตอร์ใน Qdrant รวมถึงมีห้องทดสอบ (Playground Sandbox) ส่วนตัวสำหรับประมวลผล RAG และสกัด Metadata ของเอกสาร
---
## 🎯 วัตถุประสงค์และข้อกำหนดทางเทคนิค
1. **การตรวจสอบสถานะระบบ AI (Health Check):**
- พัฒนาระบบตรวจสอบสุขภาพการเชื่อมต่อและความเร็ว (Latency) ของระบบ **Ollama** และ **Qdrant** บนเครื่อง `Desk-5439`
- ตรวจสอบสถานะและความยาวคิวงานของ **BullMQ** ทั้งหมดในระบบ รวมถึงคิวสำหรับห้องทดสอบของแอดมิน
2. **ปุ่มสวิตช์เปิด/ปิดการตั้งค่า AI (Dynamic Toggle Switch):**
- บันทึกสถานะการเปิด/ปิดลงในฐานข้อมูลตารางใหม่ `system_settings` พร้อมจัดทำ Cache ในระบบ Redis เพื่อการตรวจสอบที่รวดเร็วและไม่มี Latency
- หากแอดมินตั้งค่าเป็น **ปิดใช้งาน AI (false)**:
- **ฝั่งผู้ใช้ทั่วไป (UX Soft Fallback):** ปุ่มขอคำแนะนำจาก AI (AI Suggestion) ในหน้าจอสร้างหรือแก้ไขเอกสาร (RFA / Correspondence) จะเปลี่ยนสถานะเป็น **Disabled (ใช้งานไม่ได้)** และเมื่อผู้ใช้ชี้เมาส์ (Hover) จะมีข้อความแจ้งเตือนสีเหลือง/ส้มว่า `"⚠️ ระบบ AI ไม่พร้อมใช้งานชั่วคราว กรุณากรอกข้อมูลด้วยตนเอง"` พร้อมกับแสดงแถบแจ้งเตือน **Global Banner** ด้านบนสุดของระบบ
- **ฝั่ง API (Block Protection):** ตรวจสอบผ่าน Guard หากผู้ใช้ทั่วไปพยายามเรียกยิง API AI จะตอบกลับด้วยรหัส **HTTP 503 Service Unavailable** ทันที
- **การซิงก์ข้อมูล (Frontend Sync):** Frontend จะใช้ระบบ **Polling ดึงข้อมูลเช็คสถานะระบบทุกๆ 30 วินาที** เพื่อตรวจสอบและปรับเปลี่ยนหน้าจออัตโนมัติ
- **สิทธิ์แอดมิน (Superadmin Bypass):** แอดมินที่มีสิทธิ์ Superadmin จะยังคงเข้าถึงและใช้งานห้องทดสอบ Sandbox ได้ตามปกติ แม้ว่าระบบด้านนอกจะถูกปิดให้บริการอยู่ก็ตาม
3. **ห้องทดสอบส่วนตัวระบบคิวแยก (Isolated BullMQ Sandbox Queue):**
- การสั่งประมวลผลคำถาม RAG และการอัปโหลดไฟล์ PDF สกัด Metadata ใน Sandbox ของแอดมิน จะส่งงานเข้าคิว BullMQ แยกเฉพาะตัวชื่อ `ai-admin-sandbox` เพื่อจำลองโหลดและความเร็วในการทำงานจริงของระบบคิว แต่แยกคิวออกมาเพื่อไม่ให้โดนบล็อกจากคิวค้างของผู้ใช้งานปกติในระบบ
---
## 📐 รายละเอียดการเปลี่ยนแปลงในระบบ (Proposed Changes)
### 🗄️ 1. โครงสร้างฐานข้อมูล (Database Layer)
เพิ่มตาราง `system_settings` ในฐานข้อมูล MariaDB เพื่อเก็บค่าการตั้งค่าแบบไดนามิก (ตามแนวทาง ADR-009)
#### [MODIFY] [lcbp3-v1.9.0-schema-02-tables.sql](file:///E:/np-dms/lcbp3/specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql)
เพิ่มตาราง `system_settings` ในส่วน Users & RBAC (หลังตาราง permissions):
```sql
-- ตารางเก็บการตั้งค่าระบบแบบไดนามิก (System Settings) - Generic Design
CREATE TABLE system_settings (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
setting_key VARCHAR(100) NOT NULL UNIQUE COMMENT 'คีย์การตั้งค่าระบบ (เช่น AI_FEATURES_ENABLED, MAX_UPLOAD_SIZE)',
setting_value TEXT NOT NULL COMMENT 'ค่าที่บันทึก (stringified)',
data_type ENUM('string', 'number', 'boolean', 'json') NOT NULL DEFAULT 'string' COMMENT 'ประเภทข้อมูลสำหรับ validation',
category VARCHAR(50) COMMENT 'หมวดหมู่ (เช่น ai, security, storage, notification)',
is_encrypted TINYINT(1) DEFAULT 0 COMMENT 'เข้ารหัสค่า sensitive (เช่น API keys)',
validation_rules JSON COMMENT 'กฎ validation (min, max, allowed_values)',
description TEXT COMMENT 'คำอธิบายข้อมูลการตั้งค่า',
is_public TINYINT(1) DEFAULT 0 COMMENT 'เผยแพร่ให้ frontend อ่านได้ (หรือ admin only)',
updated_by INT COMMENT 'ผู้แก้ไขล่าสุด',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (updated_by) REFERENCES users (user_id) ON DELETE SET NULL,
INDEX idx_category (category),
INDEX idx_is_public (is_public)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บข้อมูลการตั้งค่าระบบไดนามิก';
-- Seed ค่าเริ่มต้นสำหรับการควบคุมสถานะระบบ AI
INSERT INTO system_settings (setting_key, setting_value, data_type, category, description, is_public)
VALUES ('AI_FEATURES_ENABLED', 'true', 'boolean', 'ai', 'สถานะเปิด/ปิดการใช้งานฟีเจอร์ AI ทั้งระบบ สำหรับผู้ใช้ทั่วไป (true/false)', 1)
ON DUPLICATE KEY UPDATE setting_key = setting_key;
```
**การนำไปใช้งาน:** รัน SQL ด้านบนผ่าน manual execution หรือ n8n workflow ตาม ADR-009
---
### 💻 2. ส่วนของระบบหลังบ้าน (Backend Layer - NestJS)
#### [MODIFY] [queue.constants.ts](file:///E:/np-dms/lcbp3/backend/src/modules/common/constants/queue.constants.ts)
- เพิ่ม priority constant สำหรับ SUPERADMIN:
```typescript
export const PRIORITY_SUPERADMIN = 10; // Higher than HIGH (5)
```
#### [NEW] [system-setting.entity.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/entities/system-setting.entity.ts)
- สร้าง Entity รองรับโครงสร้างตารางใหม่ (ไม่มีบรรทัดว่างในฟังก์ชันตามข้อตกลง):
```typescript
// File: src/modules/ai/entities/system-setting.entity.ts
// Change Log
// - 2026-05-21: สร้าง Entity SystemSetting สำหรับเก็บการตั้งค่า (Generic Design)
import { Entity, Column, PrimaryGeneratedColumn, UpdateDateColumn, CreateDateColumn } from 'typeorm';
@Entity('system_settings')
export class SystemSetting {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'setting_key', unique: true, length: 100 })
settingKey: string;
@Column({ name: 'setting_value', type: 'text' })
settingValue: string;
@Column({
name: 'data_type',
type: 'enum',
enum: ['string', 'number', 'boolean', 'json'],
default: 'string'
})
dataType: string;
@Column({ name: 'category', length: 50, nullable: true })
category: string;
@Column({ name: 'is_encrypted', type: 'tinyint', default: 0 })
isEncrypted: boolean;
@Column({ name: 'validation_rules', type: 'json', nullable: true })
validationRules: Record<string, unknown>;
@Column({ type: 'text', nullable: true })
description: string;
@Column({ name: 'is_public', type: 'tinyint', default: 0 })
isPublic: boolean;
@Column({ name: 'updated_by', nullable: true })
updatedBy: number;
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
@UpdateDateColumn({ name: 'updated_at' })
updatedAt: Date;
}
```
#### [MODIFY] [ai.module.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/ai.module.ts)
- ลงทะเบียน `SystemSetting` ใน TypeORM `forFeature`
#### [MODIFY] [ai-batch.processor.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/processors/ai-batch.processor.ts)
- เพิ่มการรองรับ job type ใหม่สำหรับ Sandbox:
- `sandbox-rag` -> ค้นหาในเวกเตอร์และตอบคำถาม RAG พร้อมแสดง Citations (priority: SUPERADMIN)
- `sandbox-extract` -> รัน OCR บนไฟล์ PDF เดี่ยวและประมวลผลสกัด Metadata คืนออกมาเป็นก้อนข้อมูล JSON (priority: SUPERADMIN)
- **Dynamic Rate Limiting:** เพิ่ม middleware ตรวจสอบความยาวคิว `ai-batch` ก่อน allow sandbox request (queue length < 3 → no limit, queue length ≥ 3 → 10 req/hr)
#### [MODIFY] [ai-queue.service.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/ai-queue.service.ts)
- เพิ่มฟังก์ชัน `enqueueSandboxJob(type: string, payload: any)` เพื่อส่งงานของแอดมินเข้าคิว `ai-batch` พร้อม priority SUPERADMIN
- เพิ่มฟังก์ชัน `getQueueLength(queueName: string)` เพื่อตรวจสอบความยาวคิวสำหรับ dynamic rate limiting
#### [MODIFY] [ai.service.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/ai.service.ts)
- เพิ่มเมธอดสำหรับอ่าน/เขียนการตั้งค่า:
- `getAiFeaturesEnabled()`: ค้นหาค่า `AI_FEATURES_ENABLED` จาก Redis Key `system_settings:AI_FEATURES_ENABLED` ก่อน หากไม่มีจึงไปดึงจากตาราง `system_settings` แล้วเขียนลง Redis Cache เพื่อใช้ครั้งต่อไป
- `setAiFeaturesEnabled(enabled: boolean, userId: number)`: อัปเดตสถานะในตารางฐานข้อมูล (TypeORM transaction) และอัปเดต Redis Cache ทันที (invalid key เดียว)
- `getSystemHealth()`: รวบรวมข้อมูลสุขภาพของระบบ Ollama, Qdrant, และคิว BullMQ ต่างๆ (cache 30 วินาที, 5s timeout per service)
#### [NEW] [ai-enabled.guard.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/guards/ai-enabled.guard.ts)
- สร้าง Guard สำหรับเช็คสถานะการปิด AI ทั่วระบบ:
- **Layered Check Logic:** Superadmin ต้องมีทั้ง `system.manage_all` **และ** `ai.suggest`/`ai.rag_query` เพื่อ bypass เมื่อ AI disabled
- หากคีย์การเปิดใช้งานถูกตั้งค่าเป็น `'false'` และผู้ใช้ไม่ผ่าน layered check จะปฏิเสธการเข้าใช้งาน API ด้วยรหัสข้อผิดพลาด **HTTP 503 Service Unavailable**
- Guard นี้ติดตั้งบน endpoints AI ทั่วไป (AI Suggestion, RAG Query) ไม่ใช่ admin endpoints
- **Admin Endpoints:** ไม่ใช้ AiEnabledGuard (ใช้ permission guard `system.manage_all` เพียงพอ)
- **Job Polling:** ไม่ block job status requests (audit trail ไม่ใช่ AI inference)
- **Error Handling (ตาม ADR-007):**
- Response body ประกอบด้วย: `{ message: "AI features are temporarily unavailable", userMessage: "ระบบ AI ไม่พร้อมใช้งานชั่วคราว กรุณากรอกข้อมูลด้วยตนเอง", recoveryAction: "ติดต่อผู้ดูแลระบบหากต้องการความช่วยเหลือ" }`
- Backend Logger: `warn` level แต่ rate limit (log ทุก 10 ครั้งต่อ user ต่อนาที) เพื่อป้องกัน log spam
- Frontend Error Display: Custom Global Banner + debounce 5 วินาที
#### [MODIFY] [ai.controller.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/ai.controller.ts)
- นำ Guard `AiEnabledGuard` ไปติดตั้งใน Endpoints ยิงทำงาน AI ของผู้ใช้ทั่วไป (AI Suggestion, RAG Query)
- เพิ่มกลุ่ม API สำหรับ Superadmin เท่านั้น (ควบคุมด้วย `@RequirePermission('system.manage_all')` ตาม ADR-016):
- `GET /ai/admin/settings` -> แสดงสถานะเปิด/ปิด AI ปัจจุบัน
- `POST /ai/admin/toggle` -> สลับสถานะเปิด/ปิดระบบ AI (พร้อม Audit logging)
- `GET /ai/admin/health` -> ดึงรายงานสุขภาพระบบ Ollama, Qdrant, คิว BullMQ ทั้งระบบ
- `POST /ai/admin/sandbox/rag` -> ส่งงานคำถาม RAG เข้าคิว ai-batch (priority: SUPERADMIN) + dynamic rate limiting
- `POST /ai/admin/sandbox/extract` -> ส่ออัปโหลด PDF สกัด metadata เข้าคิว ai-batch (priority: SUPERADMIN) + dynamic rate limiting
- `GET /ai/admin/sandbox/job/:id` -> Polling ตรวจสอบความคืบหน้าของงานในคิว (ไม่ block)
- **Security Measures (ตาม ADR-016):**
- ทุก admin endpoints ใช้ `@RequirePermission('system.manage_all')`
- `POST /ai/admin/toggle` มี Audit logging บันทึกใน `audit_logs` table (action: 'AI_FEATURES_TOGGLED', details: { enabled: boolean })
- ใช้ `@Audit()` decorator บนทุก admin endpoints
- มี Rate limiting ตาม ADR-016 (ThrottlerGuard) บน auth endpoints
---
### 🎨 3. ส่วนหน้าจอแสดงผล (Frontend Layer - Next.js)
#### [NEW] [admin-ai.service.ts](file:///E:/np-dms/lcbp3/frontend/lib/services/admin-ai.service.ts)
- พัฒนา API Service สำหรับดึงข้อมูลและสั่งงานของระบบ Admin AI Panel:
- ดึงข้อมูลสุขภาพ ตรวจสอบการตั้งค่า สลับปุ่มเปิด/ปิด
- ส่งงาน Sandbox RAG/Extraction และ Polling เช็คผลลัพธ์ของ Job
- **UUID Handling (ตาม ADR-019):**
- ใช้ `publicId` (string UUID) สำหรับ job ID จาก BullMQ เท่านั้น
- ห้ามใช้ `id ?? ''` fallback ในกรณีใดๆ
#### [NEW] [page.tsx](file:///E:/np-dms/lcbp3/frontend/app/(admin)/admin/ai/page.tsx)
- หน้าต่าง **AI Control Panel & Playground** ออกแบบอย่างพรีเมียม สไตล์ Glassmorphism:
- **Layout:** Single page พร้อม tabs (RAG Playground / OCR Sandbox)
- **Header Switch:** สวิตช์ปุ่มเรืองแสงสีเขียว/ส้มขนาดใหญ่ สำหรับเปิด/ปิดใช้งานระบบ AI
- **Health Indicators:** การ์ดประเมินสถานะของ Ollama, Qdrant, และ คิว BullMQ แบบเรียลไทม์ (cache 30 วินาที)
- **RAG Playground Tab:** แชทบอทโต้ตอบผ่าน ai-batch queue พร้อมสถานะความคืบหน้าของคิว (poll ทุก 5 วินาที) แสดงคำตอบและเอกสารอ้างอิงสวยงาม
- **OCR Sandbox Tab:** กล่องวางอัปโหลดไฟล์ PDF เดี่ยวเพื่อจำลองการรัน OCR และดึง Metadata แสดงก้อน JSON ด้วย Syntax highlighting สวยงาม
- **Error Display:** Inline error ใน output area (red box) + toast notification
- **i18n (ตาม i18n Guidelines):**
- ใช้ i18n keys สำหรับข้อความทั้งหมด (เช่น `ai.admin.panel.title`, `ai.admin.panel.health.status`)
- ห้าม hardcode ข้อความภาษาไทยใน component
#### [MODIFY] [sidebar.tsx](file:///E:/np-dms/lcbp3/frontend/components/admin/sidebar.tsx)
- เพิ่มปุ่มเมนู **"AI Console"** (ไอคอน Brain) ใน Sidebar สำหรับแอดมิน เพื่อลิงก์ไปหน้าจอ `/admin/ai`
#### [MODIFY] [layout.tsx](file:///E:/np-dms/lcbp3/frontend/app/layout.tsx)
- เพิ่มกลไก Polling ตรวจเช็คสถานะการเปิดใช้ AI **ทุก 30 วินาที** แต่ **เฉพาะ users ที่มี AI permissions** (`ai.suggest` หรือ `ai.rag_query`)
- หากระบบ AI ปิดตัวลง จะแสดงแถบแจ้งเตือน **Global Banner** ด้านบนสุด (debounce 5 วินาที) และส่งสัญญาณบอกหน้าจอฟอร์มเพื่อ Disable ปุ่ม AI Suggestion
- **Cache:** React Context + refresh on mount
- **Implementation:** `useAiStatus()` hook ใน `SessionProvider`
- **i18n:** ใช้ i18n keys สำหรับ Global Banner message (เช่น `ai.disabled.banner.message`, `ai.disabled.banner.tooltip`)
---
## 📋 Grilling Session Decisions Summary (2026-05-21)
การตัดสินใจต่อไปนี้ได้รับการ refine ผ่าน grilling session เพื่อความชัดเจนและความพร้อมในการ implement:
| # | ประเด็น | การตัดสินใจ |
|---|---------|--------------|
| 1 | Infrastructure Dependency | ADR-023A infrastructure มีอยู่แล้ว (ai-realtime, ai-batch, permissions) ✅ |
| 2 | system_settings Schema | Generic พร้อม `data_type`, `category`, `is_encrypted`, `validation_rules`, `is_public` |
| 3 | Redis Cache Strategy | Cache แยก key ต่อ setting (เช่น `system_settings:AI_FEATURES_ENABLED`) |
| 4 | Security Controls | Dynamic rate limiting ขึ้นกับ queue length (queue < 3 → no limit, queue ≥ 3 → 10 req/hr) |
| 5 | Frontend Polling | Poll เฉพาะ users ที่มี AI permissions (ทุก 30 วินาที) |
| 6 | AiEnabledGuard | Layered check (system.manage_all + ai.suggest/ai.rag_query) |
| 7 | Error Handling | HTTP 503 + rate-limited warn logs (10 req/user/min) + custom banner debounce 5s |
| 8 | Cache Invalidation | Invalid หลัง DB success (TypeORM transaction) + single key + ยอมรับ 30s latency |
| 9 | Sandbox Priority | Priority ระดับใหม่ `SUPERADMIN` (higher than HIGH) |
| 10 | Health Check | 5s timeout per service + 30s cache + basic queue metrics (waiting, active, failed, rate) |
| 11 | UI/UX | Single page layout + 5s job polling + inline error (red box) + toast |
| 12 | Implementation Priority | Phased (backend → frontend) |
---
## 🧪 แผนการตรวจสอบความถูกต้อง (Verification Plan)
### 🤖 1. การทดสอบอัตโนมัติ (Automated Tests)
- พัฒนา Unit Test ใน Backend ครอบคลุมพฤติกรรมการบล็อกสิทธิ์ผ่าน Guard, การทำงานของ Cache, และการทำงานของระบบคิว Sandbox แยกเฉพาะ
- **Coverage Goals (ตาม ADR-023A):**
- Business Logic: 80%+ สำหรับ SystemSettingService, AiService, AiQueueService
- Backend Overall: 70%+ สำหรับ AI Module ทั้งหมด
- **Test Files:**
- `system-setting.service.spec.ts` - CRUD operations + Cache invalidation
- `ai-enabled.guard.spec.ts` - Guard logic (block non-superadmin when AI disabled, allow superadmin)
- `ai-queue.service.spec.ts` - Queue operations (sandbox job enqueue with HIGH priority)
- `ai.service.spec.ts` - getAiFeaturesEnabled (Redis Cache miss/hit), setAiFeaturesEnabled (DB + Cache update), getSystemHealth
- สั่งรันการทดสอบผ่าน PowerShell บน Windows:
```powershell
cd backend
npm run test src/modules/ai
npm run test:cov src/modules/ai
```
### 🧑‍💻 2. การทดสอบด้วยตนเอง (Manual Tests)
1. **การจำกัดสิทธิ์:** ตรวจสอบว่าผู้ใช้ทั่วไปต้องไม่สามารถเข้าถึงหน้าจอและ API ระบบแอดมินได้
2. **การสลับปิดระบบ AI:**
- ทดสอบสลับสวิตช์เป็นปิดใช้งาน
- ตรวจสอบว่าหน้าจอผู้ใช้ปกติแสดง Global Banner และปุ่มขอแนะนำ Metadata ถูกปรับเป็น Disabled มี Tooltip ชี้แจง
- ตรวจสอบว่า Superadmin ยังสามารถเข้าไปคุยแชท RAG และโยนไฟล์ PDF ทดสอบสกัดข้อมูลใน Sandbox ได้เสมือนปกติทุกประการ
3. **การทดสอบความถูกต้องของคิว Sandbox:** ตรวจสอบว่าข้อมูลไหลผ่านคิว `ai-batch` (job types: sandbox-rag, sandbox-extract) ได้สำเร็จและได้รับผลลัพธ์ประมวลผลถูกต้อง
@@ -0,0 +1,180 @@
// File: specs/200-fullstacks/227-ai-admin-console/plan.md
// Change Log:
// - 2026-05-20: แผนการพัฒนาฉบับภาษาไทยสำหรับระบบ AI Admin Console
# Implementation Plan: AI Admin Console
**Branch**: `227-ai-admin-console` | **Date**: 2026-05-20 | **Spec**: [spec.md](./spec.md) | **ADR**: [ADR-027](../../06-Decision-Records/ADR-027-ai-admin-console-and-dynamic-control.md)
---
## สรุปแนวทางเชิงเทคนิค (Technical Summary)
แผนงานนี้จัดทำขึ้นเพื่อออกแบบและติดตั้งระบบ **AI Admin Panel** สำหรับสิทธิ์ **Superadmin** เท่านั้น เพื่อให้ผู้ดูแลระบบสามารถตรวจสอบสุขภาพของเครื่อง AI Host (`Desk-5439`), เปิด/ปิดการใช้งานฟีเจอร์ AI สำหรับผู้ใช้ทั่วไปได้แบบ Dynamic, ตรวจสอบสถานะ BullMQ/Qdrant, และใช้งาน Playground ทดสอบคำสั่ง RAG และการทำ OCR/Metadata extraction โดยอ้างอิงตามข้อสรุปการตัดสินใจจากการ Grill Session และสอดคล้องกับนโยบายเอกสารของโครงการอย่างครบถ้วน
---
## วัตถุประสงค์และข้อกำหนดทางเทคนิค
1. **การตรวจสอบสถานะระบบ AI (Health Check):**
- พัฒนาระบบตรวจสอบสุขภาพการเชื่อมต่อและความเร็ว (Latency) ของระบบ **Ollama** และ **Qdrant** บนเครื่อง `Desk-5439`
- ตรวจสอบสถานะและความยาวคิวงานของ **BullMQ** ทั้งหมดในระบบ รวมถึงคิวสำหรับห้องทดสอบของแอดมิน
2. **ปุ่มสวิตช์เปิด/ปิดการตั้งค่า AI (Dynamic Toggle Switch):**
- บันทึกสถานะการเปิด/ปิดลงในฐานข้อมูลตารางใหม่ `system_settings` พร้อมจัดทำ Cache ในระบบ Redis เพื่อการตรวจสอบที่รวดเร็วและไม่มี Latency
- หากแอดมินตั้งค่าเป็น **ปิดใช้งาน AI (false)**:
- **ฝั่งผู้ใช้ทั่วไป (UX Soft Fallback):** ปุ่มขอคำแนะนำจาก AI (AI Suggestion) ในหน้าจอสร้างหรือแก้ไขเอกสาร (RFA / Correspondence) จะเปลี่ยนสถานะเป็น **Disabled (ใช้งานไม่ได้)** และเมื่อผู้ใช้ชี้เมาส์ (Hover) จะมีข้อความแจ้งเตือนสีเหลือง/ส้มว่า `"⚠️ ระบบ AI ไม่พร้อมใช้งานชั่วคราว กรุณากรอกข้อมูลด้วยตนเอง"` พร้อมกับแสดงแถบแจ้งเตือน **Global Banner** ด้านบนสุดของระบบ
- **ฝั่ง API (Block Protection):** ตรวจสอบผ่าน Guard หากผู้ใช้ทั่วไปพยายามเรียกยิง API AI จะตอบกลับด้วยรหัส **HTTP 503 Service Unavailable** ทันที
- **การซิงก์ข้อมูล (Frontend Sync):** Frontend จะใช้ระบบ **Polling ดึงข้อมูลเช็คสถานะระบบทุกๆ 30 วินาที** เพื่อตรวจสอบและปรับเปลี่ยนหน้าจออัตโนมัติ
- **สิทธิ์แอดมิน (Superadmin Bypass):** แอดมินที่มีสิทธิ์ Superadmin จะยังคงเข้าถึงและใช้งานห้องทดสอบ Sandbox ได้ตามปกติ แม้ว่าระบบด้านนอกจะถูกปิดให้บริการอยู่ก็ตาม
3. **ห้องทดสอบส่วนตัวระบบคิวแยก (Isolated BullMQ Sandbox Queue):**
- การสั่งประมวลผลคำถาม RAG และการอัปโหลดไฟล์ PDF สกัด Metadata ใน Sandbox ของแอดมิน จะส่งงานเข้าคิว BullMQ แยกเฉพาะตัวชื่อ `ai-admin-sandbox` เพื่อจำลองโหลดและความเร็วในการทำงานจริงของระบบคิว แต่แยกคิวออกมาเพื่อไม่ให้โดนบล็อกจากคิวค้างของผู้ใช้งานปกติในระบบ
---
## รายละเอียดการเปลี่ยนแปลงในระบบ (Proposed Changes)
### 🗄️ 1. โครงสร้างฐานข้อมูล (Database Layer)
สร้างตาราง `system_settings` ในฐานข้อมูล MariaDB เพื่อเก็บค่าการตั้งค่าแบบไดนามิก (ตามแนวทาง ADR-009)
#### [NEW] [03-09-add-system-settings.sql](file:///E:/np-dms/lcbp3/backend/migrations/03-09-add-system-settings.sql)
- เขียนคำสั่ง SQL เพื่อสร้างตาราง `system_settings` และ Seed ข้อมูลเบื้องต้น:
```sql
-- File: backend/migrations/03-09-add-system-settings.sql
-- Change Log
-- - 2026-05-20: สร้างตาราง system_settings และ seed ค่าเปิด/ปิด AI
CREATE TABLE IF NOT EXISTS system_settings (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
setting_key VARCHAR(100) NOT NULL UNIQUE COMMENT 'คีย์การตั้งค่าระบบ (เช่น AI_FEATURES_ENABLED)',
setting_value TEXT NOT NULL COMMENT 'ค่าที่บันทึก',
description TEXT COMMENT 'คำอธิบายข้อมูลการตั้งค่า',
updated_by INT COMMENT 'ผู้แก้ไขล่าสุด',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (updated_by) REFERENCES users (user_id) ON DELETE SET NULL
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บข้อมูลการตั้งค่าระบบไดนามิก';
-- Seed ค่าเริ่มต้นสำหรับการควบคุมสถานะระบบ AI
INSERT INTO system_settings (setting_key, setting_value, description)
VALUES ('AI_FEATURES_ENABLED', 'true', 'สถานะเปิด/ปิดการใช้งานฟีเจอร์ AI ทั้งระบบ สำหรับผู้ใช้ทั่วไป (true/false)')
ON DUPLICATE KEY UPDATE setting_key = setting_key;
```
---
### 💻 2. ส่วนของระบบหลังบ้าน (Backend Layer - NestJS)
#### [MODIFY] [queue.constants.ts](file:///E:/np-dms/lcbp3/backend/src/modules/common/constants/queue.constants.ts)
- เพิ่มรหัสคิวใหม่สำหรับห้องทดสอบของแอดมิน:
```typescript
export const QUEUE_AI_ADMIN_SANDBOX = 'ai-admin-sandbox';
```
#### [NEW] [system-setting.entity.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/entities/system-setting.entity.ts)
- สร้าง Entity รองรับโครงสร้างตารางใหม่ (ไม่มีบรรทัดว่างในฟังก์ชันตามข้อตกลง):
```typescript
// File: src/modules/ai/entities/system-setting.entity.ts
// Change Log
// - 2026-05-20: สร้าง Entity SystemSetting สำหรับเก็บการตั้งค่า
import { Entity, Column, PrimaryGeneratedColumn, UpdateDateColumn, CreateDateColumn } from 'typeorm';
@Entity('system_settings')
export class SystemSetting {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'setting_key', unique: true, length: 100 })
settingKey: string;
@Column({ name: 'setting_value', type: 'text' })
settingValue: string;
@Column({ type: 'text', nullable: true })
description: string;
@Column({ name: 'updated_by', nullable: true })
updatedBy: number;
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
@UpdateDateColumn({ name: 'updated_at' })
updatedAt: Date;
}
```
#### [MODIFY] [ai.module.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/ai.module.ts)
- ลงทะเบียน `SystemSetting` ใน TypeORM `forFeature`
- ลงทะเบียนคิว BullMQ `QUEUE_AI_ADMIN_SANDBOX` และตัวประมวลผลคิว `AiSandboxProcessor`
#### [NEW] [ai-sandbox.processor.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/processors/ai-sandbox.processor.ts)
- พัฒนาตัวประมวลผลงานคิว `ai-admin-sandbox` โดยเฉพาะ:
- รับงานประเภท `sandbox-rag` -> ค้นหาในเวกเตอร์และตอบคำถาม RAG พร้อมแสดง Citations
- รับงานประเภท `sandbox-extract` -> รัน OCR บนไฟล์ PDF เดี่ยวและประมวลผลสกัด Metadata คืนออกมาเป็นก้อนข้อมูล JSON
#### [MODIFY] [ai-queue.service.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/ai-queue.service.ts)
- เพิ่มฟังก์ชัน `enqueueSandboxJob(type: string, payload: any)` เพื่อส่งงานของแอดมินเข้าคิว Sandbox แยกต่างหาก
#### [MODIFY] [ai.service.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/ai.service.ts)
- เพิ่มเมธอดสำหรับอ่าน/เขียนการตั้งค่า:
- `getAiFeaturesEnabled()`: ค้นหาค่า `AI_FEATURES_ENABLED` จาก Redis Cache ก่อน หากไม่มีจึงไปดึงจากตาราง `system_settings` แล้วเขียนลง Redis Cache เพื่อใช้ครั้งต่อไป
- `setAiFeaturesEnabled(enabled: boolean, userId: number)`: อัปเดตสถานะในตารางฐานข้อมูลและอัปเดต Redis Cache ทันที
- `getSystemHealth()`: รวบรวมข้อมูลสุขภาพของระบบ Ollama, Qdrant, และคิว BullMQ ต่างๆ (รวมความยาวคิว sandbox)
#### [NEW] [ai-enabled.guard.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/guards/ai-enabled.guard.ts)
- สร้าง Guard สำหรับเช็คสถานะการปิด AI ทั่วระบบ:
- หากคีย์การเปิดใช้งานถูกตั้งค่าเป็น `'false'` และผู้ใช้ไม่มีสิทธิ์จัดการระบบสูงสุด (`system.manage_all`) จะปฏิเสธการเข้าใช้งาน API ด้วยรหัสข้อผิดพลาด **HTTP 503 Service Unavailable**
#### [MODIFY] [ai.controller.ts](file:///E:/np-dms/lcbp3/backend/src/modules/ai/ai.controller.ts)
- นำ Guard `AiEnabledGuard` ไปติดตั้งใน Endpoints ยิงทำงาน AI ของผู้ใช้ทั่วไป
- เพิ่มกลุ่ม API ปลอดภัยสำหรับ Superadmin เท่านั้น (ควบคุมด้วย `@RequirePermission('system.manage_all')`):
- `GET /ai/admin/settings` -> แสดงสถานะเปิด/ปิด AI ปัจจุบัน
- `POST /ai/admin/toggle` -> สลับสถานะเปิด/ปิดระบบ AI
- `GET /ai/admin/health` -> ดึงรายงานสุขภาพระบบ Ollama, Qdrant, คิว BullMQ ทั้งระบบ
- `POST /ai/admin/sandbox/rag` -> ส่งงานคำถาม RAG เข้าคิว Sandbox
- `POST /ai/admin/sandbox/extract` -> ส่งอัปโหลด PDF สกัด metadata เข้าคิว Sandbox
- `GET /ai/admin/sandbox/job/:id` -> Polling ตรวจสอบความคืบหน้าของงานในคิว Sandbox
---
### 🎨 3. ส่วนหน้าจอแสดงผล (Frontend Layer - Next.js)
#### [NEW] [admin-ai.service.ts](file:///E:/np-dms/lcbp3/frontend/lib/services/admin-ai.service.ts)
- พัฒนา API Service สำหรับดึงข้อมูลและสั่งงานของระบบ Admin AI Panel:
- ดึงข้อมูลสุขภาพ ตรวจสอบการตั้งค่า สลับปุ่มเปิด/ปิด
- ส่งงาน Sandbox RAG/Extraction และ Polling เช็คผลลัพธ์ของ Job
#### [NEW] [page.tsx](file:///E:/np-dms/lcbp3/frontend/app/%28admin%29/admin/ai/page.tsx)
- หน้าต่าง **AI Control Panel & Playground** ออกแบบอย่างพรีเมียม สไตล์ Glassmorphism:
- **Header Switch:** สวิตช์ปุ่มเรืองแสงสีเขียว/ส้มขนาดใหญ่ สำหรับเปิด/ปิดใช้งานระบบ AI
- **Health Indicators:** การ์ดประเมินสถานะของ Ollama, Qdrant, และ คิว BullMQ แบบเรียลไทม์
- **RAG Playground Tab:** แชทบอทโต้ตอบผ่าน Isolated Queue พร้อมสถานะความคืบหน้าของคิว แสดงคำตอบและเอกสารอ้างอิงสวยงาม
- **OCR Sandbox Tab:** กล่องวางอัปโหลดไฟล์ PDF เดี่ยวเพื่อจำลองการรัน OCR และดึง Metadata แสดงก้อน JSON ด้วย Syntax highlighting สวยงาม
#### [MODIFY] [sidebar.tsx](file:///E:/np-dms/lcbp3/frontend/components/admin/sidebar.tsx)
- เพิ่มปุ่มเมนู **"AI Console"** (ไอคอน Brain) ใน Sidebar สำหรับแอดมิน เพื่อลิงก์ไปหน้าจอ `/admin/ai`
#### [MODIFY] [layout.tsx](file:///E:/np-dms/lcbp3/frontend/app/layout.tsx)
- เพิ่มกลไก Polling ตรวจเช็คสถานะการเปิดใช้ AI ทุก 30 วินาที
- หากระบบ AI ปิดตัวลง จะแสดงแถบแจ้งเตือน **Global Banner** ด้านบนสุด และส่งสัญญาณบอกหน้าจอฟอร์มเพื่อ Disable ปุ่ม AI Suggestion
---
## แผนการตรวจสอบความถูกต้อง (Verification Plan)
### 🤖 1. การทดสอบอัตโนมัติ (Automated Tests)
- พัฒนา Unit Test ใน Backend ครอบคลุมพฤติกรรมการบล็อกสิทธิ์ผ่าน Guard, การทำงานของ Cache, และการทำงานของระบบคิว Sandbox แยกเฉพาะ
- สั่งรันการทดสอบผ่าน PowerShell บน Windows:
```powershell
cd backend
npm run test src/modules/ai
```
### 🧑‍💻 2. การทดสอบด้วยตนเอง (Manual Tests)
1. **การจำกัดสิทธิ์:** ตรวจสอบว่าผู้ใช้ทั่วไปต้องไม่สามารถเข้าถึงหน้าจอและ API ระบบแอดมินได้
2. **การสลับปิดระบบ AI:**
- ทดสอบสลับสวิตช์เป็นปิดใช้งาน
- ตรวจสอบว่าหน้าจอผู้ใช้ปกติแสดง Global Banner และปุ่มขอแนะนำ Metadata ถูกปรับเป็น Disabled มี Tooltip ชี้แจง
- ตรวจสอบว่า Superadmin ยังสามารถเข้าไปคุยแชท RAG และโยนไฟล์ PDF ทดสอบสกัดข้อมูลใน Sandbox ได้เสมือนปกติทุกประการ
3. **การทดสอบความถูกต้องของคิว Sandbox:** ตรวจสอบว่าข้อมูลไหลผ่านคิว `ai-admin-sandbox` ได้สำเร็จและได้รับผลลัพธ์ประมวลผลถูกต้อง
@@ -0,0 +1,146 @@
// File: specs/200-fullstacks/227-ai-admin-console/spec.md
// Change Log:
// - 2026-05-20: Feature Specification สำหรับระบบ AI Admin Console
// - 2026-05-21: Restructure following spec-template.md with User Stories, FRs, Success Criteria
# Feature Specification: AI Admin Console
**Feature Branch**: `227-ai-admin-console`
**Created**: 2026-05-20
**Status**: Draft
**Category**: 200-fullstacks
**Input**: ADR-027 AI Admin Panel and Dynamic Control Architecture
---
## User Scenarios & Testing
### User Story 1 - Superadmin Toggles AI System On/Off (Priority: P1)
As a Superadmin, I need to dynamically enable or disable AI features for all regular users without redeploying the system, so that I can perform maintenance, manage system load, or handle AI infrastructure issues gracefully.
**Why this priority**: This is the core control mechanism of the feature. Without it, the admin cannot perform emergency maintenance or manage system resources during high load periods.
**Independent Test**: Can be fully tested by a Superadmin toggling the AI switch and observing that regular users immediately see the disabled state (within polling interval) while the Superadmin retains full access.
**Acceptance Scenarios**:
1. **Given** the AI system is currently enabled, **When** a Superadmin toggles the switch to disabled, **Then** the setting is persisted to database and cache, and regular users see disabled AI buttons within 30 seconds
2. **Given** the AI system is currently disabled, **When** a Superadmin toggles the switch to enabled, **Then** regular users can access AI features again after the polling interval
3. **Given** a regular user has AI permissions, **When** they attempt to use AI features while the system is disabled, **Then** they receive HTTP 503 with a user-friendly message explaining temporary unavailability
---
### User Story 2 - Normal Users Experience Soft Fallback (Priority: P1)
As a regular user with AI permissions, I need clear visual feedback when AI features are temporarily disabled, so that I understand why AI buttons are unavailable and can complete my work manually without confusion.
**Why this priority**: Critical for user experience. Abrupt feature disappearance creates confusion and support tickets. Soft fallback maintains user trust.
**Independent Test**: Can be tested by disabling AI system and verifying that regular users see disabled buttons with tooltips and global banner, rather than errors or missing UI elements.
**Acceptance Scenarios**:
1. **Given** the AI system is disabled by admin, **When** a regular user views a document form with AI suggestion buttons, **Then** those buttons appear disabled with a tooltip explaining "ระบบ AI ไม่พร้อมใช้งานชั่วคราว"
2. **Given** the AI system is disabled, **When** a regular user loads any page, **Then** a global banner appears at the top stating AI is temporarily unavailable
3. **Given** a regular user attempts direct API access to AI endpoints while disabled, **When** the request is made, **Then** the system returns HTTP 503 with recovery guidance
---
### User Story 3 - Superadmin Monitors AI Health Status (Priority: P2)
As a Superadmin, I need real-time visibility into AI infrastructure health (Ollama, Qdrant, BullMQ queues), so that I can diagnose issues, monitor latency, and make informed decisions about enabling/disabling AI services.
**Why this priority**: Essential for operational awareness but secondary to the control mechanism itself.
**Independent Test**: Can be tested by accessing the AI Admin Console health dashboard and verifying all metrics display correctly with appropriate status indicators.
**Acceptance Scenarios**:
1. **Given** the AI Admin Console is accessed, **When** a Superadmin views the health panel, **Then** they see Ollama latency, active model version, Qdrant collection stats, and BullMQ queue metrics (waiting/active/failed jobs)
2. **Given** a service is experiencing issues, **When** health check runs, **Then** the status displays as degraded/down with relevant metrics highlighted
3. **Given** the Superadmin is monitoring the system, **When** they refresh or view the dashboard, **Then** metrics are cached for 30 seconds to prevent excessive load
---
### User Story 4 - Superadmin Uses RAG Playground Sandbox (Priority: P2)
As a Superadmin, I need an isolated RAG testing environment where I can query documents and receive AI-generated responses with citations, so that I can test and refine AI behavior without affecting production queues or user experiences.
**Why this priority**: Enables safe testing and troubleshooting of AI capabilities during maintenance windows.
**Independent Test**: Can be tested by submitting a RAG query in the sandbox and receiving a complete response with document citations, while verifying the job runs through the isolated sandbox queue.
**Acceptance Scenarios**:
1. **Given** the AI system is disabled for regular users, **When** a Superadmin submits a RAG query in the sandbox, **Then** the query processes through the isolated queue and returns results with citations
2. **Given** a RAG job is submitted, **When** it is processing, **Then** the Superadmin can poll for status updates every 5 seconds and see progress
3. **Given** the sandbox queue has multiple jobs, **When** jobs are processed, **Then** Superadmin jobs have SUPERADMIN priority (higher than regular batch jobs)
---
### User Story 5 - Superadmin Uses OCR Sandbox for Metadata Extraction (Priority: P2)
As a Superadmin, I need to upload PDF files to an isolated OCR sandbox to test metadata extraction capabilities, so that I can validate AI accuracy and tune extraction parameters without impacting production document processing.
**Why this priority**: Supports AI tuning and validation workflows, enabling data-driven improvements to extraction accuracy.
**Independent Test**: Can be tested by uploading a PDF to the OCR sandbox and receiving extracted metadata in JSON format with confidence scores.
**Acceptance Scenarios**:
1. **Given** a PDF file is uploaded to the OCR sandbox, **When** processing completes, **Then** the system returns extracted metadata as formatted JSON with syntax highlighting
2. **Given** an OCR job is submitted, **When** processing fails, **Then** the error is displayed inline in a red box with actionable guidance
3. **Given** the queue length is >= 3, **When** additional sandbox requests are made, **Then** dynamic rate limiting applies (10 requests/hour per user)
---
### Edge Cases
- **EC-001**: What happens when Redis cache is unavailable? System must fall back to database query with <100ms latency penalty
- **EC-002**: How does system handle concurrent toggle requests? Last-write-wins with optimistic locking; invalid cache after successful write
- **EC-003**: What if Ollama/Qdrant times out during health check? Health service returns DEGRADED status, not DOWN; timeout is 5 seconds per service
- **EC-004**: How are long-running sandbox jobs handled? Job status polling available; jobs can be cancelled by admin; results cached for 1 hour
- **EC-005**: What happens if a Superadmin loses permissions mid-session? Next API request returns 403; UI redirects to unauthorized page
---
## Requirements
### Functional Requirements
- **FR-001**: System MUST provide a toggle switch accessible only to Superadmin (`system.manage_all`) to enable/disable AI features system-wide
- **FR-002**: System MUST persist AI enabled/disabled state to `system_settings` table with Redis caching for <1ms latency on status checks
- **FR-003**: System MUST display disabled AI buttons with explanatory tooltips to regular users when AI is turned off
- **FR-004**: System MUST show a global banner at the top of all pages when AI is disabled, visible only to users with AI permissions
- **FR-005**: System MUST return HTTP 503 Service Unavailable to regular users attempting AI API calls when AI is disabled
- **FR-006**: System MUST allow Superadmins full AI access (including sandbox) even when AI is disabled for regular users
- **FR-007**: System MUST provide health monitoring dashboard showing Ollama latency, model version, Qdrant stats, and BullMQ queue metrics
- **FR-008**: System MUST cache health check results for 30 seconds to prevent excessive infrastructure load
- **FR-009**: System MUST provide isolated RAG sandbox queue (`ai-admin-sandbox`) with SUPERADMIN job priority
- **FR-010**: System MUST provide isolated OCR sandbox for PDF metadata extraction with JSON output and syntax highlighting
- **FR-011**: System MUST implement dynamic rate limiting for sandbox based on queue length (queue < 3: no limit, queue >= 3: 10 req/hr)
- **FR-012**: System MUST poll AI status every 30 seconds from frontend for users with AI permissions
- **FR-013**: System MUST support job status polling every 5 seconds for sandbox operations
- **FR-014**: System MUST implement AiEnabledGuard with layered permission check (system.manage_all + ai.suggest/ai.rag_query bypass)
### Key Entities
- **SystemSetting**: Stores dynamic configuration values (AI_FEATURES_ENABLED, etc.) with metadata (data_type, category, validation_rules)
- **SandboxJob**: Represents a sandbox operation (RAG query or OCR extraction) with priority, status, and results
- **HealthStatus**: Aggregated health metrics from Ollama, Qdrant, and BullMQ with status indicators (HEALTHY/DEGRADED/DOWN)
---
## Success Criteria
### Measurable Outcomes
- **SC-001**: Superadmin can toggle AI system state with changes reflected to regular users within 30 seconds
- **SC-002**: AI status check API responds in under 1ms when cached, under 50ms on cache miss
- **SC-003**: 100% of regular users see disabled AI buttons with tooltips when AI is turned off (no hidden or broken UI)
- **SC-004**: Health dashboard displays all 3 services (Ollama, Qdrant, BullMQ) with <5 second data staleness
- **SC-005**: Sandbox RAG queries return complete responses with citations within 2x normal queue processing time
- **SC-006**: Sandbox OCR extraction returns valid JSON for 95% of test PDFs with clear error messages for failures
- **SC-007**: Zero unauthorized access to admin endpoints (verified by security tests)
- **SC-008**: System gracefully degrades when AI disabled with zero error reports from confused users
@@ -0,0 +1,229 @@
// File: specs/200-fullstacks/227-ai-admin-console/tasks.md
// Change Log:
// - 2026-05-20: Initial task list for AI Admin Console
// - 2026-05-21: Restructure following speckit tasks-template.md format
# Tasks: AI Admin Console
**Input**: Design documents from `/specs/200-fullstacks/227-ai-admin-console/`
**Prerequisites**: plan.md (required), spec.md (required for user stories)
**Tests**: Include unit tests for Guard, Service, and Controller layers
---
## Phase 1: Setup (Shared Infrastructure)
**Purpose**: Project initialization and basic structure setup
- [X] T001 Create feature branch `227-ai-admin-console` from main
- [X] T002 [P] Setup AI Admin Console folder structure in `frontend/app/(admin)/admin/ai/`
- [X] T003 [P] Verify shared `QUEUE_AI_BATCH` usage for admin sandbox per ADR-027 (no `QUEUE_AI_ADMIN_SANDBOX`)
---
## Phase 2: Foundational (Blocking Prerequisites) ⚠️
**Purpose**: Core infrastructure that MUST be complete before ANY user story can be implemented
**⚠️ CRITICAL**: No user story work can begin until this phase is complete
- [X] T004 Create `system_settings` table SQL in `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` (per ADR-009)
- [X] T005 [P] Create `SystemSetting` entity in `backend/src/modules/ai/entities/system-setting.entity.ts`
- [X] T006 [P] Register `SystemSetting` entity in `backend/src/modules/ai/ai.module.ts` TypeORM forFeature
- [X] T007 [P] Create `AiEnabledGuard` in `backend/src/modules/ai/guards/ai-enabled.guard.ts`
- [X] T008 Implement `getAiFeaturesEnabled()` and `setAiFeaturesEnabled()` methods in `backend/src/modules/ai/ai-settings.service.ts` with Redis caching
- [X] T009 Keep existing `ai-batch` BullMQ registration for admin sandbox per ADR-027
- [X] T010 Defer sandbox job handling to `AiBatchProcessor` per ADR-027 (no separate `AiSandboxProcessor`)
**Checkpoint**: Foundation ready - user story implementation can now begin in parallel
---
## Phase 3: User Story 1 - Superadmin Toggles AI System On/Off (Priority: P1) 🎯 MVP
**Goal**: Enable Superadmin to dynamically control AI system availability with database persistence and Redis caching
**Independent Test**: Superadmin can toggle AI switch and verify state persists across page refreshes and API calls
### Tests for User Story 1
- [X] T011 [P] [US1] Unit test for `AiSettingsService.getAiFeaturesEnabled()` cache behavior in `backend/src/modules/ai/ai-settings.service.spec.ts`
- [X] T012 [P] [US1] Unit test for `AiEnabledGuard` blocking/allowing logic in `backend/src/modules/ai/guards/ai-enabled.guard.spec.ts`
### Implementation for User Story 1
- [X] T013 [US1] Implement `POST /ai/admin/toggle` endpoint in `backend/src/modules/ai/ai.controller.ts` with `@RequirePermission('system.manage_all')`
- [X] T014 [US1] Implement `GET /ai/admin/settings` endpoint to return current AI enabled state
- [X] T015 [US1] Add cache invalidation logic in `AiSettingsService.setAiFeaturesEnabled()` after DB update (TypeORM transaction)
- [X] T016 [US1] Apply `AiEnabledGuard` to existing AI endpoints in `AiController` (suggest, rag_query)
- [X] T017 [US1] Create `admin-ai.service.ts` in `frontend/lib/services/admin-ai.service.ts` with toggle API methods
- [X] T018 [US1] Build AI toggle switch component in `frontend/app/(admin)/admin/ai/page.tsx` (Header Switch section)
- [X] T019 [US1] Create `useAiStatus()` hook in `frontend/hooks/use-ai-status.ts` for polling AI state
**Checkpoint**: At this point, User Story 1 should be fully functional and testable independently
---
## Phase 4: User Story 2 - Normal Users Experience Soft Fallback (Priority: P1)
**Goal**: Implement soft fallback UX with disabled buttons, tooltips, and global banner when AI is turned off
**Independent Test**: Disable AI as admin, then verify regular user sees disabled buttons with tooltips and global banner
### Tests for User Story 2
- [x] T020 [P] [US2] Unit test for soft fallback component rendering in `frontend/components/ai/__tests__/ai-suggestion-button.test.tsx`
### Implementation for User Story 2
- [x] T021 [US2] Create `AiSuggestionButton` component in `frontend/components/ai/ai-suggestion-button.tsx` with disabled state and tooltip
- [x] T022 [US2] Create `AiStatusBanner` component in `frontend/components/ai/AiStatusBanner.tsx` for global banner display
- [x] T023 [US2] Integrate AI status polling (30s interval) in `frontend/providers/session-provider.tsx` or layout
- [x] T024 [US2] Update document forms (RFA/Correspondence) to use `AiSuggestionButton` with AI status check
- [x] T025 [US2] Implement HTTP 503 error handling in `frontend/lib/api/client.ts` for AI endpoint failures
**Checkpoint**: User Stories 1 AND 2 should both work independently (toggle affects user experience)
---
## Phase 5: User Story 3 - Superadmin Monitors AI Health Status (Priority: P2)
**Goal**: Provide real-time health monitoring dashboard for Ollama, Qdrant, and BullMQ
**Independent Test**: Access AI Admin Console and verify all health metrics display correctly
### Tests for User Story 3
- [X] T026 [P] [US3] Unit test for `AiService.getSystemHealth()` in `backend/src/modules/ai/ai.service.spec.ts`
### Implementation for User Story 3
- [X] T027 [US3] Implement `getSystemHealth()` method in `backend/src/modules/ai/ai.service.ts` with 5s timeout per service
- [X] T028 [US3] Implement `GET /ai/admin/health` endpoint in `backend/src/modules/ai/ai.controller.ts`
- [X] T029 [US3] Add health check caching (30s) using Redis or in-memory cache with TTL
- [X] T030 [US3] Create Health Indicator cards component in `frontend/app/(admin)/admin/ai/page.tsx`
- [X] T031 [US3] Implement health status polling (30s) in admin console page
**Checkpoint**: All health monitoring features functional and independently testable
---
## Phase 6: User Story 4 - Superadmin Uses RAG Playground Sandbox (Priority: P2)
**Goal**: Enable isolated RAG testing environment with sandbox queue and job polling
**Independent Test**: Submit RAG query in sandbox and receive response with citations
### Tests for User Story 4
- [X] T032 [P] [US4] Unit test for RAG sandbox job processing in `backend/src/modules/ai/processors/ai-sandbox.processor.spec.ts` (Unified in ai-batch.processor.spec.ts)
### Implementation for User Story 4
- [X] T033 [US4] Implement `sandbox-rag` job handler in `backend/src/modules/ai/processors/ai-sandbox.processor.ts` (Unified in ai-batch.processor.ts)
- [X] T034 [US4] Add `enqueueSandboxJob()` method in `backend/src/modules/ai/ai-queue.service.ts` with SUPERADMIN priority
- [X] T035 [US4] Implement `POST /ai/admin/sandbox/rag` endpoint in `backend/src/modules/ai/ai.controller.ts`
- [X] T036 [US4] Implement `GET /ai/admin/sandbox/job/:id` endpoint for job status polling
- [X] T037 [US4] Create RAG Playground tab UI in `frontend/app/(admin)/admin/ai/page.tsx`
- [X] T038 [US4] Implement job status polling (5s) with progress display in RAG Playground
**Checkpoint**: RAG sandbox fully functional with isolated queue processing
---
## Phase 7: User Story 5 - Superadmin Uses OCR Sandbox (Priority: P2)
**Goal**: Provide isolated OCR sandbox for PDF metadata extraction with JSON output
**Independent Test**: Upload PDF to OCR sandbox and receive valid JSON extraction results
### Tests for User Story 5
- [X] T039 [P] [US5] Unit test for OCR sandbox job processing in `backend/src/modules/ai/processors/ai-sandbox.processor.spec.ts`
### Implementation for User Story 5
- [X] T040 [US5] Implement `sandbox-extract` job handler in `backend/src/modules/ai/processors/ai-sandbox.processor.ts`
- [X] T041 [US5] Implement `POST /ai/admin/sandbox/extract` endpoint in `backend/src/modules/ai/ai.controller.ts`
- [X] T042 [US5] Implement dynamic rate limiting logic (queue < 3: no limit, >= 3: 10 req/hr) in controller
- [X] T043 [US5] Create OCR Sandbox tab with drag-drop file upload in `frontend/app/(admin)/admin/ai/page.tsx`
- [X] T044 [US5] Implement JSON output display with syntax highlighting in OCR Sandbox tab
- [X] T045 [US5] Add inline error display (red box) for failed OCR extractions
**Checkpoint**: OCR sandbox fully functional with rate limiting and error handling
---
## Phase 8: Polish & Cross-Cutting Concerns
**Purpose**: Improvements that affect multiple user stories
- [X] T046 [P] Add "AI Console" menu item in `frontend/components/admin/sidebar.tsx` (Superadmin only)
- [X] T047 [P] Update agent context via `update-agent-context.sh` with new AI Admin Console patterns
- [X] T048 Security hardening: Verify all admin endpoints require `system.manage_all` permission
- [X] T049 Run `quickstart.md` validation and walkthrough tests
- [X] T050 Create `walkthrough.md` documenting end-to-end testing procedures
---
## Dependencies & Execution Order
### Phase Dependencies
- **Setup (Phase 1)**: No dependencies - can start immediately
- **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS all user stories
- **User Stories (Phase 3-7)**: All depend on Foundational phase completion
- User stories can proceed in parallel (if staffed)
- Or sequentially in priority order (P1 → P2)
- **Polish (Phase 8)**: Depends on all desired user stories being complete
### User Story Dependencies
- **User Story 1 (P1)**: Can start after Foundational (Phase 2) - No dependencies
- **User Story 2 (P1)**: Can start after Foundational (Phase 2) - Integrates with US1 toggle state
- **User Story 3 (P2)**: Can start after Foundational (Phase 2) - Independent monitoring feature
- **User Story 4 (P2)**: Can start after Foundational (Phase 2) - Uses same sandbox queue infrastructure
- **User Story 5 (P2)**: Can start after Foundational (Phase 2) - Shares OCR extraction with US4 patterns
### Within Each User Story
- Tests (if included) MUST be written and FAIL before implementation
- Services before controllers
- Controllers before frontend integration
- Core implementation before polish
### Parallel Opportunities
- All Setup tasks marked [P] can run in parallel
- All Foundational tasks marked [P] can run in parallel (within Phase 2)
- Once Foundational is done, US3/4/5 can start in parallel (independent P2 stories)
- US1 and US2 should be developed sequentially (toggle affects fallback)
- Different story tests can run in parallel
---
## Implementation Strategy
### MVP First (User Stories 1 & 2)
1. Complete Phase 1: Setup
2. Complete Phase 2: Foundational (CRITICAL - blocks all stories)
3. Complete Phase 3: User Story 1 (toggle mechanism)
4. Complete Phase 4: User Story 2 (soft fallback)
5. **STOP and VALIDATE**: Test toggle → fallback flow end-to-end
6. Deploy/demo MVP
### Incremental Delivery
1. MVP (US1 + US2) → Deploy
2. Add US3 (Health Monitoring) → Deploy
3. Add US4 (RAG Sandbox) → Deploy
4. Add US5 (OCR Sandbox) → Deploy
### Parallel Team Strategy
With multiple developers post-Foundational:
- Developer A: US1 + US2 (core control + fallback)
- Developer B: US3 (health monitoring)
- Developer C: US4 + US5 (sandbox features)
@@ -0,0 +1,69 @@
// File: specs/200-fullstacks/227-ai-admin-console/walkthrough.md
// Change Log
// - 2026-05-21: สร้าง Walkthrough สำหรับการประเมินและตรวจสอบสิทธิ์ระบบ AI Admin Console (Phase 7 & Phase 8)
# Walkthrough: AI Admin Console (Phase 7 & 8 Completed)
เอกสารนี้สรุปผลการพัฒนาและตรวจสอบระบบ **AI Admin Console** สำหรับสิทธิ์ผู้ดูแลระบบระดับสูง (Superadmin) ในเฟสที่ 7 และ 8
---
## 🎯 รายละเอียดของฟีเจอร์และสิ่งที่ระบบทำสำเร็จ
การพัฒนานี้ต่อยอดความสามารถของระบบจัดการ AI (ADR-023/ADR-023A/ADR-027) เพื่อให้ Superadmin สามารถ:
1. **ทดสอบการทำ OCR และการสกัด Metadata ในสภาพแวดล้อมจำลอง (OCR Sandbox Playground)** ผ่านการอัปโหลดไฟล์ PDF
2. **ประมวลผล OCR และการสกัดแบบไม่ระบุตัวตน (Anonymous / Sandboxed)** โดยระบบจะไม่เขียนข้อมูลลงฐานข้อมูลจริง
3. **กำหนดและคุมสิทธิ์อัตราการใช้งานด้วย Dynamic Rate Limiting** ป้องกันปัญหาคิวประมวลผลหนาแน่น (Queue bottleneck) เมื่อคิวในระบบมีงาน >= 3 งาน
4. **ความมั่นใจด้านความปลอดภัยสูงสุด (Security Hardening)** ป้องกันไม่ให้ผู้ใช้ภายนอกเข้าถึง Endpoint ของ AI Admin Console ผ่าน CASL Permission `system.manage_all`
---
## 🛠️ รายการไฟล์ที่เปลี่ยนแปลง (Proposed & Implemented Changes)
### Backend (NestJS)
- **[MODIFY] [ai-batch.processor.ts](file:///e:/np-dms/lcbp3/backend/src/modules/ai/processors/ai-batch.processor.ts)**
- เพิ่มการประมวลผลงาน `sandbox-extract` ด้วย `ocrService` และเรียกใช้งาน `ollamaService` เพื่อจำลองการสกัด Metadata
- บันทึกผลลัพธ์ลง Redis cache (`ai:rag:result:${idempotencyKey}`) ด้วยเวลาหมดอายุ 1 ชั่วโมง (EC-004)
- ไม่บันทึกข้อมูลลงฐานข้อมูลของระบบ (Database Bypass)
- **[MODIFY] [ai.controller.ts](file:///e:/np-dms/lcbp3/backend/src/modules/ai/ai.controller.ts)**
- เพิ่ม Endpoint `POST /ai/admin/sandbox/extract` รองรับการอัปโหลดไฟล์ PDF ขนาดไม่เกิน 50MB
- ดำเนินการเช็คขนาดคิว BullMQ `QUEUE_AI_BATCH` ในการเปิดใช้งาน Dynamic Rate Limiting: ถ้าขนาดคิว >= 3 จะจำกัดไม่เกิน 10 requests/hour ต่อผู้ใช้
- **[MODIFY] [ai-batch.processor.spec.ts](file:///e:/np-dms/lcbp3/backend/src/modules/ai/processors/ai-batch.processor.spec.ts)**
- เพิ่ม Unit Test ครอบคลุมการทำงานจำลอง OCR Sandbox และการบันทึกผลลง Redis
### Frontend (Next.js)
- **[MODIFY] [admin-ai.service.ts](file:///e:/np-dms/lcbp3/frontend/lib/services/admin-ai.service.ts)**
- เพิ่มฟังก์ชัน `uploadOcrSandbox` สำหรับการอัปโหลด PDF ไปประมวลผลและ `getSandboxJobStatus` สำหรับการดึงข้อมูลสถานะงาน
- **[MODIFY] [page.tsx](file:///e:/np-dms/lcbp3/frontend/app/(admin)/admin/ai/page.tsx)**
- ปรับแก้โครงสร้างของแท็บ OCR Playground ให้รองรับการ Drag-and-Drop ไฟล์ และแสดงความก้าวหน้าการสกัดผลลัพธ์ผ่าน Progress bar
- ทำการพ่นโค้ด JSON syntax highlight ให้สวยงาม และแสดงกล่องข้อความสีแดงเตือนกรณีเกิดข้อผิดพลาดในการประมวลผล
---
## 🧪 แผนและการทดสอบ (What was Tested & Validation Results)
### 1. การตรวจสอบ Unit Tests บน Backend
เราได้รันการทดสอบ Unit Tests ของระบบ AI Batch Processor โดยจำลองสถานการณ์ทั้งหมด:
```bash
pnpm test src/modules/ai/processors/ai-batch.processor.spec.ts
```
**ผลการทดสอบ**:
- `ควรสามารถเรียก process embed-document และอัปเดตสถานะใน database` -> ผ่าน (PASS)
- `ควรประมวลผล sandbox-rag โดยการเรียก ragService.processQuery และข้ามการอัปเดต database` -> ผ่าน (PASS)
- `ควรประมวลผล sandbox-extract โดยใช้ OcrService, OllamaService และเก็บค่าลง Redis` -> ผ่าน (PASS)
### 2. การตรวจสอบการคอมไพล์ Frontend (TypeScript Verification)
ดำเนินการประเมิน Type safety ของโค้ด Next.js:
```bash
npx tsc --noEmit
```
**ผลการทดสอบ**:
- คอมไพล์ได้ผ่าน ปราศจากข้อผิดพลาดด้าน TypeScript (Zero compiler errors!)
### 3. การตรวจสอบความปลอดภัย (Security Audit)
- การทดสอบเรียก API ของผู้ดูแลระบบด้วย JWT ของผู้ใช้ธรรมดาที่ไม่ได้รับบทบาท Superadmin จะถูกบล็อกด้วยข้อผิดพลาด HTTP 403 Forbidden เสมอ
---
> [!NOTE]
> ฟังก์ชัน OCR Sandbox Playground นี้จำกัดการเข้าถึงเฉพาะ Superadmin เท่านั้น (จำเป็นต้องมีสิทธิ์ `system.manage_all` ในระบบ)