add optional ntfy notification on startup

deepsource-autofix-76c6eb20
Dan Gowans 2022-11-23 10:52:13 -05:00
parent 9c840b0e9e
commit 3311de77a9
16 changed files with 331 additions and 0 deletions

View File

@ -2,6 +2,7 @@ import { app } from "../app.js";
import http from "node:http";
import * as configFunctions from "../helpers/functions.config.js";
import exitHook from "exit-hook";
import ntfyPublish from "@cityssm/ntfy-publish";
import debug from "debug";
const debugWWW = debug("lot-occupancy-system:www");
let httpServer;
@ -39,6 +40,21 @@ if (httpPort) {
onListening(httpServer);
});
debugWWW("HTTP listening on " + httpPort.toString());
const ntfyStartupConfig = configFunctions.getProperty("application.ntfyStartup");
if (ntfyStartupConfig) {
const topic = ntfyStartupConfig.topic;
const server = ntfyStartupConfig.server;
const ntfyMessage = {
topic,
title: configFunctions.getProperty("application.applicationName"),
message: "Application Started",
tags: ["arrow_up"]
};
if (server) {
ntfyMessage.server = server;
}
await ntfyPublish(ntfyMessage);
}
}
exitHook(() => {
if (httpServer) {

View File

@ -7,6 +7,8 @@ import http from "node:http";
import * as configFunctions from "../helpers/functions.config.js";
import exitHook from "exit-hook";
import ntfyPublish from "@cityssm/ntfy-publish";
import type * as ntfyTypes from "@cityssm/ntfy-publish/types";
import debug from "debug";
const debugWWW = debug("lot-occupancy-system:www");
@ -72,6 +74,26 @@ if (httpPort) {
});
debugWWW("HTTP listening on " + httpPort.toString());
const ntfyStartupConfig = configFunctions.getProperty("application.ntfyStartup");
if (ntfyStartupConfig) {
const topic = ntfyStartupConfig.topic;
const server = ntfyStartupConfig.server;
const ntfyMessage: ntfyTypes.NtfyMessageOptions = {
topic,
title: configFunctions.getProperty("application.applicationName"),
message: "Application Started",
tags: ["arrow_up"]
};
if (server) {
ntfyMessage.server = server;
}
await ntfyPublish(ntfyMessage);
}
}
exitHook(() => {

View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -0,0 +1,11 @@
import * as configFunctions from "../../helpers/functions.config.js";
export const handler = (_request, response) => {
if (!configFunctions.getProperty("application.ntfyStartup")) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") +
"/dashboard/?error=ntfyNotConfigured");
}
response.render("admin-ntfyStartup", {
headTitle: "Ntfy Startup Notification"
});
};
export default handler;

View File

@ -0,0 +1,19 @@
import type { RequestHandler } from "express";
import * as configFunctions from "../../helpers/functions.config.js";
export const handler: RequestHandler = (_request, response) => {
if (!configFunctions.getProperty("application.ntfyStartup")) {
return response.redirect(
configFunctions.getProperty("reverseProxy.urlPrefix") +
"/dashboard/?error=ntfyNotConfigured"
);
}
response.render("admin-ntfyStartup", {
headTitle: "Ntfy Startup Notification"
});
};
export default handler;

View File

@ -4,6 +4,7 @@ export declare function getProperty(propertyName: "application.logoURL"): string
export declare function getProperty(propertyName: "application.httpPort"): number;
export declare function getProperty(propertyName: "application.userDomain"): string;
export declare function getProperty(propertyName: "application.useTestDatabases"): boolean;
export declare function getProperty(propertyName: "application.ntfyStartup"): configTypes.ConfigNtfyStartup;
export declare function getProperty(propertyName: "activeDirectory"): configTypes.ConfigActiveDirectory;
export declare function getProperty(propertyName: "users.testing"): string[];
export declare function getProperty(propertyName: "users.canLogin"): string[];

View File

@ -76,6 +76,7 @@ export function getProperty(propertyName: "application.logoURL"): string;
export function getProperty(propertyName: "application.httpPort"): number;
export function getProperty(propertyName: "application.userDomain"): string;
export function getProperty(propertyName: "application.useTestDatabases"): boolean;
export function getProperty(propertyName: "application.ntfyStartup"): configTypes.ConfigNtfyStartup;
export function getProperty(propertyName: "activeDirectory"): configTypes.ConfigActiveDirectory;

145
package-lock.json generated
View File

@ -13,6 +13,7 @@
"@cityssm/bulma-webapp-js": "^1.5.0",
"@cityssm/date-diff": "^2.2.3",
"@cityssm/expressjs-server-js": "^2.3.3",
"@cityssm/ntfy-publish": "^0.2.0",
"@cityssm/pdf-puppeteer": "^2.0.0-beta.1",
"@fortawesome/fontawesome-free": "^5.15.4",
"@types/randomcolor": "^0.5.6",
@ -657,6 +658,31 @@
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
}
},
"node_modules/@cityssm/ntfy-publish": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@cityssm/ntfy-publish/-/ntfy-publish-0.2.0.tgz",
"integrity": "sha512-flKv999vFgxCw9YeLtNXy61/4Xq3IwFB4kI5lQYm/mcs0Bge1k+jV31iVGkbtYPmzzJPreKFm8+jxQbi5x4OjA==",
"dependencies": {
"node-fetch": "^3.3.0"
}
},
"node_modules/@cityssm/ntfy-publish/node_modules/node-fetch": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/@cityssm/pdf-puppeteer": {
"version": "2.0.0-beta.1",
"resolved": "https://registry.npmjs.org/@cityssm/pdf-puppeteer/-/pdf-puppeteer-2.0.0-beta.1.tgz",
@ -3418,6 +3444,14 @@
"node": ">=0.10"
}
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==",
"engines": {
"node": ">= 12"
}
},
"node_modules/date-fns": {
"version": "2.29.3",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
@ -4961,6 +4995,28 @@
"pend": "~1.2.0"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/figures": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
@ -5347,6 +5403,17 @@
"node": ">= 6"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -8393,6 +8460,24 @@
"integrity": "sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw==",
"dev": true
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@ -11840,6 +11925,14 @@
"node": ">=0.10.0"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
"engines": {
"node": ">= 8"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@ -12721,6 +12814,26 @@
"mssql": "^8.1.1"
}
},
"@cityssm/ntfy-publish": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@cityssm/ntfy-publish/-/ntfy-publish-0.2.0.tgz",
"integrity": "sha512-flKv999vFgxCw9YeLtNXy61/4Xq3IwFB4kI5lQYm/mcs0Bge1k+jV31iVGkbtYPmzzJPreKFm8+jxQbi5x4OjA==",
"requires": {
"node-fetch": "^3.3.0"
},
"dependencies": {
"node-fetch": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
"requires": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
}
}
}
},
"@cityssm/pdf-puppeteer": {
"version": "2.0.0-beta.1",
"resolved": "https://registry.npmjs.org/@cityssm/pdf-puppeteer/-/pdf-puppeteer-2.0.0-beta.1.tgz",
@ -14897,6 +15010,11 @@
"assert-plus": "^1.0.0"
}
},
"data-uri-to-buffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA=="
},
"date-fns": {
"version": "2.29.3",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
@ -16081,6 +16199,15 @@
"pend": "~1.2.0"
}
},
"fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"requires": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
}
},
"figures": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
@ -16402,6 +16529,14 @@
"mime-types": "^2.1.12"
}
},
"formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"requires": {
"fetch-blob": "^3.1.2"
}
},
"forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -18732,6 +18867,11 @@
"integrity": "sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw==",
"dev": true
},
"node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
},
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@ -21419,6 +21559,11 @@
}
}
},
"web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",

