Compare commits
2 Commits
master
...
with-kwita
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51de54877f | ||
|
|
cdc0118852 |
61
README.md
61
README.md
@@ -1,39 +1,62 @@
|
|||||||
Sample config file
|
# WhatsApp Gateway Service
|
||||||
|
A lightweight service for sending WhatsApp messages with PDF attachments through Qontak API. Supports multiple message types and provides automatic retries for failed messages.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Create a `config-gw-wa.json` file in the root directory:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"base_url": "https://cpone.aplikasi.web.id/one-api/",
|
"base_url": "https://cpone.aplikasi.web.id/one-api/",
|
||||||
// Setiap 2 menit dari jam 6 sampai 20
|
|
||||||
"schedule": [
|
"schedule": [
|
||||||
"*/2 6-20 * * *"
|
"*/2 6-20 * * *" // Runs every 2 minutes from 6 AM to 8 PM
|
||||||
],
|
],
|
||||||
"delay": 5,
|
"startDate": "2023-08-01", // Filter T_OrderHeaderDate from this date (YYYY-MM-DD)
|
||||||
"row_per_batch": 50,
|
"endDate": "2025-12-30" // Filter T_OrderHeaderDate until this date (YYYY-MM-DD)
|
||||||
"startDate": "2023-08-01", //yyyy-mm-dd tanggal data dicari dimulai
|
|
||||||
"endDate": "2025-12-30", //yyyy-mm-dd tanggal data dicari berakhir
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Install Dependencies
|
## Supported Message Types
|
||||||
|
* MCU Result: Medical checkup results with PDF attachment
|
||||||
|
* Payment Receipt: Payment receipts with PDF attachment
|
||||||
|
|
||||||
|
## Installation
|
||||||
```bash
|
```bash
|
||||||
|
# Install dependencies
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
# Format code
|
||||||
|
npm run format
|
||||||
```
|
```
|
||||||
|
|
||||||
Build the source
|
## Build & Run
|
||||||
```bash
|
```bash
|
||||||
|
# Build TypeScript source
|
||||||
npm run build
|
npm run build
|
||||||
```
|
|
||||||
|
|
||||||
Compile
|
# Compile to executable
|
||||||
```bash
|
npm run compile
|
||||||
npm run compile
|
|
||||||
```
|
|
||||||
|
|
||||||
Run
|
# Run the application
|
||||||
```bash
|
|
||||||
npm run start
|
npm run start
|
||||||
|
|
||||||
|
# Or build, compile and start in one command
|
||||||
|
npm run bcs
|
||||||
```
|
```
|
||||||
|
|
||||||
Run Script
|
## Run Compiled File
|
||||||
|
```sh
|
||||||
|
# Dijalankan di lokasi yang sama dengan `config-gw-wa.json`
|
||||||
|
node ./dist/gw-wa.js
|
||||||
```
|
```
|
||||||
node .\dist\gw-wa.js
|
|
||||||
```
|
## Development Notes
|
||||||
|
The gateway checks for outbox items with status:
|
||||||
|
|
||||||
|
* N: New messages
|
||||||
|
* E: Error messages (with retry logic, max 5 attempts)
|
||||||
|
* R: Rejected messages
|
||||||
|
|
||||||
|
Each message type uses its own API endpoints for:
|
||||||
|
* listing,
|
||||||
|
* uploading files,
|
||||||
|
* sending messages,
|
||||||
|
* then updating status.
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
{
|
{
|
||||||
"base_url": "https://devcpone.aplikasi.web.id/one-api/",
|
"base_url": "https://cpone.aplikasi.web.id/one-api/",
|
||||||
"schedule": ["*/2 6-20 * * *"],
|
"schedule": ["*/2 6-20 * * *"],
|
||||||
"delay": 5,
|
|
||||||
"row_per_batch": 50,
|
|
||||||
"startDate": "2023-08-01",
|
"startDate": "2023-08-01",
|
||||||
"endDate": "2025-12-30"
|
"endDate": "2025-12-30"
|
||||||
}
|
}
|
||||||
|
|||||||
987
dist/gw-wa.js
vendored
987
dist/gw-wa.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -7,9 +7,9 @@
|
|||||||
"build": "rimraf build/ && prettier --write source/ && tsc",
|
"build": "rimraf build/ && prettier --write source/ && tsc",
|
||||||
"format": "prettier --write \"**/*.{ts,js,json}\"",
|
"format": "prettier --write \"**/*.{ts,js,json}\"",
|
||||||
"format:check": "prettier --check \"**/*.{ts,js,json}\"",
|
"format:check": "prettier --check \"**/*.{ts,js,json}\"",
|
||||||
"compile": "cross-env NODE_OPTIONS=--openssl-legacy-provider ncc build build/server.js -m -o dist && move dist\\index.js dist\\gw-wa.js",
|
"compile": "cross-env NODE_OPTIONS=--openssl-legacy-provider ncc build build/server.js -m -o dist && mv dist/index.js dist/gw-wa.js",
|
||||||
"start": "node ./dist/gw-wa.js",
|
"start": "node ./dist/gw-wa.js",
|
||||||
"bcs": "rimraf build/ && prettier --write source/ && tsc && cross-env NODE_OPTIONS=--openssl-legacy-provider ncc build build/server.js -m -o dist && move dist\\index.js dist\\gw-wa.js && node ./dist/gw-wa.js"
|
"bcs": "rimraf build/ && prettier --write source/ && tsc && cross-env NODE_OPTIONS=--openssl-legacy-provider ncc build build/server.js -m -o dist && mv dist/index.js dist/gw-wa.js && node ./dist/gw-wa.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
@@ -3,19 +3,61 @@ import { readFileSync } from "fs";
|
|||||||
interface IConfig {
|
interface IConfig {
|
||||||
base_url: string;
|
base_url: string;
|
||||||
schedule: string[];
|
schedule: string[];
|
||||||
delay: number;
|
|
||||||
row_per_batch: number;
|
|
||||||
startDate: string;
|
startDate: string;
|
||||||
endDate: string;
|
endDate: string;
|
||||||
|
messageTypes: {
|
||||||
|
mcu: {
|
||||||
|
name: string;
|
||||||
|
endpoints: {
|
||||||
|
listOutbox: string;
|
||||||
|
uploadFile: string;
|
||||||
|
sendMsg: string;
|
||||||
|
changeStatus: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
payment: {
|
||||||
|
name: string;
|
||||||
|
endpoints: {
|
||||||
|
listOutbox: string;
|
||||||
|
uploadFile: string;
|
||||||
|
sendMsg: string;
|
||||||
|
changeStatus: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const j_config = JSON.parse(readFileSync("./config-gw-wa.json").toString());
|
const j_config = JSON.parse(readFileSync("./config-gw-wa.json").toString());
|
||||||
|
|
||||||
const config: IConfig = {
|
const config: IConfig = {
|
||||||
base_url: j_config["base_url"],
|
base_url: j_config["base_url"],
|
||||||
schedule: j_config["schedule"],
|
schedule: j_config["schedule"],
|
||||||
delay: j_config["delay"],
|
startDate: j_config["startDate"], // T_OrderHeaderDate. Format: YYYY-MM-DD
|
||||||
row_per_batch: j_config["row_per_batch"],
|
endDate: j_config["endDate"], // T_OrderHeaderDate. Format: YYYY-MM-DD
|
||||||
startDate: j_config["startDate"],
|
messageTypes: {
|
||||||
endDate: j_config["endDate"],
|
mcu: {
|
||||||
|
name: "MCU Result",
|
||||||
|
endpoints: {
|
||||||
|
listOutbox: "mockup/sendwa/sendwa/listoutbox",
|
||||||
|
uploadFile: "mockup/sendwa/sendwa/uploadfile",
|
||||||
|
sendMsg: "mockup/sendwa/sendwa/QontakSendMsg",
|
||||||
|
changeStatus: "mockup/sendwa/sendwa/changeStatusOutbox",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payment: {
|
||||||
|
name: "Payment Receipt",
|
||||||
|
endpoints: {
|
||||||
|
listOutbox:
|
||||||
|
"mockup/fo/cashiernewpayment-cpone-v2/payment/listOutbox",
|
||||||
|
uploadFile:
|
||||||
|
"mockup/fo/cashiernewpayment-cpone-v2/payment/uploadFile",
|
||||||
|
sendMsg:
|
||||||
|
"mockup/fo/cashiernewpayment-cpone-v2/payment/qontakSendMsg",
|
||||||
|
changeStatus:
|
||||||
|
"mockup/fo/cashiernewpayment-cpone-v2/payment/changeStatusOutbox",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
@@ -3,47 +3,46 @@ import config from "./config/config";
|
|||||||
import logging from "./config/logging";
|
import logging from "./config/logging";
|
||||||
import { NAME_SPACE } from "./server";
|
import { NAME_SPACE } from "./server";
|
||||||
|
|
||||||
|
type MessageType = "mcu" | "payment";
|
||||||
|
|
||||||
export const getListOutbox = async (
|
export const getListOutbox = async (
|
||||||
|
messageType: MessageType,
|
||||||
statusOutbox: string,
|
statusOutbox: string,
|
||||||
startDate: string,
|
startDate: string,
|
||||||
endDate: string
|
endDate: string
|
||||||
) => {
|
) => {
|
||||||
const url = config.base_url + "mockup/sendwa/sendwa/listoutbox";
|
const url =
|
||||||
logging.info(NAME_SPACE, "\t INFO : " + url);
|
config.base_url + config.messageTypes[messageType].endpoints.listOutbox;
|
||||||
|
logging.info(
|
||||||
|
NAME_SPACE,
|
||||||
|
`\t INFO [${config.messageTypes[messageType].name}]: ${url}`
|
||||||
|
);
|
||||||
logging.info(NAME_SPACE, "\t INFO Status Outbox: " + statusOutbox);
|
logging.info(NAME_SPACE, "\t INFO Status Outbox: " + statusOutbox);
|
||||||
logging.info(NAME_SPACE, "\t INFO Start Date: " + startDate);
|
|
||||||
logging.info(NAME_SPACE, "\t INFO End Date: " + endDate);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await axios.post(
|
const resp = await axios.post(
|
||||||
url,
|
url,
|
||||||
{
|
{ statusOutbox, startDate, endDate },
|
||||||
statusOutbox: statusOutbox,
|
|
||||||
startDate: startDate,
|
|
||||||
endDate: endDate,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
||||||
responseType: "text",
|
responseType: "text",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const jresp =
|
return typeof resp.data === "string"
|
||||||
typeof resp.data === "string" ? JSON.parse(resp.data) : resp.data;
|
? JSON.parse(resp.data)
|
||||||
return jresp;
|
: resp.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (axios.isAxiosError(e)) {
|
handleAxiosError(e, messageType);
|
||||||
logging.error(NAME_SPACE, "Error di lib inject catch axios");
|
|
||||||
logging.error(NAME_SPACE, e.response?.data.toString());
|
|
||||||
} else if (e instanceof Error) {
|
|
||||||
logging.error(NAME_SPACE, "Error di instance of error");
|
|
||||||
logging.error(NAME_SPACE, e.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sendToQontak = async (param: any) => {
|
export const sendToQontak = async (messageType: MessageType, param: any) => {
|
||||||
const url = config.base_url + "mockup/sendwa/sendwa/QontakSendMsg";
|
const url =
|
||||||
logging.info(NAME_SPACE, "\t INFO : " + url);
|
config.base_url + config.messageTypes[messageType].endpoints.sendMsg;
|
||||||
|
logging.info(
|
||||||
|
NAME_SPACE,
|
||||||
|
`\t INFO [${config.messageTypes[messageType].name}]: ${url}`
|
||||||
|
);
|
||||||
logging.info(NAME_SPACE, "\t INFO Payload: " + JSON.stringify(param));
|
logging.info(NAME_SPACE, "\t INFO Payload: " + JSON.stringify(param));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -55,29 +54,31 @@ export const sendToQontak = async (param: any) => {
|
|||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
"\t INFO Resp Qontak: " + JSON.stringify(statusResp)
|
`\t INFO Resp [${
|
||||||
|
config.messageTypes[messageType].name
|
||||||
|
}]: ${JSON.stringify(statusResp)}`
|
||||||
);
|
);
|
||||||
if (statusResp != "OK") {
|
if (statusResp != "OK") {
|
||||||
logging.error(
|
logging.error(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
"\t Error Qontak: " + JSON.stringify(resp.data)
|
`\t Error [${
|
||||||
|
config.messageTypes[messageType].name
|
||||||
|
}]: ${JSON.stringify(resp.data)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return statusResp;
|
return statusResp;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (axios.isAxiosError(e)) {
|
handleAxiosError(e, messageType);
|
||||||
logging.error(NAME_SPACE, "Error di lib inject catch axios");
|
|
||||||
logging.error(NAME_SPACE, e.response?.data.toString());
|
|
||||||
} else if (e instanceof Error) {
|
|
||||||
logging.error(NAME_SPACE, "Error di instance of error");
|
|
||||||
logging.error(NAME_SPACE, e.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const uploadFileCdn = async (param: any) => {
|
export const uploadFileCdn = async (messageType: MessageType, param: any) => {
|
||||||
const url = config.base_url + "mockup/sendwa/sendwa/uploadfile";
|
const url =
|
||||||
logging.info(NAME_SPACE, "\t INFO : " + url);
|
config.base_url + config.messageTypes[messageType].endpoints.uploadFile;
|
||||||
|
logging.info(
|
||||||
|
NAME_SPACE,
|
||||||
|
`\t INFO [${config.messageTypes[messageType].name}]: ${url}`
|
||||||
|
);
|
||||||
logging.info(NAME_SPACE, "\t INFO Payload: " + JSON.stringify(param));
|
logging.info(NAME_SPACE, "\t INFO Payload: " + JSON.stringify(param));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -85,28 +86,31 @@ export const uploadFileCdn = async (param: any) => {
|
|||||||
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
||||||
responseType: "text",
|
responseType: "text",
|
||||||
});
|
});
|
||||||
|
|
||||||
const statusResp = resp.data.status;
|
const statusResp = resp.data.status;
|
||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
"\t INFO Resp Upload: " + JSON.stringify(statusResp)
|
`\t INFO Resp Upload [${
|
||||||
|
config.messageTypes[messageType].name
|
||||||
|
}]: ${JSON.stringify(statusResp)}`
|
||||||
);
|
);
|
||||||
return statusResp;
|
return statusResp;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (axios.isAxiosError(e)) {
|
handleAxiosError(e, messageType);
|
||||||
logging.error(NAME_SPACE, "Error di lib inject catch axios");
|
|
||||||
logging.error(NAME_SPACE, e.response?.data.toString());
|
|
||||||
} else if (e instanceof Error) {
|
|
||||||
logging.error(NAME_SPACE, "Error di instance of error");
|
|
||||||
logging.error(NAME_SPACE, e.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const changeStatusOutbox = async (param: any) => {
|
export const changeStatusOutbox = async (
|
||||||
const url = config.base_url + "mockup/sendwa/sendwa/changeStatusOutbox";
|
messageType: MessageType,
|
||||||
logging.info(NAME_SPACE, "\t INFO : " + url);
|
param: any
|
||||||
|
) => {
|
||||||
|
const url =
|
||||||
|
config.base_url +
|
||||||
|
config.messageTypes[messageType].endpoints.changeStatus;
|
||||||
|
logging.info(
|
||||||
|
NAME_SPACE,
|
||||||
|
`\t INFO [${config.messageTypes[messageType].name}]: ${url}`
|
||||||
|
);
|
||||||
logging.info(NAME_SPACE, "\t INFO Payload: " + JSON.stringify(param));
|
logging.info(NAME_SPACE, "\t INFO Payload: " + JSON.stringify(param));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -114,21 +118,31 @@ export const changeStatusOutbox = async (param: any) => {
|
|||||||
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
||||||
responseType: "text",
|
responseType: "text",
|
||||||
});
|
});
|
||||||
|
|
||||||
const statusResp = resp.data.status;
|
const statusResp = resp.data.status;
|
||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
"\t INFO Resp Change Status: " + JSON.stringify(statusResp)
|
`\t INFO Resp Change Status [${
|
||||||
|
config.messageTypes[messageType].name
|
||||||
|
}]: ${JSON.stringify(statusResp)}`
|
||||||
);
|
);
|
||||||
return statusResp;
|
return statusResp;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (axios.isAxiosError(e)) {
|
handleAxiosError(e, messageType);
|
||||||
logging.error(NAME_SPACE, "Error di lib inject catch axios");
|
|
||||||
logging.error(NAME_SPACE, e.response?.data.toString());
|
|
||||||
} else if (e instanceof Error) {
|
|
||||||
logging.error(NAME_SPACE, "Error di instance of error");
|
|
||||||
logging.error(NAME_SPACE, e.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function handleAxiosError(e: any, messageType: MessageType) {
|
||||||
|
if (axios.isAxiosError(e)) {
|
||||||
|
logging.error(
|
||||||
|
NAME_SPACE,
|
||||||
|
`Error [${config.messageTypes[messageType].name}] - Axios Error`
|
||||||
|
);
|
||||||
|
logging.error(NAME_SPACE, e.response?.data?.toString());
|
||||||
|
} else if (e instanceof Error) {
|
||||||
|
logging.error(
|
||||||
|
NAME_SPACE,
|
||||||
|
`Error [${config.messageTypes[messageType].name}] - ${e.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
327
source/server.ts
327
source/server.ts
@@ -9,22 +9,24 @@ import {
|
|||||||
changeStatusOutbox,
|
changeStatusOutbox,
|
||||||
} from "./lib-inject";
|
} from "./lib-inject";
|
||||||
|
|
||||||
export const NAME_SPACE = "Gateway Send WA";
|
export const NAME_SPACE = "WA_GATEWAY";
|
||||||
const VERSION = "1.1";
|
const VERSION = "1.1";
|
||||||
|
|
||||||
let isRunning = false;
|
let isRunning = false;
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "Starting. Ver:", `${VERSION}`);
|
logging.info(NAME_SPACE, "Starting. Ver:", `${VERSION}`);
|
||||||
|
|
||||||
|
type MessageType = "mcu" | "payment";
|
||||||
|
|
||||||
const on_init = async () => {
|
const on_init = async () => {
|
||||||
logging.info(
|
logging.info(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
"-------------------- ON INIT START --------------------"
|
"-------------------- ON INIT START --------------------"
|
||||||
);
|
);
|
||||||
|
|
||||||
await main("N");
|
// Process both message types
|
||||||
await main("E");
|
await processMessageType("mcu");
|
||||||
await main("R");
|
await processMessageType("payment");
|
||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
@@ -34,7 +36,56 @@ const on_init = async () => {
|
|||||||
|
|
||||||
on_init();
|
on_init();
|
||||||
|
|
||||||
async function main(status: string) {
|
async function processMessageType(messageType: MessageType) {
|
||||||
|
logging.info(
|
||||||
|
NAME_SPACE,
|
||||||
|
`Processing ${config.messageTypes[messageType].name} messages`
|
||||||
|
);
|
||||||
|
|
||||||
|
await main(messageType, "N");
|
||||||
|
await main(messageType, "E");
|
||||||
|
await main(messageType, "R");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processOutboxItem(
|
||||||
|
messageType: MessageType,
|
||||||
|
outbox: any,
|
||||||
|
status: string,
|
||||||
|
retry: number
|
||||||
|
) {
|
||||||
|
if (outbox.fileUrl == null) {
|
||||||
|
const uploadResult = await uploadFile(messageType, outbox);
|
||||||
|
logging.info(
|
||||||
|
NAME_SPACE,
|
||||||
|
`\t Uploading File to CDN [${config.messageTypes[messageType].name}]:`,
|
||||||
|
uploadResult
|
||||||
|
);
|
||||||
|
await delay(1000);
|
||||||
|
|
||||||
|
if (uploadResult === "OK") {
|
||||||
|
logging.info(NAME_SPACE, "\t File Uploaded");
|
||||||
|
const response = await sentMsg(messageType, outbox, status, retry);
|
||||||
|
await delay(1000);
|
||||||
|
logging.info(
|
||||||
|
NAME_SPACE,
|
||||||
|
`\t Resp Qontak [${config.messageTypes[messageType].name}]:`,
|
||||||
|
response
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await changeStatus(messageType, outbox, "E", retry);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const response = await sentMsg(messageType, outbox, status, retry);
|
||||||
|
await delay(1000);
|
||||||
|
logging.info(
|
||||||
|
NAME_SPACE,
|
||||||
|
`\t Resp Qontak [${config.messageTypes[messageType].name}]:`,
|
||||||
|
response
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main(messageType: MessageType, status: string) {
|
||||||
if (isRunning) {
|
if (isRunning) {
|
||||||
logging.info(NAME_SPACE, "Process is running. Skip this run.");
|
logging.info(NAME_SPACE, "Process is running. Skip this run.");
|
||||||
return;
|
return;
|
||||||
@@ -44,185 +95,106 @@ async function main(status: string) {
|
|||||||
try {
|
try {
|
||||||
logging.info(
|
logging.info(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
"Process is running. Start to get list outbox."
|
`Process is running. Start to get list outbox for ${config.messageTypes[messageType].name}.`
|
||||||
);
|
);
|
||||||
|
const outboxs = await getLists(messageType, status);
|
||||||
|
|
||||||
let outboxs = await getLists(status);
|
switch (status) {
|
||||||
|
case "N":
|
||||||
/* Pesan minta diproses kirim */
|
logging.info(
|
||||||
if (status == "N") {
|
NAME_SPACE,
|
||||||
logging.info(NAME_SPACE, "GET Processed (N) Message");
|
`GET Processed (N) Message - ${config.messageTypes[messageType].name}`
|
||||||
|
);
|
||||||
for (let i = 0; i < outboxs.length; i++) {
|
for (const outbox of outboxs) {
|
||||||
let outbox = outboxs[i];
|
await processOutboxItem(
|
||||||
// logging.info(NAME_SPACE, "\t Detail: ", outbox);
|
messageType,
|
||||||
let retry = outbox.XWaOutboxIsRetry;
|
outbox,
|
||||||
|
status,
|
||||||
/* Jika fileUrl masih 0, maka panggil upload file */
|
outbox.XWaOutboxIsRetry
|
||||||
if (outbox.fileUrl == null) {
|
|
||||||
let resp = uploadFile(outbox);
|
|
||||||
logging.info(
|
|
||||||
NAME_SPACE,
|
|
||||||
"\t Uploading File to CDN: ",
|
|
||||||
resp
|
|
||||||
);
|
);
|
||||||
logging.info(NAME_SPACE, "\t wait 2s \t");
|
|
||||||
await delay(2000);
|
|
||||||
|
|
||||||
if ((await resp) == "OK") {
|
|
||||||
logging.info(NAME_SPACE, "\t File Uploaded");
|
|
||||||
|
|
||||||
let response = sentMsg(outbox, status, retry);
|
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "\t wait 4s \t");
|
|
||||||
await delay(4000);
|
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "\t Resp Qontak: ", response);
|
|
||||||
} else {
|
|
||||||
// Change Status ke Error
|
|
||||||
changeStatus(outbox, "E", retry);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Jika fileUrl sudah ada maka langsung kirim file
|
|
||||||
let response = sentMsg(outbox, status, retry);
|
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "\t wait 4s \t");
|
|
||||||
await delay(4000);
|
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "\t Resp Qontak: ", response);
|
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
} else if (status == "E") {
|
|
||||||
/* Pesan Eror < 5x retry */
|
|
||||||
logging.info(NAME_SPACE, "GET Error (E) Message");
|
|
||||||
for (let i = 0; i < outboxs.length; i++) {
|
|
||||||
let outbox = outboxs[i];
|
|
||||||
// logging.info(NAME_SPACE, "\t Detail: ", outbox);
|
|
||||||
let retry = outbox.XWaOutboxIsRetry;
|
|
||||||
|
|
||||||
if (retry >= 5) {
|
case "E":
|
||||||
logging.info(
|
logging.info(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
"\t Retry count more than 5. Skip this message."
|
`GET Error (E) Message - ${config.messageTypes[messageType].name}`
|
||||||
);
|
);
|
||||||
continue;
|
for (const outbox of outboxs) {
|
||||||
} else {
|
const retry = outbox.XWaOutboxIsRetry;
|
||||||
retry++;
|
if (retry >= 5) {
|
||||||
|
|
||||||
if (outbox.fileUrl == null) {
|
|
||||||
let resp = uploadFile(outbox);
|
|
||||||
logging.info(
|
logging.info(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
"\t Uploading File to CDN: ",
|
"\t Retry count more than 5. Skip this message."
|
||||||
resp
|
|
||||||
);
|
);
|
||||||
logging.info(NAME_SPACE, "\t wait 2s \t");
|
continue;
|
||||||
await delay(2000);
|
|
||||||
|
|
||||||
if ((await resp) == "OK") {
|
|
||||||
logging.info(NAME_SPACE, "\t File Uploaded");
|
|
||||||
|
|
||||||
let response = sentMsg(outbox, status, retry);
|
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "\t wait 4s \t");
|
|
||||||
await delay(4000);
|
|
||||||
|
|
||||||
logging.info(
|
|
||||||
NAME_SPACE,
|
|
||||||
"\t Resp Qontak: ",
|
|
||||||
response
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
changeStatus(outbox, "E", retry);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let response = sentMsg(outbox, status, retry);
|
|
||||||
logging.info(NAME_SPACE, "\t wait 4s \t");
|
|
||||||
await delay(4000);
|
|
||||||
logging.info(NAME_SPACE, "\t Resp Qontak: ", response);
|
|
||||||
}
|
}
|
||||||
}
|
await processOutboxItem(
|
||||||
}
|
messageType,
|
||||||
} else if (status == "R") {
|
outbox,
|
||||||
logging.info(NAME_SPACE, "GET Rejected (R) Message");
|
status,
|
||||||
|
retry + 1
|
||||||
for (let i = 0; i < outboxs.length; i++) {
|
|
||||||
let outbox = outboxs[i];
|
|
||||||
// logging.info(NAME_SPACE, "\t Detail: ", outbox);
|
|
||||||
let retry = outbox.XWaOutboxIsRetry;
|
|
||||||
|
|
||||||
/* Jika fileUrl masih 0, maka panggil upload file */
|
|
||||||
if (outbox.fileUrl == null) {
|
|
||||||
let resp = uploadFile(outbox);
|
|
||||||
logging.info(
|
|
||||||
NAME_SPACE,
|
|
||||||
"\t Uploading File to CDN: ",
|
|
||||||
resp
|
|
||||||
);
|
);
|
||||||
logging.info(NAME_SPACE, "\t wait 2s \t");
|
|
||||||
await delay(2000);
|
|
||||||
|
|
||||||
if ((await resp) == "OK") {
|
|
||||||
logging.info(NAME_SPACE, "\t File Uploaded");
|
|
||||||
|
|
||||||
let response = sentMsg(outbox, status, retry);
|
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "\t wait 4s \t");
|
|
||||||
await delay(4000);
|
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "\t Resp Qontak: ", response);
|
|
||||||
} else {
|
|
||||||
// Change Status ke Error
|
|
||||||
changeStatus(outbox, "E", retry);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Jika fileUrl sudah ada maka langsung kirim file
|
|
||||||
let response = sentMsg(outbox, status, retry);
|
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "\t wait 4s \t");
|
|
||||||
await delay(4000);
|
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "\t Resp Qontak: ", response);
|
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
|
||||||
|
case "R":
|
||||||
|
logging.info(
|
||||||
|
NAME_SPACE,
|
||||||
|
`GET Rejected (R) Message - ${config.messageTypes[messageType].name}`
|
||||||
|
);
|
||||||
|
for (const outbox of outboxs) {
|
||||||
|
await processOutboxItem(
|
||||||
|
messageType,
|
||||||
|
outbox,
|
||||||
|
status,
|
||||||
|
outbox.XWaOutboxIsRetry
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
logging.info(NAME_SPACE, "End Job Get Order");
|
logging.info(
|
||||||
isRunning = false;
|
NAME_SPACE,
|
||||||
|
`End Job Get Order - ${config.messageTypes[messageType].name}`
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
isRunning = false;
|
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
logging.error(NAME_SPACE, e.message);
|
logging.error(NAME_SPACE, e.message);
|
||||||
} else {
|
} else {
|
||||||
logging.error(NAME_SPACE, "Unknown");
|
logging.error(NAME_SPACE, "Unknown error");
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
isRunning = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLists(status: string) {
|
async function getLists(messageType: MessageType, status: string) {
|
||||||
const statusOutbox = status;
|
const { startDate, endDate } = config;
|
||||||
// const statusOutbox = config.statusOutbox;
|
|
||||||
const startDate = config.startDate;
|
|
||||||
const endDate = config.endDate;
|
|
||||||
|
|
||||||
var resp = await getListOutbox(statusOutbox, startDate, endDate);
|
const resp = await getListOutbox(messageType, status, startDate, endDate);
|
||||||
if (resp["status"] != "OK") {
|
if (resp?.status !== "OK") {
|
||||||
logging.error(NAME_SPACE, "\t Error get outbox data", resp);
|
logging.error(NAME_SPACE, "\t Error get outbox data", resp);
|
||||||
logging.error(NAME_SPACE, "\t Error: ", resp);
|
return [];
|
||||||
} else {
|
|
||||||
logging.info(
|
|
||||||
NAME_SPACE,
|
|
||||||
"\t Success get order data found ",
|
|
||||||
resp.data.length
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const result =
|
|
||||||
typeof resp.data === "string" ? JSON.parse(resp.data) : resp.data;
|
logging.info(
|
||||||
return result;
|
NAME_SPACE,
|
||||||
|
`\t Success get order data found [${config.messageTypes[messageType].name}]`,
|
||||||
|
resp.data?.length || 0
|
||||||
|
);
|
||||||
|
return typeof resp.data === "string"
|
||||||
|
? JSON.parse(resp.data)
|
||||||
|
: resp.data || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sentMsg(item: any, status: string, retry: number) {
|
async function sentMsg(
|
||||||
let payload = {
|
messageType: MessageType,
|
||||||
|
item: any,
|
||||||
|
status: string,
|
||||||
|
retry: number
|
||||||
|
) {
|
||||||
|
const payload = {
|
||||||
orderID: item.orderID,
|
orderID: item.orderID,
|
||||||
orderDate: item.orderDate,
|
orderDate: item.orderDate,
|
||||||
patientDOB: item.patientDOB,
|
patientDOB: item.patientDOB,
|
||||||
@@ -234,45 +206,41 @@ async function sentMsg(item: any, status: string, retry: number) {
|
|||||||
retryOutbox: retry,
|
retryOutbox: retry,
|
||||||
sendWaID: item.sendWaID,
|
sendWaID: item.sendWaID,
|
||||||
};
|
};
|
||||||
// return payload;
|
|
||||||
|
|
||||||
/* Axios for POST to the WA API */
|
return await sendToQontak(messageType, payload);
|
||||||
var response = await sendToQontak(payload); // Response "OK"
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadFile(item: any) {
|
async function uploadFile(messageType: MessageType, item: any) {
|
||||||
let payload = {
|
const payload = {
|
||||||
fileName: item.fileName,
|
fileName: item.fileName,
|
||||||
rptUrl: item.localUrl,
|
rptUrl: item.localUrl,
|
||||||
mime: "application/pdf",
|
mime: "application/pdf",
|
||||||
XWaOutboxID: item.sendWaID,
|
XWaOutboxID: item.sendWaID,
|
||||||
};
|
};
|
||||||
// return payload;
|
|
||||||
|
|
||||||
var response = await uploadFileCdn(payload); // Response "OK"
|
return await uploadFileCdn(messageType, payload);
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeStatus(item: any, status: string, retry: number) {
|
async function changeStatus(
|
||||||
let payload = {
|
messageType: MessageType,
|
||||||
|
item: any,
|
||||||
|
status: string,
|
||||||
|
retry: number
|
||||||
|
) {
|
||||||
|
const payload = {
|
||||||
toStatus: status,
|
toStatus: status,
|
||||||
XWaOutboxID: item.sendWaID,
|
XWaOutboxID: item.sendWaID,
|
||||||
retry: retry,
|
retry: retry,
|
||||||
};
|
};
|
||||||
// return payload;
|
|
||||||
|
|
||||||
var response = await changeStatusOutbox(payload);
|
return await changeStatusOutbox(messageType, payload);
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function delay(ms: number) {
|
async function delay(ms: number) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
function elseif(arg0: boolean) {
|
|
||||||
throw new Error("Function not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Schedule both message types
|
||||||
for (const sched of config.schedule) {
|
for (const sched of config.schedule) {
|
||||||
Xcron.schedule(
|
Xcron.schedule(
|
||||||
sched,
|
sched,
|
||||||
@@ -281,14 +249,11 @@ for (const sched of config.schedule) {
|
|||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
" -------------------- ON INIT SCHEDULE --------------------"
|
" -------------------- ON INIT SCHEDULE --------------------"
|
||||||
);
|
);
|
||||||
|
await processMessageType("mcu");
|
||||||
await main("N");
|
await processMessageType("payment");
|
||||||
await main("E");
|
|
||||||
await main("R");
|
|
||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
NAME_SPACE,
|
NAME_SPACE,
|
||||||
"-------------------- END SCHEDULE--------------------"
|
"-------------------- END SCHEDULE --------------------"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user