feat(notify): intial nofity system added to monitor crashes and rfid wrapper

This commit is contained in:
2025-03-26 22:08:53 -05:00
parent 7a1a4773e7
commit eb051d51f2
9 changed files with 649 additions and 72 deletions

View File

@@ -21,7 +21,10 @@ import loggerService from "./services/logger/loggerService.js";
import ocpService from "./services/ocp/ocpService.js";
import { db } from "../database/dbclient.js";
import { settings } from "../database/schema/settings.js";
import { count } from "drizzle-orm";
import os from "os";
import { tryCatch } from "./globalUtils/tryCatch.js";
import { sendEmail } from "./services/notifications/controller/sendMail.js";
import notify from "./services/notifications/notifyService.js";
// create the main prodlogin here
const username = "lst_user";
@@ -29,9 +32,17 @@ const password = "Alpla$$Prod";
export const lstAuth = btoa(`${username}:${password}`);
// checking to make sure we have the settings intialized
const serverIntialized = await db.select({ count: count() }).from(settings);
const { data: settingsData, error: settingError } = await tryCatch(
db.select().from(settings)
);
if (settingError) {
throw Error("Error getting settings from the db. critical error.");
}
const serverIntialized: any = settingsData;
export const installed =
serverIntialized[0].count === 0 && process.env.NODE_ENV !== "development"
serverIntialized.length === 0 && process.env.NODE_ENV !== "development"
? false
: true;
createLog("info", "LST", "server", `Server is installed: ${installed}`);
@@ -88,6 +99,7 @@ const routes = [
printers,
loggerService,
ocpService,
notify,
] as const;
const appRoutes = routes.forEach((route) => {
@@ -146,7 +158,18 @@ process.on("SIGTERM", async () => {
process.on("uncaughtException", async (err) => {
console.log("Uncaught Exception:", err);
//await closePool();
process.exit(1);
const emailData = {
email: "blake.matthes@alpla.com", // should be moved to the db so it can be reused.
subject: `${os.hostname()} has just encountered a crash.`,
template: "serverCrash",
context: {
error: err,
plant: `${os.hostname()}`,
},
};
await sendEmail(emailData);
//process.exit(1);
});
process.on("beforeExit", async () => {

View File

@@ -0,0 +1,144 @@
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { db } from "../../../../database/dbclient.js";
import { settings } from "../../../../database/schema/settings.js";
import nodemailer from "nodemailer";
import type { Transporter } from "nodemailer";
import type SMTPTransport from "nodemailer/lib/smtp-transport/index.js";
import type Mail from "nodemailer/lib/mailer/index.js";
import type { Address } from "nodemailer/lib/mailer/index.js";
import path from "path";
import { fileURLToPath } from "url";
import hbs from "nodemailer-express-handlebars";
import { promisify } from "util";
import { createLog } from "../../logger/logger.js";
interface HandlebarsMailOptions extends Mail.Options {
template: string;
context: Record<string, unknown>; // Use a generic object for context
}
interface EmailData {
email: string;
subject: string;
template: string;
context: [];
}
export const sendEmail = async (data: any): Promise<any> => {
let transporter: Transporter;
let fromEmail: string | Address;
const { data: settingData, error: settingError } = await tryCatch(
db.select().from(settings)
);
if (settingError) {
return {
success: false,
message: "There was an error getting the settings.",
settingError,
};
}
// get the plantToken
const server = settingData.filter((n) => n.name === "server");
if (
server[0].value === "localhost" &&
process.env.EMAIL_USER &&
process.env.EMAIL_PASSWORD
) {
transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD,
},
//debug: true,
});
// update the from email
fromEmail = process.env.EMAIL_USER;
} else {
// convert to the correct plant token.
const plantToken = settingData.filter((s) => s.name === "plantToken");
let host = `${plantToken[0].value}-smtp.alpla.net`;
const testServers = ["test1", "test2", "test3"];
if (testServers.includes(plantToken[0].value)) {
host = "USMCD1-smtp.alpla.net";
}
if (plantToken[0].value === "usiow2") {
host = "USIOW1-smtp.alpla.net";
}
transporter = nodemailer.createTransport({
host: host,
port: 25,
rejectUnauthorized: false,
//secure: false,
// auth: {
// user: "alplaprod",
// pass: "obelix",
// },
debug: true,
} as SMTPTransport.Options);
// update the from email
fromEmail = `noreply@alpla.com`;
}
// creating the handlbar options
const viewPath = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"../utils/views/"
);
const handlebarOptions = {
viewEngine: {
extname: ".hbs",
//layoutsDir: path.resolve(viewPath, "layouts"), // Path to layouts directory
defaultLayout: "", // Specify the default layout
partialsDir: viewPath,
},
viewPath: viewPath,
extName: ".hbs", // File extension for Handlebars templates
};
transporter.use("compile", hbs(handlebarOptions));
const mailOptions: HandlebarsMailOptions = {
from: fromEmail,
to: data.email,
subject: data.subject,
//text: "You will have a reset token here and only have 30min to click the link before it expires.",
//html: emailTemplate("BlakesTest", "This is an example with css"),
template: data.template, // Name of the Handlebars template (e.g., 'welcome.hbs')
context: data.context,
};
// now verify and send the email
const sendMailPromise = promisify(transporter.sendMail).bind(transporter);
try {
// Send email and await the result
const info = await sendMailPromise(mailOptions);
createLog(
"info",
"notification",
"system",
`Email was sent to: ${data.email}`
);
return { success: true, message: "Email sent.", data: info };
} catch (err) {
console.log(err);
createLog(
"error",
"notification",
"system",
`Error sending Email: ${JSON.stringify(err)}`
);
return { success: false, message: "Error sending email.", error: err };
}
};

View File

@@ -0,0 +1,19 @@
import { OpenAPIHono } from "@hono/zod-openapi";
import sendemail from "./routes/sendMail.js";
const app = new OpenAPIHono();
const routes = [sendemail] as const;
const appRoutes = routes.forEach((route) => {
app.route("/notify", route);
});
app.all("/notify/*", (c) => {
return c.json({
success: false,
message: "you have encounted a notication route that dose not exist.",
});
});
export default app;

View File

@@ -0,0 +1,73 @@
// an external way to creating logs
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { authMiddleware } from "../../auth/middleware/authMiddleware.js";
import { sendEmail } from "../controller/sendMail.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
const app = new OpenAPIHono({ strict: false });
const EmailSchema = z
.object({
email: z.string().email().openapi({ example: "smith@example.come" }),
subject: z.string().openapi({ example: "Welcome to LST" }),
template: z.string().openapi({ example: "exampleTemplate" }),
context: z
.object({
name: z.string().optional(),
score: z.string().optional(),
})
.optional()
.openapi({}),
})
.openapi("User");
app.openapi(
createRoute({
tags: ["server"],
summary: "Returns current active lots that are tech released",
method: "post",
path: "/sendmail",
middleware: authMiddleware,
request: {
body: {
content: {
"application/json": { schema: EmailSchema },
},
},
},
responses: responses(),
}),
async (c) => {
const { data: bodyData, error: bodyError } = await tryCatch(
c.req.json()
);
if (bodyError) {
return c.json(
{
success: false,
message: "There was an error sending the email",
data: bodyError,
},
400
);
}
const { data: emailData, error: emailError } = await tryCatch(
sendEmail(bodyData)
);
if (emailError) {
return c.json({
success: false,
message: "There was an error sending the email",
data: emailError,
});
}
return c.json({
success: emailData.success,
message: emailData.message,
data: emailData.data,
});
}
);
export default app;

View File

@@ -0,0 +1,33 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Order Summary</title>
{{> styles}}
{{!-- <link rel="stylesheet" href="styles/styles.css" /> --}}
</head>
<body>
<h1>{{name}}, your Order Summary</h1>
<p>All,
This is an example of the test email
</p>
<table>
<thead>
<tr>
<th>Item Name</th>
<th>Quantity</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{{#each items}}
<tr>
<td>{{name}}</td>
<td>{{quantity}}</td>
<td>{{price}}</td>
</tr>
{{/each}}
</tbody>
</table>
</body>
</html>

View File

@@ -0,0 +1,35 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{{!--<title>Order Summary</title> --}}
{{> styles}}
<style>
pre {
background-color: #f8f9fa;
color: #d63384;
padding: 10px;
border-radius: 5px;
white-space: pre-wrap;
font-family: monospace;
}
</style>
{{!-- <link rel="stylesheet" href="styles/styles.css" /> --}}
</head>
<body>
<h3>{{plant}},<br/> Has encountered an unexpected error.</h1>
<p>
Please see below the stack error from the crash.
</p>
<hr/>
<div>
<h3>Error Message: </h3>
<p>{{error.message}}</p>
</div>
<hr/>
<div>
<h3>Stack trace</h3>
<pre>{{{error.stack}}}</pre>
</div>
</body>
</html>

View File

@@ -0,0 +1,6 @@
<style>
table { width: 100%; background-color: #ffffff; border-collapse: collapse;
border-width: 2px; border-color: #14BDEA; border-style: solid; color:
#000000; } th, td { border: 1px solid #ddd; padding: 8px; } th {
background-color: #f4f4f4; }
</style>

View File

@@ -0,0 +1,19 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{{!--<title>Order Summary</title> --}}
{{> styles}}
{{!-- <link rel="stylesheet" href="styles/styles.css" /> --}}
</head>
<body>
<p>All,<br/>
There has been {{count}} manual prints in the last {{hours}}.<br/>
Please consider checking the reason for this.<br/>
Thank you,<br/>
LST
</p>
</body>
</html>