diff --git a/exthernal-login-api/src/app.js b/exthernal-login-api/src/app.js index 48ea973..2c086e8 100644 --- a/exthernal-login-api/src/app.js +++ b/exthernal-login-api/src/app.js @@ -25,7 +25,7 @@ app.use((err, req, res, next) => { next() }) -app.use('/api', router) +app.use('/api/login', router) app.listen(process.env.PORT, () => { console.log(`✅ ${process.env.PJ_NAME} running on port ${process.env.PORT}`) diff --git a/exthernal-login-api/src/controllers/loginController.js b/exthernal-login-api/src/controllers/loginController.js index fda9df8..676969f 100644 --- a/exthernal-login-api/src/controllers/loginController.js +++ b/exthernal-login-api/src/controllers/loginController.js @@ -33,35 +33,7 @@ export class loginController { } finally { if (idx === 1) return sendError('เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', 'Unexpected error'); if (!result) return sendError('ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง', 'Invalid credentials'); - if(result) { return result } - return null + return result } } - - // async onBiometricLogin(req, res) { - // try { - - // const result = await this.loginService.loginWithBiometric(organization, biometric_id) - // } catch (error) { - // idx = 1 - // } finally { // สำคัญมากต้อง จดจำไม่มีดัดแปลง อัปเดทเลย เรื่อง idx - // if(idx == 1){ return sendResponse(res, 400, 'เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', 'Unexpected error') } - // } - // return { result, timestamp: new Date().toISOString() } - // } - - // async onBiometricRegister(req, res) { - // const { organization, request } = req.body || {} - // const { biometric_id } = request || {} - // const userId = req.user.id - - // const result = await this.loginService.registerBiometric(organization, userId, biometric_id) - // return { result, timestamp: new Date().toISOString() } - // } - - // async onRenewToken(req, res) { - // const user = req.user - // const newToken = generateToken({ id: user.id, name: user.name }) - // return { token: newToken, renewed_at: new Date().toISOString() } - // } } diff --git a/exthernal-login-api/src/controllers/registercontroller.js b/exthernal-login-api/src/controllers/registercontroller.js index 5ea635b..28f1c83 100644 --- a/exthernal-login-api/src/controllers/registercontroller.js +++ b/exthernal-login-api/src/controllers/registercontroller.js @@ -1,26 +1,72 @@ -import { RegisterService } from '../services/registerservice.js'; -import { sendError } from '../utils/response.js'; +import { RegisterService } from '../services/registerservice.js' +import { sendError } from '../utils/response.js' import { GeneralService } from '../share/generalservice.js'; +import bcrypt from 'bcrypt' +import { Interface } from '../interfaces/Interface.js'; +import { validateSave } from '../utils/validate.js'; // import เข้ามา export class RegisterController { + constructor() { - this.registerService = new RegisterService(); this.generalService = new GeneralService(); + this.registerService = new RegisterService(); + this.Interface = new Interface(); } async onNavigate(req, res) { - let idx = -1, result = []; + this.generalService.devhint(1, 'registercontroller.js', 'onNavigate() start'); + let organization = req.body.organization; + const prommis = await this.onRegisterController(req, res, organization); + return prommis; + } + + async onRegisterController(req, res, database) { + let idx = -1 + let result = [] try { - const { organization, request } = req.body; - const { email, fname, lname, password } = request; - result = await this.registerService.requestRegistration(organization, email, fname, lname, password); + // 1. ดึง Sequence ล่าสุดจาก Service (เพื่อเอามา +1) + const Seq = await this.registerService.genNum(database); + + // 2. Hash Password + let passwordRaw = req.body.request.password; + const saltRounds = 10; + const hashedPassword = await bcrypt.hash(passwordRaw, saltRounds); + + // 3. เรียก makeArySave เพื่อเตรียมข้อมูลและบันทึกผ่าน saveInterface + // ส่ง nextSeq และ hashedPassword เข้าไป + result = await this.makeArySave(req, Seq, hashedPassword); + + this.generalService.devhint(1, 'registercontroller.js', 'Register success'); + } catch (error) { idx = 1; - this.generalService.devhint(1, 'registercontroller.js', 'Jumpout', error.message); - result = error; // จะถูก Global Handler จัด format - } finally { - if (idx === 1) return result; - return result; + } finally { + if (idx === 1) return sendError('เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', 'Unexpected error'); + if (!result) return sendError('ไม่สามารถลงทะเบียนได้', 'Registration failed'); + return result } } -} + + async makeArySave(req, seq, hashedPassword) { + // Map ข้อมูลเข้า Field ตามตาราง usrmst + let arysave = { + methods: 'post', // สั่งให้ saveInterface ทำการ INSERT + usrseq: seq + 1, // PK: integer + usrnam: validateSave(req.body.request.username, 'username'), + usrpwd: hashedPassword, // character varying(255) + usreml: validateSave(req.body.request.email, 'Email'), // character varying(50) + usrthinam: validateSave(req.body.request.firstname, 'firstname'), // character varying(100) + usrthilstnam: validateSave(req.body.request.lastname, 'lastname'), // character varying(100) + + // Field อื่นๆ ที่อาจต้อง Default ค่าไว้ก่อน (ตาม Schema) + usrrol: 'U', // Default User Role + adpdte: '', // Approved Date (รออนุมัติ) + expdte: '', // Expire Date + lstlgn: '', // Last Login + usrorg: req.body.request.organization, // ถ้ามี field นี้ + } + + // เรียก saveInterface ผ่าน generalService (ระบุชื่อตาราง 'usrmst') + return this.Interface.saveInterface('usrmst', arysave, req); + } +} \ No newline at end of file diff --git a/exthernal-login-api/src/interfaces/Interface.js b/exthernal-login-api/src/interfaces/Interface.js new file mode 100644 index 0000000..444df5c --- /dev/null +++ b/exthernal-login-api/src/interfaces/Interface.js @@ -0,0 +1,70 @@ +import jwt from 'jsonwebtoken' +import { BdgmstInterface } from './table/bdgmstInterface.js' +import { ActmstInterface } from './table/actmstInterface.js' +import { UsrmstInterface } from './table/usrmstInterface.js' +import { sendError } from '../utils/response.js' + +// ------------------------------- +// GLOBAL FILE +// ----------------------------- + +export class Interface { + + constructor() { + this.map = { + bdgmst: new BdgmstInterface(), + actmst: new ActmstInterface(), + usrmst: new UsrmstInterface(), + } + } + + // =============================================================== + // saveInterface → แกะ token เอง และ route ไปยัง interface เฉพาะ table + // =============================================================== + async saveInterface(tableName, data, req) { + + // ------------------------------ + // 1) จับ Interface ที่ตรงกับ table + // ------------------------------ + const handler = this.map[tableName.toLowerCase()] + if (!handler) { + return new sendError(`Interface not found for table: ${tableName}`) + } + + let schema; + + // ------------------------------ + // 2) ตรวจสอบเงื่อนไข (Exception for usrmst) + // ------------------------------ + if (tableName.toLowerCase() === 'usrmst') { + // กรณี usrmst (เช่น Register/Login) ไม่บังคับ Token + // เราต้องกำหนด schema เอง เพราะไม่มี token ให้แกะ + schema = 'nuttakit' + } else { + // ------------------------------ + // 3) ตารางอื่น ๆ บังคับ Token ตามปกติ + // ------------------------------ + const token = req.headers.authorization?.split(' ')[1] + if (!token) { + return new sendError('ไม่พบการยืนยันตัวตน' ,'Missing token in request header') + } + + let decoded + try { + decoded = jwt.verify(token, process.env.JWT_SECRET) + } catch (err) { + return new sendError('Invalid token: ' + err.message) + } + + schema = decoded.organization + } + + if (!schema) return new sendError("Token missing 'organization' field or Schema undefined") + + // ------------------------------ + // ✔ 4) ส่งงานไปยัง interface ของ table นั้น ๆ + // ------------------------------ + return await handler.saveInterface(schema, data) + } + +} \ No newline at end of file diff --git a/exthernal-login-api/src/interfaces/table/actmstInterface.js b/exthernal-login-api/src/interfaces/table/actmstInterface.js new file mode 100644 index 0000000..d3ed1bf --- /dev/null +++ b/exthernal-login-api/src/interfaces/table/actmstInterface.js @@ -0,0 +1,86 @@ +import { GeneralService } from '../../share/generalservice.js' + +export class ActmstInterface { + + constructor() { + this.general = new GeneralService() + this.table = 'actmst' + this.pk = ['actseq', 'actnum'] // ⭐ PK ตาม CREATE TABLE + } + + async saveInterface(database, data) { + const method = data.methods.toLowerCase() + const { methods, ...payload } = data + + if (method === 'put') return this.update(database, payload) + if (method === 'post') return this.insert(database, payload) + if (method === 'delete') return this.remove(database, payload) + + throw new Error(`Unknown method: ${method}`) + } + + async insert(database, payload) { + const cols = Object.keys(payload) + const vals = Object.values(payload) + const placeholders = cols.map((_, i) => `$${i + 1}`).join(', ') + + const sql = ` + INSERT INTO ${database}.${this.table} (${cols.join(', ')}) + VALUES (${placeholders}) + RETURNING * + ` + return await this.general.executeQueryParam(database, sql, vals) + } + + async update(database, payload) { + const where = {} + const update = {} + + for (const col in payload) { + if (this.pk.includes(col)) where[col] = payload[col] + else update[col] = payload[col] + } + + const setCols = Object.keys(update) + .map((col, i) => `${col} = $${i + 1}`) + .join(', ') + + const whereCols = Object.keys(where) + .map((col, i) => `${col} = $${i + 1 + Object.keys(update).length}`) + .join(' AND ') + + const params = [...Object.values(update), ...Object.values(where)] + + const sql = ` + UPDATE ${database}.${this.table} + SET ${setCols} + WHERE ${whereCols} + RETURNING * + ` + + return await this.general.executeQueryParam(database, sql, params) + } + + async remove(database, payload) { + const where = {} + + this.pk.forEach(pk => { + if (!payload[pk]) throw new Error(`Missing PK: ${pk}`) + where[pk] = payload[pk] + }) + + const whereCols = Object.keys(where) + .map((col, i) => `${col} = $${i + 1}`) + .join(' AND ') + + const params = Object.values(where) + + const sql = ` + DELETE FROM ${database}.${this.table} + WHERE ${whereCols} + RETURNING * + ` + + return await this.general.executeQueryParam(database, sql, params) + } +} diff --git a/exthernal-login-api/src/interfaces/table/bdgmstInterface.js b/exthernal-login-api/src/interfaces/table/bdgmstInterface.js new file mode 100644 index 0000000..c1abb87 --- /dev/null +++ b/exthernal-login-api/src/interfaces/table/bdgmstInterface.js @@ -0,0 +1,86 @@ +import { GeneralService } from '../../share/generalservice.js' + +export class BdgmstInterface { + + constructor() { + this.general = new GeneralService() + this.table = 'bdgmst' + this.pk = ['bdgseq'] + } + + async saveInterface(database, data) { + const method = data.method.toLowerCase() + const payload = { ...data } + delete payload.method + + if (method === 'put') return this.update(database, payload) + if (method === 'post') return this.insert(database, payload) + if (method === 'delete') return this.remove(database, payload) + + throw new Error(`Unknown method: ${method}`) + } + + async insert(database, payload) { + const cols = Object.keys(payload) + const vals = Object.values(payload) + const placeholders = cols.map((_, i) => `$${i + 1}`).join(', ') + + const sql = ` + INSERT INTO ${database}.${this.table} (${cols.join(', ')}) + VALUES (${placeholders}) + RETURNING * + ` + return await this.general.executeQueryParam(database, sql, vals) + } + + async update(database, payload) { + const where = {} + const update = {} + + for (const col in payload) { + if (this.pk.includes(col)) where[col] = payload[col] + else update[col] = payload[col] + } + + const setCols = Object.keys(update) + .map((col, i) => `${col} = $${i + 1}`) + .join(', ') + + const whereCols = Object.keys(where) + .map((col, i) => `${col} = $${i + 1 + Object.keys(update).length}`) + .join(' AND ') + + const params = [...Object.values(update), ...Object.values(where)] + + const sql = ` + UPDATE ${database}.${this.table} + SET ${setCols} + WHERE ${whereCols} + RETURNING * + ` + + return await this.general.executeQueryParam(database, sql, params) + } + + async remove(database, payload) { + const where = {} + this.pk.forEach(pk => { + if (!payload[pk]) throw new Error(`Missing PK: ${pk}`) + where[pk] = payload[pk] + }) + + const whereCols = Object.keys(where) + .map((col, i) => `${col} = $${i + 1}`) + .join(' AND ') + + const params = Object.values(where) + + const sql = ` + DELETE FROM ${database}.${this.table} + WHERE ${whereCols} + RETURNING * + ` + + return await this.general.executeQueryParam(database, sql, params) + } +} diff --git a/exthernal-login-api/src/interfaces/table/usrmstInterface.js b/exthernal-login-api/src/interfaces/table/usrmstInterface.js new file mode 100644 index 0000000..7f628ca --- /dev/null +++ b/exthernal-login-api/src/interfaces/table/usrmstInterface.js @@ -0,0 +1,86 @@ +import { GeneralService } from '../../share/generalservice.js' + +export class UsrmstInterface { + + constructor() { + this.general = new GeneralService() + this.table = 'usrmst' + this.pk = ['usrseq'] // ⭐ PK ตาม CREATE TABLE + } + + async saveInterface(database, data) { + const method = data.methods.toLowerCase() + const { methods, ...payload } = data + + if (method === 'put') return this.update(database, payload) + if (method === 'post') return this.insert(database, payload) + if (method === 'delete') return this.remove(database, payload) + + throw new Error(`Unknown method: ${method}`) + } + + async insert(database, payload) { + const cols = Object.keys(payload) + const vals = Object.values(payload) + const placeholders = cols.map((_, i) => `$${i + 1}`).join(', ') + + const sql = ` + INSERT INTO ${database}.${this.table} (${cols.join(', ')}) + VALUES (${placeholders}) + RETURNING * + ` + return await this.general.executeQueryParam(database, sql, vals) + } + + async update(database, payload) { + const where = {} + const update = {} + + for (const col in payload) { + if (this.pk.includes(col)) where[col] = payload[col] + else update[col] = payload[col] + } + + const setCols = Object.keys(update) + .map((col, i) => `${col} = $${i + 1}`) + .join(', ') + + const whereCols = Object.keys(where) + .map((col, i) => `${col} = $${i + 1 + Object.keys(update).length}`) + .join(' AND ') + + const params = [...Object.values(update), ...Object.values(where)] + + const sql = ` + UPDATE ${database}.${this.table} + SET ${setCols} + WHERE ${whereCols} + RETURNING * + ` + + return await this.general.executeQueryParam(database, sql, params) + } + + async remove(database, payload) { + const where = {} + + this.pk.forEach(pk => { + if (!payload[pk]) throw new Error(`Missing PK: ${pk}`) + where[pk] = payload[pk] + }) + + const whereCols = Object.keys(where) + .map((col, i) => `${col} = $${i + 1}`) + .join(' AND ') + + const params = Object.values(where) + + const sql = ` + DELETE FROM ${database}.${this.table} + WHERE ${whereCols} + RETURNING * + ` + + return await this.general.executeQueryParam(database, sql, params) + } +} \ No newline at end of file diff --git a/exthernal-login-api/src/routes/route.js b/exthernal-login-api/src/routes/route.js index b2c5dd8..2c34015 100644 --- a/exthernal-login-api/src/routes/route.js +++ b/exthernal-login-api/src/routes/route.js @@ -18,18 +18,18 @@ const controller_login_post = new loginController() const otpController = new OtpController() const otpVerifyController = new OtpVerifyController() -router.post('/login/login', async (req, res) => {const result = await controller_login_post.onNavigate(req, res); return res.json(result)}) +router.post('/login', async (req, res) => {const result = await controller_login_post.onNavigate(req, res); return res.json(result)}) -router.post('/login/otp/send', async (req, res) => { +router.post('/otp/send', async (req, res) => { const result = await otpController.onSendOtp(req, res) if (result) return res.json(result) }) -router.post('/login/register', async (req, res) => { +router.post('/register', async (req, res) => { const result = await registerController.onNavigate(req, res); if (result) return res.json(result); }); -router.get('/login/verify-email', async (req, res) => { +router.get('/verify-email', async (req, res) => { const result = await verifyEmailController.onVerifyEmail(req, res); if (result?.code && result.code !== '200') return res.status(400).send(result.message_th); // ถ้า controller ส่ง HTML string กลับมา → render ตรง ๆ @@ -38,13 +38,13 @@ router.get('/login/verify-email', async (req, res) => { }); -router.post('/login/reset-password', async (req, res) => { +router.post('/reset-password', async (req, res) => { const result = await resetPasswordController.onNavigate(req, res); if (result) return res.json(result); }); -router.post('/login/otp/verify', async (req, res) => { +router.post('/otp/verify', async (req, res) => { const result = await otpVerifyController.onNavigate(req, res) if (result) return res.json(result) }) diff --git a/exthernal-login-api/src/services/loginservice.js b/exthernal-login-api/src/services/loginservice.js index 9c925e1..25b29f2 100644 --- a/exthernal-login-api/src/services/loginservice.js +++ b/exthernal-login-api/src/services/loginservice.js @@ -6,7 +6,7 @@ export class LoginService { constructor() { this.generalService = new GeneralService() } - async verifyLogin(database, username, password) { + async verifyLogin(database, username) { this.generalService.devhint(2, 'loginservice.js', `verifyLogin() start for username=${username}`) let user = null @@ -15,7 +15,7 @@ export class LoginService { let sql = ` SELECT usrseq, usrnam, usrorg, usrrol, usrpwd, usrthinam, usrthilstnam FROM nuttakit.usrmst - WHERE usrnam = $1 + WHERE usrnam = $1 OR (usreml = $1) ` let params = [username] const rows = await this.generalService.executeQueryParam(database, sql, params) diff --git a/exthernal-login-api/src/services/registerservice OLD.js b/exthernal-login-api/src/services/registerservice OLD.js new file mode 100644 index 0000000..70e838e --- /dev/null +++ b/exthernal-login-api/src/services/registerservice OLD.js @@ -0,0 +1,88 @@ +import bcrypt from 'bcrypt'; +import crypto from 'crypto'; +import nodemailer from 'nodemailer'; +import { GeneralService } from '../share/generalservice.js'; +import { sendError } from '../utils/response.js'; +import redis from '../utils/redis.js'; + +export class RegisterService { + + constructor() { + // this.redis = new Redis(); + this.generalService = new GeneralService(); + } + + async requestRegistration(database, email, fname, lname, password) { + let result = []; + try { + let sql = ` + SELECT usrseq FROM ${database}.usrmst WHERE usrnam = $1 + `; + let param = [email]; + const userCheck = await this.generalService.executeQueryParam(database, sql, param); + + if (userCheck.length > 0) { + this.generalService.devhint(1, 'registerservice.js', `❌ Duplicate email (${email})`); + throw sendError('อีเมลนี้ถูกใช้แล้ว', 'Email already registered'); + } + + const hashedPwd = await bcrypt.hash(password, 10); + const token = crypto.randomBytes(32).toString('hex'); + + + const payload = JSON.stringify({ fname, lname, hashedPwd, token, database }); + await redis.set(`verify:${email}`, payload, 'EX', 86400); // 24h + + + const verifyUrl = `http://localhost:1012/login/verify-email?token=${token}&email=${encodeURIComponent(email)}&organization=${database}`; + await this.sendVerifyEmail(email, verifyUrl); + + this.generalService.devhint(2, 'registerservice.js', `✅ Verify link sent to ${email}`); + + result = { + code: '200', + message_th: 'ส่งลิงก์ยืนยันอีเมลแล้ว', + data: {} + }; + } catch (error) { + this.generalService.devhint(1, 'registerservice.js', '❌ Registration Error', error.message); + throw error; + } + return result; + } + + async sendVerifyEmail(email, verifyUrl) { + try { + const transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASS, + }, + }); + + const html = ` +
+

ยืนยันการสมัครสมาชิก

+

กรุณากดยืนยันภายใน 24 ชั่วโมง เพื่อเปิดใช้งานบัญชีของคุณ

+ ยืนยันอีเมล +

หากคุณไม่ได้สมัคร โปรดละเว้นอีเมลนี้

+
+ `; + + await transporter.sendMail({ + from: `"System" <${process.env.SMTP_USER}>`, + to: email, + subject: '📩 ยืนยันอีเมลสำหรับสมัครสมาชิก', + html, + }); + + this.generalService.devhint(2, 'registerservice.js', `📤 Verification email sent (${email})`); + } catch (error) { + this.generalService.devhint(1, 'registerservice.js', '❌ Email Send Failed', error.message); + throw sendError('ไม่สามารถส่งอีเมลได้', 'Email send failed'); + } + } +} diff --git a/exthernal-login-api/src/services/registerservice.js b/exthernal-login-api/src/services/registerservice.js index 70e838e..db27d5a 100644 --- a/exthernal-login-api/src/services/registerservice.js +++ b/exthernal-login-api/src/services/registerservice.js @@ -1,88 +1,57 @@ -import bcrypt from 'bcrypt'; -import crypto from 'crypto'; -import nodemailer from 'nodemailer'; -import { GeneralService } from '../share/generalservice.js'; -import { sendError } from '../utils/response.js'; -import redis from '../utils/redis.js'; +import { GeneralService } from '../share/generalservice.js' +import bcrypt from 'bcrypt' export class RegisterService { - constructor() { - // this.redis = new Redis(); - this.generalService = new GeneralService(); + this.generalService = new GeneralService() } - async requestRegistration(database, email, fname, lname, password) { - let result = []; + async createUser(database, userData) { + + // 1. ทำการ Hash Password + const saltRounds = 10; + const hashedPassword = await bcrypt.hash(userData.password, saltRounds); + + // 2. เตรียม SQL + const sql = ` + INSERT INTO ${database}.usrmst + (username, password, email, firstname, lastname, created_at) + VALUES (?, ?, ?, ?, ?, NOW()) + ` + + // 3. ใช้ hashedPassword แทน password เดิม + const params = [ + userData.username, + hashedPassword, // ส่งตัวที่ Hash แล้วเข้า DB + userData.email, + userData.firstname, + userData.lastname + ] + try { - let sql = ` - SELECT usrseq FROM ${database}.usrmst WHERE usrnam = $1 - `; - let param = [email]; - const userCheck = await this.generalService.executeQueryParam(database, sql, param); - - if (userCheck.length > 0) { - this.generalService.devhint(1, 'registerservice.js', `❌ Duplicate email (${email})`); - throw sendError('อีเมลนี้ถูกใช้แล้ว', 'Email already registered'); - } - - const hashedPwd = await bcrypt.hash(password, 10); - const token = crypto.randomBytes(32).toString('hex'); - - - const payload = JSON.stringify({ fname, lname, hashedPwd, token, database }); - await redis.set(`verify:${email}`, payload, 'EX', 86400); // 24h - - - const verifyUrl = `http://localhost:1012/login/verify-email?token=${token}&email=${encodeURIComponent(email)}&organization=${database}`; - await this.sendVerifyEmail(email, verifyUrl); - - this.generalService.devhint(2, 'registerservice.js', `✅ Verify link sent to ${email}`); - - result = { - code: '200', - message_th: 'ส่งลิงก์ยืนยันอีเมลแล้ว', - data: {} - }; + const result = await this.generalService.executeQueryParam(database, sql, params); + + // เช็คผลลัพธ์ตาม Structure ของ GeneralService + // สมมติว่าถ้า Error ตัว executeQueryParam อาจจะ throw หรือ return null + return { status: true, message: 'Registration successful' }; } catch (error) { - this.generalService.devhint(1, 'registerservice.js', '❌ Registration Error', error.message); - throw error; - } - return result; - } - - async sendVerifyEmail(email, verifyUrl) { - try { - const transporter = nodemailer.createTransport({ - service: 'gmail', - auth: { - user: process.env.SMTP_USER, - pass: process.env.SMTP_PASS, - }, - }); - - const html = ` -
-

ยืนยันการสมัครสมาชิก

-

กรุณากดยืนยันภายใน 24 ชั่วโมง เพื่อเปิดใช้งานบัญชีของคุณ

- ยืนยันอีเมล -

หากคุณไม่ได้สมัคร โปรดละเว้นอีเมลนี้

-
- `; - - await transporter.sendMail({ - from: `"System" <${process.env.SMTP_USER}>`, - to: email, - subject: '📩 ยืนยันอีเมลสำหรับสมัครสมาชิก', - html, - }); - - this.generalService.devhint(2, 'registerservice.js', `📤 Verification email sent (${email})`); - } catch (error) { - this.generalService.devhint(1, 'registerservice.js', '❌ Email Send Failed', error.message); - throw sendError('ไม่สามารถส่งอีเมลได้', 'Email send failed'); + console.error('Register Service Error:', error); + return null; } } -} + + + async genNum(database) { + const sql = ` + SELECT + MAX(usrseq) as max_seq + FROM nuttakit.usrmst + ` + const params = [] + const aryResult = await this.generalService.executeQueryParam(database, sql, params); + + const lastSeq = aryResult[0]?.max_seq || 0; + + return lastSeq + 1; + } +} \ No newline at end of file diff --git a/exthernal-login-api/src/utils/response.js b/exthernal-login-api/src/utils/response.js index ed277ac..0b662c3 100644 --- a/exthernal-login-api/src/utils/response.js +++ b/exthernal-login-api/src/utils/response.js @@ -1,19 +1,13 @@ -// =================================================== -// ⚙️ Nuttakit Response Layer vFinal++++++ -// =================================================== -export function sendError(thMsg = 'เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', enMsg = 'Unexpected error', code = 400) { +export function sendError(thMsg = 'เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', enMsg = 'Unexpected error', code = 400, data = []) { return { code: String(code), message: enMsg, message_th: thMsg, - data: [] + data: data } } -// =================================================== -// 🔹 Auto Success Response (ใช้โดย Global Handler เท่านั้น) -// =================================================== export function formatSuccessResponse(data) { return { code: "200", @@ -21,4 +15,4 @@ export function formatSuccessResponse(data) { message_th: "ดำเนินการสำเร็จ", data: data || null } -} +} \ No newline at end of file diff --git a/exthernal-login-api/src/utils/validate.js b/exthernal-login-api/src/utils/validate.js new file mode 100644 index 0000000..22839a9 --- /dev/null +++ b/exthernal-login-api/src/utils/validate.js @@ -0,0 +1,25 @@ +import { sendError } from './response.js'; + +export const validateSave = (value, columnName) => { + // เช็คว่าค่าเป็น null, undefined หรือ empty string + if (value === undefined || value === null || value === '') { + + // สร้างก้อน data ที่จะบอกว่า column ไหนหายไป + // ตามโจทย์: data: { "email": "ไม่พบข้อมูล" } + const errorDetail = {}; + errorDetail[columnName] = "ไม่พบข้อมูล"; + + // เรียก sendError ใส่ message และ errorDetail ลงไปใน parameter ตัวที่ 4 + sendError( + 'ข้อมูลพารามิเตอร์ ไม่ถูกต้อง', // thMsg + 'Invalid Parameter', // enMsg + 400, // code + errorDetail // data + ); + + // ปาลูกระเบิดออกไปให้ Controller รับ + // throw errorObj; + } + + return value; +} \ No newline at end of file