feat(notify): intial nofity system added to monitor crashes and rfid wrapper
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
144
server/services/notifications/controller/sendMail.ts
Normal file
144
server/services/notifications/controller/sendMail.ts
Normal 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 };
|
||||
}
|
||||
};
|
||||
19
server/services/notifications/notifyService.ts
Normal file
19
server/services/notifications/notifyService.ts
Normal 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;
|
||||
73
server/services/notifications/routes/sendMail.ts
Normal file
73
server/services/notifications/routes/sendMail.ts
Normal 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;
|
||||
33
server/services/notifications/utils/views/exampleEmail.hbs
Normal file
33
server/services/notifications/utils/views/exampleEmail.hbs
Normal 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>
|
||||
35
server/services/notifications/utils/views/serverCrash.hbs
Normal file
35
server/services/notifications/utils/views/serverCrash.hbs
Normal 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>
|
||||
6
server/services/notifications/utils/views/styles.hbs
Normal file
6
server/services/notifications/utils/views/styles.hbs
Normal 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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user