diff --git a/@knowleadge/dbchange/202511300710.txt b/@knowleadge/dbchange/202511300710.txt new file mode 100644 index 0000000..165df88 --- /dev/null +++ b/@knowleadge/dbchange/202511300710.txt @@ -0,0 +1,59 @@ +-- ⚠️ PostgreSQL ไม่รองรับคำสั่ง AFTER ในการเพิ่ม Column +-- จำเป็นต้องสร้างตารางใหม่เพื่อจัดเรียงลำดับ Column + +BEGIN; -- เริ่ม Transaction (ถ้า Error ข้อมูลจะไม่เสียหาย) + +-- 1. เปลี่ยนชื่อตารางเดิมเป็น Backup +ALTER TABLE dbo.prjmst RENAME TO prjmst_backup; + +-- 2. สร้างตารางใหม่โดยมี prjdoc อยู่ในตำแหน่งที่ต้องการ +CREATE TABLE dbo.prjmst +( + prjseq integer NOT NULL, + prjnam character varying(150) COLLATE pg_catalog."default" NOT NULL, + prjusrseq integer, + prjwntbdg numeric(14,2), + prjacpbdg numeric(14,2), + prjbdgcod character varying(3) COLLATE pg_catalog."default", + prjcomstt character varying(3) COLLATE pg_catalog."default", + + -- ✅ แทรก Column ใหม่ตรงนี้ + prjdoc character varying(255) COLLATE pg_catalog."default", + + prjacpdtm character(12) COLLATE pg_catalog."default", + + CONSTRAINT prjmst_pkey PRIMARY KEY (prjseq, prjnam) +) +TABLESPACE pg_default; + +-- 3. กำหนด Owner (ถ้าจำเป็น) +ALTER TABLE dbo.prjmst OWNER to postgres; + +-- 4. ย้ายข้อมูลจากตาราง Backup มาใส่ตารางใหม่ (Map ข้อมูลให้ตรง Column) +INSERT INTO dbo.prjmst ( + prjseq, + prjnam, + prjusrseq, + prjwntbdg, + prjacpbdg, + prjbdgcod, + prjcomstt, + prjacpdtm + -- prjdoc จะเป็น NULL อัตโนมัติสำหรับข้อมูลเก่า +) +SELECT + prjseq, + prjnam, + prjusrseq, + prjwntbdg, + prjacpbdg, + prjbdgcod, + prjcomstt, + prjacpdtm +FROM prjmst_backup; + +-- 5. ยืนยันการทำงาน +COMMIT; + +-- หมายเหตุ: หลังจากตรวจสอบข้อมูลแล้ว สามารถลบตาราง Backup ได้ด้วยคำสั่ง: +-- DROP TABLE dbo.prjmst_backup; \ No newline at end of file diff --git a/@knowleadge/dbchange/alter new.txt b/@knowleadge/dbchange/alter new.txt new file mode 100644 index 0000000..9ba4244 --- /dev/null +++ b/@knowleadge/dbchange/alter new.txt @@ -0,0 +1,63 @@ +-- ⚠️ IMPORTANT: คำสั่ง ROLLBACK จะช่วยเคลียร์สถานะ "current transaction is aborted" +ROLLBACK; + +BEGIN; + +-- 1. ส่วนจัดการเปลี่ยนชื่อตารางและ Key (แบบปลอดภัย เช็คก่อนทำ) +DO $$ +BEGIN + -- เช็ค: ถ้ามีตาราง 'prjmst' อยู่ และยังไม่มี 'prjmst_backup' ให้ทำการเปลี่ยนชื่อ (กรณีรันครั้งแรก) + IF EXISTS (SELECT FROM pg_tables WHERE schemaname = 'dbo' AND tablename = 'prjmst') THEN + IF NOT EXISTS (SELECT FROM pg_tables WHERE schemaname = 'dbo' AND tablename = 'prjmst_backup') THEN + ALTER TABLE dbo.prjmst RENAME TO prjmst_backup; + END IF; + END IF; + + -- เช็ค: แก้ชื่อ Primary Key ในตาราง Backup ถ้ามันยังชื่อเดิม (แก้ปัญหา duplicate key name) + IF EXISTS ( + SELECT 1 FROM pg_constraint con + JOIN pg_class rel ON rel.oid = con.conrelid + JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace + WHERE nsp.nspname = 'dbo' AND rel.relname = 'prjmst_backup' AND con.conname = 'prjmst_pkey' + ) THEN + ALTER TABLE dbo.prjmst_backup RENAME CONSTRAINT prjmst_pkey TO prjmst_backup_pkey; + END IF; +END $$; + +-- 2. สร้างตารางใหม่ (New Structure) +-- ใช้ IF NOT EXISTS กัน Error ถ้ารันซ้ำ +CREATE TABLE IF NOT EXISTS dbo.prjmst +( + prjseq integer NOT NULL, + prjnam character varying(150) COLLATE pg_catalog."default" NOT NULL, + prjusrseq integer, + prjwntbdg numeric(14,2), + prjacpbdg numeric(14,2), + prjbdgcod character varying(3) COLLATE pg_catalog."default", + prjcomstt character varying(3) COLLATE pg_catalog."default", + + -- ✅ แทรก Column ใหม่ตรงนี้ + prjdoc character varying(255) COLLATE pg_catalog."default", + + prjacpdtm character(12) COLLATE pg_catalog."default", + + CONSTRAINT prjmst_pkey PRIMARY KEY (prjseq, prjnam) +) +TABLESPACE pg_default; + +ALTER TABLE dbo.prjmst OWNER to postgres; + +-- 3. ย้ายข้อมูลกลับมา (Data Migration) +-- เช็คก่อนว่าตารางใหม่ว่างอยู่ไหม ค่อย Insert +INSERT INTO dbo.prjmst ( + prjseq, prjnam, prjusrseq, prjwntbdg, prjacpbdg, prjbdgcod, prjcomstt, prjacpdtm +) +SELECT + prjseq, prjnam, prjusrseq, prjwntbdg, prjacpbdg, prjbdgcod, prjcomstt, prjacpdtm +FROM dbo.prjmst_backup +WHERE NOT EXISTS (SELECT 1 FROM dbo.prjmst); + +COMMIT; + +-- หมายเหตุ: เมื่อตรวจสอบข้อมูลครบถ้วนแล้ว สามารถสั่งลบตาราง backup ได้: +-- DROP TABLE dbo.prjmst_backup; \ No newline at end of file diff --git a/exthernal-ttc-api/src/middlewares/uploadMiddleware.js b/exthernal-ttc-api/src/middlewares/uploadMiddleware.js new file mode 100644 index 0000000..7adab1f --- /dev/null +++ b/exthernal-ttc-api/src/middlewares/uploadMiddleware.js @@ -0,0 +1,84 @@ +import multer from 'multer' +import path from 'path' +import fs from 'fs' +import { sendError } from '../utils/response.js' + +const tempDir = 'uploads/temp' +if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }) +} + +const storage = multer.diskStorage({ + destination: function (req, file, cb) { + cb(null, tempDir) + }, + filename: function (req, file, cb) { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9) + cb(null, uniqueSuffix + path.extname(file.originalname)) + } +}) + +const fileFilter = (req, file, cb) => { + const allowedMimes = [ + 'image/jpeg', 'image/png', 'image/jpg', + 'application/pdf', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + ] + if (allowedMimes.includes(file.mimetype)) { + cb(null, true) + } else { + cb(new Error('รองรับเฉพาะไฟล์รูปภาพ, PDF หรือเอกสาร Word เท่านั้น'), false) + } +} + +const upload = multer({ + storage: storage, + fileFilter: fileFilter, + limits: { fileSize: 10 * 1024 * 1024 } +}) + +function verifyFileSignature(filePath) { + try { + const buffer = Buffer.alloc(8) + const fd = fs.openSync(filePath, 'r') + fs.readSync(fd, buffer, 0, 8, 0) + fs.closeSync(fd) + const hex = buffer.toString('hex').toUpperCase() + + if (hex.startsWith('FFD8FF')) return true // JPG + if (hex.startsWith('89504E47')) return true // PNG + if (hex.startsWith('25504446')) return true // PDF + if (hex.startsWith('D0CF11E0')) return true // DOC + if (hex.startsWith('504B0304')) return true // DOCX + + return false + } catch (err) { + return false + } +} + +export const uploadMiddleware = (req, res, next) => { + // เปลี่ยนเป็น .array() เพื่อรับหลายไฟล์ (รองรับสูงสุด 10 ไฟล์ต่อครั้ง) + const uploadHandler = upload.array('prjdoc', 10) + + uploadHandler(req, res, (err) => { + if (err) return res.status(400).json(sendError(err.message)) + + // ถ้าไม่มีไฟล์ ข้ามไป + if (!req.files || req.files.length === 0) return next() + + // Loop ตรวจสอบ Signature ทุกไฟล์ + for (const file of req.files) { + const isSafe = verifyFileSignature(file.path) + if (!isSafe) { + // ลบไฟล์ทั้งหมดทิ้งทันทีถ้าเจอไฟล์อันตรายแม้แต่ไฟล์เดียว + req.files.forEach(f => { + if (fs.existsSync(f.path)) fs.unlinkSync(f.path) + }) + return res.status(400).json(sendError(`ไฟล์ ${file.originalname} ไม่ปลอดภัย (Invalid Signature)`)) + } + } + next() + }) +} \ No newline at end of file diff --git a/exthernal-ttc-api/uploads/projects/1764504521158-123821263.docx b/exthernal-ttc-api/uploads/projects/1764504521158-123821263.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504521158-123821263.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504584000-133438404.docx b/exthernal-ttc-api/uploads/projects/1764504584000-133438404.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504584000-133438404.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504589491-532615394.docx b/exthernal-ttc-api/uploads/projects/1764504589491-532615394.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504589491-532615394.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504605122-350813611.docx b/exthernal-ttc-api/uploads/projects/1764504605122-350813611.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504605122-350813611.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504623958-252845613.docx b/exthernal-ttc-api/uploads/projects/1764504623958-252845613.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504623958-252845613.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504649758-558938587.docx b/exthernal-ttc-api/uploads/projects/1764504649758-558938587.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504649758-558938587.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504665734-82185672.docx b/exthernal-ttc-api/uploads/projects/1764504665734-82185672.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504665734-82185672.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504705776-32739561.docx b/exthernal-ttc-api/uploads/projects/1764504705776-32739561.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504705776-32739561.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504713709-764652284.docx b/exthernal-ttc-api/uploads/projects/1764504713709-764652284.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504713709-764652284.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504763972-112141709.docx b/exthernal-ttc-api/uploads/projects/1764504763972-112141709.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504763972-112141709.docx differ diff --git a/exthernal-ttc-api/uploads/projects/1764504878090-18492765.docx b/exthernal-ttc-api/uploads/projects/1764504878090-18492765.docx new file mode 100644 index 0000000..37a1dcb Binary files /dev/null and b/exthernal-ttc-api/uploads/projects/1764504878090-18492765.docx differ