From 88ce628204b1f6adae07a41cf91f769cfa91e19b Mon Sep 17 00:00:00 2001 From: mhmmdafif Date: Mon, 16 Mar 2026 11:09:36 +0700 Subject: [PATCH 1/6] add: api restart wa --- controllers/url_token.controller.js | 23 ++++++++++ db/url_token.db.js | 29 ++++++++++++ package.json | 1 + routes/index.js | 4 +- routes/notifikasi-wa.route.js | 14 ++++++ services/notifikasi-wa.service.js | 68 ++++++++++++++++++++++++----- 6 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 controllers/url_token.controller.js create mode 100644 db/url_token.db.js create mode 100644 routes/notifikasi-wa.route.js diff --git a/controllers/url_token.controller.js b/controllers/url_token.controller.js new file mode 100644 index 0000000..30a1b4a --- /dev/null +++ b/controllers/url_token.controller.js @@ -0,0 +1,23 @@ +const { getTokenByUidDb } = require("../db/url_token.db"); + +class urlTokenController { + static async getUrlToken(req, res) { + try { + const { uid } = req.params; + + const data = await getTokenByUidDb(uid); + + if (!data) { + return res.status(404).send("Link tidak valid."); + } + + const targetUrl = `${process.env.BASE_URL_FRONTEND}/auth/redirect?token=${data.token}`; + + return res.redirect(targetUrl); + } catch (err) { + return err + } + } +} + +module.exports = urlTokenController; \ No newline at end of file diff --git a/db/url_token.db.js b/db/url_token.db.js new file mode 100644 index 0000000..b907410 --- /dev/null +++ b/db/url_token.db.js @@ -0,0 +1,29 @@ +const pool = require("../config"); + +const insertUrlTokenDb = async (data) => { + try { + const queryText = ` + INSERT INTO url_token (url_token_id, url_token) + VALUES ($1, $2) + `; + + const queryParams = [data.url_token_id, data.url_token]; + + const result = await pool.query(queryText, queryParams); + + return result; + } catch (err) { + return err; + } +}; + +const getTokenByUidDb = async (url_token_id) => { + const queryText = `SELECT * FROM url_token WHERE url_token_id = $1 AND deleted_at IS NULL`; + const result = await pool.query(queryText, [url_token_id]); + return result.recordset[0]; +}; + +module.exports = { + getTokenByUidDb, + insertUrlTokenDb, // Export fungsi baru +}; \ No newline at end of file diff --git a/package.json b/package.json index ee2d46c..3bc2bdb 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "nodemailer": "^6.8.0", "pg": "^8.8.0", "pino": "^6.11.3", + "pm2": "^6.0.14", "stripe": "^8.138.0", "svg-captcha": "^1.4.0", "swagger-ui-express": "^4.6.0", diff --git a/routes/index.js b/routes/index.js index fabe125..c6c6732 100644 --- a/routes/index.js +++ b/routes/index.js @@ -19,7 +19,8 @@ const notificationErrorSparepart = require("./notification_error_sparepart.route const sparepart = require("./sparepart.route") const notificationErrorLog = require("./notification_error_log.route") const notificationErrorUser = require("./notification_error_user.route") -const errorCode = require("./error_code.route") +const errorCode = require("./error_code.route"); +const notifikasiWA = require("./notifikasi-wa.route"); router.use("/auth", auth); router.use("/user", users); @@ -42,5 +43,6 @@ router.use("/sparepart", sparepart) router.use("/notification-log", notificationErrorLog) router.use("/notification-user", notificationErrorUser) router.use("/error-code", errorCode) +router.use("/notifikasi-wa", notifikasiWA) module.exports = router; diff --git a/routes/notifikasi-wa.route.js b/routes/notifikasi-wa.route.js new file mode 100644 index 0000000..8fd7b3a --- /dev/null +++ b/routes/notifikasi-wa.route.js @@ -0,0 +1,14 @@ +const express = require('express'); +const router = express.Router(); +const NotifikasiWaService = require('../services/notifikasi-wa.service'); + +router.post('/restart-wa', async (req, res) => { + try { + const result = await NotifikasiWaService.restartWhatsapp(); + return res.status(200).json(result); + } catch (error) { + return res.status(500).json(error); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/services/notifikasi-wa.service.js b/services/notifikasi-wa.service.js index c6461c2..81d38ca 100644 --- a/services/notifikasi-wa.service.js +++ b/services/notifikasi-wa.service.js @@ -1,5 +1,8 @@ const { getAllContactDb } = require("../db/contact.db"); -const { InsertNotificationErrorDb, updateNotificationErrorDb } = require("../db/notification_error.db"); +const { + InsertNotificationErrorDb, + updateNotificationErrorDb, +} = require("../db/notification_error.db"); const { createNotificationErrorUserDb, updateNotificationErrorUserDb, @@ -11,6 +14,9 @@ const { } = require("../db/notification_wa.db"); const { getErrorCodeByBrandAndCodeDb } = require("../db/brand_code.db"); const { getDeviceNotificationByIdDb } = require("../db/notification_error.db"); +const { exec } = require("child_process"); +const fs = require("fs"); +const path = require("path"); class NotifikasiWaService { async onNotification(topic, message) { @@ -52,7 +58,10 @@ class NotifikasiWaService { Number(chanel.chanel_id) ); - const errorCode = await getErrorCodeByBrandAndCodeDb(deviceNotification?.brand_id ?? 0, chanel.value); + const errorCode = await getErrorCodeByBrandAndCodeDb( + deviceNotification?.brand_id ?? 0, + chanel.value + ); const data = { error_code_id: chanel.value, @@ -65,7 +74,7 @@ class NotifikasiWaService { const resultNotificationError = await InsertNotificationErrorDb(data); - let isSendNotification = false + let isSendNotification = false; for (const dataUser of dataUsers) { if (dataUser.is_active) { @@ -81,7 +90,9 @@ class NotifikasiWaService { const bodyMessage = `Hai ${dataUser.contact_name || "-"},\n` + - `Terjadi peringatan dengan kode ${chanel?.value ?? "-"} "${errorCode?.error_code_name ?? ""}", Chanel ${chanel?.chanel_id ?? "-"} ` + + `Terjadi peringatan dengan kode ${chanel?.value ?? "-"} "${ + errorCode?.error_code_name ?? "" + }", Chanel ${chanel?.chanel_id ?? "-"} ` + `pada device ${deviceNotification?.device_name ?? "berikut"},` + `\nSilahkan cek detail pada link :` + `${shortUrl}`; @@ -107,8 +118,6 @@ class NotifikasiWaService { param.bodyMessage ); - - await updateNotificationErrorUserDb( resultNotificationErrorUser[0].notification_error_user_id, { @@ -117,7 +126,7 @@ class NotifikasiWaService { ); if (resultSend.success) { - isSendNotification = resultSend.success + isSendNotification = resultSend.success; } } } @@ -131,11 +140,48 @@ class NotifikasiWaService { ); } } - } catch (error) { - // throw new ErrorHandler(error.statusCode, error.message); - return error; + } catch (err) { + return err; } } + + async restartWhatsapp() { + return new Promise((resolve, reject) => { + exec('pm2 jlist', (err, stdout) => { + if (err) return reject({ success: false, message: "Error list PM2" }); + + try { + const processes = JSON.parse(stdout); + const waProcess = processes.find(p => + p.name.toLowerCase().includes('whatsapp') || + p.name.toLowerCase().includes('wa-api') + ); + + if (!waProcess) return reject({ success: false, message: "PM2 List PM2 Not Found" }); + + const processId = waProcess.pm_id; + + exec(`pm2 stop ${processId}`, () => { + const paths = [ + path.join(__dirname, "../../.wwebjs_auth"), + path.join(__dirname, "../../.wwebjs_cache") + ]; + + paths.forEach(dir => { + if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true }); + }); + + exec(`pm2 restart ${processId}`, (reErr) => { + if (reErr) return reject({ success: false, message: "Gagal restart" }); + resolve({ success: true, message: `WA has been restart.` }); + }); + }); + } catch (e) { + reject({ success: false, message: "JSON Parse Error: " + e.message }); + } + }); + }); + } } -module.exports = new NotifikasiWaService(); +module.exports = new NotifikasiWaService(); \ No newline at end of file From a20d36c01242bbf0393ef1a083985b3eeaca5cd2 Mon Sep 17 00:00:00 2001 From: mhmmdafif Date: Mon, 16 Mar 2026 11:11:29 +0700 Subject: [PATCH 2/6] add: api restart wa --- controllers/url_token.controller.js | 23 ----------------------- db/url_token.db.js | 29 ----------------------------- 2 files changed, 52 deletions(-) delete mode 100644 controllers/url_token.controller.js delete mode 100644 db/url_token.db.js diff --git a/controllers/url_token.controller.js b/controllers/url_token.controller.js deleted file mode 100644 index 30a1b4a..0000000 --- a/controllers/url_token.controller.js +++ /dev/null @@ -1,23 +0,0 @@ -const { getTokenByUidDb } = require("../db/url_token.db"); - -class urlTokenController { - static async getUrlToken(req, res) { - try { - const { uid } = req.params; - - const data = await getTokenByUidDb(uid); - - if (!data) { - return res.status(404).send("Link tidak valid."); - } - - const targetUrl = `${process.env.BASE_URL_FRONTEND}/auth/redirect?token=${data.token}`; - - return res.redirect(targetUrl); - } catch (err) { - return err - } - } -} - -module.exports = urlTokenController; \ No newline at end of file diff --git a/db/url_token.db.js b/db/url_token.db.js deleted file mode 100644 index b907410..0000000 --- a/db/url_token.db.js +++ /dev/null @@ -1,29 +0,0 @@ -const pool = require("../config"); - -const insertUrlTokenDb = async (data) => { - try { - const queryText = ` - INSERT INTO url_token (url_token_id, url_token) - VALUES ($1, $2) - `; - - const queryParams = [data.url_token_id, data.url_token]; - - const result = await pool.query(queryText, queryParams); - - return result; - } catch (err) { - return err; - } -}; - -const getTokenByUidDb = async (url_token_id) => { - const queryText = `SELECT * FROM url_token WHERE url_token_id = $1 AND deleted_at IS NULL`; - const result = await pool.query(queryText, [url_token_id]); - return result.recordset[0]; -}; - -module.exports = { - getTokenByUidDb, - insertUrlTokenDb, // Export fungsi baru -}; \ No newline at end of file From d53f0cba33e78e2e99cfb3562d9b8d51de314137 Mon Sep 17 00:00:00 2001 From: mhmmdafif Date: Mon, 16 Mar 2026 14:22:19 +0700 Subject: [PATCH 3/6] repair restart api: replace name in pm2 list --- services/notifikasi-wa.service.js | 60 ++++++++++++++++--------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/services/notifikasi-wa.service.js b/services/notifikasi-wa.service.js index 81d38ca..e976f19 100644 --- a/services/notifikasi-wa.service.js +++ b/services/notifikasi-wa.service.js @@ -14,7 +14,9 @@ const { } = require("../db/notification_wa.db"); const { getErrorCodeByBrandAndCodeDb } = require("../db/brand_code.db"); const { getDeviceNotificationByIdDb } = require("../db/notification_error.db"); -const { exec } = require("child_process"); + +const util = require("util"); +const exec = util.promisify(require("child_process").exec); const fs = require("fs"); const path = require("path"); @@ -146,42 +148,42 @@ class NotifikasiWaService { } async restartWhatsapp() { - return new Promise((resolve, reject) => { - exec('pm2 jlist', (err, stdout) => { - if (err) return reject({ success: false, message: "Error list PM2" }); + try { + const processName = "Whatsapp-API-notification"; - try { - const processes = JSON.parse(stdout); - const waProcess = processes.find(p => - p.name.toLowerCase().includes('whatsapp') || - p.name.toLowerCase().includes('wa-api') - ); + const { stdout } = await exec("pm2 jlist"); - if (!waProcess) return reject({ success: false, message: "PM2 List PM2 Not Found" }); + const processes = JSON.parse(stdout); + const waProcess = processes.find((p) => p.name === processName); - const processId = waProcess.pm_id; + if (!waProcess) { + throw new Error("PM2 Not Found"); + } - exec(`pm2 stop ${processId}`, () => { - const paths = [ - path.join(__dirname, "../../.wwebjs_auth"), - path.join(__dirname, "../../.wwebjs_cache") - ]; + const processId = waProcess.pm_id; - paths.forEach(dir => { - if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true }); - }); + await exec(`pm2 stop ${processId}`); - exec(`pm2 restart ${processId}`, (reErr) => { - if (reErr) return reject({ success: false, message: "Gagal restart" }); - resolve({ success: true, message: `WA has been restart.` }); - }); - }); - } catch (e) { - reject({ success: false, message: "JSON Parse Error: " + e.message }); + const pathsToDelete = [ + path.join(__dirname, ".wwebjs_auth"), + path.join(__dirname, ".wwebjs_cache"), + ]; + + pathsToDelete.forEach((dir) => { + if (fs.existsSync(dir)) { + fs.rmSync(dir, { recursive: true, force: true }); } }); - }); + + await exec(`pm2 restart ${processId}`); + return { + success: true, + message: `WhatsApp has been restart.`, + }; + } catch (err) { + return err; + } } } -module.exports = new NotifikasiWaService(); \ No newline at end of file +module.exports = new NotifikasiWaService(); From f4b400fe021344ce07830158ce0f960a1e4d6a30 Mon Sep 17 00:00:00 2001 From: mhmmdafif Date: Wed, 18 Mar 2026 17:25:40 +0700 Subject: [PATCH 4/6] repair: api restart wa --- package.json | 1 + services/notifikasi-wa.service.js | 44 ++++++++++++++++++------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 3bc2bdb..6e1f9ae 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "cookie-parser": "^1.4.6", "cors": "^2.8.5", "crypto": "^1.0.1", + "crypto-js": "^4.2.0", "dotenv": "^8.2.0", "exceljs": "^4.4.0", diff --git a/services/notifikasi-wa.service.js b/services/notifikasi-wa.service.js index e976f19..a002607 100644 --- a/services/notifikasi-wa.service.js +++ b/services/notifikasi-wa.service.js @@ -15,8 +15,9 @@ const { const { getErrorCodeByBrandAndCodeDb } = require("../db/brand_code.db"); const { getDeviceNotificationByIdDb } = require("../db/notification_error.db"); +const { exec } = require("child_process"); const util = require("util"); -const exec = util.promisify(require("child_process").exec); +const execPromise = util.promisify(exec); const fs = require("fs"); const path = require("path"); @@ -150,36 +151,43 @@ class NotifikasiWaService { async restartWhatsapp() { try { const processName = "Whatsapp-API-notification"; - - const { stdout } = await exec("pm2 jlist"); - + const { stdout } = await execPromise("pm2 jlist"); const processes = JSON.parse(stdout); const waProcess = processes.find((p) => p.name === processName); - if (!waProcess) { - throw new Error("PM2 Not Found"); - } + if (!waProcess) throw new Error(`PM2 ${processName} not found`); - const processId = waProcess.pm_id; + const waProcessId = waProcess.pm_id; + const pathDelete = waProcess.pm2_env.pm_cwd; - await exec(`pm2 stop ${processId}`); + // const execLogs = (cmd) => { + // const p = exec(cmd); + // p.stdout.pipe(process.stdout); + // p.stderr.pipe(process.stderr); + // return new Promise((res) => p.on("exit", res)); + // }; - const pathsToDelete = [ - path.join(__dirname, ".wwebjs_auth"), - path.join(__dirname, ".wwebjs_cache"), + // console.log(`stop proses id: ${waProcessId}`) + await execPromise(`start powershell -Command "pm2 stop ${waProcessId}"`); + + const pathFolderDelete = [ + path.join(pathDelete, ".wwebjs_auth"), + path.join(pathDelete, ".wwebjs_cache"), ]; - pathsToDelete.forEach((dir) => { + // console.log(`path: ${pathDelete}`); + + pathFolderDelete.forEach((dir) => { if (fs.existsSync(dir)) { + // console.log(`path folder: ${dir}`); fs.rmSync(dir, { recursive: true, force: true }); } }); - await exec(`pm2 restart ${processId}`); - return { - success: true, - message: `WhatsApp has been restart.`, - }; + // console.log(`start proses id: ${waProcessId}`); + await execPromise(`start powershell -Command "pm2 restart ${waProcessId}"`); + + return { success: true, message: "WhatsApp has been restarted." }; } catch (err) { return err; } From 76ac604ba8bc7260590273941e48d3c17c968df8 Mon Sep 17 00:00:00 2001 From: mhmmdafif Date: Wed, 18 Mar 2026 18:36:29 +0700 Subject: [PATCH 5/6] restart change to start in api restart wa --- package.json | 1 - services/notifikasi-wa.service.js | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6e1f9ae..3bc2bdb 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "cookie-parser": "^1.4.6", "cors": "^2.8.5", "crypto": "^1.0.1", - "crypto-js": "^4.2.0", "dotenv": "^8.2.0", "exceljs": "^4.4.0", diff --git a/services/notifikasi-wa.service.js b/services/notifikasi-wa.service.js index a002607..eb3608d 100644 --- a/services/notifikasi-wa.service.js +++ b/services/notifikasi-wa.service.js @@ -110,6 +110,7 @@ class NotifikasiWaService { const resultNotificationErrorUser = await createNotificationErrorUserDb({ notification_error_id: param.idData, + contact_phone: param.userPhone, contact_name: param.userName, message_error_issue: param.bodyMessage, @@ -185,7 +186,7 @@ class NotifikasiWaService { }); // console.log(`start proses id: ${waProcessId}`); - await execPromise(`start powershell -Command "pm2 restart ${waProcessId}"`); + await execPromise(`start powershell -Command "pm2 start ${waProcessId}"`); return { success: true, message: "WhatsApp has been restarted." }; } catch (err) { From 0ce6a04f97b9b0415e647d744ad3ac0b753e1758 Mon Sep 17 00:00:00 2001 From: mhmmdafif Date: Wed, 1 Apr 2026 15:09:24 +0700 Subject: [PATCH 6/6] repair: add cod-whatsapp-notif in process pm2 --- services/notifikasi-wa.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/notifikasi-wa.service.js b/services/notifikasi-wa.service.js index eb3608d..3869d38 100644 --- a/services/notifikasi-wa.service.js +++ b/services/notifikasi-wa.service.js @@ -151,7 +151,7 @@ class NotifikasiWaService { async restartWhatsapp() { try { - const processName = "Whatsapp-API-notification"; + const processName = "Whatsapp-API-notification" && "cod-whatsapp-notif"; const { stdout } = await execPromise("pm2 jlist"); const processes = JSON.parse(stdout); const waProcess = processes.find((p) => p.name === processName);