refer from report-server-ts
Find a file
2025-02-28 10:31:11 +07:00
libs support puppeteer's PDFOptions, add param for preload 2025-02-28 10:31:11 +07:00
templates url 2 pdf support, update test run, reduse imge size, 2025-02-25 19:50:21 +07:00
test-run url 2 pdf support, update test run, reduse imge size, 2025-02-25 19:50:21 +07:00
ThaiFonts Support TS, swagger,Template(xls,docx),PDF convert 2023-10-07 17:29:53 +07:00
.gitignore url 2 pdf support, update test run, reduse imge size, 2025-02-25 19:50:21 +07:00
.onedev-buildspec.yml modify CI 2024-12-20 10:07:15 +00:00
api.http support puppeteer's PDFOptions, add param for preload 2025-02-28 10:31:11 +07:00
app.ts url 2 pdf support, update test run, reduse imge size, 2025-02-25 19:50:21 +07:00
compose.yaml fix Payload Too Large for convert file api 2024-06-28 10:39:52 +07:00
Dockerfile url 2 pdf support, update test run, reduse imge size, 2025-02-25 19:50:21 +07:00
package-lock.json url 2 pdf support, update test run, reduse imge size, 2025-02-25 19:50:21 +07:00
package.json url 2 pdf support, update test run, reduse imge size, 2025-02-25 19:50:21 +07:00
README.md support puppeteer's PDFOptions, add param for preload 2025-02-28 10:31:11 +07:00
tsconfig.json url 2 pdf support, update test run, reduse imge size, 2025-02-25 19:50:21 +07:00

report-server-ts

เป็น Microservice(Web API) ออกแบบมาเพื่อสร้างเอกสารจาก templste หรือแปลงไฟล์ สามารถใช้ซ้ำได้หลายโปรเจ็ก

