260322:1648 Correct Coresspondence / Doing RFA / Correct CI
CI Pipeline / build (push) Failing after 12m41s
Build and Deploy / deploy (push) Failing after 2m44s

This commit is contained in:
admin
2026-03-22 16:48:12 +07:00
parent e5deedb42e
commit 11984bfa29
683 changed files with 105251 additions and 29068 deletions
@@ -196,7 +196,7 @@ pnpm add @nestjs/typeorm typeorm mysql2 @nestjs/config
สร้างไฟล์ `docker-compose.yml` ที่ root ของโปรเจกต์ (ถ้ายังไม่มี):
```yaml
version: "3.8"
version: '3.8'
services:
mariadb:
@@ -209,7 +209,7 @@ services:
MYSQL_USER: admin
MYSQL_PASSWORD: password123
ports:
- "3306:3306"
- '3306:3306'
volumes:
- db_data:/var/lib/mysql
networks:
@@ -222,7 +222,7 @@ services:
environment:
PMA_HOST: mariadb
ports:
- "8080:80"
- '8080:80'
depends_on:
- mariadb
networks:
@@ -248,36 +248,36 @@ docker-compose up -d
```typescript
// 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 { 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';
@Module({
imports: [
// 1. Load Config Module
ConfigModule.forRoot({
isGlobal: true, // ให้เรียกใช้ได้ทุกที่โดยไม่ต้อง import ใหม่
envFilePath: ".env", // อ่านค่าจากไฟล์ .env
envFilePath: '.env', // อ่านค่าจากไฟล์ .env
}),
// 2. Setup TypeORM Connection (Async เพื่อรออ่าน Config ก่อน)
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
type: "mariadb", // หรือ 'mysql' ก็ได้เพราะใช้ driver เดียวกัน
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"),
type: 'mariadb', // หรือ 'mysql' ก็ได้เพราะใช้ driver เดียวกัน
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'),
// Auto Load Entities: โหลด Entity ทั้งหมดที่อยู่ในโปรเจกต์อัตโนมัติ
autoLoadEntities: true,
// Synchronize: true เฉพาะ Dev environment (ห้ามใช้ใน Prod)
synchronize: configService.get<string>("NODE_ENV") === "development",
synchronize: configService.get<string>('NODE_ENV') === 'development',
// Logging: เปิดดู Query SQL ตอน Dev
logging: configService.get<string>("NODE_ENV") === "development",
logging: configService.get<string>('NODE_ENV') === 'development',
}),
inject: [ConfigService],
}),
@@ -397,14 +397,12 @@ pnpm add @nestjs/config joi
```typescript
// File: src/common/config/env.validation.ts
import Joi from "joi";
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"),
NODE_ENV: Joi.string().valid('development', 'production', 'test', 'provision').default('development'),
PORT: Joi.number().default(3000),
// 2. Database Configuration (MariaDB)
@@ -417,11 +415,8 @@ export const envValidationSchema = Joi.object({
// 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"),
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'),
});
```
@@ -431,19 +426,19 @@ export const envValidationSchema = Joi.object({
```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
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)
envFilePath: '.env', // อ่านไฟล์ .env (สำหรับ Dev)
validationSchema: envValidationSchema, // ใช้ Schema ที่เราสร้างเพื่อตรวจสอบ
validationOptions: {
// ถ้ามีค่าไหนไม่ผ่าน Validation ให้ Error และหยุดทำงานทันที
@@ -456,15 +451,15 @@ import { envValidationSchema } from "./common/config/env.validation.js"; // ส
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"),
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",
synchronize: configService.get<string>('NODE_ENV') === 'development',
}),
}),
],
@@ -481,7 +476,7 @@ export class AppModule {}
สร้างไฟล์: `docker-compose.override.yml.example` ที่ root project:
```yaml
version: "3.8"
version: '3.8'
services:
# Override ค่า Config ของ Service Backend (เมื่อเราสร้าง Container Backend ในอนาคต)
@@ -533,7 +528,7 @@ App **ต้อง Crash** และแสดง Error Message ชัดเจ
**ไฟล์: `docker-compose.yml`**
```yaml
version: "3.8"
version: '3.8'
services:
# ... (mariadb & pma เดิม) ...
@@ -546,7 +541,7 @@ services:
# ใช้ Command นี้เพื่อตั้ง Password
command: redis-server --requirepass "redis_password_secure"
ports:
- "6379:6379"
- '6379:6379'
volumes:
- redis_data:/data
networks:
@@ -614,20 +609,20 @@ pnpm add @nestjs/bullmq bullmq
**ไฟล์: `src/app.module.ts`**
```typescript
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { TypeOrmModule } from "@nestjs/typeorm";
import { BullModule } from "@nestjs/bullmq"; // Import BullModule
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { envValidationSchema } from "./common/config/env.validation.js";
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BullModule } from '@nestjs/bullmq'; // Import BullModule
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { envValidationSchema } from './common/config/env.validation.js';
@Module({
imports: [
// 1. Config (เดิม)
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ".env",
envFilePath: '.env',
validationSchema: envValidationSchema,
validationOptions: { abortEarly: true },
}),
@@ -637,14 +632,14 @@ import { envValidationSchema } from "./common/config/env.validation.js";
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"),
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: configService.get<string>("NODE_ENV") === "development",
synchronize: configService.get<string>('NODE_ENV') === 'development',
}),
}),
@@ -654,9 +649,9 @@ import { envValidationSchema } from "./common/config/env.validation.js";
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
connection: {
host: configService.get<string>("REDIS_HOST"),
port: configService.get<number>("REDIS_PORT"),
password: configService.get<string>("REDIS_PASSWORD"),
host: configService.get<string>('REDIS_HOST'),
port: configService.get<number>('REDIS_PORT'),
password: configService.get<string>('REDIS_PASSWORD'),
},
}),
}),
@@ -686,25 +681,25 @@ pnpm start:dev
ให้เพิ่มบรรทัดนี้ที่ส่วนบนสุดของไฟล์ `src/app.module.ts` ครับ:
```typescript
import { BullModule } from "@nestjs/bullmq";
import { BullModule } from '@nestjs/bullmq';
```
หรือลองตรวจสอบไฟล์ `src/app.module.ts` ให้ครบถ้วนตามนี้ครับ:
```typescript
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { TypeOrmModule } from "@nestjs/typeorm";
import { BullModule } from "@nestjs/bullmq"; // <--- ต้องมีบรรทัดนี้ครับ
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { envValidationSchema } from "./common/config/env.validation.js";
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BullModule } from '@nestjs/bullmq'; // <--- ต้องมีบรรทัดนี้ครับ
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { envValidationSchema } from './common/config/env.validation.js';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ".env",
envFilePath: '.env',
validationSchema: envValidationSchema,
validationOptions: { abortEarly: true },
}),
@@ -712,14 +707,14 @@ import { envValidationSchema } from "./common/config/env.validation.js";
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"),
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: configService.get<string>("NODE_ENV") === "development",
synchronize: configService.get<string>('NODE_ENV') === 'development',
}),
}),
// Queue Configuration
@@ -728,9 +723,9 @@ import { envValidationSchema } from "./common/config/env.validation.js";
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
connection: {
host: configService.get<string>("REDIS_HOST"),
port: configService.get<number>("REDIS_PORT"),
password: configService.get<string>("REDIS_PASSWORD"),
host: configService.get<string>('REDIS_HOST'),
port: configService.get<number>('REDIS_PORT'),
password: configService.get<string>('REDIS_PASSWORD'),
},
}),
}),