Compare commits
4 Commits
33ffdb2264
...
a8c5aad833
| Author | SHA1 | Date | |
|---|---|---|---|
| a8c5aad833 | |||
| 2c0a8608ac | |||
| dc159e4404 | |||
| c8b554eb42 |
17
.vscode/lst.code-snippets
vendored
Normal file
17
.vscode/lst.code-snippets
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"Return Function Template": {
|
||||||
|
"prefix": "returnfunc",
|
||||||
|
"body": [
|
||||||
|
"return returnFunc({",
|
||||||
|
"\tsuccess: ${1:true},",
|
||||||
|
"\tlevel: \"${2:error}\",",
|
||||||
|
"\tmodule: \"${3:system}\",",
|
||||||
|
"\tsubModule: \"${4:db}\",",
|
||||||
|
"\tmessage: \"${5:Failed to connect to the prod sql server.}\",",
|
||||||
|
"\tdata: ${6:[]},",
|
||||||
|
"\tnotify: ${7:false},",
|
||||||
|
"});"
|
||||||
|
],
|
||||||
|
"description": "Insert a returnFunc template"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
|
import { connectProdSql } from "./src/prodSql/sqlConnection.controller.js";
|
||||||
import { setupRoutes } from "./src/routeHandler.route.js";
|
import { setupRoutes } from "./src/routeHandler.route.js";
|
||||||
|
|
||||||
const port = Number(process.env.PORT);
|
const port = Number(process.env.PORT);
|
||||||
@@ -6,6 +7,9 @@ export const baseUrl = "";
|
|||||||
const startApp = async () => {
|
const startApp = async () => {
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
// start the connection to the prod sql server
|
||||||
|
connectProdSql();
|
||||||
|
|
||||||
setupRoutes(baseUrl, app);
|
setupRoutes(baseUrl, app);
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
|
|||||||
19
backend/src/configs/prodSql.config.ts
Normal file
19
backend/src/configs/prodSql.config.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type sql from "mssql";
|
||||||
|
export const prodSqlConfig: sql.config = {
|
||||||
|
server: `${process.env.PROD_SERVER}`,
|
||||||
|
database: `AlplaPROD_${process.env.PROD_PLANT_TOKEN}_cus`,
|
||||||
|
user: process.env.PROD_USER,
|
||||||
|
password: process.env.PROD_PASSWORD,
|
||||||
|
options: {
|
||||||
|
encrypt: true,
|
||||||
|
trustServerCertificate: true,
|
||||||
|
},
|
||||||
|
requestTimeout: 90000, // how long until we kill the query and fail it
|
||||||
|
pool: {
|
||||||
|
max: 20, // Maximum number of connections in the pool
|
||||||
|
min: 0, // Minimum number of connections in the pool
|
||||||
|
idleTimeoutMillis: 10000, // How long a connection is allowed to be idle before being released
|
||||||
|
reapIntervalMillis: 1000, // how often to check for idle resources to destroy
|
||||||
|
acquireTimeoutMillis: 100000, // How long until a complete timeout happens
|
||||||
|
},
|
||||||
|
};
|
||||||
5
backend/src/prodSql/sql.route.ts
Normal file
5
backend/src/prodSql/sql.route.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
const r = Router();
|
||||||
|
|
||||||
|
export default r;
|
||||||
135
backend/src/prodSql/sqlConnection.controller.ts
Normal file
135
backend/src/prodSql/sqlConnection.controller.ts
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import sql from "mssql";
|
||||||
|
import { prodSqlConfig } from "../configs/prodSql.config.js";
|
||||||
|
import { checkHostnamePort } from "../utils/checkHost.utils.js";
|
||||||
|
import { returnFunc } from "../utils/returnHelper.utils.js";
|
||||||
|
|
||||||
|
export let pool: sql.ConnectionPool;
|
||||||
|
export let connected: boolean = false;
|
||||||
|
export let reconnecting = false;
|
||||||
|
|
||||||
|
export const connectProdSql = async () => {
|
||||||
|
const serverUp = await checkHostnamePort(`${process.env.PROD_SERVER}:1433`);
|
||||||
|
|
||||||
|
if (!serverUp) {
|
||||||
|
// we will try to reconnect
|
||||||
|
connected = false;
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "Prod server is offline or unreachable.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are trying to click restart from the api for some reason we want to kick back and say no
|
||||||
|
if (connected) {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "The Sql server is already connected.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to connect to the sql server
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(prodSqlConfig);
|
||||||
|
connected = true;
|
||||||
|
console.log(
|
||||||
|
`${prodSqlConfig.server} is connected to ${prodSqlConfig.database}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "Failed to connect to the prod sql server.",
|
||||||
|
data: [error],
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const closePool = async () => {
|
||||||
|
if (!connected) {
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "There is no connection to the prod server currently.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await pool.close();
|
||||||
|
console.log("Connection pool closed");
|
||||||
|
connected = false;
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "The sql server connection has been closed",
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
connected = false;
|
||||||
|
console.log("There was an error closing the sql connection", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const reconnectToSql = async () => {
|
||||||
|
if (reconnecting) return;
|
||||||
|
|
||||||
|
//set reconnecting to true while we try to reconnect
|
||||||
|
reconnecting = true;
|
||||||
|
|
||||||
|
// start the delay out as 2 seconds
|
||||||
|
let delayStart = 2000;
|
||||||
|
let attempt = 0;
|
||||||
|
const maxAttempts = 10;
|
||||||
|
|
||||||
|
while (!connected && attempt < maxAttempts) {
|
||||||
|
attempt++;
|
||||||
|
console.log(
|
||||||
|
`Reconnect attempt ${attempt}/${maxAttempts} in ${delayStart / 1000}s ...`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await new Promise((res) => setTimeout(res, delayStart));
|
||||||
|
|
||||||
|
const serverUp = await checkHostnamePort(`${process.env.PROD_SERVER}:1433`);
|
||||||
|
|
||||||
|
if (!serverUp) {
|
||||||
|
delayStart = Math.min(delayStart * 2, 30000); // exponential backoff until up to 30000
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pool = await sql.connect(prodSqlConfig);
|
||||||
|
reconnecting = false;
|
||||||
|
connected = true;
|
||||||
|
console.log(
|
||||||
|
`${prodSqlConfig.server} is connected to ${prodSqlConfig.database}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
delayStart = Math.min(delayStart * 2, 30000);
|
||||||
|
return returnFunc({
|
||||||
|
success: false,
|
||||||
|
level: "error",
|
||||||
|
module: "system",
|
||||||
|
subModule: "db",
|
||||||
|
message: "Failed to reconnect to the prod sql server.",
|
||||||
|
data: [error],
|
||||||
|
notify: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connected) {
|
||||||
|
console.log(
|
||||||
|
"Max reconnect attempts reached on the prodSql server. Stopping retries.",
|
||||||
|
);
|
||||||
|
|
||||||
|
reconnecting = false;
|
||||||
|
// exit alert someone here
|
||||||
|
}
|
||||||
|
};
|
||||||
0
backend/src/prodSql/sqlQuery.controller.ts
Normal file
0
backend/src/prodSql/sqlQuery.controller.ts
Normal file
83
backend/src/utils/checkHost.utils.ts
Normal file
83
backend/src/utils/checkHost.utils.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import dns from "node:dns";
|
||||||
|
import net from "node:net";
|
||||||
|
|
||||||
|
type IP = {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
export const checkHostnamePort = async (host: string): Promise<boolean> => {
|
||||||
|
const [hostname, port] = host.split(":");
|
||||||
|
|
||||||
|
// a just incase to make sure we have the host name and port
|
||||||
|
if (!hostname || !port) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the hostname to an IP address
|
||||||
|
const ip = (await checkHostUp(hostname)) as IP;
|
||||||
|
|
||||||
|
if (!ip.success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const portNum = parseInt(port, 10);
|
||||||
|
|
||||||
|
const hostUp = (await pingHost(hostname, portNum)) as IP;
|
||||||
|
|
||||||
|
if (!hostUp.success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const pingHost = (host: string, port: number) => {
|
||||||
|
return new Promise((res) => {
|
||||||
|
const s = new net.Socket();
|
||||||
|
|
||||||
|
s.setTimeout(2000);
|
||||||
|
|
||||||
|
s.connect(port, host);
|
||||||
|
|
||||||
|
s.on("connect", () => {
|
||||||
|
s.destroy();
|
||||||
|
res({
|
||||||
|
success: true,
|
||||||
|
message: "Server up",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
s.on("timeout", () => {
|
||||||
|
s.destroy();
|
||||||
|
res({
|
||||||
|
success: true,
|
||||||
|
message: "Server Offline or unreachable, please check connection",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
s.on("error", (e) => {
|
||||||
|
s.destroy();
|
||||||
|
return res({
|
||||||
|
success: false,
|
||||||
|
message: "Encountered error while checking if host:port up",
|
||||||
|
error: e,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const checkHostUp = (host: string) => {
|
||||||
|
return new Promise((res) => {
|
||||||
|
dns.lookup(host, (err, address) => {
|
||||||
|
if (err) {
|
||||||
|
return res({
|
||||||
|
success: false,
|
||||||
|
message: "Error connecting to the server",
|
||||||
|
error: err,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res({ success: true, message: "Server Ip", data: address });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//checkHostnamePort("usmcd1vms036:1433");
|
||||||
48
backend/src/utils/returnHelper.utils.ts
Normal file
48
backend/src/utils/returnHelper.utils.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
interface Data {
|
||||||
|
success: boolean;
|
||||||
|
module: "system" | "ocp";
|
||||||
|
subModule?: "db" | "labeling" | "printer";
|
||||||
|
level: "info" | "error" | "debug" | "fatal";
|
||||||
|
message: string;
|
||||||
|
data?: unknown[];
|
||||||
|
notify?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This dose the return process and log at the same time, vs needing to log and return at the same time.
|
||||||
|
* When to use.
|
||||||
|
* APIs
|
||||||
|
* actual returns and needing to stop.
|
||||||
|
* Example Data
|
||||||
|
* success:true
|
||||||
|
* module: system | printers | etc
|
||||||
|
* submodule: sql connection | printer test | etc
|
||||||
|
* level "info" | "error" | "debug" | "fatal"
|
||||||
|
* message: "short description of the return"
|
||||||
|
* data: [] the data that will be passed back
|
||||||
|
* notify: false by default this is to send a notification to a users email to alert them of an issue.
|
||||||
|
*/
|
||||||
|
export const returnFunc = (data: Data) => {
|
||||||
|
const notify = data.notify ? data.notify : false;
|
||||||
|
// handle the logging part
|
||||||
|
switch (data.level) {
|
||||||
|
case "info":
|
||||||
|
console.log({ notify: notify }, data.message);
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
console.log({ notify: notify }, data.message);
|
||||||
|
break;
|
||||||
|
case "debug":
|
||||||
|
console.log({ notify: notify }, data.message);
|
||||||
|
break;
|
||||||
|
case "fatal":
|
||||||
|
console.log({ notify: notify }, data.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// api section to return
|
||||||
|
return {
|
||||||
|
success: data.success,
|
||||||
|
message: data.message,
|
||||||
|
data: data.data || [],
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"build": "esbuild backend/app.ts --bundle --platform=node --minify --outfile=dist/index.js --format=esm --packages=external",
|
"build": "esbuild backend/app.ts --bundle --platform=node --minify --outfile=dist/index.js --format=esm --packages=external",
|
||||||
"build:app": "ncc build backend/app.ts -o dist -m -s",
|
"build:app": "ncc build backend/app.ts -o dist -m -s",
|
||||||
"lint": "tsc",
|
"lint": "tsc",
|
||||||
"start": "node dist/index.js",
|
"start": "dotenvx run -f .env -- node dist/index.js",
|
||||||
"commit": "cz",
|
"commit": "cz",
|
||||||
"changeset": "changeset",
|
"changeset": "changeset",
|
||||||
"version": "changeset version",
|
"version": "changeset version",
|
||||||
|
|||||||
Reference in New Issue
Block a user