Feature & Change

  • ใช้ไฟล์ docx/xlsx เป็น template คล้าย Mail Merge ยูสเซอร์ออกแบบเองได้ รวมกับ ข้อมูลใน JSON เพื่อออกเป็น docx/xlsx
  • JSON + template จะได้ไฟล์ output ฟอร์แม็ตจะเป็นไฟล์ของ template(docx/xlsx) สามารถแปลเป็นฟอร์แม็ต png, pdf, jpeg ฯลฯ ได้ด้วยความสามารถของ Libreoffice(soffice)
  • ใช้ url ของหน้าเวปแปลงเป็นไฟล์รองรับ pdf, png, jpeg เพื่อออกรายงานจากหน้าเวปได้ รองรับ Query Selector
  • แปลงไฟล์เป็น ภาพหรือ PDF รองรับการตัดคำไทย (ตัวออกรายงานตัวอื่นๆมักจะมีปัญหาการตัดคำ)
  • โค้ดมีการ obfuscator เพื่อลดขนาดและกันลูกค้าเอาโค้ดไปใช้
  • มี Swagger ไว้ทดสอบ API มี libs/swagger-specs.json เพื่อนำเข้า Postman หรือเครื่องมื่อื่นๆได้
  • มีโปรแกรมช่วยทดสอบ template แบบง่ายๆ ให้ทดสอบก่อนเอาเข้าเซิร์ฟเวอร์
  • build เป็น Docker Image เพื่อใช้ใน Microservice ในโปรเจ็กอื่นๆ ใช้ prefix /api/v1/report-template/* ลดขนาดแล้วด้วย node:22-bookworm-slim

development

พัฒนาและทดสอบบน node 22 ,Linux AMD x86-64 ให้ clone project ติดตั้ง Fonts และ LibreOffice ตามวิธีใน Dockerfile

# clone project
npm install
# add type support for yaqrcode
cd node_modules/yaqrcode
wget https://raw.githubusercontent.com/zenozeng/node-yaqrcode/master/index.d.ts


การใช้งาน

ดู scripts ใน package.json และ compose.yaml ให้ดูให้เข้าใจ การสร้าง docker อิมเมจ จะต้อง swaggergen,obfuscator ก่อน หลังจากนั้นขึ้น Docker registry บน production ให้ดู compose.yaml จะต้องนำ template ไปเก็บไว้ที่โฟลเดอร์ templates ด้วย

npm run dev
npm run build
npm run serve
npm run obfuscator
npm run preview
npm run build:docker
docker compose up -d
npm run push:docker

ทดสอบ template/unit test

ไปที่โฟลเดอร์ test-run มีโปรแกรมเพื่อทดสอบ template ให้ทดสอบที่นี้ก่อนเอา template ไปวางในเซิร์ฟเวอร์ มีค่า default สำหรับการทดสอบที่ใช้ได้เลย สามารถแปลงไปไฟล์แบบต่างๆที่ Libreoffice รองรับ(จำเป็นต้องติดตั้ง ) ควรทดสอบรูปแบบข้อมูล(json) ให้เข้ากับ template(docx,xlsx) ถ้าเกิดปัญหา ถ้าค่าไม่ครบ template แบบ docx จะ error log ที่เซิร์ฟเวอร์ ส่วน xlsx ไม่แจ้งปัญหา แค่ไม่แสดงค่านั้นๆ คู่มือการใช้งานที่สมบูรณ์ให้ไปที่เวปของ library ที่ใช้ docx-templates และ xlsx-template-next

cd test-run
$ npx ts-node docx-template.ts 
Output extension(docx,pdf,odt): pdf
JSON data path(./docx.json): 
Base path of templates-docx(..): 

$ npx ts-node xlsx-template.ts 
Output extension(xlsx,pdf): ods
JSON data path(./xlsx.json): 
Base path of templates-docx(..):

$ npx ts-node html-template.ts 

API

หลัง npm run dev ไปดูที่ http://localhost:3001/swagger หรือใช้ Rest Client ดูไฟล์ api.http

ตรง http header จะใช้ accept เป็นตัวบอกว่าต้องการผลเป็นไฟล์แบบไหนโดยใช้ Mime type เพื่อเป็นมาตรฐาน ให้ดูที่รองรับในฟังก์ชั่น mimeToExtension การตั้งค่าของ template อยู่ที่ templateOption

HTML

แปลงจาก URL เป็น pdf,png,jpg รองรับการตัดคำไทย การแปลงเป็น

  • templateOption.htmlTemplateOption เป็นการตั้งค่าเฉพาะของโมดุลนี้
  • templateOption.htmlTemplateOption.PDFOptions ซึ่งเป็น PDFOptions ของ puppeteer ฟีเจอร์ template ยังไม่เสร็จ
# Grafana dashboard to pdf
curl -X 'POST' \
  'http://localhost:3001/api/v1/report-template/html' \
  -H 'accept: application/pdf' -H 'Content-Type: application/json' \
  -d '{
  "template": "https://bma-dashboard.frappet.synology.me/d/ANtkJay4z/4Lic4Li54LmJ4Lie4Li04LiB4Liy4Lij?orgId=1&kiosk",
  "reportName": "html-grafana",
  "htmlOption": {
    "querySelector": ".scrollbar-view"
  }
}' -o html-grafana.pdf


# url to png
curl -X 'POST' 'http://localhost:3001/api/v1/report-template/html' \
  -H 'accept: image/png' -H 'Content-Type: application/json' \
  -d '{"template": "https://www.blognone.com/","reportName": "html-blognone"}' -o html-blognone.png
# url to jpeg
curl -X 'POST' 'http://localhost:3001/api/v1/report-template/html' \
  -H 'accept: image/jpeg' -H 'Content-Type: application/json' \
  -d '{"template": "https://pantip.com/","reportName": "html-blognone"}' -o html-pantip.jpeg
# url to pdf pantip.com very long lazyload you 
curl -X 'POST' 'http://localhost:3001/api/v1/report-template/html' \
  -H 'accept: application/pdf' -H 'Content-Type: application/json' \
  -d '{"template": "https://pantip.com/","reportName": "html-blognone",
    "htmlOption": {
      "preloadWait":500,
      "preloadScroll":1000,
      "preloadLoop":25,
      "pdfOption":{"format":"A4"}
      }
  }' -o html-pantip.pdf


pantip.com to pdf

curl -X 'POST'
'http://localhost:3001/api/v1/report-template/html'
-H 'accept: application/pdf' -H 'Content-Type: application/json'
-d '{ "template": "https://pantip.com", "reportName": "html-pantip.com" }' -o html-pantip.com.pdf

docx

แปลงจากเทมเพลทไฟล์ .docx เป็น docx,pdf,png

curl -X 'POST' \
  'https://report-server.frappet.synology.me/api/v1/report-template/docx?folder=command' \
  -H 'accept: application/vnd.openxmlformats-officedocument.wordprocessingml.document' \
  -H 'Content-Type: application/json' \
  -d '{
  "template": "C-PM-01_cover",
  "reportName": "command-C-PM-01_cover",
  "data": {
    "issue": "............",
    "title": "......",
    "commandNo": "......",
    "commandYear": "......",
    "commandTitle": "คำสั่งบรรจุและแต่งตั้งผู้สอบแข่งขันได้",
    "detailHeader": "",
    "detailBody": "อาศัยอำนาจตามความในมาตรา ๔๔ มาตรา ๕๒ (๔) แห่งพระราชบัญญัติระเบียบข้าราชการกรุงเทพมหานครและบุคลากรกรุงเทพมหานคร พ.ศ.๒๕๕๔  ประกอบกับกฎ ก.ก. ว่าด้วยการทดลองปฏิบัติหน้าที่ราชการและการพัฒนาข้าราชการกรุงเทพมหานครสามัญที่อยู่ระหว่างทดลองปฏิบัติหน้าที่ราชการ พ.ศ. ๒๕๕๕ มติคณะกรรมการข้าราชการกรุงเทพมหานครและบุคลากรกรุงเทพมหานคร ครั้งที่ ๑/๒๕๕๔ เมื่อวันที่ ๒๒ ธันวาคม ๒๕๕๔ มติ อ.ก.ก. วิสามัญเกี่ยวกับระบบราชการ การจัดส่วนราชการและค่าตอบแทน ครั้งที่ ๙/๒๕๕๖ เมื่อ ๑๘ กันยายน ๒๕๕๖ ประกาศสำนักงาน ก.ก. ลงวันที่ ………………………………….. เรื่อง รับสมัครสอบแข่งขันเพื่อบรรจุและแต่งตั้งบุคคลเข้ารับราชการเป็นข้าราชการการกรุงเทพมหานครสามัญ ครั้งที่ ………………………………….. และประกาศสำนักงาน ก.ก. ลงวันที่ ………………………………….. เรื่อง ผลการสอบแข่งขันเพื่อบรรจุและแต่งตั้งบุคคลเข้ารับราชการเป็นข้าราชการกรุงเทพมหานครสามัญ ครั้งที่ ………………………………….. ตำแหน่ง………………………………….. ให้บรรจุผู้สอบแข่งขันได้เข้ารับราชการเป็นข้าราชการกรุงเทพมหานครสามัญ และแต่งตั้งให้ดำรงตำแหน่ง………………………………….. จำนวน ………………………………….. ราย โดยให้ทดลองปฏิบัติหน้าที่ราชการในตำแหน่งที่ได้รับแต่งตั้งดังบัญชีรายละเอียดแนบท้ายคำสั่งนี้",
    "detailFooter": "",
    "commandDate": "..................",
    "commandAffectDate": "..................",
    "commandExcecuteDate": "..................",
    "name": "....................................",
    "position": "ผู้อำนวยการสำนัก/เขต",
    "authorizedUserFullName": "............",
    "authorizedPosition": "..................."
  }
}' -o docx-command-C-PM-01_cover.docx


curl -X 'POST' \
  'https://report-server.frappet.synology.me/api/v1/report-template/docx?folder=command' \
  -H 'accept: application/pdf' \
  -H 'Content-Type: application/json' \
  -d '{
  "template": "C-PM-01_cover",
  "reportName": "command-C-PM-01_cover",
  "data": {
    "issue": "............",
    "title": "......",
    "commandNo": "......",
    "commandYear": "......",
    "commandTitle": "คำสั่งบรรจุและแต่งตั้งผู้สอบแข่งขันได้",
    "detailHeader": "",
    "detailBody": "อาศัยอำนาจตามความในมาตรา ๔๔ มาตรา ๕๒ (๔) แห่งพระราชบัญญัติระเบียบข้าราชการกรุงเทพมหานครและบุคลากรกรุงเทพมหานคร พ.ศ.๒๕๕๔  ประกอบกับกฎ ก.ก. ว่าด้วยการทดลองปฏิบัติหน้าที่ราชการและการพัฒนาข้าราชการกรุงเทพมหานครสามัญที่อยู่ระหว่างทดลองปฏิบัติหน้าที่ราชการ พ.ศ. ๒๕๕๕ มติคณะกรรมการข้าราชการกรุงเทพมหานครและบุคลากรกรุงเทพมหานคร ครั้งที่ ๑/๒๕๕๔ เมื่อวันที่ ๒๒ ธันวาคม ๒๕๕๔ มติ อ.ก.ก. วิสามัญเกี่ยวกับระบบราชการ การจัดส่วนราชการและค่าตอบแทน ครั้งที่ ๙/๒๕๕๖ เมื่อ ๑๘ กันยายน ๒๕๕๖ ประกาศสำนักงาน ก.ก. ลงวันที่ ………………………………….. เรื่อง รับสมัครสอบแข่งขันเพื่อบรรจุและแต่งตั้งบุคคลเข้ารับราชการเป็นข้าราชการการกรุงเทพมหานครสามัญ ครั้งที่ ………………………………….. และประกาศสำนักงาน ก.ก. ลงวันที่ ………………………………….. เรื่อง ผลการสอบแข่งขันเพื่อบรรจุและแต่งตั้งบุคคลเข้ารับราชการเป็นข้าราชการกรุงเทพมหานครสามัญ ครั้งที่ ………………………………….. ตำแหน่ง………………………………….. ให้บรรจุผู้สอบแข่งขันได้เข้ารับราชการเป็นข้าราชการกรุงเทพมหานครสามัญ และแต่งตั้งให้ดำรงตำแหน่ง………………………………….. จำนวน ………………………………….. ราย โดยให้ทดลองปฏิบัติหน้าที่ราชการในตำแหน่งที่ได้รับแต่งตั้งดังบัญชีรายละเอียดแนบท้ายคำสั่งนี้",
    "detailFooter": "",
    "commandDate": "..................",
    "commandAffectDate": "..................",
    "commandExcecuteDate": "..................",
    "name": "....................................",
    "position": "ผู้อำนวยการสำนัก/เขต",
    "authorizedUserFullName": "............",
    "authorizedPosition": "..................."
  }
}' -o docx-command-C-PM-01_cover.pdf


curl -X 'POST' \
-H 'accept: application/pdf' \
-H 'Content-Type: application/json' \
'http://localhost:3001/api/v1/report-template/docx' \
-d '{
  "template": "hello",
  "reportName": "docx-report",
  "data": {
    "docNo": "๑๒๓๔๕",
    "me": "กระผม",
    "prefix": "นาย",
    "name": "สรวิชญ์",
    "surname": "พลสิทธิ์",
    "position": "Chief Technology Officer",
    "org": {
      "type": "บริษัท",
      "name": "เฟรปเป้ที",
      "url": "https://frappet.com"
    },
    "employees": [
      {
        "name": "ภาวิชญ์",
        "surname": "พลสิทธิ์"
      },
      {
        "name": "วิชญาภา",
        "surname": "พลสิทธิ์"
      }
    ]
  }
}' -o docx-report.pdf

xlsx

แปลงจากเทมเพลทไฟล์ .xlsx เป็น xlsx,pdf,png

curl -X 'POST' \
  'http://localhost:3001/api/v1/report-template/xlsx' \
  -H 'accept: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' \
  -H 'Content-Type: application/json' \
  -d '{
  "template": "hello",
  "reportName": "xlsx-report",
  "data": {
    "docNo": "๑๒๓๔๕",
    "me": "กระผม",
    "prefix": "นาย",
    "name": "สรวิชญ์",
    "surname": "พลสิทธิ์",
    "position": "Chief Technology Officer",
    "org": {
      "type": "บริษัท",
      "name": "เฟรปเป้ที",
      "url": "https://frappet.com"
    },
    "employees": [
      {
        "id": 1,
        "name": "ภาวิชญ์",
        "surname": "พลสิทธิ์",
        "score": 80
      },
      {
        "id": 2,
        "name": "วิชญาภา",
        "surname": "พลสิทธิ์",
        "score": 50
      },
      {
        "id": 3,
        "name": "ฐิตาภา",
        "surname": "พลสิทธิ์",
        "score": 90
      },
      {
        "id": 4,
        "name": "สรวิชญ์ พลสิทธิ์",
        "surname": "พลสิทธิ์",
        "score": 99
      }
    ]
  }
}' -o xlsx-report.xlsx

Known Issue

  • soffice แปลงเป็น png หรือ jpeg ได้แค่ 96dpi มันฮาร์ดโค้ดอยู่ สามารถแก้โค้ด compile ใหม่(ยากไปหน่อย) work around(ยังไม่ได้ทำ) ภาพความละเอียดต่ำให้แปลงเป็น pdf แล้วแปลงเป็นภาพ ใช้ Imagemagick ในการแปลงข้อดีคือรองรับฟอร์แม็ตได้หลายแบบ แต่มัน disable PDF เป็น default ต้องแก้คอนฟิกก่อน ค่อนช้างกิน CPU ถ้ามีการแปลงหลายต่อ วิธีการนี้อาจจะทำเป็น API ในอนาคต คำสั่งด้านล่างลองแล้วใช้ได้
convert -density 300 -background white -alpha remove report-docx.pdf report-docx2.png
convert -density 300 -background white -alpha remove report-docx.pdf report-docx2.jpeg
  • url แปลงเป็น pdf หรือภาพ จะเป็นหน้าเดียวเลย ยังไม่สามารถกำหนดขนาดทำเป็นหลายๆหน้าได้ อาจจะเป็นไปได้ยังหาวิธีอยู่เลยทำหน้าเดียวไปก่อน ยังมีปัญหากับการโหลดที่เป็น lazy load ยังไม่รองรับ Authentication header

Note

  • ยังไม่สามารถใช้ bun runtime มีปัญหากับ libreoffice-file-converter และ docker-template เลยไม่ได้เอามาใช้ รุ่นใหม่อาจจะแก้ปัญหานี้แล้วต้องลองอีกที
  • Playwright ยังตรวจสอบความสูงของหน้าไม่ถูกต้องเลยใช้ puppeteer แทน
  • น่าจะทำให้รองรับ Authentication Header เพื่อให้ยูสเซอร์ในระบบใช้งานได้เท่านั้น
  • หาทางสร้างเอกสารจาก text เช่น Markdown เป็นเอกสาร MS Office
  • น่าจะทำ license key เผื่อขายให้ลูกค้าติดตั้งใช้งานต่อ (โค้ดที่ผ่าน obfuscator แล้วย้อนกลับมาได้ง่ายหรือเปล่า ?)
  • อาจจะลอง inkscape รองรับ command line อาจจะเอามาทำอะไรได้ "inkscape -z -e out.png -w 1024 in.svg"
  • การแปลงภาพสามารใช้ image magick ได้ยังไม่ได้ลอง
  • ถ้าใช้อิมเมจแบบ Distroless น่าจะเล็กลงแต่ตอนนี้ยังไม่มีรายการ library ที่จำเป็นทั้งหมดอีกทั้งอาจจะต้องใช้ shell เพื่อรัน soffice ใช้แบบ slim น่าจะเหมาะกับการนี้
  • docker image น่าจะทำให้ติดตั้ง font เพิ่มได้
  • ควรจำกัด domain ที่เรียกใช้ได้