From eb051d51f21b1ad617851fa3f4a1b8ba2f4fe4ac Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Wed, 26 Mar 2025 22:08:53 -0500 Subject: [PATCH] feat(notify): intial nofity system added to monitor crashes and rfid wrapper --- package-lock.json | 361 ++++++++++++++---- server/index.ts | 31 +- .../notifications/controller/sendMail.ts | 144 +++++++ .../services/notifications/notifyService.ts | 19 + .../services/notifications/routes/sendMail.ts | 73 ++++ .../utils/views/exampleEmail.hbs | 33 ++ .../notifications/utils/views/serverCrash.hbs | 35 ++ .../notifications/utils/views/styles.hbs | 6 + .../utils/views/toManyManualPrints.hbs | 19 + 9 files changed, 649 insertions(+), 72 deletions(-) create mode 100644 server/services/notifications/controller/sendMail.ts create mode 100644 server/services/notifications/notifyService.ts create mode 100644 server/services/notifications/routes/sendMail.ts create mode 100644 server/services/notifications/utils/views/exampleEmail.hbs create mode 100644 server/services/notifications/utils/views/serverCrash.hbs create mode 100644 server/services/notifications/utils/views/styles.hbs create mode 100644 server/services/notifications/utils/views/toManyManualPrints.hbs diff --git a/package-lock.json b/package-lock.json index 46d536d..53833e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,13 +13,16 @@ "@hono/zod-openapi": "^0.19.2", "@scalar/hono-api-reference": "^0.7.2", "@types/jsonwebtoken": "^9.0.9", + "@types/nodemailer-express-handlebars": "^4.0.5", "adm-zip": "^0.5.16", "axios": "^1.8.4", "bcryptjs": "^3.0.2", "date-fns": "^4.1.0", + "drizzle-kit": "^0.30.5", "drizzle-orm": "^0.41.0", "drizzle-zod": "^0.7.0", "fast-xml-parser": "^5.0.9", + "fs-extra": "^11.3.0", "jsonwebtoken": "^9.0.2", "mssql": "^11.0.1", "nodemailer": "^6.10.0", @@ -30,6 +33,7 @@ "pino-pretty": "^13.0.0", "postgres": "^3.4.5", "rimraf": "^6.0.1", + "st-ethernet-ip": "^2.7.3", "ws": "^8.18.1", "zod": "^3.24.2" }, @@ -40,12 +44,11 @@ "@types/js-cookie": "^3.0.6", "@types/mssql": "^9.1.7", "@types/node": "^22.13.11", + "@types/nodemailer": "^6.4.17", "@types/pg": "^8.11.11", "@types/ws": "^8.18.0", "concurrently": "^9.1.2", "cz-conventional-changelog": "^3.3.0", - "drizzle-kit": "^0.30.5", - "fs-extra": "^11.3.0", "standard-version": "^9.5.0", "tsx": "^4.19.3", "typescript": "^5.8.2" @@ -520,7 +523,6 @@ "version": "0.10.2", "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==", - "dev": true, "license": "Apache-2.0" }, "node_modules/@ecies/ciphers": { @@ -542,7 +544,6 @@ "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", "deprecated": "Merged into tsx: https://tsx.is", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "~0.18.20", @@ -556,7 +557,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -573,7 +573,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -590,7 +589,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -607,7 +605,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -624,7 +621,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -641,7 +637,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -658,7 +653,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -675,7 +669,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -692,7 +685,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -709,7 +701,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -726,7 +717,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -743,7 +733,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -760,7 +749,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -777,7 +765,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -794,7 +781,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -811,7 +797,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -828,7 +813,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -845,7 +829,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -862,7 +845,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -879,7 +861,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -896,7 +877,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -913,7 +893,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -927,7 +906,6 @@ "version": "0.18.20", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -966,7 +944,6 @@ "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", "deprecated": "Merged into tsx: https://tsx.is", - "dev": true, "license": "MIT", "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", @@ -980,7 +957,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -997,7 +973,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1014,7 +989,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1031,7 +1005,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1048,7 +1021,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1065,7 +1037,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1082,7 +1053,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1099,7 +1069,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1116,7 +1085,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1133,7 +1101,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1150,7 +1117,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1167,7 +1133,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1184,7 +1149,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1201,7 +1165,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1218,7 +1181,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1235,7 +1197,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1252,7 +1213,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1286,7 +1246,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1320,7 +1279,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1337,7 +1295,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1354,7 +1311,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1371,7 +1327,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1388,7 +1343,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1592,7 +1546,6 @@ "version": "3.9.2", "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz", "integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==", - "devOptional": true, "license": "MIT" }, "node_modules/@scalar/core": { @@ -1682,6 +1635,12 @@ "@types/node": "*" } }, + "node_modules/@types/express-handlebars": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@types/express-handlebars/-/express-handlebars-5.3.1.tgz", + "integrity": "sha512-DSzaERLO4gHb8AqnrL58jzSDyT0yDdl6HqDc+bGz1Hf0nrG1FK30nHGzv8NBEGR8QV9eUGB/YaE0Qj3NjF7siw==", + "license": "MIT" + }, "node_modules/@types/fs-extra": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", @@ -1754,6 +1713,25 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/nodemailer-express-handlebars": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/nodemailer-express-handlebars/-/nodemailer-express-handlebars-4.0.5.tgz", + "integrity": "sha512-SuSYGNQPGtgMkDlQTO7zacXDENqQR3QXW1Ip5PvvookodvUKCbNVRF1tisY3Bgew1h8Wjfsf6dPQ5E45pJ1bJA==", + "license": "MIT", + "dependencies": { + "@types/express-handlebars": "^5", + "@types/nodemailer": "*" + } + }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", @@ -2085,7 +2063,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT" }, "node_modules/bundle-name": { @@ -2113,6 +2090,24 @@ "node": ">=6" } }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -2126,6 +2121,22 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2993,7 +3004,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true, "license": "MIT", "engines": { "node": "*" @@ -3060,6 +3070,26 @@ "dev": true, "license": "MIT" }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "license": "MIT", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/default-browser": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", @@ -3101,6 +3131,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -3113,6 +3160,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3261,7 +3325,6 @@ "version": "0.30.5", "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.30.5.tgz", "integrity": "sha512-l6dMSE100u7sDaTbLczibrQZjA35jLsHNqIV+jmhNVO3O8jzM6kywMOmV9uOz9ZVSCMPQhAZEFjL/qDPVrqpUA==", - "dev": true, "license": "MIT", "dependencies": { "@drizzle-team/brocli": "^0.10.2", @@ -3536,7 +3599,6 @@ "version": "0.19.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -3575,7 +3637,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.3.4" @@ -3894,7 +3955,6 @@ "version": "11.3.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", - "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -3936,11 +3996,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gel": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/gel/-/gel-2.0.1.tgz", "integrity": "sha512-gfem3IGvqKqXwEq7XseBogyaRwGsQGuE7Cw/yQsjLGdgiyqX92G1xENPCE0ltunPGcsJIa6XBOTx/PK169mOqw==", - "devOptional": true, "license": "Apache-2.0", "dependencies": { "@petamoriken/float16": "^3.8.7", @@ -3961,7 +4029,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", - "devOptional": true, "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -3974,7 +4041,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "devOptional": true, "license": "ISC", "engines": { "node": ">=16" @@ -3984,7 +4050,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "devOptional": true, "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -4122,7 +4187,6 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", - "dev": true, "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -4381,6 +4445,18 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -4724,6 +4800,31 @@ "node": ">=8" } }, + "node_modules/int64-buffer": { + "version": "0.99.1007", + "resolved": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-0.99.1007.tgz", + "integrity": "sha512-XDBEu44oSTqlvCSiOZ/0FoUkpWu/vwjJLGSKDabNISPQNZ5wub1FodGHBljRsrR0IXRPq7SslshZYMuA55CgTQ==", + "license": "MIT", + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -4747,6 +4848,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -4852,6 +4969,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -5030,7 +5165,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -5817,6 +5951,31 @@ "node": ">=8" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object-treeify": { "version": "1.1.33", "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", @@ -6757,6 +6916,26 @@ "node": ">=8" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6828,7 +7007,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" @@ -6965,6 +7143,38 @@ "node": ">=10" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6990,7 +7200,6 @@ "version": "1.8.2", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", - "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7033,7 +7242,6 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -7105,6 +7313,18 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "license": "BSD-3-Clause" }, + "node_modules/st-ethernet-ip": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/st-ethernet-ip/-/st-ethernet-ip-2.7.3.tgz", + "integrity": "sha512-pc7cjrLwlZGzezuqEThensjk8o9lvI73xlNf4Ufzsfl51Akfe8Td9RdQJPWaAjaCCYV3h0pgRp5NrudgKCMBww==", + "license": "MIT", + "dependencies": { + "dateformat": "^3.0.3", + "deep-equal": "^1.1.1", + "int64-buffer": "^0.99.1007", + "task-easy": "^0.2.0" + } + }, "node_modules/standard-version": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-9.5.0.tgz", @@ -7306,6 +7526,12 @@ "node": ">=8.0.0" } }, + "node_modules/task-easy": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/task-easy/-/task-easy-0.2.1.tgz", + "integrity": "sha512-VMYezcVhfyaTkc3cWf4sSQG6YgYBdiNCAg4iXuUWRGB7tp7A/rZA9Nter0ZIG5chSoidLS0VWf87koFD9XHwDg==", + "license": "MIT" + }, "node_modules/tedious": { "version": "18.6.1", "resolved": "https://registry.npmjs.org/tedious/-/tedious-18.6.1.tgz", @@ -7988,7 +8214,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" diff --git a/server/index.ts b/server/index.ts index e9a13ec..2741067 100644 --- a/server/index.ts +++ b/server/index.ts @@ -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 () => { diff --git a/server/services/notifications/controller/sendMail.ts b/server/services/notifications/controller/sendMail.ts new file mode 100644 index 0000000..b133e1d --- /dev/null +++ b/server/services/notifications/controller/sendMail.ts @@ -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; // Use a generic object for context +} + +interface EmailData { + email: string; + subject: string; + template: string; + context: []; +} + +export const sendEmail = async (data: any): Promise => { + 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 }; + } +}; diff --git a/server/services/notifications/notifyService.ts b/server/services/notifications/notifyService.ts new file mode 100644 index 0000000..44173d1 --- /dev/null +++ b/server/services/notifications/notifyService.ts @@ -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; diff --git a/server/services/notifications/routes/sendMail.ts b/server/services/notifications/routes/sendMail.ts new file mode 100644 index 0000000..67fdce6 --- /dev/null +++ b/server/services/notifications/routes/sendMail.ts @@ -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; diff --git a/server/services/notifications/utils/views/exampleEmail.hbs b/server/services/notifications/utils/views/exampleEmail.hbs new file mode 100644 index 0000000..b0d24da --- /dev/null +++ b/server/services/notifications/utils/views/exampleEmail.hbs @@ -0,0 +1,33 @@ + + + + + Order Summary + {{> styles}} + {{!-- --}} + + +

{{name}}, your Order Summary

+

All, + This is an example of the test email +

+ + + + + + + + + + {{#each items}} + + + + + + {{/each}} + +
Item NameQuantityPrice
{{name}}{{quantity}}{{price}}
+ + \ No newline at end of file diff --git a/server/services/notifications/utils/views/serverCrash.hbs b/server/services/notifications/utils/views/serverCrash.hbs new file mode 100644 index 0000000..4b47f51 --- /dev/null +++ b/server/services/notifications/utils/views/serverCrash.hbs @@ -0,0 +1,35 @@ + + + + + {{!--Order Summary --}} + {{> styles}} + + {{!-- --}} + + +

{{plant}},
Has encountered an unexpected error.

+

+ Please see below the stack error from the crash. +

+
+
+

Error Message:

+

{{error.message}}

+
+
+
+

Stack trace

+
{{{error.stack}}}
+
+ + \ No newline at end of file diff --git a/server/services/notifications/utils/views/styles.hbs b/server/services/notifications/utils/views/styles.hbs new file mode 100644 index 0000000..09e1061 --- /dev/null +++ b/server/services/notifications/utils/views/styles.hbs @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/server/services/notifications/utils/views/toManyManualPrints.hbs b/server/services/notifications/utils/views/toManyManualPrints.hbs new file mode 100644 index 0000000..432e6bc --- /dev/null +++ b/server/services/notifications/utils/views/toManyManualPrints.hbs @@ -0,0 +1,19 @@ + + + + + {{!--Order Summary --}} + {{> styles}} + {{!-- --}} + + + +

All,
+ There has been {{count}} manual prints in the last {{hours}}.
+ Please consider checking the reason for this.
+ Thank you,
+ LST +

+ + + \ No newline at end of file