-web-service

This commit is contained in:
2025-11-11 15:11:56 +07:00
parent 9ad26fa5ef
commit fcf59ce5db
21 changed files with 537 additions and 253 deletions

View File

@@ -0,0 +1,62 @@
import nodemailer from 'nodemailer'
import dotenv from 'dotenv'
dotenv.config()
export async function sendMockOtpMail(to, otp) {
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
})
const html = `
<div style="
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #f6f9fc 0%, #ecf3f9 100%);
padding: 48px 24px;
text-align: center;
color: #1a1f36;
">
<div style="
max-width: 480px;
max-height: auto;
margin: 0 auto;
background-color: white;
border-radius: 16px;
padding: 40px;
box-shadow: 0 4px 24px rgba(0,0,0,0.08);
">
<img src="https://cdn.discordapp.com/attachments/1416337856988971152/1431895137595822152/TTCLOGO-Photoroom.png?ex=68ff13c4&is=68fdc244&hm=5af0596e08b3b8a97dcdcca3d6a00d68a1081e6d642c033a4a1cbf8d03e660a6&" alt="Logo" style="height: 80px; margin-bottom: 24px;">
<h2 style="
color: #1a1f36;
font-size: 24px;
font-weight: 600;
margin-bottom: 16px;
">รหัส OTP สำหรับเปลี่ยนรหัสผ่าน</h2>
<p style="color: #4f566b; font-size: 16px; line-height: 1.5;">กรุณาใส่รหัสยืนยันด้านล่างนี้</p>
<div style="
font-size: 32px;
letter-spacing: 8px;
background: linear-gradient(135deg, #fa0000ff 0%, #ff5100ff 100%);
color: white;
padding: 16px 32px;
margin: 24px 0;
border-radius: 12px;
font-weight: 600;
">${otp}</div>
<p style="color: #4f566b; font-size: 14px; margin: 24px 0;">รหัสนี้จะหมดอายุใน 5 นาที</p>
<hr style="border: none; border-top: 1px solid #e6e8eb; margin: 24px 0;">
<p style="color: #697386; font-size: 13px;">หากคุณไม่ได้ร้องขอการเปลี่ยนรหัสผ่าน กรุณาละเว้นอีเมลนี้</p>
</div>
</div>
`
await transporter.sendMail({
from: `"Support" <${process.env.SMTP_USER}>`,
to,
subject: 'OTP สำหรับเปลี่ยนรหัสผ่าน',
html
})
}

View File

@@ -0,0 +1,3 @@
export function generateOTP(length = 6) {
return Math.floor(100000 + Math.random() * 900000).toString()
}

View File

@@ -0,0 +1,28 @@
import Redis from 'ioredis'
import dotenv from 'dotenv'
dotenv.config()
const redis = new Redis({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT
})
export async function saveOtp(email, otp) {
const key = `otp:${email}`
const ttl = parseInt(process.env.OTP_TTL_SECONDS || '300')
await redis.setex(key, ttl, otp)
}
export async function verifyOtp(email, otp) {
const key = `otp:${email}`
const stored = await redis.get(key)
if (!stored) return false
return stored === otp
}
export async function removeOtp(email) {
const key = `otp:${email}`
await redis.del(key)
}
export default redis

View File

@@ -1,43 +1,24 @@
/**
* sendResponse
* ----------------------------------------------
* ส่ง response แบบมาตรฐาน รองรับข้อความ 2 ภาษา
* ----------------------------------------------
* @param {object} res - Express response object
* @param {number} status - HTTP Status code (200, 400, 500, etc.)
* @param {string} msg_th - ข้อความภาษาไทย
* @param {string} msg_en - ข้อความภาษาอังกฤษ
* @param {any} [data=null] - optional data
*/
// ===================================================
// ⚙️ Nuttakit Response Layer vFinal++++++
// ===================================================
// ===================================================
// 🧩 Unified Response Handler (vFinal+)
// ===================================================
// ===================================================
// 📁 src/utils/response.js
// ===================================================
export function sendResponse(res, status, msg_th = null, msg_en = null, data = null) {
const safeData = safeJson(data)
const success = status < 400
const response = {
status: success ? 'succeed' : 'error',
message: {
th: msg_th ?? (success ? 'สำเร็จ' : 'เกิดข้อผิดพลาด'),
en: msg_en ?? (success ? 'Succeed' : 'Error')
},
data: safeData
}
res.status(status).json(response)
}
// ✅ ป้องกัน circular reference
function safeJson(obj) {
try {
if (obj && typeof obj === 'object') {
return JSON.parse(JSON.stringify(obj))
}
return obj
} catch (err) {
return '[Unserializable Object]'
export function sendError(thMsg = 'เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', enMsg = 'Unexpected error', code = 400) {
return {
code: String(code),
message: enMsg,
message_th: thMsg,
data: []
}
}
// ===================================================
// 🔹 Auto Success Response (ใช้โดย Global Handler เท่านั้น)
// ===================================================
export function formatSuccessResponse(data) {
return {
code: "200",
message: "successful",
message_th: "ดำเนินการสำเร็จ",
data: data || null
}
}