feat(sql): full sql start stop and query with crash prevention

This commit is contained in:
2025-12-22 14:54:43 -06:00
parent 878c3b3638
commit 6bb27e588d
15 changed files with 468 additions and 30 deletions

View File

@@ -0,0 +1,16 @@
export const prodSqlServerStats = `
DECLARE @UptimeSeconds INT;
DECLARE @StartTime DATETIME;
SELECT @StartTime = sqlserver_start_time FROM sys.dm_os_sys_info;
SET @UptimeSeconds = DATEDIFF(SECOND, @StartTime, GETDATE());
SELECT
@StartTime AS [Server Start Time],
GETDATE() AS [Current Time],
@UptimeSeconds AS [UptimeSeconds],
@UptimeSeconds / 86400 AS [Days],
(@UptimeSeconds % 86400) / 3600 AS [Hours],
(@UptimeSeconds % 3600) / 60 AS [Minutes],
(@UptimeSeconds % 60) AS [Seconds];
`;

View File

@@ -1,5 +1,47 @@
import { Router } from "express";
import { apiReturn } from "../utils/returnHelper.utils.js";
import { closePool, connectProdSql } from "./sqlConnection.controller.js";
const r = Router();
r.post("/start", async (_, res) => {
const connect = await connectProdSql();
apiReturn(res, {
success: connect.success,
level: connect.success ? "info" : "error",
module: "routes",
subModule: "prodSql",
message: connect.message,
data: connect.data,
status: connect.success ? 200 : 400,
});
});
r.post("/stop", async (_, res) => {
const connect = await closePool();
apiReturn(res, {
success: connect.success,
level: connect.success ? "info" : "error",
module: "routes",
subModule: "prodSql",
message: connect.message,
data: connect.data,
status: connect.success ? 200 : 400,
});
});
r.post("/restart", async (_, res) => {
await closePool();
await new Promise((r) => setTimeout(r, 2000));
const connect = await connectProdSql();
apiReturn(res, {
success: connect.success,
level: connect.success ? "info" : "error",
module: "routes",
subModule: "prodSql",
message: "Sql Server has been restarted",
data: connect.data,
status: connect.success ? 200 : 400,
});
});
export default r;

View File

@@ -10,10 +10,6 @@ export let reconnecting = false;
export const connectProdSql = async () => {
const serverUp = await checkHostnamePort(`${process.env.PROD_SERVER}:1433`);
const log = createLogger({
module: "system",
subModule: "db",
});
if (!serverUp) {
// we will try to reconnect
connected = false;
@@ -41,9 +37,15 @@ export const connectProdSql = async () => {
try {
pool = await sql.connect(prodSqlConfig);
connected = true;
log.info(
`${prodSqlConfig.server} is connected to ${prodSqlConfig.database}`,
);
return returnFunc({
success: true,
level: "info",
module: "system",
subModule: "db",
message: `${prodSqlConfig.server} is connected to ${prodSqlConfig.database}`,
data: [],
notify: false,
});
} catch (error) {
return returnFunc({
success: false,
@@ -58,10 +60,6 @@ export const connectProdSql = async () => {
};
export const closePool = async () => {
const log = createLogger({
module: "system",
subModule: "db",
});
if (!connected) {
return returnFunc({
success: false,
@@ -74,15 +72,25 @@ export const closePool = async () => {
try {
await pool.close();
log.info("Connection pool closed");
connected = false;
return {
return returnFunc({
success: true,
message: "The sql server connection has been closed",
};
level: "info",
module: "system",
subModule: "db",
message: "The sql connection has been closed.",
});
} catch (error) {
connected = false;
console.log("There was an error closing the sql connection", error);
return returnFunc({
success: false,
level: "error",
module: "system",
subModule: "db",
message: "There was an error closing the sql connection",
data: [error],
});
}
};
export const reconnectToSql = async () => {

View File

@@ -0,0 +1,100 @@
import { returnFunc } from "../utils/returnHelper.utils.js";
import {
closePool,
connected,
pool,
reconnecting,
reconnectToSql,
} from "./sqlConnection.controller.js";
interface SqlError extends Error {
code?: string;
originalError?: {
info?: { message?: string };
};
}
/**
* Run a prod query
* just pass over the query as a string and the name of the query.
* Query should be like below.
* * select * from AlplaPROD_test1.dbo.table
* You must use test1 always as it will be changed via query
*/
export const prodQuery = async (queryToRun: string, name: string) => {
if (!connected) {
reconnectToSql();
if (reconnecting) {
return returnFunc({
success: false,
level: "error",
module: "system",
subModule: "prodSql",
message: `The sql ${process.env.PROD_PLANT_TOKEN} is trying to reconnect already`,
data: [],
notify: false,
});
} else {
return returnFunc({
success: false,
level: "error",
module: "system",
subModule: "prodSql",
message: `${process.env.PROD_PLANT_TOKEN} is not connected, and failed to connect.`,
data: [],
notify: true,
});
}
}
//change to the correct server
const query = queryToRun.replaceAll(
"test1",
`${process.env.PROD_PLANT_TOKEN}`,
);
try {
const result = await pool.request().query(query);
return {
success: true,
message: `Query results for: ${name}`,
data: result.recordset,
};
} catch (error: unknown) {
const err = error as SqlError;
if (err.code === "ETIMEOUT") {
closePool();
return returnFunc({
success: false,
module: "system",
subModule: "prodSql",
level: "error",
message: `${name} did not run due to a timeout.`,
notify: false,
data: [],
});
}
if (err.code === "EREQUEST") {
closePool();
return returnFunc({
success: false,
module: "system",
subModule: "prodSql",
level: "error",
message: `${name} encountered an error ${err.originalError?.info?.message || "undefined error"}`,
data: [],
});
}
return returnFunc({
success: false,
module: "system",
subModule: "prodSql",
level: "error",
message: `${name} encountered an unknown error.`,
data: [],
});
}
};