251120:0800 add .
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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**:
|
||||
|
||||
|
||||
@@ -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 ไหมครับ?
|
||||
|
||||
|
||||
Reference in New Issue
Block a user