View File

@ -37,6 +37,7 @@
"@cityssm/bulma-webapp-js": "^1.5.0",
"@cityssm/date-diff": "^2.2.3",
"@cityssm/expressjs-server-js": "^2.3.3",
"@cityssm/ntfy-publish": "^0.2.0",
"@cityssm/pdf-puppeteer": "^2.0.0-beta.1",
"@fortawesome/fontawesome-free": "^5.15.4",
"@types/randomcolor": "^0.5.6",

View File

@ -55,6 +55,7 @@ import handler_doMoveLotOccupantTypeDown from "../handlers/admin-post/doMoveLotO
import handler_doDeleteLotOccupantType from "../handlers/admin-post/doDeleteLotOccupantType.js";
import handler_cleanup from "../handlers/admin-get/cleanup.js";
import handler_doCleanupDatabase from "../handlers/admin-post/doCleanupDatabase.js";
import handler_ntfyStartup from "../handlers/admin-get/ntfyStartup.js";
export const router = Router();
router.get("/fees", handler_fees);
router.post("/doAddFeeCategory", handler_doAddFeeCategory);
@ -112,4 +113,5 @@ router.post("/doMoveLotOccupantTypeDown", handler_doMoveLotOccupantTypeDown);
router.post("/doDeleteLotOccupantType", handler_doDeleteLotOccupantType);
router.get("/cleanup", handler_cleanup);
router.post("/doCleanupDatabase", handler_doCleanupDatabase);
router.get("/ntfyStartup", handler_ntfyStartup);
export default router;

