251120:0800 add .

This commit is contained in:
admin
2025-11-20 08:14:27 +07:00
parent 4c961aa4ee
commit 859475b9f0
3 changed files with 1816 additions and 1656 deletions

View File

@@ -24,7 +24,7 @@ DROP VIEW IF EXISTS v_user_tasks;
DROP VIEW IF EXISTS v_contract_parties_all;
DROP VIEW IF EXISTS v_current_rfas;
DROP VIEW IF EXISTS v_current_correspondences;
DROP PROCEDURE IF EXISTS sp_get_next_document_number;
-- DROP PROCEDURE IF EXISTS sp_get_next_document_number;
-- 🗑️ DROP TABLE SCRIPT: LCBP3-DMS v1.4.2
-- คำเตือน: ข้อมูลทั้งหมดจะหายไป กรุณา Backup ก่อนรันบน Production
SET FOREIGN_KEY_CHECKS = 0;
@@ -36,7 +36,8 @@ DROP TABLE IF EXISTS search_indices;
DROP TABLE IF EXISTS notifications;
DROP TABLE IF EXISTS audit_logs;
-- [NEW v1.4.2] ตารางการตั้งค่าส่วนตัวของผู้ใช้ (FK -> users)
DROP TABLE IF EXISTS user_preferences -- [NEW v1.4.2] ตารางเก็บ Schema สำหรับ Validate JSON (Stand-alone)
DROP TABLE IF EXISTS user_preferences;
-- [NEW v1.4.2] ตารางเก็บ Schema สำหรับ Validate JSON (Stand-alone)
DROP TABLE IF EXISTS json_schemas;
-- ============================================================
-- ส่วนที่ 2: ตาราง Junction (เชื่อมโยงข้อมูล M:N)
@@ -333,6 +334,7 @@ CREATE TABLE users (
last_login_at TIMESTAMP NULL COMMENT 'วันที่และเวลาที่ล็อกอินล่าสุด',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด',
deleted_at DATETIME NULL DEFAULT NULL COMMENT 'วันที่ลบ',
FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE
SET NULL
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลผู้ใช้งาน (User)';
@@ -340,7 +342,7 @@ CREATE TABLE users (
INSERT INTO users (username, password_hash, email, is_active)
VALUES (
'superadmin',
'$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq',
'$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h',
'superadmin @example.com',
1
) ON DUPLICATE KEY
@@ -352,7 +354,7 @@ VALUES(is_active);
INSERT IGNORE INTO users (username, password_hash, email, is_active)
VALUES (
'editor01',
'$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq',
'$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h',
'editor01 @example.com',
1
);
@@ -360,7 +362,7 @@ VALUES (
INSERT IGNORE INTO users (username, password_hash, email, is_active)
VALUES (
'viewer01',
'$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq',
'$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h',
'viewer01 @example.com',
1
);
@@ -1303,7 +1305,9 @@ CREATE TABLE correspondence_routings (
'FOR_REVIEW',
'FOR_INFORMATION',
'FOR_ACTION '
) NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้ เช่น เพื่ออนุมัติ, เพื่อตรวจสอบ, หรือเพื่อรับทราบ',
) NOT NULL DEFAULT 'FOR_REVIEW ' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้ เช่น เพื่ออนุมัติ,
เพื่อตรวจสอบ,
หรือเพื่อรับทราบ',
status ENUM(
'SENT',
'RECEIVED',
@@ -1373,7 +1377,11 @@ VALUES ('DFT', 'Draft', 'ฉบับร่าง', 1),
-- ตาราง Master สำหรับรหัสผลการอนุมัติ RFA
CREATE TABLE rfa_approve_codes (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
approve_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสผลการอนุมัติ (เช่น 1A - Approved, 3R - Revise and Resubmit)',
approve_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสผลการอนุมัติ (
เช่น 1A - Approved,
3R - Revise
and Resubmit
)',
approve_name VARCHAR(100) NOT NULL COMMENT 'ชื่อผลการอนุมัติ',
description TEXT COMMENT 'คำอธิบาย',
sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล',
@@ -1390,7 +1398,8 @@ VALUES ('1A', 'Approved by Authority', 10, 1),
('1N', 'Approved As Note', 12, 1),
('1R', 'Approved with Remarks', 13, 1),
('3C', 'Consultant Comments', 31, 1),
('3R', 'Revise and Resubmit', 32, 1),
('3R', 'Revise
and Resubmit', 32, 1),
('4X', 'Reject', 40, 1),
('5N', 'No Further Action', 50, 1);
-- ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions)
@@ -1748,9 +1757,12 @@ CREATE TABLE attachments (
mime_type VARCHAR(100) NOT NULL COMMENT 'ประเภทไฟล์ (เช่น application / pdf)',
file_size INT NOT NULL COMMENT 'ขนาดไฟล์ (bytes)',
is_temporary BOOLEAN DEFAULT TRUE COMMENT 'True = ยังไม่ Commit ลง DB จริง',
temp_id VARCHAR(100) NULL COMMENT 'ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1' uploaded_by_user_id INT NOT NULL COMMENT 'ผู้อัปโหลดไฟล์',
temp_id VARCHAR(100) NULL COMMENT 'ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1',
uploaded_by_user_id INT NOT NULL COMMENT 'ผู้อัปโหลดไฟล์',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่อัปโหลด',
expires_at DATETIME NULL COMMENT 'เวลาหมดอายุของไฟล์ Temp' checksum VARCHAR(64) NULL COMMENT 'SHA-256 Checksum' FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE
expires_at DATETIME NULL COMMENT 'เวลาหมดอายุของไฟล์ Temp',
checksum VARCHAR(64) NULL COMMENT 'SHA -256 Checksum',
FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ';
-- ตารางเชื่อม correspondences กับ attachments (M:N)
CREATE TABLE correspondence_attachments (
@@ -1835,7 +1847,8 @@ CREATE TABLE document_number_counters (
-- เหตุผล: เพื่อ Validate โครงสร้าง JSON Details ของเอกสารแต่ละประเภทแบบ Centralized
CREATE TABLE IF NOT EXISTS json_schemas (
id INT AUTO_INCREMENT PRIMARY KEY,
schema_code VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัส Schema เช่น RFA_DWG_V1, CORR_GENERIC',
schema_code VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัส Schema เช่น RFA_DWG_V1,
CORR_GENERIC',
version INT NOT NULL DEFAULT 1 COMMENT 'เวอร์ชันของ Schema',
schema_definition JSON NOT NULL COMMENT 'โครงสร้าง JSON Schema (Standard Format)',
is_active BOOLEAN DEFAULT TRUE,
@@ -1863,7 +1876,11 @@ CREATE TABLE audit_logs (
audit_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Log',
request_id VARCHAR(100) NULL COMMENT 'Trace ID linking to app logs',
user_id INT COMMENT 'ผู้กระทำ',
action VARCHAR(100) NOT NULL COMMENT 'การกระทำ (เช่น rfa.create, correspondence.update, login.success)',
action VARCHAR(100) NOT NULL COMMENT 'การกระทำ (
เช่น rfa.create,
correspondence.update,
login.success
)',
severity ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL ') DEFAULT 'INFO',
entity_type VARCHAR(50) COMMENT 'ตาราง / โมดูล (เช่น ''rfa '', ''correspondence '')',
entity_id VARCHAR(50) COMMENT 'Primary ID ของระเบียนที่ได้รับผลกระทำ',
@@ -1882,7 +1899,8 @@ CREATE TABLE notifications (
message TEXT NOT NULL COMMENT 'รายละเอียดการแจ้งเตือน',
notification_type ENUM('EMAIL', 'LINE', 'SYSTEM ') NOT NULL COMMENT 'ประเภท (EMAIL, LINE, SYSTEM)',
is_read BOOLEAN DEFAULT FALSE COMMENT 'สถานะการอ่าน',
entity_type VARCHAR(50) COMMENT 'เช่น ''rfa'', ''circulation''',
entity_type VARCHAR(50) COMMENT 'เช่น ''rfa '',
''circulation ''',
entity_id INT COMMENT 'ID ของเอนทิตีที่เกี่ยวข้อง',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง',
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE

View File

@@ -146,6 +146,7 @@
| last_login_at | TIMESTAMP | NULL | Last successful login timestamp |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
| deleted_at | DATETIME | NULL | Deleted at |
**Indexes**:

View File

@@ -117,7 +117,7 @@ PORT=3000
NODE_ENV=development
# Security
JWT_SECRET=your_super_secret_key_change_it
JWT_SECRET=eebc122aa65adde8c76c6a0847d9649b2b67a06db1504693e6c912e51499b76e
```
### ขั้นตอนต่อไป (Next Steps)
@@ -374,3 +374,144 @@ JWT_EXPIRATION=8h
2. **Environment:** ควรแยก Secret ระหว่างเครื่อง `Development` และ `Production` ไม่ควรใช้ Key เดียวกัน
คุณต้องการให้ผมช่วยตั้งค่า `ConfigModule` ใน NestJS เพื่ออ่านค่า `JWT_SECRET` นี้อย่างปลอดภัยพร้อม Validate ว่าห้ามเป็นค่าว่างไหมครับ?
## ได้เลยครับ เรามาลุยงาน **T0.1 Secure Configuration Setup** ตามแผน Backend Plan กันต่อครับ
เป้าหมายของ Task นี้คือการทำให้ระบบอ่านค่า Environment Variables ได้อย่างถูกต้อง และ **"ต้องไม่ Start"** ถ้าค่า Config สำคัญๆ (เช่น Database Password, JWT Secret) หายไป หรือไม่ถูกต้อง เพื่อความปลอดภัยครับ
### 1. ติดตั้ง Library ที่จำเป็น
เราจะใช้ `@nestjs/config` สำหรับจัดการ Config และ `joi` สำหรับตรวจสอบความถูกต้อง (Validation) ครับ
รันคำสั่งนี้ใน Terminal:
```bash
pnpm add @nestjs/config joi
```
### 2. สร้าง Validation Schema
เราจะแยก Logic การตรวจสอบออกมาเป็นไฟล์เฉพาะ เพื่อให้โค้ดเป็นระเบียบครับ
สร้างไฟล์ใหม่: `src/common/config/env.validation.ts`
```typescript
// File: src/common/config/env.validation.ts
import Joi from 'joi';
// สร้าง Schema สำหรับตรวจสอบค่า Environment Variables
export const envValidationSchema = Joi.object({
// 1. Application Environment
NODE_ENV: Joi.string()
.valid('development', 'production', 'test', 'provision')
.default('development'),
PORT: Joi.number().default(3000),
// 2. Database Configuration (MariaDB)
// ห้ามเป็นค่าว่าง (required)
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number().default(3306),
DB_USERNAME: Joi.string().required(),
DB_PASSWORD: Joi.string().required(),
DB_DATABASE: Joi.string().required(),
// 3. Security (JWT)
// ต้องมีค่า และควรยาวพอ (ตรวจสอบความยาวได้ถ้าระบุ min)
JWT_SECRET: Joi.string().required().min(32).message('JWT_SECRET must be at least 32 characters long for security.'),
JWT_EXPIRATION: Joi.string().default('8h'),
});
```
### 3. อัปเดต `AppModule` ให้ใช้ Validation
กลับไปแก้ไขไฟล์ `src/app.module.ts` เพื่อนำ Schema ที่เราสร้างมาใช้งานครับ
```typescript
// File: src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { envValidationSchema } from './common/config/env.validation.js'; // สังเกต .js สำหรับ ESM
@Module({
imports: [
// 1. Setup Config Module พร้อม Validation
ConfigModule.forRoot({
isGlobal: true, // เรียกใช้ได้ทั่วทั้ง App ไม่ต้อง import ซ้ำ
envFilePath: '.env', // อ่านไฟล์ .env (สำหรับ Dev)
validationSchema: envValidationSchema, // ใช้ Schema ที่เราสร้างเพื่อตรวจสอบ
validationOptions: {
// ถ้ามีค่าไหนไม่ผ่าน Validation ให้ Error และหยุดทำงานทันที
abortEarly: true,
},
}),
// 2. Setup TypeORM (MariaDB)
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
type: 'mariadb',
host: configService.get<string>('DB_HOST'),
port: configService.get<number>('DB_PORT'),
username: configService.get<string>('DB_USERNAME'),
password: configService.get<string>('DB_PASSWORD'),
database: configService.get<string>('DB_DATABASE'),
autoLoadEntities: true,
// synchronize: true เฉพาะตอน Dev เท่านั้น ห้ามใช้บน Prod
synchronize: configService.get<string>('NODE_ENV') === 'development',
}),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
```
### 4\. สร้างไฟล์ Template สำหรับ Docker Override (ตามแผน T0.1)
ตามแผนงาน เราต้องเตรียมไฟล์ `docker-compose.override.yml.example` เพื่อให้นักพัฒนาคนอื่นรู้ว่าต้อง Inject Secrets อย่างไร โดยไม่เปิดเผยความลับใน `docker-compose.yml` หลัก
สร้างไฟล์: `docker-compose.override.yml.example` ที่ root project:
```yaml
version: '3.8'
services:
# Override ค่า Config ของ Service Backend (เมื่อเราสร้าง Container Backend ในอนาคต)
backend:
environment:
# ใส่ Secrets จริงในไฟล์ override นี้ (และห้าม commit ไฟล์นี้)
- DB_PASSWORD=secret_password_here
- JWT_SECRET=very_long_and_secure_secret_here
- NODE_ENV=development
# Override สำหรับ Database (Local Dev)
mariadb:
environment:
- MYSQL_ROOT_PASSWORD=secret_password_here
- MYSQL_PASSWORD=secret_password_here
```
### 5\. ทดสอบความปลอดภัย (Validation Check)
ลองทดสอบว่าระบบ Validation ทำงานถูกต้องหรือไม่ โดยการ **ลบ** หรือ **เปลี่ยนชื่อ** ตัวแปรสำคัญในไฟล์ `.env` (เช่น ลบ `JWT_SECRET`) แล้วลองรัน:
```bash
pnpm start:dev
```
**ผลลัพธ์ที่ควรได้:**
App **ต้อง Crash** และแสดง Error Message ชัดเจน เช่น:
`Config validation error: "JWT_SECRET" is required`
ถ้าขึ้นแบบนี้แสดงว่าระบบ **Secure Configuration** ของเราทำงานถูกต้องตามแผน T0.1 แล้วครับ\! 🎉
-----
**ขั้นตอนถัดไป:**
เราจะไปต่อที่ **T0.2 Redis & Queue Infrastructure** เพื่อเตรียมระบบ Caching และ Background Job ไหมครับ?