ThreadCount Community edition
Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { prisma } from "../db";
|
||||
import { sendTo } from "../mail";
|
||||
|
||||
/* Notices to the owner, by email, over the SMTP the product already has.
|
||||
*
|
||||
* Who gets them: OPS_ALERT_TO when set, otherwise every active OWNER operator. What they carry:
|
||||
* the name of a facility and what happened to it — never a coordinator's name or email, never a
|
||||
* wearer, never a record. A notice must be safe to sit in a mailbox. Each caller names its own
|
||||
* condition; nothing here fires on a timer, so there is nothing to dedupe — one event, one mail.
|
||||
*
|
||||
* Never throws and never awaited for its own sake: a notice that could fail the action it
|
||||
* describes would be a reason to skip sending it. */
|
||||
export async function ownerAddresses(): Promise<string[]> {
|
||||
const fixed = (process.env.OPS_ALERT_TO || "").split(",").map((s) => s.trim()).filter(Boolean);
|
||||
if (fixed.length) return fixed;
|
||||
try {
|
||||
const owners = await prisma.operator.findMany({ where: { role: "OWNER", inactive: false }, select: { email: true } });
|
||||
return owners.map((o) => o.email);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function notifyOwners(subject: string, text: string): void {
|
||||
ownerAddresses()
|
||||
.then((to) => Promise.all(to.map((a) => sendTo(a, subject, text))))
|
||||
.catch(() => { /* a notice must not fail the action */ });
|
||||
}
|
||||
|
||||
const when = () => new Date().toLocaleString("en-AU", { timeZone: "Australia/Brisbane", dateStyle: "medium", timeStyle: "short" });
|
||||
|
||||
/** A facility signed up. Name only — the person who did it is a contact, and contacts are revealed, not mailed. */
|
||||
export function alertNewSignup(facility: { id: string; name: string }): void {
|
||||
notifyOwners(`[ops] New facility — ${facility.name}`, [
|
||||
`A facility signed up for ThreadCount.`,
|
||||
"",
|
||||
`Facility: ${facility.name}`,
|
||||
`When: ${when()} (Brisbane)`,
|
||||
`Console: https://ops.threadcount.tech/ops/facilities/${facility.id}`,
|
||||
"",
|
||||
"It appears on the console as \"never set up\" until its staff groups are named.",
|
||||
].join("\n"));
|
||||
}
|
||||
|
||||
/** A facility was deleted from the console. Sent to every owner so a second operator's deletion reaches the first. */
|
||||
export function alertFacilityDeleted(args: { name: string; by: string; ip: string; counts: string }): void {
|
||||
notifyOwners(`[ops] Facility deleted — ${args.name}`, [
|
||||
`${args.by} deleted a facility from the ThreadCount operations console.`,
|
||||
"",
|
||||
`Facility: ${args.name}`,
|
||||
`Had: ${args.counts}`,
|
||||
`When: ${when()} (Brisbane)`,
|
||||
`From: ${args.ip || "unknown address"}`,
|
||||
"",
|
||||
"Every record, image and account belonging to it is gone. The pre-deploy database dumps on the box are the only copies left.",
|
||||
].join("\n"));
|
||||
}
|
||||
Reference in New Issue
Block a user