251119:1200 set backend

This commit is contained in:
2025-11-19 11:57:46 +07:00
parent a3e25d6c52
commit 2b36e5554b
29 changed files with 7719 additions and 204 deletions

56
backend/.gitignore vendored Normal file
View File

@@ -0,0 +1,56 @@
# compiled output
/dist
/node_modules
/build
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# temp directory
.temp
.tmp
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

View File

@@ -1,48 +0,0 @@
# File: Dockerfile
# บันทึกการแก้ไข: (สร้างไฟล์)
# --- STAGE 1: Builder ---
# ติดตั้ง Dependencies และ Build โค้ด
FROM node:18-alpine AS builder
WORKDIR /usr/src/app
# Copy package.json และ lock file
COPY package*.json ./
# ติดตั้ง Dependencies (สำหรับ Build)
RUN npm install
# Copy source code ทั้งหมด
COPY . .
# Build application
RUN npm run build
# ติดตั้งเฉพาะ Production Dependencies (สำหรับ Stage สุดท้าย)
RUN npm prune --production
# --- STAGE 2: Runner ---
# Image สุดท้ายที่มีขนาดเล็ก
FROM node:18-alpine
WORKDIR /usr/src/app
# (Security) สร้าง User ที่ไม่มีสิทธิ์ Root
RUN addgroup -S nestjs && adduser -S nestjs -G nestjs
USER nestjs
# Copy Production Dependencies (จาก Stage 1)
COPY --from=builder /usr/src/app/node_modules ./node_modules
# Copy Build Artifacts (จาก Stage 1)
COPY --from=builder /usr/src/app/dist ./dist
# Copy package.json (เผื่อจำเป็น)
COPY package*.json ./
# เปิด Port (อ่านจาก Environment Variable)
EXPOSE ${PORT:-3000}
# รัน Application
CMD [ "node", "dist/main" ]

View File

@@ -1,62 +1,38 @@
# File: docker-compose.yml
# บันทึกการแก้ไข: (สร้างไฟล์)
# (สำคัญ: ไฟล์นี้จะถูก import หรือคัดลอกไปใส่ใน UI ของ QNAP Container Station)
version: '3.8'
services:
# ---------------------------------
# Service 1: Backend (NestJS)
# (Req 2.3)
# ---------------------------------
backend:
build:
context: ./backend # (สมมติว่า Dockerfile อยู่ในโฟลเดอร์ backend)
dockerfile: Dockerfile
image: lcbp3-backend:1.3.0 # (ตั้งชื่อ Image)
container_name: lcbp3-backend
restart: unless-stopped
# (สำคัญ) กำหนด Environment Variables ที่นี่ (ห้ามใช้ .env)
# (Req 6.5, 2.1)
mariadb:
image: mariadb:11.8
container_name: lcbp3-db-local
restart: always
environment:
# --- App Config ---
- PORT=3000
- NODE_ENV=production
# --- Database (Req 2.4) ---
# (ชี้ไปที่ Service 'mariadb' ใน Network 'lcbp3')
- DATABASE_HOST=mariadb
- DATABASE_PORT=3306
- DATABASE_USER=your_db_user # (ต้องเปลี่ยน)
- DATABASE_PASSWORD=your_db_pass # (ต้องเปลี่ยน)
- DATABASE_NAME=lcbp3_dms
# --- Security (JWT) (Req 6.5) ---
- JWT_SECRET=YOUR_VERY_STRONG_JWT_SECRET_KEY # (ต้องเปลี่ยน)
- JWT_EXPIRATION_TIME=3600s # (เช่น 1 ชั่วโมง)
# --- Phase 4 Services ---
- ELASTICSEARCH_URL=http://elasticsearch:9200 # (ชี้ไปที่ Service ES ถ้ามี)
- N8N_WEBHOOK_URL=http://n8n:5678/webhook/your-webhook-id # (ชี้ไปที่ N8N)
# (สำคัญ) เชื่อมต่อ Network กลาง (Req 2.1)
MYSQL_ROOT_PASSWORD: Center#2025
MYSQL_DATABASE: lcbp3_dev
MYSQL_USER: admin
MYSQL_PASSWORD: Center2025
ports:
- '3306:3306'
volumes:
- db_data:/var/lib/mysql
networks:
- lcbp3
- lcbp3-net
# (Deploy) ตั้งค่า Health Check
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s # (รอให้ App เริ่มก่อน)
# Optional: phpMyAdmin สำหรับจัดการ DB ง่ายๆ
pma:
image: phpmyadmin/phpmyadmin
container_name: lcbp3-pma-local
environment:
PMA_HOST: mariadb
ports:
- '8080:80'
depends_on:
- mariadb
networks:
- lcbp3-net
volumes:
db_data:
# ---------------------------------
# Network กลาง (Req 2.1)
# (ต้องสร้าง Network นี้ไว้ก่อนใน QNAP หรือสร้างพร้อมกัน)
# ---------------------------------
networks:
lcbp3:
external: true # (ถ้าสร้างไว้แล้ว)
# name: lcbp3 # (ถ้าต้องการให้ Compose สร้าง)
lcbp3-net:
driver: bridge