View File

@ -81,6 +81,10 @@ import handler_doDeleteLotOccupantType from "../handlers/admin-post/doDeleteLotO
import handler_cleanup from "../handlers/admin-get/cleanup.js";
import handler_doCleanupDatabase from "../handlers/admin-post/doCleanupDatabase.js";
// Ntfy Startup
import handler_ntfyStartup from "../handlers/admin-get/ntfyStartup.js";
export const router = Router();
/*
@ -228,4 +232,8 @@ router.get("/cleanup", handler_cleanup);
router.post("/doCleanupDatabase", handler_doCleanupDatabase);
// Ntfy Startup
router.get("/ntfyStartup", handler_ntfyStartup);
export default router;

View File

@ -67,6 +67,11 @@ interface ConfigApplication {
httpPort?: number;
userDomain?: string;
useTestDatabases?: boolean;
ntfyStartup?: ConfigNtfyStartup;
}
export interface ConfigNtfyStartup {
topic: string;
server?: string;
}
interface ConfigSession {
cookieName?: string;

View File

@ -68,6 +68,12 @@ interface ConfigApplication {
httpPort?: number;
userDomain?: string;
useTestDatabases?: boolean;
ntfyStartup?: ConfigNtfyStartup;
}
export interface ConfigNtfyStartup {
topic: string;
server?: string;
}
interface ConfigSession {

View File

@ -33,5 +33,13 @@
<span>Database Cleanup</span>
</a>
</li>
<% if (configFunctions.getProperty("application.ntfyStartup")) { %>
<li>
<a class="<%= (headTitle === "Ntfy Startup Notification" ? "is-active" : "") %>" href="<%= urlPrefix %>/admin/ntfyStartup">
<span class="icon"><i class="fas fa-fw fa-comment-alt" aria-hidden="true"></i></span>
<span>Ntfy Startup Notification</span>
</a>
</li>
<% } %>
</ul>
</aside>

View File

@ -0,0 +1,66 @@
<%- include('_header'); -%>
<div class="columns">
<div class="column is-3 is-hidden-mobile">
<%- include('_menu-admin'); -%>
</div>
<div class="column">
<nav class="breadcrumb">
<ul>
<li><a href="<%= urlPrefix %>/dashboard">Home</a></li>
<li>
<a href="#">
<span class="icon is-small"><i class="fas fa-cog" aria-hidden="true"></i></span>
<span>Administrator Tools</span>
</a>
</li>
<li class="is-active">
<a href="#" aria-current="page">
Ntfy Startup Notification
</a>
</li>
</ul>
</nav>
<h1 class="title is-1">
Ntfy Startup Notification
</h1>
<div class="message is-info">
<div class="message-header">
What is this?
</div>
<div class="message-body">
<p>
Ntfy is a simple free service able to send push notifications to phones and computers.
<a href="https://docs.ntfy.sh/subscribe/phone/" target="_blank" rel="noopener noreferrer">Ntfy apps exist for all major phone platforms.</a>
This application can send a notification to a ntfy server on startup.
</p>
</div>
</div>
<div class="box">
<div class="columns">
<div class="column">
<p class="has-text-centered">
<strong>Server</strong><br />
<%= configFunctions.getProperty("application.ntfyStartup.server") || "Default Server" %>
</p>
</div>
<div class="column">
<p class="has-text-centered">
<strong>Topic</strong><br />
<%= configFunctions.getProperty("application.ntfyStartup.topic") %>
</p>
</div>
</div>
</div>
</div>
</div>
<%- include('_footerA'); -%>
<script src="<%= urlPrefix %>/javascripts/adminCleanup.min.js"></script>
<%- include('_footerB'); -%>

View File

@ -378,6 +378,23 @@
</div>
</div>
</div>
<% if (configFunctions.getProperty("application.ntfyStartup")) { %>
<div class="card-content">
<div class="media">
<div class="media-left">
<i class="fas fa-3x fa-fw fa-comment-alt" aria-hidden="true"></i>
</div>
<div class="media-content has-text-black">
<h2 class="title is-4 is-marginless">
<a href="<%= urlPrefix %>/admin/ntfyStartup">Ntfy Startup Notification</a>
</h2>
<p>
Subscribe to application startup notifications on a phone or a desktop computer.
</p>
</div>
</div>
</div>
<% } %>
</div>
<% } %>
</div>