diff --git a/README.md b/README.md index f8206bf..bf83eb3 100644 --- a/README.md +++ b/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 { "base_url": "https://cpone.aplikasi.web.id/one-api/", - // Setiap 2 menit dari jam 6 sampai 20 "schedule": [ - "*/2 6-20 * * *" + "*/2 6-20 * * *" // Runs every 2 minutes from 6 AM to 8 PM ], - "delay": 5, - "row_per_batch": 50, - "startDate": "2023-08-01", //yyyy-mm-dd tanggal data dicari dimulai - "endDate": "2025-12-30", //yyyy-mm-dd tanggal data dicari berakhir + "startDate": "2023-08-01", // Filter T_OrderHeaderDate from this date (YYYY-MM-DD) + "endDate": "2025-12-30" // Filter T_OrderHeaderDate until this date (YYYY-MM-DD) } ``` -Install Dependencies +## Supported Message Types +* MCU Result: Medical checkup results with PDF attachment +* Payment Receipt: Payment receipts with PDF attachment + +## Installation ```bash +# Install dependencies npm install + +# Format code +npm run format ``` -Build the source +## Build & Run ```bash +# Build TypeScript source npm run build -``` -Compile -```bash -npm run compile -``` +# Compile to executable +npm run compile -Run -```bash +# Run the application 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 -``` \ No newline at end of file + +## 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. \ No newline at end of file diff --git a/config-gw-wa.json b/config-gw-wa.json index d8fda6d..4c5c9b5 100644 --- a/config-gw-wa.json +++ b/config-gw-wa.json @@ -1,8 +1,6 @@ { "base_url": "https://devcpone.aplikasi.web.id/one-api/", "schedule": ["*/2 6-20 * * *"], - "delay": 5, - "row_per_batch": 50, "startDate": "2023-08-01", "endDate": "2025-12-30" } diff --git a/dist/gw-wa.js b/dist/gw-wa.js index b7ef9d9..18a4d9c 100644 --- a/dist/gw-wa.js +++ b/dist/gw-wa.js @@ -10,10 +10,33 @@ const a = { base_url: s["base_url"], schedule: s["schedule"], - delay: s["delay"], - row_per_batch: s["row_per_batch"], startDate: s["startDate"], endDate: s["endDate"], + messageTypes: { + 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", + }, + }, + }, }; t.default = a; }, @@ -79,17 +102,19 @@ const a = n(r(5442)); const i = n(r(7107)); const o = r(4200); - const getListOutbox = async (e, t, r) => { - const n = - a.default.base_url + "mockup/sendwa/sendwa/listoutbox"; - i.default.info(o.NAME_SPACE, "\t INFO : " + n); - i.default.info(o.NAME_SPACE, "\t INFO Status Outbox: " + e); - i.default.info(o.NAME_SPACE, "\t INFO Start Date: " + t); - i.default.info(o.NAME_SPACE, "\t INFO End Date: " + r); + const getListOutbox = async (e, t, r, n) => { + const u = + a.default.base_url + + a.default.messageTypes[e].endpoints.listOutbox; + i.default.info( + o.NAME_SPACE, + `\t INFO [${a.default.messageTypes[e].name}]: ${u}` + ); + i.default.info(o.NAME_SPACE, "\t INFO Status Outbox: " + t); try { - const a = await s.default.post( - n, - { statusOutbox: e, startDate: t, endDate: r }, + const e = await s.default.post( + u, + { statusOutbox: t, startDate: r, endDate: n }, { headers: { "Content-Type": @@ -98,161 +123,132 @@ responseType: "text", } ); - const i = - typeof a.data === "string" - ? JSON.parse(a.data) - : a.data; - return i; - } catch (e) { - if (s.default.isAxiosError(e)) { - i.default.error( - o.NAME_SPACE, - "Error di lib inject catch axios" - ); - i.default.error( - o.NAME_SPACE, - e.response?.data.toString() - ); - } else if (e instanceof Error) { - i.default.error( - o.NAME_SPACE, - "Error di instance of error" - ); - i.default.error(o.NAME_SPACE, e.message); - } + return typeof e.data === "string" + ? JSON.parse(e.data) + : e.data; + } catch (t) { + handleAxiosError(t, e); } }; t.getListOutbox = getListOutbox; - const sendToQontak = async e => { - const t = - a.default.base_url + "mockup/sendwa/sendwa/QontakSendMsg"; - i.default.info(o.NAME_SPACE, "\t INFO : " + t); + const sendToQontak = async (e, t) => { + const r = + a.default.base_url + + a.default.messageTypes[e].endpoints.sendMsg; i.default.info( o.NAME_SPACE, - "\t INFO Payload: " + JSON.stringify(e) + `\t INFO [${a.default.messageTypes[e].name}]: ${r}` + ); + i.default.info( + o.NAME_SPACE, + "\t INFO Payload: " + JSON.stringify(t) ); try { - const r = await s.default.post(t, e, { + const n = await s.default.post(r, t, { headers: { "Content-Type": "application/json; charset=UTF-8", }, responseType: "text", }); - const n = r.data.status; + const u = n.data.status; i.default.info( o.NAME_SPACE, - "\t INFO Resp Qontak: " + JSON.stringify(n) + `\t INFO Resp [${ + a.default.messageTypes[e].name + }]: ${JSON.stringify(u)}` ); - if (n != "OK") { + if (u != "OK") { i.default.error( o.NAME_SPACE, - "\t Error Qontak: " + JSON.stringify(r.data) + `\t Error [${ + a.default.messageTypes[e].name + }]: ${JSON.stringify(n.data)}` ); } - return n; - } catch (e) { - if (s.default.isAxiosError(e)) { - i.default.error( - o.NAME_SPACE, - "Error di lib inject catch axios" - ); - i.default.error( - o.NAME_SPACE, - e.response?.data.toString() - ); - } else if (e instanceof Error) { - i.default.error( - o.NAME_SPACE, - "Error di instance of error" - ); - i.default.error(o.NAME_SPACE, e.message); - } + return u; + } catch (t) { + handleAxiosError(t, e); } }; t.sendToQontak = sendToQontak; - const uploadFileCdn = async e => { - const t = - a.default.base_url + "mockup/sendwa/sendwa/uploadfile"; - i.default.info(o.NAME_SPACE, "\t INFO : " + t); + const uploadFileCdn = async (e, t) => { + const r = + a.default.base_url + + a.default.messageTypes[e].endpoints.uploadFile; i.default.info( o.NAME_SPACE, - "\t INFO Payload: " + JSON.stringify(e) + `\t INFO [${a.default.messageTypes[e].name}]: ${r}` + ); + i.default.info( + o.NAME_SPACE, + "\t INFO Payload: " + JSON.stringify(t) ); try { - const r = await s.default.post(t, e, { + const n = await s.default.post(r, t, { headers: { "Content-Type": "application/json; charset=UTF-8", }, responseType: "text", }); - const n = r.data.status; + const u = n.data.status; i.default.info( o.NAME_SPACE, - "\t INFO Resp Upload: " + JSON.stringify(n) + `\t INFO Resp Upload [${ + a.default.messageTypes[e].name + }]: ${JSON.stringify(u)}` ); - return n; - } catch (e) { - if (s.default.isAxiosError(e)) { - i.default.error( - o.NAME_SPACE, - "Error di lib inject catch axios" - ); - i.default.error( - o.NAME_SPACE, - e.response?.data.toString() - ); - } else if (e instanceof Error) { - i.default.error( - o.NAME_SPACE, - "Error di instance of error" - ); - i.default.error(o.NAME_SPACE, e.message); - } + return u; + } catch (t) { + handleAxiosError(t, e); } }; t.uploadFileCdn = uploadFileCdn; - const changeStatusOutbox = async e => { - const t = + const changeStatusOutbox = async (e, t) => { + const r = a.default.base_url + - "mockup/sendwa/sendwa/changeStatusOutbox"; - i.default.info(o.NAME_SPACE, "\t INFO : " + t); + a.default.messageTypes[e].endpoints.changeStatus; i.default.info( o.NAME_SPACE, - "\t INFO Payload: " + JSON.stringify(e) + `\t INFO [${a.default.messageTypes[e].name}]: ${r}` + ); + i.default.info( + o.NAME_SPACE, + "\t INFO Payload: " + JSON.stringify(t) ); try { - const r = await s.default.post(t, e, { + const n = await s.default.post(r, t, { headers: { "Content-Type": "application/json; charset=UTF-8", }, responseType: "text", }); - const n = r.data.status; + const u = n.data.status; i.default.info( o.NAME_SPACE, - "\t INFO Resp Change Status: " + JSON.stringify(n) + `\t INFO Resp Change Status [${ + a.default.messageTypes[e].name + }]: ${JSON.stringify(u)}` ); - return n; - } catch (e) { - if (s.default.isAxiosError(e)) { - i.default.error( - o.NAME_SPACE, - "Error di lib inject catch axios" - ); - i.default.error( - o.NAME_SPACE, - e.response?.data.toString() - ); - } else if (e instanceof Error) { - i.default.error( - o.NAME_SPACE, - "Error di instance of error" - ); - i.default.error(o.NAME_SPACE, e.message); - } + return u; + } catch (t) { + handleAxiosError(t, e); } }; t.changeStatusOutbox = changeStatusOutbox; + function handleAxiosError(e, t) { + if (s.default.isAxiosError(e)) { + i.default.error( + o.NAME_SPACE, + `Error [${a.default.messageTypes[t].name}] - Axios Error` + ); + i.default.error(o.NAME_SPACE, e.response?.data?.toString()); + } else if (e instanceof Error) { + i.default.error( + o.NAME_SPACE, + `Error [${a.default.messageTypes[t].name}] - ${e.message}` + ); + } + } }, 4200: function (e, t, r) { "use strict"; @@ -319,8 +315,8 @@ const u = i(r(7107)); const l = a(r(2148)); const c = r(3595); - t.NAME_SPACE = "SENT RESULT VIA WA"; - const f = "1.0"; + t.NAME_SPACE = "WA_GATEWAY"; + const f = "1.1"; let d = false; u.default.info(t.NAME_SPACE, "Starting. Ver:", `${f}`); const on_init = async () => { @@ -328,16 +324,55 @@ t.NAME_SPACE, "-------------------- ON INIT START --------------------" ); - await main("N"); - await main("E"); - await main("R"); + await processMessageType("mcu"); + await processMessageType("payment"); u.default.info( t.NAME_SPACE, "-------------------- ON INIT END --------------------" ); }; on_init(); - async function main(e) { + async function processMessageType(e) { + u.default.info( + t.NAME_SPACE, + `Processing ${o.default.messageTypes[e].name} messages` + ); + await main(e, "N"); + await main(e, "E"); + await main(e, "R"); + } + async function processOutboxItem(e, r, n, s) { + if (r.fileUrl == null) { + const a = await uploadFile(e, r); + u.default.info( + t.NAME_SPACE, + `\t Uploading File to CDN [${o.default.messageTypes[e].name}]:`, + a + ); + await delay(2e3); + if (a === "OK") { + u.default.info(t.NAME_SPACE, "\t File Uploaded"); + const a = await sentMsg(e, r, n, s); + await delay(4e3); + u.default.info( + t.NAME_SPACE, + `\t Resp Qontak [${o.default.messageTypes[e].name}]:`, + a + ); + } else { + await changeStatus(e, r, "E", s); + } + } else { + const a = await sentMsg(e, r, n, s); + await delay(4e3); + u.default.info( + t.NAME_SPACE, + `\t Resp Qontak [${o.default.messageTypes[e].name}]:`, + a + ); + } + } + async function main(e, r) { if (d) { u.default.info( t.NAME_SPACE, @@ -349,234 +384,121 @@ try { u.default.info( t.NAME_SPACE, - "Process is running. Start to get list outbox." + `Process is running. Start to get list outbox for ${o.default.messageTypes[e].name}.` ); - let r = await getLists(e); - if (e == "N") { - u.default.info( - t.NAME_SPACE, - "GET Processed (N) Message" - ); - for (let n = 0; n < r.length; n++) { - let s = r[n]; - let a = s.XWaOutboxIsRetry; - if (s.fileUrl == null) { - let r = uploadFile(s); - u.default.info( - t.NAME_SPACE, - "\t Uploading File to CDN: ", - r - ); - u.default.info(t.NAME_SPACE, "\t wait 2s \t"); - await delay(2e3); - if ((await r) == "OK") { - u.default.info( - t.NAME_SPACE, - "\t File Uploaded" - ); - let r = sentMsg(s, e, a); - u.default.info( - t.NAME_SPACE, - "\t wait 4s \t" - ); - await delay(4e3); - u.default.info( - t.NAME_SPACE, - "\t Resp Qontak: ", - r - ); - } else { - changeStatus(s, "E", a); - } - } else { - let r = sentMsg(s, e, a); - u.default.info(t.NAME_SPACE, "\t wait 4s \t"); - await delay(4e3); - u.default.info( - t.NAME_SPACE, - "\t Resp Qontak: ", - r + const n = await getLists(e, r); + switch (r) { + case "N": + u.default.info( + t.NAME_SPACE, + `GET Processed (N) Message - ${o.default.messageTypes[e].name}` + ); + for (const t of n) { + await processOutboxItem( + e, + t, + r, + t.XWaOutboxIsRetry ); } - } - } else if (e == "E") { - u.default.info(t.NAME_SPACE, "GET Error (E) Message"); - for (let n = 0; n < r.length; n++) { - let s = r[n]; - let a = s.XWaOutboxIsRetry; - if (a >= 5) { - u.default.info( - t.NAME_SPACE, - "\t Retry count more than 5. Skip this message." - ); - continue; - } else { - a++; - if (s.fileUrl == null) { - let r = uploadFile(s); + break; + case "E": + u.default.info( + t.NAME_SPACE, + `GET Error (E) Message - ${o.default.messageTypes[e].name}` + ); + for (const s of n) { + const n = s.XWaOutboxIsRetry; + if (n >= 5) { u.default.info( t.NAME_SPACE, - "\t Uploading File to CDN: ", - r - ); - u.default.info( - t.NAME_SPACE, - "\t wait 2s \t" - ); - await delay(2e3); - if ((await r) == "OK") { - u.default.info( - t.NAME_SPACE, - "\t File Uploaded" - ); - let r = sentMsg(s, e, a); - u.default.info( - t.NAME_SPACE, - "\t wait 4s \t" - ); - await delay(4e3); - u.default.info( - t.NAME_SPACE, - "\t Resp Qontak: ", - r - ); - } else { - changeStatus(s, "E", a); - } - } else { - let r = sentMsg(s, e, a); - u.default.info( - t.NAME_SPACE, - "\t wait 4s \t" - ); - await delay(4e3); - u.default.info( - t.NAME_SPACE, - "\t Resp Qontak: ", - r + "\t Retry count more than 5. Skip this message." ); + continue; } + await processOutboxItem(e, s, r, n + 1); } - } - } else if (e == "R") { - u.default.info( - t.NAME_SPACE, - "GET Rejected (R) Message" - ); - for (let n = 0; n < r.length; n++) { - let s = r[n]; - let a = s.XWaOutboxIsRetry; - if (s.fileUrl == null) { - let r = uploadFile(s); - u.default.info( - t.NAME_SPACE, - "\t Uploading File to CDN: ", - r - ); - u.default.info(t.NAME_SPACE, "\t wait 2s \t"); - await delay(2e3); - if ((await r) == "OK") { - u.default.info( - t.NAME_SPACE, - "\t File Uploaded" - ); - let r = sentMsg(s, e, a); - u.default.info( - t.NAME_SPACE, - "\t wait 4s \t" - ); - await delay(4e3); - u.default.info( - t.NAME_SPACE, - "\t Resp Qontak: ", - r - ); - } else { - changeStatus(s, "E", a); - } - } else { - let r = sentMsg(s, e, a); - u.default.info(t.NAME_SPACE, "\t wait 4s \t"); - await delay(4e3); - u.default.info( - t.NAME_SPACE, - "\t Resp Qontak: ", - r + break; + case "R": + u.default.info( + t.NAME_SPACE, + `GET Rejected (R) Message - ${o.default.messageTypes[e].name}` + ); + for (const t of n) { + await processOutboxItem( + e, + t, + r, + t.XWaOutboxIsRetry ); } - } + break; } - u.default.info(t.NAME_SPACE, "End Job Get Order"); - d = false; + u.default.info( + t.NAME_SPACE, + `End Job Get Order - ${o.default.messageTypes[e].name}` + ); } catch (e) { - d = false; if (e instanceof Error) { u.default.error(t.NAME_SPACE, e.message); } else { - u.default.error(t.NAME_SPACE, "Unknown"); + u.default.error(t.NAME_SPACE, "Unknown error"); } + } finally { + d = false; } } - async function getLists(e) { - const r = e; - const n = o.default.startDate; - const s = o.default.endDate; - var a = await (0, c.getListOutbox)(r, n, s); - if (a["status"] != "OK") { + async function getLists(e, r) { + const { startDate: n, endDate: s } = o.default; + const a = await (0, c.getListOutbox)(e, r, n, s); + if (a?.status !== "OK") { u.default.error( t.NAME_SPACE, "\t Error get outbox data", a ); - u.default.error(t.NAME_SPACE, "\t Error: ", a); - } else { - u.default.info( - t.NAME_SPACE, - "\t Success get order data found ", - a.data.length - ); + return []; } - const i = - typeof a.data === "string" ? JSON.parse(a.data) : a.data; - return i; + u.default.info( + t.NAME_SPACE, + `\t Success get order data found [${o.default.messageTypes[e].name}]`, + a.data?.length || 0 + ); + return typeof a.data === "string" + ? JSON.parse(a.data) + : a.data || []; } - async function sentMsg(e, t, r) { - let n = { - orderID: e.orderID, - orderDate: e.orderDate, - patientDOB: e.patientDOB, - patientName: e.patientName, - patientHp: e.patientHp, - corpName: e.CorporateName, - fileName: e.fileName, - statusOutbox: t, - retryOutbox: r, - sendWaID: e.sendWaID, + async function sentMsg(e, t, r, n) { + const s = { + orderID: t.orderID, + orderDate: t.orderDate, + patientDOB: t.patientDOB, + patientName: t.patientName, + patientHp: t.patientHp, + corpName: t.CorporateName, + fileName: t.fileName, + statusOutbox: r, + retryOutbox: n, + sendWaID: t.sendWaID, }; - var s = await (0, c.sendToQontak)(n); - return s; + return await (0, c.sendToQontak)(e, s); } - async function uploadFile(e) { - let t = { - fileName: e.fileName, - rptUrl: e.localUrl, + async function uploadFile(e, t) { + const r = { + fileName: t.fileName, + rptUrl: t.localUrl, mime: "application/pdf", - XWaOutboxID: e.sendWaID, + XWaOutboxID: t.sendWaID, }; - var r = await (0, c.uploadFileCdn)(t); - return r; + return await (0, c.uploadFileCdn)(e, r); } - async function changeStatus(e, t, r) { - let n = { toStatus: t, XWaOutboxID: e.sendWaID, retry: r }; - var s = await (0, c.changeStatusOutbox)(n); - return s; + async function changeStatus(e, t, r, n) { + const s = { toStatus: r, XWaOutboxID: t.sendWaID, retry: n }; + return await (0, c.changeStatusOutbox)(e, s); } async function delay(e) { return new Promise(t => setTimeout(t, e)); } - function elseif(e) { - throw new Error("Function not implemented."); - } for (const e of o.default.schedule) { l.schedule( e, @@ -585,12 +507,11 @@ t.NAME_SPACE, " -------------------- ON INIT SCHEDULE --------------------" ); - await main("N"); - await main("E"); - await main("R"); + await processMessageType("mcu"); + await processMessageType("payment"); u.default.info( t.NAME_SPACE, - "-------------------- END SCHEDULE--------------------" + "-------------------- END SCHEDULE --------------------" ); }, { timezone: "Asia/Jakarta" } @@ -638,10 +559,10 @@ var y = function resolve(e) { t(e); }; - var _ = function reject(e) { + var v = function reject(e) { r(e); }; - var v = e.data; + var _ = e.data; var k = e.headers; if ("User-Agent" in k || "user-agent" in k) { if (!k["User-Agent"] && !k["user-agent"]) { @@ -651,21 +572,21 @@ } else { k["User-Agent"] = "axios/" + h.version; } - if (v && !n.isStream(v)) { - if (Buffer.isBuffer(v)) { - } else if (n.isArrayBuffer(v)) { - v = Buffer.from(new Uint8Array(v)); - } else if (n.isString(v)) { - v = Buffer.from(v, "utf-8"); + if (_ && !n.isStream(_)) { + if (Buffer.isBuffer(_)) { + } else if (n.isArrayBuffer(_)) { + _ = Buffer.from(new Uint8Array(_)); + } else if (n.isString(_)) { + _ = Buffer.from(_, "utf-8"); } else { - return _( + return v( m( "Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream", e ) ); } - k["Content-Length"] = v.length; + k["Content-Length"] = _.length; } var w = undefined; if (e.auth) { @@ -673,43 +594,43 @@ var S = e.auth.password || ""; w = b + ":" + S; } - var O = a(e.baseURL, e.url); - var T = f.parse(O); - var M = T.protocol || "http:"; - if (!w && T.auth) { - var D = T.auth.split(":"); - var x = D[0] || ""; + var T = a(e.baseURL, e.url); + var O = f.parse(T); + var x = O.protocol || "http:"; + if (!w && O.auth) { + var D = O.auth.split(":"); + var M = D[0] || ""; var R = D[1] || ""; - w = x + ":" + R; + w = M + ":" + R; } if (w) { delete k.Authorization; } - var E = g.test(M); - var C = E ? e.httpsAgent : e.httpAgent; - var F = { - path: i(T.path, e.params, e.paramsSerializer).replace( + var C = g.test(x); + var F = C ? e.httpsAgent : e.httpAgent; + var E = { + path: i(O.path, e.params, e.paramsSerializer).replace( /^\?/, "" ), method: e.method.toUpperCase(), headers: k, - agent: C, + agent: F, agents: { http: e.httpAgent, https: e.httpsAgent }, auth: w, }; if (e.socketPath) { - F.socketPath = e.socketPath; + E.socketPath = e.socketPath; } else { - F.hostname = T.hostname; - F.port = T.port; + E.hostname = O.hostname; + E.port = O.port; } var P = e.proxy; if (!P && P !== false) { - var A = M.slice(0, -1) + "_proxy"; - var N = process.env[A] || process.env[A.toUpperCase()]; - if (N) { - var Y = f.parse(N); + var N = x.slice(0, -1) + "_proxy"; + var A = process.env[N] || process.env[N.toUpperCase()]; + if (A) { + var Y = f.parse(A); var I = process.env.no_proxy || process.env.NO_PROXY; var U = true; @@ -726,13 +647,13 @@ } if ( e[0] === "." && - T.hostname.substr( - T.hostname.length - e.length + O.hostname.substr( + O.hostname.length - e.length ) === e ) { return true; } - return T.hostname === e; + return O.hostname === e; }); } if (U) { @@ -749,34 +670,34 @@ } } if (P) { - F.headers.host = - T.hostname + (T.port ? ":" + T.port : ""); + E.headers.host = + O.hostname + (O.port ? ":" + O.port : ""); setProxy( - F, + E, P, - M + + x + "//" + - T.hostname + - (T.port ? ":" + T.port : "") + - F.path + O.hostname + + (O.port ? ":" + O.port : "") + + E.path ); } var j; - var q = E && (P ? g.test(P.protocol) : true); + var q = C && (P ? g.test(P.protocol) : true); if (e.transport) { j = e.transport; } else if (e.maxRedirects === 0) { j = q ? u : o; } else { if (e.maxRedirects) { - F.maxRedirects = e.maxRedirects; + E.maxRedirects = e.maxRedirects; } j = q ? c : l; } if (e.maxBodyLength > -1) { - F.maxBodyLength = e.maxBodyLength; + E.maxBodyLength = e.maxBodyLength; } - var H = j.request(F, function handleResponse(t) { + var H = j.request(E, function handleResponse(t) { if (H.aborted) return; var r = t; var a = t.req || H; @@ -803,7 +724,7 @@ }; if (e.responseType === "stream") { i.data = r; - s(y, _, i); + s(y, v, i); } else { var o = []; var u = 0; @@ -815,7 +736,7 @@ u > e.maxContentLength ) { r.destroy(); - _( + v( m( "maxContentLength size of " + e.maxContentLength + @@ -829,7 +750,7 @@ }); r.on("error", function handleStreamError(t) { if (H.aborted) return; - _(p(t, e, null, a)); + v(p(t, e, null, a)); }); r.on("end", function handleStreamEnd() { var t = Buffer.concat(o); @@ -843,19 +764,19 @@ } } i.data = t; - s(y, _, i); + s(y, v, i); }); } }); H.on("error", function handleRequestError(t) { if (H.aborted && t.code !== "ERR_FR_TOO_MANY_REDIRECTS") return; - _(p(t, e, null, H)); + v(p(t, e, null, H)); }); if (e.timeout) { - var B = parseInt(e.timeout, 10); - if (isNaN(B)) { - _( + var $ = parseInt(e.timeout, 10); + if (isNaN($)) { + v( m( "error trying to parse `config.timeout` to int", e, @@ -865,11 +786,11 @@ ); return; } - H.setTimeout(B, function handleRequestTimeout() { + H.setTimeout($, function handleRequestTimeout() { H.abort(); - _( + v( m( - "timeout of " + B + "ms exceeded", + "timeout of " + $ + "ms exceeded", e, e.transitional && e.transitional.clarifyTimeoutError @@ -884,15 +805,15 @@ e.cancelToken.promise.then(function onCanceled(e) { if (H.aborted) return; H.abort(); - _(e); + v(e); }); } - if (n.isStream(v)) { - v.on("error", function handleStreamError(t) { - _(p(t, e, null, H)); + if (n.isStream(_)) { + _.on("error", function handleStreamError(t) { + v(p(t, e, null, H)); }).pipe(H); } else { - H.end(v); + H.end(_); } }); }; @@ -1002,12 +923,12 @@ m = null; }; if (n.isStandardBrowserEnv()) { - var _ = + var v = (e.withCredentials || l(y)) && e.xsrfCookieName ? a.read(e.xsrfCookieName) : undefined; - if (_) { - d[e.xsrfHeaderName] = _; + if (v) { + d[e.xsrfHeaderName] = v; } } if ("setRequestHeader" in m) { @@ -2662,9 +2583,21 @@ var o = r(2413).Writable; var u = r(2357); var l = r(1133); + (function detectUnsupportedEnvironment() { + var e = typeof process !== "undefined"; + var t = + typeof window !== "undefined" && + typeof document !== "undefined"; + var r = isFunction(Error.captureStackTrace); + if (!e && (t || !r)) { + console.warn( + "The follow-redirects package should be excluded from browser builds." + ); + } + })(); var c = false; try { - u(new s()); + u(new s("")); } catch (e) { c = e.code === "ERR_INVALID_URL"; } @@ -2713,11 +2646,11 @@ "ERR_FR_MAX_BODY_LENGTH_EXCEEDED", "Request body larger than maxBodyLength limit" ); - var _ = createErrorType( + var v = createErrorType( "ERR_STREAM_WRITE_AFTER_END", "write after end" ); - var v = o.prototype.destroy || noop; + var _ = o.prototype.destroy || noop; function RedirectableRequest(e, t) { o.call(this); this._sanitizeOptions(e); @@ -2752,12 +2685,12 @@ }; RedirectableRequest.prototype.destroy = function (e) { destroyRequest(this._currentRequest, e); - v.call(this, e); + _.call(this, e); return this; }; RedirectableRequest.prototype.write = function (e, t, r) { if (this._ending) { - throw new _(); + throw new v(); } if (!isString(e) && !isBuffer(e)) { throw new TypeError( @@ -3131,7 +3064,9 @@ } function createErrorType(e, t, r) { function CustomError(r) { - Error.captureStackTrace(this, this.constructor); + if (isFunction(Error.captureStackTrace)) { + Error.captureStackTrace(this, this.constructor); + } Object.assign(this, r || {}); this.code = e; this.message = this.cause @@ -3796,7 +3731,7 @@ } return t; } - var _ = { + var v = { date: 9, day: 11, weekday: 11, @@ -3819,7 +3754,7 @@ r; for (r in e) { if (hasOwnProp(e, r)) { - t.push({ unit: r, priority: _[r] }); + t.push({ unit: r, priority: v[r] }); } } t.sort(function (e, t) { @@ -3827,23 +3762,23 @@ }); return t; } - var v = /\d/, + var _ = /\d/, k = /\d\d/, w = /\d{3}/, b = /\d{4}/, S = /[+-]?\d{6}/, - O = /\d\d?/, - T = /\d\d\d\d?/, - M = /\d\d\d\d\d\d?/, + T = /\d\d?/, + O = /\d\d\d\d?/, + x = /\d\d\d\d\d\d?/, D = /\d{1,3}/, - x = /\d{1,4}/, + M = /\d{1,4}/, R = /[+-]?\d{1,6}/, - E = /\d+/, - C = /[+-]?\d+/, - F = /Z|[+-]\d\d:?\d\d/gi, + C = /\d+/, + F = /[+-]?\d+/, + E = /Z|[+-]\d\d:?\d\d/gi, P = /Z|[+-]\d\d(?::?\d\d)?/gi, - A = /[+-]?\d+(\.\d{1,3})?/, - N = + N = /[+-]?\d+(\.\d{1,3})?/, + A = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i, Y = /^[1-9]\d?/, I = /^([1-9]\d|\d)/, @@ -3928,10 +3863,10 @@ j = 1, q = 2, H = 3, - B = 4, - V = 5, - G = 6, - $ = 7, + $ = 4, + B = 5, + V = 6, + G = 7, z = 8; addFormatToken("Y", 0, 0, function () { var e = this.year(); @@ -3943,9 +3878,9 @@ addFormatToken(0, ["YYYY", 4], 0, "year"); addFormatToken(0, ["YYYYY", 5], 0, "year"); addFormatToken(0, ["YYYYYY", 6, true], 0, "year"); - addRegexToken("Y", C); - addRegexToken("YY", O, k); - addRegexToken("YYYY", x, b); + addRegexToken("Y", F); + addRegexToken("YY", T, k); + addRegexToken("YYYY", M, b); addRegexToken("YYYYY", R, S); addRegexToken("YYYYYY", R, S); addParseToken(["YYYYY", "YYYYYY"], W); @@ -4108,8 +4043,8 @@ addFormatToken("MMMM", 0, 0, function (e) { return this.localeData().months(this, e); }); - addRegexToken("M", O, Y); - addRegexToken("MM", O, k); + addRegexToken("M", T, Y); + addRegexToken("MM", T, k); addRegexToken("MMM", function (e, t) { return t.monthsShortRegex(e); }); @@ -4127,16 +4062,16 @@ getParsingFlags(r).invalidMonth = e; } }); - var Q = + var X = "January_February_March_April_May_June_July_August_September_October_November_December".split( "_" ), - X = "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split( + Q = "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split( "_" ), K = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/, - ee = N, - te = N; + ee = A, + te = A; function localeMonths(e, t) { if (!e) { return isArray(this._months) @@ -4444,10 +4379,10 @@ } addFormatToken("w", ["ww", 2], "wo", "week"); addFormatToken("W", ["WW", 2], "Wo", "isoWeek"); - addRegexToken("w", O, Y); - addRegexToken("ww", O, k); - addRegexToken("W", O, Y); - addRegexToken("WW", O, k); + addRegexToken("w", T, Y); + addRegexToken("ww", T, k); + addRegexToken("W", T, Y); + addRegexToken("WW", T, k); addWeekParseToken( ["w", "ww", "W", "WW"], function (e, t, r, n) { @@ -4484,9 +4419,9 @@ }); addFormatToken("e", 0, 0, "weekday"); addFormatToken("E", 0, 0, "isoWeekday"); - addRegexToken("d", O); - addRegexToken("e", O); - addRegexToken("E", O); + addRegexToken("d", T); + addRegexToken("e", T); + addRegexToken("E", T); addRegexToken("dd", function (e, t) { return t.weekdaysMinRegex(e); }); @@ -4535,9 +4470,9 @@ ), se = "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), ae = "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), - ie = N, - oe = N, - ue = N; + ie = A, + oe = A, + ue = A; function localeWeekdays(e, t) { var r = isArray(this._weekdays) ? this._weekdays @@ -4898,16 +4833,16 @@ } addRegexToken("a", matchMeridiem); addRegexToken("A", matchMeridiem); - addRegexToken("H", O, I); - addRegexToken("h", O, Y); - addRegexToken("k", O, Y); - addRegexToken("HH", O, k); - addRegexToken("hh", O, k); - addRegexToken("kk", O, k); - addRegexToken("hmm", T); - addRegexToken("hmmss", M); - addRegexToken("Hmm", T); - addRegexToken("Hmmss", M); + addRegexToken("H", T, I); + addRegexToken("h", T, Y); + addRegexToken("k", T, Y); + addRegexToken("HH", T, k); + addRegexToken("hh", T, k); + addRegexToken("kk", T, k); + addRegexToken("hmm", O); + addRegexToken("hmmss", x); + addRegexToken("Hmm", O); + addRegexToken("Hmmss", x); addParseToken(["H", "HH"], H); addParseToken(["k", "kk"], function (e, t, r) { var n = toInt(e); @@ -4924,28 +4859,28 @@ addParseToken("hmm", function (e, t, r) { var n = e.length - 2; t[H] = toInt(e.substr(0, n)); - t[B] = toInt(e.substr(n)); + t[$] = toInt(e.substr(n)); getParsingFlags(r).bigHour = true; }); addParseToken("hmmss", function (e, t, r) { var n = e.length - 4, s = e.length - 2; t[H] = toInt(e.substr(0, n)); - t[B] = toInt(e.substr(n, 2)); - t[V] = toInt(e.substr(s)); + t[$] = toInt(e.substr(n, 2)); + t[B] = toInt(e.substr(s)); getParsingFlags(r).bigHour = true; }); addParseToken("Hmm", function (e, t, r) { var n = e.length - 2; t[H] = toInt(e.substr(0, n)); - t[B] = toInt(e.substr(n)); + t[$] = toInt(e.substr(n)); }); addParseToken("Hmmss", function (e, t, r) { var n = e.length - 4, s = e.length - 2; t[H] = toInt(e.substr(0, n)); - t[B] = toInt(e.substr(n, 2)); - t[V] = toInt(e.substr(s)); + t[$] = toInt(e.substr(n, 2)); + t[B] = toInt(e.substr(s)); }); function localeIsPM(e) { return (e + "").toLowerCase().charAt(0) === "p"; @@ -4966,8 +4901,8 @@ ordinal: m, dayOfMonthOrdinalParse: p, relativeTime: g, - months: Q, - monthsShort: X, + months: X, + monthsShort: Q, week: re, weekdays: ne, weekdaysMin: ae, @@ -5181,14 +5116,14 @@ : r[H] < 0 || r[H] > 24 || (r[H] === 24 && - (r[B] !== 0 || r[V] !== 0 || r[G] !== 0)) + (r[$] !== 0 || r[B] !== 0 || r[V] !== 0)) ? H + : r[$] < 0 || r[$] > 59 + ? $ : r[B] < 0 || r[B] > 59 ? B - : r[V] < 0 || r[V] > 59 + : r[V] < 0 || r[V] > 999 ? V - : r[G] < 0 || r[G] > 999 - ? G : -1; if ( getParsingFlags(e)._overflowDayOfYear && @@ -5197,7 +5132,7 @@ t = q; } if (getParsingFlags(e)._overflowWeeks && t === -1) { - t = $; + t = G; } if (getParsingFlags(e)._overflowWeekday && t === -1) { t = z; @@ -5211,7 +5146,7 @@ ge = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/, ye = /Z|[+-]\d\d(?::?\d\d)?/, - _e = [ + ve = [ ["YYYYYY-MM-DD", /[+-]\d{6}-\d\d-\d\d/], ["YYYY-MM-DD", /\d{4}-\d\d-\d\d/], ["GGGG-[W]WW-E", /\d{4}-W\d\d-\d/], @@ -5226,7 +5161,7 @@ ["YYYYMM", /\d{6}/, false], ["YYYY", /\d{4}/, false], ], - ve = [ + _e = [ ["HH:mm:ss.SSSS", /\d\d:\d\d:\d\d\.\d+/], ["HH:mm:ss,SSSS", /\d\d:\d\d:\d\d,\d+/], ["HH:mm:ss", /\d\d:\d\d:\d\d/], @@ -5261,14 +5196,14 @@ i, o, u, - l = _e.length, - c = ve.length; + l = ve.length, + c = _e.length; if (s) { getParsingFlags(e).iso = true; for (t = 0, r = l; t < r; t++) { - if (_e[t][1].exec(s[1])) { - i = _e[t][0]; - a = _e[t][2] !== false; + if (ve[t][1].exec(s[1])) { + i = ve[t][0]; + a = ve[t][2] !== false; break; } } @@ -5278,8 +5213,8 @@ } if (s[3]) { for (t = 0, r = c; t < r; t++) { - if (ve[t][1].exec(s[3])) { - o = (s[2] || " ") + ve[t][0]; + if (_e[t][1].exec(s[3])) { + o = (s[2] || " ") + _e[t][0]; break; } } @@ -5309,7 +5244,7 @@ function extractFromRFC2822Strings(e, t, r, n, s, a) { var i = [ untruncateYear(e), - X.indexOf(t), + Q.indexOf(t), parseInt(r, 10), parseInt(n, 10), parseInt(s, 10), @@ -5470,9 +5405,9 @@ } if ( e._a[H] === 24 && + e._a[$] === 0 && e._a[B] === 0 && - e._a[V] === 0 && - e._a[G] === 0 + e._a[V] === 0 ) { e._nextDay = true; e._a[H] = 0; @@ -5787,7 +5722,7 @@ } } ), - Oe = deprecate( + Te = deprecate( "moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/", function () { var e = createLocal.apply(null, arguments); @@ -5825,7 +5760,7 @@ var now = function () { return Date.now ? Date.now() : +new Date(); }; - var Te = [ + var Oe = [ "year", "quarter", "month", @@ -5840,12 +5775,12 @@ var t, r = false, n, - s = Te.length; + s = Oe.length; for (t in e) { if ( hasOwnProp(e, t) && !( - J.call(Te, t) !== -1 && + J.call(Oe, t) !== -1 && (e[t] == null || !isNaN(e[t])) ) ) { @@ -5853,11 +5788,11 @@ } } for (n = 0; n < s; ++n) { - if (e[Te[n]]) { + if (e[Oe[n]]) { if (r) { return false; } - if (parseFloat(e[Te[n]]) !== toInt(e[Te[n]])) { + if (parseFloat(e[Oe[n]]) !== toInt(e[Oe[n]])) { r = true; } } @@ -5939,7 +5874,7 @@ r._useUTC = true; r._tzm = offsetFromString(P, e); }); - var Me = /([\+\-]|\d\d)/gi; + var xe = /([\+\-]|\d\d)/gi; function offsetFromString(e, t) { var r = (t || "").match(e), n, @@ -5949,7 +5884,7 @@ return null; } n = r[r.length - 1] || []; - s = (n + "").match(Me) || ["-", 0, 0]; + s = (n + "").match(xe) || ["-", 0, 0]; a = +(s[1] * 60) + toInt(s[2]); return a === 0 ? 0 : s[0] === "+" ? a : -a; } @@ -6042,7 +5977,7 @@ if (this._tzm != null) { this.utcOffset(this._tzm, false, true); } else if (typeof this._i === "string") { - var e = offsetFromString(F, this._i); + var e = offsetFromString(E, this._i); if (e != null) { this.utcOffset(e); } else { @@ -6095,7 +6030,7 @@ } var De = /^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/, - xe = + Me = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; function createDuration(e, t) { var r = e, @@ -6118,11 +6053,11 @@ y: 0, d: toInt(n[q]) * s, h: toInt(n[H]) * s, - m: toInt(n[B]) * s, - s: toInt(n[V]) * s, - ms: toInt(absRound(n[G] * 1e3)) * s, + m: toInt(n[$]) * s, + s: toInt(n[B]) * s, + ms: toInt(absRound(n[V] * 1e3)) * s, }; - } else if ((n = xe.exec(e))) { + } else if ((n = Me.exec(e))) { s = n[1] === "-" ? -1 : 1; r = { y: parseIso(n[2], s), @@ -6231,7 +6166,7 @@ } } var Re = createAdder(1, "add"), - Ee = createAdder(-1, "subtract"); + Ce = createAdder(-1, "subtract"); function isString(e) { return typeof e === "string" || e instanceof String; } @@ -6596,7 +6531,7 @@ return this; } } - var Ce = deprecate( + var Fe = deprecate( "moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.", function (e) { if (e === undefined) { @@ -6609,23 +6544,23 @@ function localeData() { return this._locale; } - var Fe = 1e3, - Pe = 60 * Fe, - Ae = 60 * Pe, - Ne = (365 * 400 + 97) * 24 * Ae; + var Ee = 1e3, + Pe = 60 * Ee, + Ne = 60 * Pe, + Ae = (365 * 400 + 97) * 24 * Ne; function mod$1(e, t) { return ((e % t) + t) % t; } function localStartOfDate(e, t, r) { if (e < 100 && e >= 0) { - return new Date(e + 400, t, r) - Ne; + return new Date(e + 400, t, r) - Ae; } else { return new Date(e, t, r).valueOf(); } } function utcStartOfDate(e, t, r) { if (e < 100 && e >= 0) { - return Date.UTC(e + 400, t, r) - Ne; + return Date.UTC(e + 400, t, r) - Ae; } else { return Date.UTC(e, t, r); } @@ -6677,7 +6612,7 @@ t = this._d.valueOf(); t -= mod$1( t + (this._isUTC ? 0 : this.utcOffset() * Pe), - Ae + Ne ); break; case "minute": @@ -6686,7 +6621,7 @@ break; case "second": t = this._d.valueOf(); - t -= mod$1(t, Fe); + t -= mod$1(t, Ee); break; } this._d.setTime(t); @@ -6744,13 +6679,13 @@ case "hour": t = this._d.valueOf(); t += - Ae - + Ne - mod$1( t + (this._isUTC ? 0 : this.utcOffset() * Pe), - Ae + Ne ) - 1; break; @@ -6760,7 +6695,7 @@ break; case "second": t = this._d.valueOf(); - t += Fe - mod$1(t, Fe) - 1; + t += Ee - mod$1(t, Ee) - 1; break; } this._d.setTime(t); @@ -6846,10 +6781,10 @@ } } ); - addRegexToken("y", E); - addRegexToken("yy", E); - addRegexToken("yyy", E); - addRegexToken("yyyy", E); + addRegexToken("y", C); + addRegexToken("yy", C); + addRegexToken("yyy", C); + addRegexToken("yyyy", C); addRegexToken("yo", matchEraYearOrdinal); addParseToken(["y", "yy", "yyy", "yyyy"], W); addParseToken(["yo"], function (e, t, r, n) { @@ -7029,7 +6964,7 @@ return t.erasNarrowRegex(e); } function matchEraYearOrdinal(e, t) { - return t._eraYearOrdinalRegex || E; + return t._eraYearOrdinalRegex || C; } function computeErasParse() { var e = [], @@ -7080,12 +7015,12 @@ addWeekYearFormatToken("ggggg", "weekYear"); addWeekYearFormatToken("GGGG", "isoWeekYear"); addWeekYearFormatToken("GGGGG", "isoWeekYear"); - addRegexToken("G", C); - addRegexToken("g", C); - addRegexToken("GG", O, k); - addRegexToken("gg", O, k); - addRegexToken("GGGG", x, b); - addRegexToken("gggg", x, b); + addRegexToken("G", F); + addRegexToken("g", F); + addRegexToken("GG", T, k); + addRegexToken("gg", T, k); + addRegexToken("GGGG", M, b); + addRegexToken("gggg", M, b); addRegexToken("GGGGG", R, S); addRegexToken("ggggg", R, S); addWeekParseToken( @@ -7152,7 +7087,7 @@ return this; } addFormatToken("Q", 0, "Qo", "quarter"); - addRegexToken("Q", v); + addRegexToken("Q", _); addParseToken("Q", function (e, t) { t[j] = (toInt(e) - 1) * 3; }); @@ -7162,8 +7097,8 @@ : this.month((e - 1) * 3 + (this.month() % 3)); } addFormatToken("D", ["DD", 2], "Do", "date"); - addRegexToken("D", O, Y); - addRegexToken("DD", O, k); + addRegexToken("D", T, Y); + addRegexToken("DD", T, k); addRegexToken("Do", function (e, t) { return e ? t._dayOfMonthOrdinalParse || t._ordinalParse @@ -7171,7 +7106,7 @@ }); addParseToken(["D", "DD"], q); addParseToken("Do", function (e, t) { - t[q] = toInt(e.match(O)[0]); + t[q] = toInt(e.match(T)[0]); }); var Ye = makeGetSet("Date", true); addFormatToken("DDD", ["DDDD", 3], "DDDo", "dayOfYear"); @@ -7190,14 +7125,14 @@ return e == null ? t : this.add(e - t, "d"); } addFormatToken("m", ["mm", 2], 0, "minute"); - addRegexToken("m", O, I); - addRegexToken("mm", O, k); - addParseToken(["m", "mm"], B); + addRegexToken("m", T, I); + addRegexToken("mm", T, k); + addParseToken(["m", "mm"], $); var Ie = makeGetSet("Minutes", false); addFormatToken("s", ["ss", 2], 0, "second"); - addRegexToken("s", O, I); - addRegexToken("ss", O, k); - addParseToken(["s", "ss"], V); + addRegexToken("s", T, I); + addRegexToken("ss", T, k); + addParseToken(["s", "ss"], B); var Ue = makeGetSet("Seconds", false); addFormatToken("S", 0, 0, function () { return ~~(this.millisecond() / 100); @@ -7224,15 +7159,15 @@ addFormatToken(0, ["SSSSSSSSS", 9], 0, function () { return this.millisecond() * 1e6; }); - addRegexToken("S", D, v); + addRegexToken("S", D, _); addRegexToken("SS", D, k); addRegexToken("SSS", D, w); var Le, We; for (Le = "SSSS"; Le.length <= 9; Le += "S") { - addRegexToken(Le, E); + addRegexToken(Le, C); } function parseMs(e, t) { - t[G] = toInt(("0." + e) * 1e3); + t[V] = toInt(("0." + e) * 1e3); } for (Le = "S"; Le.length <= 9; Le += "S") { addParseToken(Le, parseMs); @@ -7266,15 +7201,15 @@ je.isSameOrAfter = isSameOrAfter; je.isSameOrBefore = isSameOrBefore; je.isValid = isValid$2; - je.lang = Ce; + je.lang = Fe; je.locale = locale; je.localeData = localeData; - je.max = Oe; + je.max = Te; je.min = Se; je.parsingFlags = parsingFlags; je.set = stringSet; je.startOf = startOf; - je.subtract = Ee; + je.subtract = Ce; je.toArray = toArray; je.toObject = toObject; je.toDate = toDate; @@ -7620,16 +7555,16 @@ return this.as(e); }; } - var Be = makeAs("ms"), - Ve = makeAs("s"), - Ge = makeAs("m"), - $e = makeAs("h"), + var $e = makeAs("ms"), + Be = makeAs("s"), + Ve = makeAs("m"), + Ge = makeAs("h"), ze = makeAs("d"), Ze = makeAs("w"), Je = makeAs("M"), - Qe = makeAs("Q"), - Xe = makeAs("y"), - Ke = Be; + Xe = makeAs("Q"), + Qe = makeAs("y"), + Ke = $e; function clone$1() { return createDuration(this); } @@ -7790,15 +7725,15 @@ ct.add = add$1; ct.subtract = subtract$1; ct.as = as; - ct.asMilliseconds = Be; - ct.asSeconds = Ve; - ct.asMinutes = Ge; - ct.asHours = $e; + ct.asMilliseconds = $e; + ct.asSeconds = Be; + ct.asMinutes = Ve; + ct.asHours = Ge; ct.asDays = ze; ct.asWeeks = Ze; ct.asMonths = Je; - ct.asQuarters = Qe; - ct.asYears = Xe; + ct.asQuarters = Xe; + ct.asYears = Qe; ct.valueOf = Ke; ct._bubble = bubble; ct.clone = clone$1; @@ -7821,11 +7756,11 @@ "toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)", toISOString$1 ); - ct.lang = Ce; + ct.lang = Fe; addFormatToken("X", 0, 0, "unix"); addFormatToken("x", 0, 0, "valueOf"); - addRegexToken("x", C); - addRegexToken("X", A); + addRegexToken("x", F); + addRegexToken("X", N); addParseToken("X", function (e, t, r) { r._d = new Date(parseFloat(e) * 1e3); }); diff --git a/package.json b/package.json index 612cd5c..ec28eb9 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "build": "rimraf build/ && prettier --write source/ && tsc", "format": "prettier --write \"**/*.{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", - "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": [], "author": "", diff --git a/source/config/config.ts b/source/config/config.ts index 344a914..9d061e5 100644 --- a/source/config/config.ts +++ b/source/config/config.ts @@ -3,19 +3,61 @@ import { readFileSync } from "fs"; interface IConfig { base_url: string; schedule: string[]; - delay: number; - row_per_batch: number; startDate: 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 config: IConfig = { base_url: j_config["base_url"], schedule: j_config["schedule"], - delay: j_config["delay"], - row_per_batch: j_config["row_per_batch"], - startDate: j_config["startDate"], - endDate: j_config["endDate"], + startDate: j_config["startDate"], // T_OrderHeaderDate. Format: YYYY-MM-DD + endDate: j_config["endDate"], // T_OrderHeaderDate. Format: YYYY-MM-DD + messageTypes: { + 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; diff --git a/source/lib-inject.ts b/source/lib-inject.ts index 77852ea..ba43276 100644 --- a/source/lib-inject.ts +++ b/source/lib-inject.ts @@ -3,47 +3,46 @@ import config from "./config/config"; import logging from "./config/logging"; import { NAME_SPACE } from "./server"; +type MessageType = "mcu" | "payment"; + export const getListOutbox = async ( + messageType: MessageType, statusOutbox: string, startDate: string, endDate: string ) => { - const url = config.base_url + "mockup/sendwa/sendwa/listoutbox"; - logging.info(NAME_SPACE, "\t INFO : " + url); + const 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 Start Date: " + startDate); - logging.info(NAME_SPACE, "\t INFO End Date: " + endDate); try { const resp = await axios.post( url, - { - statusOutbox: statusOutbox, - startDate: startDate, - endDate: endDate, - }, + { statusOutbox, startDate, endDate }, { headers: { "Content-Type": "application/json; charset=UTF-8" }, responseType: "text", } ); - const jresp = - typeof resp.data === "string" ? JSON.parse(resp.data) : resp.data; - return jresp; + return typeof resp.data === "string" + ? JSON.parse(resp.data) + : resp.data; } catch (e) { - if (axios.isAxiosError(e)) { - 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); - } + handleAxiosError(e, messageType); } }; -export const sendToQontak = async (param: any) => { - const url = config.base_url + "mockup/sendwa/sendwa/QontakSendMsg"; - logging.info(NAME_SPACE, "\t INFO : " + url); +export const sendToQontak = async (messageType: MessageType, param: any) => { + const 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)); try { @@ -55,29 +54,31 @@ export const sendToQontak = async (param: any) => { logging.info( NAME_SPACE, - "\t INFO Resp Qontak: " + JSON.stringify(statusResp) + `\t INFO Resp [${ + config.messageTypes[messageType].name + }]: ${JSON.stringify(statusResp)}` ); if (statusResp != "OK") { logging.error( NAME_SPACE, - "\t Error Qontak: " + JSON.stringify(resp.data) + `\t Error [${ + config.messageTypes[messageType].name + }]: ${JSON.stringify(resp.data)}` ); } return statusResp; } catch (e) { - if (axios.isAxiosError(e)) { - 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); - } + handleAxiosError(e, messageType); } }; -export const uploadFileCdn = async (param: any) => { - const url = config.base_url + "mockup/sendwa/sendwa/uploadfile"; - logging.info(NAME_SPACE, "\t INFO : " + url); +export const uploadFileCdn = async (messageType: MessageType, param: any) => { + const 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)); try { @@ -85,28 +86,31 @@ export const uploadFileCdn = async (param: any) => { headers: { "Content-Type": "application/json; charset=UTF-8" }, responseType: "text", }); - const statusResp = resp.data.status; logging.info( NAME_SPACE, - "\t INFO Resp Upload: " + JSON.stringify(statusResp) + `\t INFO Resp Upload [${ + config.messageTypes[messageType].name + }]: ${JSON.stringify(statusResp)}` ); return statusResp; } catch (e) { - if (axios.isAxiosError(e)) { - 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); - } + handleAxiosError(e, messageType); } }; -export const changeStatusOutbox = async (param: any) => { - const url = config.base_url + "mockup/sendwa/sendwa/changeStatusOutbox"; - logging.info(NAME_SPACE, "\t INFO : " + url); +export const changeStatusOutbox = async ( + messageType: MessageType, + 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)); try { @@ -114,21 +118,31 @@ export const changeStatusOutbox = async (param: any) => { headers: { "Content-Type": "application/json; charset=UTF-8" }, responseType: "text", }); - const statusResp = resp.data.status; logging.info( NAME_SPACE, - "\t INFO Resp Change Status: " + JSON.stringify(statusResp) + `\t INFO Resp Change Status [${ + config.messageTypes[messageType].name + }]: ${JSON.stringify(statusResp)}` ); return statusResp; } catch (e) { - if (axios.isAxiosError(e)) { - 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); - } + handleAxiosError(e, messageType); } }; + +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}` + ); + } +} diff --git a/source/server.ts b/source/server.ts index 4457227..432068a 100644 --- a/source/server.ts +++ b/source/server.ts @@ -9,22 +9,24 @@ import { changeStatusOutbox, } from "./lib-inject"; -export const NAME_SPACE = "Gateway Send WA"; +export const NAME_SPACE = "WA_GATEWAY"; const VERSION = "1.1"; let isRunning = false; logging.info(NAME_SPACE, "Starting. Ver:", `${VERSION}`); +type MessageType = "mcu" | "payment"; + const on_init = async () => { logging.info( NAME_SPACE, "-------------------- ON INIT START --------------------" ); - await main("N"); - await main("E"); - await main("R"); + // Process both message types + await processMessageType("mcu"); + await processMessageType("payment"); logging.info( NAME_SPACE, @@ -34,7 +36,56 @@ const on_init = async () => { 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) { logging.info(NAME_SPACE, "Process is running. Skip this run."); return; @@ -44,185 +95,106 @@ async function main(status: string) { try { logging.info( 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); - - /* Pesan minta diproses kirim */ - if (status == "N") { - logging.info(NAME_SPACE, "GET Processed (N) Message"); - - 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 + switch (status) { + case "N": + logging.info( + NAME_SPACE, + `GET Processed (N) Message - ${config.messageTypes[messageType].name}` + ); + for (const outbox of outboxs) { + await processOutboxItem( + messageType, + outbox, + status, + outbox.XWaOutboxIsRetry ); - 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); } - } - } 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; + break; - if (retry >= 5) { - logging.info( - NAME_SPACE, - "\t Retry count more than 5. Skip this message." - ); - continue; - } else { - retry++; - - if (outbox.fileUrl == null) { - let resp = uploadFile(outbox); + case "E": + logging.info( + NAME_SPACE, + `GET Error (E) Message - ${config.messageTypes[messageType].name}` + ); + for (const outbox of outboxs) { + const retry = outbox.XWaOutboxIsRetry; + if (retry >= 5) { logging.info( NAME_SPACE, - "\t Uploading File to CDN: ", - resp + "\t Retry count more than 5. Skip this message." ); - 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 { - 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); + continue; } - } - } - } else if (status == "R") { - logging.info(NAME_SPACE, "GET Rejected (R) Message"); - - 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 + await processOutboxItem( + messageType, + outbox, + status, + retry + 1 ); - 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"); - isRunning = false; + logging.info( + NAME_SPACE, + `End Job Get Order - ${config.messageTypes[messageType].name}` + ); } catch (e) { - isRunning = false; if (e instanceof Error) { logging.error(NAME_SPACE, e.message); } else { - logging.error(NAME_SPACE, "Unknown"); + logging.error(NAME_SPACE, "Unknown error"); } + } finally { + isRunning = false; } } -async function getLists(status: string) { - const statusOutbox = status; - // const statusOutbox = config.statusOutbox; - const startDate = config.startDate; - const endDate = config.endDate; +async function getLists(messageType: MessageType, status: string) { + const { startDate, endDate } = config; - var resp = await getListOutbox(statusOutbox, startDate, endDate); - if (resp["status"] != "OK") { + const resp = await getListOutbox(messageType, status, startDate, endDate); + if (resp?.status !== "OK") { logging.error(NAME_SPACE, "\t Error get outbox data", resp); - logging.error(NAME_SPACE, "\t Error: ", resp); - } else { - logging.info( - NAME_SPACE, - "\t Success get order data found ", - resp.data.length - ); + return []; } - const result = - typeof resp.data === "string" ? JSON.parse(resp.data) : resp.data; - return result; + + logging.info( + 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) { - let payload = { +async function sentMsg( + messageType: MessageType, + item: any, + status: string, + retry: number +) { + const payload = { orderID: item.orderID, orderDate: item.orderDate, patientDOB: item.patientDOB, @@ -234,45 +206,41 @@ async function sentMsg(item: any, status: string, retry: number) { retryOutbox: retry, sendWaID: item.sendWaID, }; - // return payload; - /* Axios for POST to the WA API */ - var response = await sendToQontak(payload); // Response "OK" - return response; + return await sendToQontak(messageType, payload); } -async function uploadFile(item: any) { - let payload = { +async function uploadFile(messageType: MessageType, item: any) { + const payload = { fileName: item.fileName, rptUrl: item.localUrl, mime: "application/pdf", XWaOutboxID: item.sendWaID, }; - // return payload; - var response = await uploadFileCdn(payload); // Response "OK" - return response; + return await uploadFileCdn(messageType, payload); } -async function changeStatus(item: any, status: string, retry: number) { - let payload = { +async function changeStatus( + messageType: MessageType, + item: any, + status: string, + retry: number +) { + const payload = { toStatus: status, XWaOutboxID: item.sendWaID, retry: retry, }; - // return payload; - var response = await changeStatusOutbox(payload); - return response; + return await changeStatusOutbox(messageType, payload); } async function delay(ms: number) { 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) { Xcron.schedule( sched, @@ -281,14 +249,11 @@ for (const sched of config.schedule) { NAME_SPACE, " -------------------- ON INIT SCHEDULE --------------------" ); - - await main("N"); - await main("E"); - await main("R"); - + await processMessageType("mcu"); + await processMessageType("payment"); logging.info( NAME_SPACE, - "-------------------- END SCHEDULE--------------------" + "-------------------- END SCHEDULE --------------------" ); }, {