View File

@@ -20,57 +20,34 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@elastic/elasticsearch": "^9.2.0",
"@nestjs/cache-manager": "^3.0.1",
"@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.0.1",
"@nestjs/elasticsearch": "^11.1.0",
"@nestjs/jwt": "^11.0.1",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.1.9",
"@nestjs/schedule": "^6.0.1",
"@nestjs/swagger": "^11.2.1",
"@nestjs/platform-express": "^11.0.1",
"@nestjs/typeorm": "^11.0.0",
"@types/nodemailer": "^7.0.3",
"@types/uuid": "^10.0.0",
"bcrypt": "^6.0.0",
"cache-manager": "^7.2.4",
"casl": "^0.2.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"helmet": "^8.1.0",
"multer": "^2.0.2",
"mysql2": "^3.15.3",
"nodemailer": "^7.0.10",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"rate-limiter-flexible": "^8.2.1",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"typeorm": "^0.3.27",
"uuid": "^13.0.0"
"typeorm": "^0.3.27"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.18.0",
"@nestjs/cli": "^11.0.0",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.1.9",
"@nestjs/testing": "^11.0.1",
"@types/express": "^5.0.0",
"@types/jest": "^30.0.0",
"@types/multer": "^2.0.0",
"@types/node": "^22.10.7",
"@types/passport-jwt": "^4.0.1",
"@types/supertest": "^6.0.2",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2",
"globals": "^16.0.0",
"jest": "^30.2.0",
"jest": "^30.0.0",
"prettier": "^3.4.2",
"source-map-support": "^0.5.21",
"supertest": "^7.1.4",
"supertest": "^7.0.0",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.2",
"ts-node": "^10.9.2",

6725
backend/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,39 @@
// 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';
@Module({
imports: [],
imports: [
// 1. Load Config Module
ConfigModule.forRoot({
isGlobal: true, // ให้เรียกใช้ได้ทุกที่โดยไม่ต้อง import ใหม่
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'),
// Auto Load Entities: โหลด Entity ทั้งหมดที่อยู่ในโปรเจกต์อัตโนมัติ
autoLoadEntities: true,
// Synchronize: true เฉพาะ Dev environment (ห้ามใช้ใน Prod)
synchronize: configService.get<string>('NODE_ENV') === 'development',
// Logging: เปิดดู Query SQL ตอน Dev
logging: configService.get<string>('NODE_ENV') === 'development',
}),
inject: [ConfigService],
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
export class AppModule {}

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"module": "nodenext",
"module": "commonjs",
"moduleResolution": "nodenext",
"resolvePackageJsonExports": true,
"esModuleInterop": true,
@@ -10,16 +10,18 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2023",
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": true,
/* Strict Type-Checking Options */
"strict": true, // บังคับใช้ Strict Mode
"strictNullChecks": true, // ตรวจสอบค่า Null อย่างเคร่งครัด
"forceConsistentCasingInFileNames": true,
"noImplicitAny": false,
"strictBindCallApply": false,
"noFallthroughCasesInSwitch": false
"noImplicitAny": true, // ห้ามใช้ Any โดยไม่จำเป็น
"strictBindCallApply": true,
"noFallthroughCasesInSwitch": true
}
}