Merged in feature/IO-2460-Node-18-Server (pull request #1061)

Feature/IO-2460 Node 18 Server

Approved-by: Patrick Fic
This commit is contained in:
Dave Richer
2023-11-21 18:16:12 +00:00
committed by Patrick Fic
13 changed files with 7567 additions and 5885 deletions

View File

@@ -8,13 +8,13 @@ orbs:
jobs: jobs:
api-deploy: api-deploy:
docker: docker:
- image: "cimg/base:stable" - image: cimg/node:18
steps: steps:
- checkout - checkout
- eb/setup - eb/setup
- run: - run:
command: | command: |
eb init imex-online-production-api -r ca-central-1 -p "Node.js 16 running on 64bit Amazon Linux 2" eb init imex-online-production-api -r ca-central-1 -p "Node.js 18 running on 64bit Amazon Linux 2"
eb status --verbose eb status --verbose
eb deploy eb deploy
eb status eb status

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
legacy-peer-deps=true

View File

@@ -89,8 +89,8 @@
"analyze": "source-map-explorer 'build/static/js/*.js'", "analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "craco start", "start": "craco start",
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build", "build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"build:test": "env-cmd -f .env.test yarn run build", "build:test": "env-cmd -f .env.test npm run build",
"build-deploy:test": "yarn run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'", "build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build", "buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"test": "cypress open", "test": "cypress open",
"eject": "react-scripts eject", "eject": "react-scripts eject",

7156
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,64 +3,63 @@
"version": "0.0.1", "version": "0.0.1",
"license": "UNLICENSED", "license": "UNLICENSED",
"engines": { "engines": {
"node": ">=16.0.0", "node": ">=18.0.0",
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"scripts": { "scripts": {
"setup": "rm -rf node_modules && yarn && cd client && rm -rf node_modules && yarn", "setup": "rm -rf node_modules && npm i && cd client && rm -rf node_modules && npm i",
"admin": "cd admin && yarn start", "admin": "cd admin && npm start",
"client": "cd client && yarn start", "client": "cd client && npm start",
"server": "nodemon server.js", "server": "nodemon server.js",
"build": "cd client && yarn run build", "build": "cd client && npm run build",
"dev": "concurrently --kill-others-on-fail \"yarn run server\" \"yarn run client\"", "dev": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\"",
"deva": "concurrently --kill-others-on-fail \"yarn run server\" \"yarn run client\" \"yarn run admin\"", "deva": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\" \"npm run admin\"",
"start": "node server.js" "start": "node server.js"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-secrets-manager": "^3.388.0", "@aws-sdk/client-secrets-manager": "^3.454.0",
"@aws-sdk/credential-provider-node": "^3.319.0", "@aws-sdk/client-ses": "^3.454.0",
"@opensearch-project/opensearch": "^2.2.1", "@aws-sdk/credential-provider-node": "^3.451.0",
"aws-sdk": "^2.1326.0", "@opensearch-project/opensearch": "^2.4.0",
"aws4": "^1.12.0", "aws4": "^1.12.0",
"axios": "^0.27.2", "axios": "^1.6.2",
"bluebird": "^3.7.2", "bluebird": "^3.7.2",
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"cloudinary": "^1.34.0", "cloudinary": "^1.41.0",
"compression": "^1.7.4", "compression": "^1.7.4",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cors": "2.8.5", "cors": "2.8.5",
"csrf": "^3.1.0", "csrf": "^3.1.0",
"dinero.js": "^1.9.1", "dinero.js": "^1.9.1",
"dotenv": "16.0.3", "dotenv": "^16.3.1",
"express": "^4.18.2", "express": "^4.18.2",
"firebase-admin": "^11.5.0", "firebase-admin": "^11.11.0",
"graphql": "^16.6.0", "graphql": "^16.8.1",
"graphql-request": "^4.2.0", "graphql-request": "^6.1.0",
"graylog2": "^0.2.1", "graylog2": "^0.2.1",
"inline-css": "^4.0.2", "inline-css": "^4.0.2",
"intuit-oauth": "^4.0.0", "intuit-oauth": "^4.0.0",
"json-2-csv": "^3.19.0", "json-2-csv": "^5.0.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.29.4", "moment": "^2.29.4",
"moment-timezone": "^0.5.41", "moment-timezone": "^0.5.41",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"node-mailjet": "^6.0.2", "node-mailjet": "^6.0.4",
"node-persist": "^3.1.3", "node-persist": "^3.1.3",
"node-quickbooks": "^2.0.41", "node-quickbooks": "^2.0.43",
"nodemailer": "^6.9.1", "nodemailer": "^6.9.7",
"phone": "^3.1.35", "phone": "^3.1.41",
"query-string": "^7.1.1",
"soap": "^1.0.0", "soap": "^1.0.0",
"socket.io": "^4.6.1", "socket.io": "^4.7.2",
"ssh2-sftp-client": "^9.0.4", "ssh2-sftp-client": "^9.1.0",
"stripe": "^9.15.0", "stripe": "^14.5.0",
"twilio": "^4.8.0", "twilio": "^4.19.0",
"uuid": "^9.0.0", "uuid": "^9.0.1",
"xml2js": "^0.4.23", "xml2js": "^0.6.2",
"xmlbuilder2": "^3.0.2" "xmlbuilder2": "^3.1.1"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^7.3.0", "concurrently": "^8.2.2",
"source-map-explorer": "^2.5.2" "source-map-explorer": "^2.5.2"
} }
} }

View File

@@ -5,8 +5,8 @@ const path = require("path");
const compression = require("compression"); const compression = require("compression");
const twilio = require("twilio"); const twilio = require("twilio");
const logger = require("./server/utils/logger"); const logger = require("./server/utils/logger");
var fb = require("./server/firebase/firebase-handler"); const fb = require("./server/firebase/firebase-handler");
var cookieParser = require("cookie-parser"); const cookieParser = require("cookie-parser");
const multer = require("multer"); const multer = require("multer");
const upload = multer(); const upload = multer();
//var enforce = require("express-sslify"); //var enforce = require("express-sslify");

View File

@@ -9,7 +9,7 @@ const logger = require("../../utils/logger");
const OAuthClient = require("intuit-oauth"); const OAuthClient = require("intuit-oauth");
const client = require("../../graphql-client/graphql-client").client; const client = require("../../graphql-client/graphql-client").client;
const queries = require("../../graphql-client/queries"); const queries = require("../../graphql-client/queries");
const queryString = require("query-string"); const {parse, stringify} = require("querystring");
const oauthClient = new OAuthClient({ const oauthClient = new OAuthClient({
clientId: process.env.QBO_CLIENT_ID, clientId: process.env.QBO_CLIENT_ID,
@@ -30,7 +30,8 @@ if (process.env.NODE_ENV === "production") {
} }
exports.default = async (req, res) => { exports.default = async (req, res) => {
const params = queryString.parse(req.url.split("?").reverse()[0]); const queryString = req.url.split("?").reverse()[0];
const params = parse(queryString);
try { try {
logger.log("qbo-callback-create-token", "DEBUG", params.state, null, null); logger.log("qbo-callback-create-token", "DEBUG", params.state, null, null);
const authResponse = await oauthClient.createToken(req.url); const authResponse = await oauthClient.createToken(req.url);
@@ -58,7 +59,7 @@ exports.default = async (req, res) => {
); );
res.redirect( res.redirect(
`${url}/manage/accounting/qbo?${queryString.stringify(params)}` `${url}/manage/accounting/qbo?${stringify(params)}`
); );
} }
} catch (e) { } catch (e) {

View File

@@ -7,15 +7,19 @@ require("dotenv").config({
}); });
const axios = require("axios"); const axios = require("axios");
let nodemailer = require("nodemailer"); let nodemailer = require("nodemailer");
let aws = require("aws-sdk"); let aws = require("@aws-sdk/client-ses");
let { defaultProvider } = require("@aws-sdk/credential-provider-node");
const logger = require("../utils/logger"); const logger = require("../utils/logger");
const client = require("../graphql-client/graphql-client").client; const client = require("../graphql-client/graphql-client").client;
const queries = require("../graphql-client/queries"); const queries = require("../graphql-client/queries");
const ses = new aws.SES({ const ses = new aws.SES({
// The key apiVersion is no longer supported in v3, and can be removed.
// @deprecated The client uses the "latest" apiVersion.
apiVersion: "latest", apiVersion: "latest",
region: "ca-central-1", region: "ca-central-1",
defaultProvider
}); });
let transporter = nodemailer.createTransport({ let transporter = nodemailer.createTransport({
@@ -48,7 +52,6 @@ exports.sendServerEmail = async function ({ subject, text }) {
} catch (error) { } catch (error) {
console.log(error); console.log(error);
logger.log("server-email-failure", "error", null, null, error); logger.log("server-email-failure", "error", null, null, error);
res.status(500).json(error);
} }
}; };
exports.sendTaskEmail = async function ({ to, subject, text, attachments }) { exports.sendTaskEmail = async function ({ to, subject, text, attachments }) {

View File

@@ -1,6 +1,6 @@
"use strict"; "use strict";
const AWS = require("aws-sdk"); const awsSecretManager = require("@aws-sdk/client-secrets-manager");
class SecretsManager { class SecretsManager {
/** /**
@@ -8,11 +8,10 @@ class SecretsManager {
*/ */
static async getSecret(secretName, region) { static async getSecret(secretName, region) {
const config = { region: region }; const config = { region: region };
let secretsManager = new AWS.SecretsManager(config); let secretsManager = new awsSecretManager.SecretsManager(config);
try { try {
let secretValue = await secretsManager let secretValue = await secretsManager
.getSecretValue({ SecretId: secretName }) .getSecretValue({ SecretId: secretName });
.promise();
if ("SecretString" in secretValue) { if ("SecretString" in secretValue) {
return secretValue.SecretString; return secretValue.SecretString;
} else { } else {

View File

@@ -1,9 +1,8 @@
const Dinero = require("dinero.js");
const queries = require("../graphql-client/queries"); const queries = require("../graphql-client/queries");
//const client = require("../graphql-client/graphql-client").client; const {pick} = require("lodash");
const _ = require("lodash");
const GraphQLClient = require("graphql-request").GraphQLClient; const GraphQLClient = require("graphql-request").GraphQLClient;
const logger = require("../utils/logger"); const logger = require("../utils/logger");
//const client = require("../graphql-client/graphql-client").client;
const path = require("path"); const path = require("path");
const client = require("../graphql-client/graphql-client").client; const client = require("../graphql-client/graphql-client").client;
@@ -13,10 +12,10 @@ require("dotenv").config({
`.env.${process.env.NODE_ENV || "development"}` `.env.${process.env.NODE_ENV || "development"}`
), ),
}); });
const { Client, Connection } = require("@opensearch-project/opensearch"); const {Client, Connection} = require("@opensearch-project/opensearch");
const { defaultProvider } = require("@aws-sdk/credential-provider-node"); const {defaultProvider} = require("@aws-sdk/credential-provider-node");
const aws4 = require("aws4"); const aws4 = require("aws4");
const { gql } = require("graphql-request"); const {gql} = require("graphql-request");
var host = process.env.OPEN_SEARCH_HOST; var host = process.env.OPEN_SEARCH_HOST;
@@ -32,6 +31,7 @@ const createAwsConnector = (credentials, region) => {
return aws4.sign(request, credentials); return aws4.sign(request, credentials);
} }
} }
return { return {
Connection: AmazonConnection, Connection: AmazonConnection,
}; };
@@ -68,7 +68,7 @@ async function OpenSearchUpdateHandler(req, res) {
switch (req.body.table.name) { switch (req.body.table.name) {
case "jobs": case "jobs":
document = _.pick(req.body.event.data.new, [ document = pick(req.body.event.data.new, [
"id", "id",
"bodyshopid", "bodyshopid",
"clm_no", "clm_no",
@@ -92,7 +92,7 @@ async function OpenSearchUpdateHandler(req, res) {
document.bodyshopid = req.body.event.data.new.shopid; document.bodyshopid = req.body.event.data.new.shopid;
break; break;
case "vehicles": case "vehicles":
document = _.pick(req.body.event.data.new, [ document = pick(req.body.event.data.new, [
"id", "id",
"v_model_yr", "v_model_yr",
"v_model_desc", "v_model_desc",
@@ -104,7 +104,7 @@ async function OpenSearchUpdateHandler(req, res) {
document.bodyshopid = req.body.event.data.new.shopid; document.bodyshopid = req.body.event.data.new.shopid;
break; break;
case "owners": case "owners":
document = _.pick(req.body.event.data.new, [ document = pick(req.body.event.data.new, [
"id", "id",
"ownr_fn", "ownr_fn",
"ownr_ln", "ownr_ln",
@@ -131,10 +131,10 @@ async function OpenSearchUpdateHandler(req, res) {
} }
} }
`, `,
{ billId: req.body.event.data.new.id } {billId: req.body.event.data.new.id}
); );
document = { document = {
..._.pick(req.body.event.data.new, [ ...pick(req.body.event.data.new, [
"id", "id",
"date", "date",
"exported", "exported",
@@ -171,10 +171,10 @@ async function OpenSearchUpdateHandler(req, res) {
} }
} }
`, `,
{ paymentId: req.body.event.data.new.id } {paymentId: req.body.event.data.new.id}
); );
document = { document = {
..._.pick(req.body.event.data.new, [ ...pick(req.body.event.data.new, [
"id", "id",
"amount", "amount",
"created_at", "created_at",
@@ -210,7 +210,7 @@ async function OpenSearchUpdateHandler(req, res) {
async function OpenSearchSearchHandler(req, res) { async function OpenSearchSearchHandler(req, res) {
try { try {
const { search, bodyshopid, index } = req.body; const {search, bodyshopid, index} = req.body;
if (!req.user) { if (!req.user) {
res.sendStatus(401); res.sendStatus(401);
@@ -229,7 +229,7 @@ async function OpenSearchSearchHandler(req, res) {
}); });
const assocs = await client const assocs = await client
.setHeaders({ Authorization: BearerToken }) .setHeaders({Authorization: BearerToken})
.request(queries.ACTIVE_SHOP_BY_USER, { .request(queries.ACTIVE_SHOP_BY_USER, {
user: req.user.email, user: req.user.email,
}); });
@@ -238,10 +238,10 @@ async function OpenSearchSearchHandler(req, res) {
res.sendStatus(401); res.sendStatus(401);
} }
var osClient = await getClient(); const osClient = await getClient();
const { body } = await osClient.search({ const {body} = await osClient.search({
...(index ? { index } : {}), ...(index ? {index} : {}),
body: { body: {
size: 100, size: 100,
query: { query: {

View File

@@ -1,7 +1,4 @@
const GraphQLClient = require("graphql-request").GraphQLClient;
const path = require("path"); const path = require("path");
const queries = require("../graphql-client/queries");
const Dinero = require("dinero.js");
require("dotenv").config({ require("dotenv").config({
path: path.resolve( path: path.resolve(
@@ -9,10 +6,11 @@ require("dotenv").config({
`.env.${process.env.NODE_ENV || "development"}` `.env.${process.env.NODE_ENV || "development"}`
), ),
}); });
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
exports.payment = async (req, res) => { const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const { amount, stripe_acct_id } = req.body;
const processor = async (req, res) => {
const {amount, stripe_acct_id} = req.body;
try { try {
await stripe.paymentIntents await stripe.paymentIntents
@@ -44,36 +42,5 @@ exports.payment = async (req, res) => {
} }
}; };
exports.mobile_payment = async (req, res) => { exports.payment = processor;
const { amount, stripe_acct_id } = req.body; exports.mobile_payment = processor;
try {
await stripe.paymentIntents
.create(
{
//Pull the amounts from the payment request.
payment_method_types: ["card"],
amount: amount,
currency: "cad",
application_fee_amount: 50,
},
{
stripeAccount: stripe_acct_id,
}
)
.then(function (paymentIntent) {
try {
return res.send({
clientSecret: paymentIntent.client_secret,
});
} catch (err) {
return res.status(500).send({
error: err.message,
});
}
});
} catch (error) {
console.log("error", error);
res.status(400).send(error);
}
};

View File

@@ -5,16 +5,14 @@ require("dotenv").config({
`.env.${process.env.NODE_ENV || "development"}` `.env.${process.env.NODE_ENV || "development"}`
), ),
}); });
const axios = require("axios");
const client = require("../graphql-client/graphql-client").client; const client = require("../graphql-client/graphql-client").client;
const emailer = require("../email/sendemail"); const emailer = require("../email/sendemail");
const logger = require("../utils/logger");
const moment = require("moment-timezone"); const moment = require("moment-timezone");
const converter = require("json-2-csv");
exports.taskHandler = async (req, res) => { exports.taskHandler = async (req, res) => {
try { try {
const { bodyshopid, query, variables, text, to, subject, timezone } = const {bodyshopid, query, variables, text, to, subject, timezone} = req.body;
req.body;
//Run the query
//Check the variables to see if they are an object. //Check the variables to see if they are an object.
Object.keys(variables).forEach((key) => { Object.keys(variables).forEach((key) => {
@@ -26,51 +24,45 @@ exports.taskHandler = async (req, res) => {
}); });
const response = await client.request(query, variables); const response = await client.request(query, variables);
//Massage the data const rootElement = response[Object.keys(response)[0]]; //This element should always be an array.
//Send the email
const rootElement = response[Object.keys(response)[0]]; //This element shoudl always be an array. const csv = converter.json2csv(rootElement, {emptyFieldValue: ""});
let converter = require("json-2-csv");
converter.json2csv(
rootElement,
(err, csv) => {
if (err) {
res.status(500).json(err);
}
emailer.sendTaskEmail({ emailer.sendTaskEmail({
to, to,
subject, subject,
text, text,
attachments: [{ filename: "query.csv", content: csv }], attachments: [{filename: "query.csv", content: csv}],
}).catch(err => {
console.error('Errors sending CSV Email.')
}); });
res.status(200).send(csv);
}, return res.status(200).send(csv);
{ emptyFieldValue: "" }
);
} catch (error) { } catch (error) {
res.status(500).json({ error: error.message, stack: error.stackTrace }); res.status(500).json({error: error.message, stack: error.stackTrace});
} }
}; };
const isoformat = "YYYY-MM-DD"; const isoFormat = "YYYY-MM-DD";
function functionMapper(f, timezone) { function functionMapper(f, timezone) {
switch (f) { switch (f) {
case "date.today": case "date.today":
return moment().tz(timezone).format(isoformat); return moment().tz(timezone).format(isoFormat);
case "date.now": case "date.now":
return moment().tz(timezone); return moment().tz(timezone);
case "date.yesterday": case "date.yesterday":
return moment().tz(timezone).subtract(1, "day").format(isoformat); return moment().tz(timezone).subtract(1, "day").format(isoFormat);
case "date.3daysago": case "date.3daysago":
return moment().tz(timezone).subtract(3, "days").format(isoformat); return moment().tz(timezone).subtract(3, "days").format(isoFormat);
case "date.7daysago": case "date.7daysago":
return moment().tz(timezone).subtract(7, "days").format(isoformat); return moment().tz(timezone).subtract(7, "days").format(isoFormat);
case "date.tomorrow": case "date.tomorrow":
return moment().tz(timezone).add(1, "day").format(isoformat); return moment().tz(timezone).add(1, "day").format(isoFormat);
case "date.3daysfromnow": case "date.3daysfromnow":
return moment().tz(timezone).add(3, "days").format(isoformat); return moment().tz(timezone).add(3, "days").format(isoFormat);
case "date.7daysfromnow": case "date.7daysfromnow":
return moment().tz(timezone).add(7, "days").format(isoformat); return moment().tz(timezone).add(7, "days").format(isoFormat);
case "date.yesterdaytz": case "date.yesterdaytz":
return moment().tz(timezone).subtract(1, "day"); return moment().tz(timezone).subtract(1, "day");
case "date.3daysagotz": case "date.3daysagotz":

5436
yarn.lock

File diff suppressed because it is too large Load Diff