@camstack/addon-notifiers 1.2.130 → 1.2.131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +178 -12
- package/dist/addon.mjs +178 -12
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -13714,7 +13714,14 @@ var BrokerProviderInfoSchema = object({
|
|
|
13714
13714
|
/** Broker kinds this provider can create, with a display label. */
|
|
13715
13715
|
kinds: array(object({
|
|
13716
13716
|
kind: string(),
|
|
13717
|
-
label: string()
|
|
13717
|
+
label: string(),
|
|
13718
|
+
/**
|
|
13719
|
+
* Relative hub path to start an interactive account link (OAuth popup).
|
|
13720
|
+
* May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
|
|
13721
|
+
*/
|
|
13722
|
+
interactiveConnectPath: string().min(1).optional(),
|
|
13723
|
+
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
13724
|
+
interactiveDisconnectPath: string().min(1).optional()
|
|
13718
13725
|
}))
|
|
13719
13726
|
});
|
|
13720
13727
|
var ListInputSchema = object({
|
|
@@ -51976,18 +51983,18 @@ var HTML_ESCAPES = [
|
|
|
51976
51983
|
[/"/g, """],
|
|
51977
51984
|
[/'/g, "'"]
|
|
51978
51985
|
];
|
|
51979
|
-
function escapeHtml$
|
|
51986
|
+
function escapeHtml$2(text) {
|
|
51980
51987
|
let out = text;
|
|
51981
51988
|
for (const [re, rep] of HTML_ESCAPES) out = out.replace(re, rep);
|
|
51982
51989
|
return out;
|
|
51983
51990
|
}
|
|
51984
51991
|
/** Plain text → HTML-lite (escape + newlines to <br>). */
|
|
51985
51992
|
function textToHtml(text) {
|
|
51986
|
-
return escapeHtml$
|
|
51993
|
+
return escapeHtml$2(text).replace(/\n/g, "<br>");
|
|
51987
51994
|
}
|
|
51988
51995
|
/** Markdown → HTML-lite (bold/italic/code/links; used e.g. for Pushover). */
|
|
51989
51996
|
function markdownToHtmlLite(md) {
|
|
51990
|
-
return escapeHtml$
|
|
51997
|
+
return escapeHtml$2(md).replace(/(\*\*|__)(.*?)\1/g, "<b>$2</b>").replace(/(\*|_)(.*?)\1/g, "<i>$2</i>").replace(/`([^`]*)`/g, "<code>$1</code>").replace(/\[([^\]]*)\]\(([^)]*)\)/g, "<a href=\"$2\">$1</a>").replace(/\n/g, "<br>");
|
|
51991
51998
|
}
|
|
51992
51999
|
/**
|
|
51993
52000
|
* Preference order when the source format is NOT supported — first supported
|
|
@@ -53345,6 +53352,116 @@ var discordAdapter = {
|
|
|
53345
53352
|
};
|
|
53346
53353
|
}
|
|
53347
53354
|
};
|
|
53355
|
+
var descriptor$2 = {
|
|
53356
|
+
kind: "email",
|
|
53357
|
+
label: "Email",
|
|
53358
|
+
icon: "mail",
|
|
53359
|
+
addonId: "notifiers",
|
|
53360
|
+
supportsDiscovery: false,
|
|
53361
|
+
configSchema: { sections: [{
|
|
53362
|
+
id: "email",
|
|
53363
|
+
title: "Email",
|
|
53364
|
+
description: "Send via a Connectivity → Email provider (Google Services or SMTP). Install and Verify a provider first.",
|
|
53365
|
+
fields: [
|
|
53366
|
+
{
|
|
53367
|
+
type: "text",
|
|
53368
|
+
key: "to",
|
|
53369
|
+
label: "To",
|
|
53370
|
+
description: "Recipient address, or comma-separated list.",
|
|
53371
|
+
required: true
|
|
53372
|
+
},
|
|
53373
|
+
{
|
|
53374
|
+
type: "text",
|
|
53375
|
+
key: "smtpAddonId",
|
|
53376
|
+
label: "Email provider addon id (optional)",
|
|
53377
|
+
description: "Leave blank to use the first ready smtp-provider. Examples: google-services, smtp-nodemailer."
|
|
53378
|
+
},
|
|
53379
|
+
{
|
|
53380
|
+
type: "text",
|
|
53381
|
+
key: "from",
|
|
53382
|
+
label: "From (optional)",
|
|
53383
|
+
description: "Override From when the relay allows it."
|
|
53384
|
+
}
|
|
53385
|
+
]
|
|
53386
|
+
}] },
|
|
53387
|
+
caps: {
|
|
53388
|
+
attachments: {
|
|
53389
|
+
mediaTypes: ["image"],
|
|
53390
|
+
mode: "url",
|
|
53391
|
+
max: 0
|
|
53392
|
+
},
|
|
53393
|
+
actions: 0,
|
|
53394
|
+
levels: [{
|
|
53395
|
+
id: "normal",
|
|
53396
|
+
label: "Normal",
|
|
53397
|
+
ordinal: 3
|
|
53398
|
+
}, {
|
|
53399
|
+
id: "high",
|
|
53400
|
+
label: "High",
|
|
53401
|
+
ordinal: 4
|
|
53402
|
+
}],
|
|
53403
|
+
format: [
|
|
53404
|
+
"text",
|
|
53405
|
+
"html",
|
|
53406
|
+
"markdown"
|
|
53407
|
+
],
|
|
53408
|
+
clickUrl: true,
|
|
53409
|
+
sound: false,
|
|
53410
|
+
ttl: false,
|
|
53411
|
+
bodyMaxLen: 1e5
|
|
53412
|
+
}
|
|
53413
|
+
};
|
|
53414
|
+
function requireTo(config) {
|
|
53415
|
+
const raw = config["to"];
|
|
53416
|
+
if (typeof raw !== "string" || raw.trim() === "") throw new Error("email: missing required config 'to'");
|
|
53417
|
+
const list = raw.split(/[,;\s]+/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
53418
|
+
if (list.length === 0) throw new Error("email: missing required config 'to'");
|
|
53419
|
+
return list;
|
|
53420
|
+
}
|
|
53421
|
+
function renderBodies(prepared) {
|
|
53422
|
+
const body = prepared.bodyParts[0] ?? prepared.body;
|
|
53423
|
+
const title = prepared.title?.trim() ?? "";
|
|
53424
|
+
const click = prepared.clickUrl !== null && prepared.clickUrl !== void 0 ? `\n\n${prepared.clickUrl}` : "";
|
|
53425
|
+
const text = title !== "" ? `${title}\n\n${body}${click}` : `${body}${click}`;
|
|
53426
|
+
if (prepared.format === "html") return {
|
|
53427
|
+
text,
|
|
53428
|
+
html: `${title !== "" ? `<h2>${escapeHtml$1(title)}</h2>` : ""}<div>${body}</div>${prepared.clickUrl !== null && prepared.clickUrl !== void 0 ? `<p><a href="${escapeHtml$1(prepared.clickUrl)}">${escapeHtml$1(prepared.clickUrl)}</a></p>` : ""}`
|
|
53429
|
+
};
|
|
53430
|
+
return { text };
|
|
53431
|
+
}
|
|
53432
|
+
function escapeHtml$1(value) {
|
|
53433
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
53434
|
+
}
|
|
53435
|
+
/** Build the email adapter bound to a live smtp-provider client. */
|
|
53436
|
+
function createEmailAdapter(smtp) {
|
|
53437
|
+
return {
|
|
53438
|
+
descriptor: descriptor$2,
|
|
53439
|
+
destinationKeys: ["to"],
|
|
53440
|
+
async sendDirect(config, prepared) {
|
|
53441
|
+
try {
|
|
53442
|
+
const to = requireTo(config);
|
|
53443
|
+
const bodies = renderBodies(prepared);
|
|
53444
|
+
const addonId = (typeof config["smtpAddonId"] === "string" && config["smtpAddonId"].trim() !== "" ? config["smtpAddonId"].trim() : void 0) ?? await smtp.resolveDefaultAddonId?.();
|
|
53445
|
+
const subject = prepared.title?.trim() && prepared.title.trim().length > 0 ? prepared.title.trim() : "CamStack notification";
|
|
53446
|
+
const from = typeof config["from"] === "string" && config["from"].trim() !== "" ? config["from"].trim() : void 0;
|
|
53447
|
+
await smtp.sendEmail({
|
|
53448
|
+
to,
|
|
53449
|
+
subject,
|
|
53450
|
+
text: bodies.text,
|
|
53451
|
+
...bodies.html !== void 0 ? { html: bodies.html } : {},
|
|
53452
|
+
...addonId !== void 0 ? { addonId } : {},
|
|
53453
|
+
...from !== void 0 ? { from } : {}
|
|
53454
|
+
});
|
|
53455
|
+
return { success: true };
|
|
53456
|
+
} catch (err) {
|
|
53457
|
+
return {
|
|
53458
|
+
success: false,
|
|
53459
|
+
error: err instanceof Error ? err.message : String(err)
|
|
53460
|
+
};
|
|
53461
|
+
}
|
|
53462
|
+
}
|
|
53463
|
+
};
|
|
53464
|
+
}
|
|
53348
53465
|
//#endregion
|
|
53349
53466
|
//#region src/http.ts
|
|
53350
53467
|
/** Default per-request timeout for notification dispatch. */
|
|
@@ -55280,7 +55397,8 @@ function zentikConfigSchema() {
|
|
|
55280
55397
|
}
|
|
55281
55398
|
//#endregion
|
|
55282
55399
|
//#region src/adapters/registry.ts
|
|
55283
|
-
|
|
55400
|
+
/** HTTP adapters that need no runtime deps. */
|
|
55401
|
+
var HTTP_NOTIFIER_ADAPTERS = [
|
|
55284
55402
|
ntfyAdapter,
|
|
55285
55403
|
pushoverAdapter,
|
|
55286
55404
|
gotifyAdapter,
|
|
@@ -55432,6 +55550,17 @@ var NOTIFIER_ADAPTERS = [
|
|
|
55432
55550
|
},
|
|
55433
55551
|
whatsappAdapter
|
|
55434
55552
|
];
|
|
55553
|
+
/**
|
|
55554
|
+
* Full catalog. When `smtp` is omitted the Email kind is still listed but
|
|
55555
|
+
* `sendDirect` fails until the addon wires a client (tests use this).
|
|
55556
|
+
*/
|
|
55557
|
+
function buildNotifierAdapters(smtp) {
|
|
55558
|
+
const emailSmtp = smtp ?? { async sendEmail() {
|
|
55559
|
+
throw new Error("email: no smtp-provider client — install Google Services or SMTP Email under Connectivity → Email and Verify it");
|
|
55560
|
+
} };
|
|
55561
|
+
return [...HTTP_NOTIFIER_ADAPTERS, createEmailAdapter(emailSmtp)];
|
|
55562
|
+
}
|
|
55563
|
+
buildNotifierAdapters();
|
|
55435
55564
|
var NOTIFIER_ICONS = {
|
|
55436
55565
|
telegram: {
|
|
55437
55566
|
contentType: "image/svg+xml",
|
|
@@ -56273,6 +56402,32 @@ function createNotificationOutputProvider(deps) {
|
|
|
56273
56402
|
...prepared,
|
|
56274
56403
|
attachments
|
|
56275
56404
|
};
|
|
56405
|
+
if (adapter.sendDirect) try {
|
|
56406
|
+
const result = await adapter.sendDirect(target.config, readyPrepared);
|
|
56407
|
+
return result.error === void 0 ? {
|
|
56408
|
+
...result,
|
|
56409
|
+
renderedAs: prepared.renderedAs
|
|
56410
|
+
} : {
|
|
56411
|
+
...result,
|
|
56412
|
+
error: stripUrls(result.error),
|
|
56413
|
+
renderedAs: prepared.renderedAs
|
|
56414
|
+
};
|
|
56415
|
+
} catch (err) {
|
|
56416
|
+
logger?.warn("sendDirect failed", { meta: {
|
|
56417
|
+
kind: target.kind,
|
|
56418
|
+
error: errMsg(err)
|
|
56419
|
+
} });
|
|
56420
|
+
return {
|
|
56421
|
+
success: false,
|
|
56422
|
+
error: `sendDirect failed: ${errMsg(err)}`,
|
|
56423
|
+
renderedAs: prepared.renderedAs
|
|
56424
|
+
};
|
|
56425
|
+
}
|
|
56426
|
+
if (!adapter.buildRequest) return {
|
|
56427
|
+
success: false,
|
|
56428
|
+
error: `No adapter send path for kind '${target.kind}'`,
|
|
56429
|
+
renderedAs: prepared.renderedAs
|
|
56430
|
+
};
|
|
56276
56431
|
let plan;
|
|
56277
56432
|
try {
|
|
56278
56433
|
plan = adapter.buildRequest(target.config, readyPrepared);
|
|
@@ -56640,11 +56795,12 @@ var TargetStore = class {
|
|
|
56640
56795
|
* Notifiers addon — a system `notification-output` collection provider.
|
|
56641
56796
|
*
|
|
56642
56797
|
* It hosts per-kind `NotifierAdapter`s (ntfy, pushover, gotify, telegram,
|
|
56643
|
-
* discord, webhook, zentik, whatsapp) behind one uniform cap surface.
|
|
56644
|
-
* persist via the `settings-store` cap; the canonical notification is
|
|
56645
|
-
* per each target's declared capabilities by the pure
|
|
56646
|
-
* engine in `@camstack/types`. The catalog is
|
|
56647
|
-
* (`adapters/registry.ts`)
|
|
56798
|
+
* discord, webhook, zentik, whatsapp, email) behind one uniform cap surface.
|
|
56799
|
+
* Targets persist via the `settings-store` cap; the canonical notification is
|
|
56800
|
+
* degraded per each target's declared capabilities by the pure
|
|
56801
|
+
* `prepareNotification` engine in `@camstack/types`. The catalog is
|
|
56802
|
+
* `buildNotifierAdapters` (`adapters/registry.ts`); email uses `sendDirect` →
|
|
56803
|
+
* `smtp-provider`.
|
|
56648
56804
|
*/
|
|
56649
56805
|
var NotifiersAddon = class extends BaseAddon {
|
|
56650
56806
|
constructor() {
|
|
@@ -56656,9 +56812,19 @@ var NotifiersAddon = class extends BaseAddon {
|
|
|
56656
56812
|
if (!api) this.ctx.logger.warn("notifiers: no ctx.api — targets persist in-memory only");
|
|
56657
56813
|
const store = new TargetStore(port);
|
|
56658
56814
|
await store.init();
|
|
56815
|
+
const adapters = buildNotifierAdapters(api !== void 0 ? { sendEmail: async (input) => {
|
|
56816
|
+
return { messageId: (await api.smtpProvider.sendEmail({
|
|
56817
|
+
to: input.to,
|
|
56818
|
+
subject: input.subject,
|
|
56819
|
+
text: input.text,
|
|
56820
|
+
...input.html !== void 0 ? { html: input.html } : {},
|
|
56821
|
+
...input.from !== void 0 ? { from: input.from } : {},
|
|
56822
|
+
...input.addonId !== void 0 ? { addonId: input.addonId } : {}
|
|
56823
|
+
})).messageId };
|
|
56824
|
+
} } : void 0);
|
|
56659
56825
|
const provider = createNotificationOutputProvider({
|
|
56660
56826
|
store,
|
|
56661
|
-
adapters
|
|
56827
|
+
adapters,
|
|
56662
56828
|
logger: this.ctx.logger.child("notification-output"),
|
|
56663
56829
|
iconUrlFor,
|
|
56664
56830
|
iconMediaTypeFor
|
|
@@ -56668,7 +56834,7 @@ var NotifiersAddon = class extends BaseAddon {
|
|
|
56668
56834
|
provider,
|
|
56669
56835
|
logger: this.ctx.logger.child("nc-target-actions")
|
|
56670
56836
|
});
|
|
56671
|
-
this.ctx.logger.info("NotifiersAddon initialized", { meta: { adapterCount:
|
|
56837
|
+
this.ctx.logger.info("NotifiersAddon initialized", { meta: { adapterCount: adapters.length } });
|
|
56672
56838
|
return {
|
|
56673
56839
|
providers: [{
|
|
56674
56840
|
capability: notificationOutputCapability,
|
package/dist/addon.mjs
CHANGED
|
@@ -13687,7 +13687,14 @@ var BrokerProviderInfoSchema = object({
|
|
|
13687
13687
|
/** Broker kinds this provider can create, with a display label. */
|
|
13688
13688
|
kinds: array(object({
|
|
13689
13689
|
kind: string(),
|
|
13690
|
-
label: string()
|
|
13690
|
+
label: string(),
|
|
13691
|
+
/**
|
|
13692
|
+
* Relative hub path to start an interactive account link (OAuth popup).
|
|
13693
|
+
* May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
|
|
13694
|
+
*/
|
|
13695
|
+
interactiveConnectPath: string().min(1).optional(),
|
|
13696
|
+
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
13697
|
+
interactiveDisconnectPath: string().min(1).optional()
|
|
13691
13698
|
}))
|
|
13692
13699
|
});
|
|
13693
13700
|
var ListInputSchema = object({
|
|
@@ -51949,18 +51956,18 @@ var HTML_ESCAPES = [
|
|
|
51949
51956
|
[/"/g, """],
|
|
51950
51957
|
[/'/g, "'"]
|
|
51951
51958
|
];
|
|
51952
|
-
function escapeHtml$
|
|
51959
|
+
function escapeHtml$2(text) {
|
|
51953
51960
|
let out = text;
|
|
51954
51961
|
for (const [re, rep] of HTML_ESCAPES) out = out.replace(re, rep);
|
|
51955
51962
|
return out;
|
|
51956
51963
|
}
|
|
51957
51964
|
/** Plain text → HTML-lite (escape + newlines to <br>). */
|
|
51958
51965
|
function textToHtml(text) {
|
|
51959
|
-
return escapeHtml$
|
|
51966
|
+
return escapeHtml$2(text).replace(/\n/g, "<br>");
|
|
51960
51967
|
}
|
|
51961
51968
|
/** Markdown → HTML-lite (bold/italic/code/links; used e.g. for Pushover). */
|
|
51962
51969
|
function markdownToHtmlLite(md) {
|
|
51963
|
-
return escapeHtml$
|
|
51970
|
+
return escapeHtml$2(md).replace(/(\*\*|__)(.*?)\1/g, "<b>$2</b>").replace(/(\*|_)(.*?)\1/g, "<i>$2</i>").replace(/`([^`]*)`/g, "<code>$1</code>").replace(/\[([^\]]*)\]\(([^)]*)\)/g, "<a href=\"$2\">$1</a>").replace(/\n/g, "<br>");
|
|
51964
51971
|
}
|
|
51965
51972
|
/**
|
|
51966
51973
|
* Preference order when the source format is NOT supported — first supported
|
|
@@ -53318,6 +53325,116 @@ var discordAdapter = {
|
|
|
53318
53325
|
};
|
|
53319
53326
|
}
|
|
53320
53327
|
};
|
|
53328
|
+
var descriptor$2 = {
|
|
53329
|
+
kind: "email",
|
|
53330
|
+
label: "Email",
|
|
53331
|
+
icon: "mail",
|
|
53332
|
+
addonId: "notifiers",
|
|
53333
|
+
supportsDiscovery: false,
|
|
53334
|
+
configSchema: { sections: [{
|
|
53335
|
+
id: "email",
|
|
53336
|
+
title: "Email",
|
|
53337
|
+
description: "Send via a Connectivity → Email provider (Google Services or SMTP). Install and Verify a provider first.",
|
|
53338
|
+
fields: [
|
|
53339
|
+
{
|
|
53340
|
+
type: "text",
|
|
53341
|
+
key: "to",
|
|
53342
|
+
label: "To",
|
|
53343
|
+
description: "Recipient address, or comma-separated list.",
|
|
53344
|
+
required: true
|
|
53345
|
+
},
|
|
53346
|
+
{
|
|
53347
|
+
type: "text",
|
|
53348
|
+
key: "smtpAddonId",
|
|
53349
|
+
label: "Email provider addon id (optional)",
|
|
53350
|
+
description: "Leave blank to use the first ready smtp-provider. Examples: google-services, smtp-nodemailer."
|
|
53351
|
+
},
|
|
53352
|
+
{
|
|
53353
|
+
type: "text",
|
|
53354
|
+
key: "from",
|
|
53355
|
+
label: "From (optional)",
|
|
53356
|
+
description: "Override From when the relay allows it."
|
|
53357
|
+
}
|
|
53358
|
+
]
|
|
53359
|
+
}] },
|
|
53360
|
+
caps: {
|
|
53361
|
+
attachments: {
|
|
53362
|
+
mediaTypes: ["image"],
|
|
53363
|
+
mode: "url",
|
|
53364
|
+
max: 0
|
|
53365
|
+
},
|
|
53366
|
+
actions: 0,
|
|
53367
|
+
levels: [{
|
|
53368
|
+
id: "normal",
|
|
53369
|
+
label: "Normal",
|
|
53370
|
+
ordinal: 3
|
|
53371
|
+
}, {
|
|
53372
|
+
id: "high",
|
|
53373
|
+
label: "High",
|
|
53374
|
+
ordinal: 4
|
|
53375
|
+
}],
|
|
53376
|
+
format: [
|
|
53377
|
+
"text",
|
|
53378
|
+
"html",
|
|
53379
|
+
"markdown"
|
|
53380
|
+
],
|
|
53381
|
+
clickUrl: true,
|
|
53382
|
+
sound: false,
|
|
53383
|
+
ttl: false,
|
|
53384
|
+
bodyMaxLen: 1e5
|
|
53385
|
+
}
|
|
53386
|
+
};
|
|
53387
|
+
function requireTo(config) {
|
|
53388
|
+
const raw = config["to"];
|
|
53389
|
+
if (typeof raw !== "string" || raw.trim() === "") throw new Error("email: missing required config 'to'");
|
|
53390
|
+
const list = raw.split(/[,;\s]+/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
53391
|
+
if (list.length === 0) throw new Error("email: missing required config 'to'");
|
|
53392
|
+
return list;
|
|
53393
|
+
}
|
|
53394
|
+
function renderBodies(prepared) {
|
|
53395
|
+
const body = prepared.bodyParts[0] ?? prepared.body;
|
|
53396
|
+
const title = prepared.title?.trim() ?? "";
|
|
53397
|
+
const click = prepared.clickUrl !== null && prepared.clickUrl !== void 0 ? `\n\n${prepared.clickUrl}` : "";
|
|
53398
|
+
const text = title !== "" ? `${title}\n\n${body}${click}` : `${body}${click}`;
|
|
53399
|
+
if (prepared.format === "html") return {
|
|
53400
|
+
text,
|
|
53401
|
+
html: `${title !== "" ? `<h2>${escapeHtml$1(title)}</h2>` : ""}<div>${body}</div>${prepared.clickUrl !== null && prepared.clickUrl !== void 0 ? `<p><a href="${escapeHtml$1(prepared.clickUrl)}">${escapeHtml$1(prepared.clickUrl)}</a></p>` : ""}`
|
|
53402
|
+
};
|
|
53403
|
+
return { text };
|
|
53404
|
+
}
|
|
53405
|
+
function escapeHtml$1(value) {
|
|
53406
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
53407
|
+
}
|
|
53408
|
+
/** Build the email adapter bound to a live smtp-provider client. */
|
|
53409
|
+
function createEmailAdapter(smtp) {
|
|
53410
|
+
return {
|
|
53411
|
+
descriptor: descriptor$2,
|
|
53412
|
+
destinationKeys: ["to"],
|
|
53413
|
+
async sendDirect(config, prepared) {
|
|
53414
|
+
try {
|
|
53415
|
+
const to = requireTo(config);
|
|
53416
|
+
const bodies = renderBodies(prepared);
|
|
53417
|
+
const addonId = (typeof config["smtpAddonId"] === "string" && config["smtpAddonId"].trim() !== "" ? config["smtpAddonId"].trim() : void 0) ?? await smtp.resolveDefaultAddonId?.();
|
|
53418
|
+
const subject = prepared.title?.trim() && prepared.title.trim().length > 0 ? prepared.title.trim() : "CamStack notification";
|
|
53419
|
+
const from = typeof config["from"] === "string" && config["from"].trim() !== "" ? config["from"].trim() : void 0;
|
|
53420
|
+
await smtp.sendEmail({
|
|
53421
|
+
to,
|
|
53422
|
+
subject,
|
|
53423
|
+
text: bodies.text,
|
|
53424
|
+
...bodies.html !== void 0 ? { html: bodies.html } : {},
|
|
53425
|
+
...addonId !== void 0 ? { addonId } : {},
|
|
53426
|
+
...from !== void 0 ? { from } : {}
|
|
53427
|
+
});
|
|
53428
|
+
return { success: true };
|
|
53429
|
+
} catch (err) {
|
|
53430
|
+
return {
|
|
53431
|
+
success: false,
|
|
53432
|
+
error: err instanceof Error ? err.message : String(err)
|
|
53433
|
+
};
|
|
53434
|
+
}
|
|
53435
|
+
}
|
|
53436
|
+
};
|
|
53437
|
+
}
|
|
53321
53438
|
//#endregion
|
|
53322
53439
|
//#region src/http.ts
|
|
53323
53440
|
/** Default per-request timeout for notification dispatch. */
|
|
@@ -55253,7 +55370,8 @@ function zentikConfigSchema() {
|
|
|
55253
55370
|
}
|
|
55254
55371
|
//#endregion
|
|
55255
55372
|
//#region src/adapters/registry.ts
|
|
55256
|
-
|
|
55373
|
+
/** HTTP adapters that need no runtime deps. */
|
|
55374
|
+
var HTTP_NOTIFIER_ADAPTERS = [
|
|
55257
55375
|
ntfyAdapter,
|
|
55258
55376
|
pushoverAdapter,
|
|
55259
55377
|
gotifyAdapter,
|
|
@@ -55405,6 +55523,17 @@ var NOTIFIER_ADAPTERS = [
|
|
|
55405
55523
|
},
|
|
55406
55524
|
whatsappAdapter
|
|
55407
55525
|
];
|
|
55526
|
+
/**
|
|
55527
|
+
* Full catalog. When `smtp` is omitted the Email kind is still listed but
|
|
55528
|
+
* `sendDirect` fails until the addon wires a client (tests use this).
|
|
55529
|
+
*/
|
|
55530
|
+
function buildNotifierAdapters(smtp) {
|
|
55531
|
+
const emailSmtp = smtp ?? { async sendEmail() {
|
|
55532
|
+
throw new Error("email: no smtp-provider client — install Google Services or SMTP Email under Connectivity → Email and Verify it");
|
|
55533
|
+
} };
|
|
55534
|
+
return [...HTTP_NOTIFIER_ADAPTERS, createEmailAdapter(emailSmtp)];
|
|
55535
|
+
}
|
|
55536
|
+
buildNotifierAdapters();
|
|
55408
55537
|
var NOTIFIER_ICONS = {
|
|
55409
55538
|
telegram: {
|
|
55410
55539
|
contentType: "image/svg+xml",
|
|
@@ -56246,6 +56375,32 @@ function createNotificationOutputProvider(deps) {
|
|
|
56246
56375
|
...prepared,
|
|
56247
56376
|
attachments
|
|
56248
56377
|
};
|
|
56378
|
+
if (adapter.sendDirect) try {
|
|
56379
|
+
const result = await adapter.sendDirect(target.config, readyPrepared);
|
|
56380
|
+
return result.error === void 0 ? {
|
|
56381
|
+
...result,
|
|
56382
|
+
renderedAs: prepared.renderedAs
|
|
56383
|
+
} : {
|
|
56384
|
+
...result,
|
|
56385
|
+
error: stripUrls(result.error),
|
|
56386
|
+
renderedAs: prepared.renderedAs
|
|
56387
|
+
};
|
|
56388
|
+
} catch (err) {
|
|
56389
|
+
logger?.warn("sendDirect failed", { meta: {
|
|
56390
|
+
kind: target.kind,
|
|
56391
|
+
error: errMsg(err)
|
|
56392
|
+
} });
|
|
56393
|
+
return {
|
|
56394
|
+
success: false,
|
|
56395
|
+
error: `sendDirect failed: ${errMsg(err)}`,
|
|
56396
|
+
renderedAs: prepared.renderedAs
|
|
56397
|
+
};
|
|
56398
|
+
}
|
|
56399
|
+
if (!adapter.buildRequest) return {
|
|
56400
|
+
success: false,
|
|
56401
|
+
error: `No adapter send path for kind '${target.kind}'`,
|
|
56402
|
+
renderedAs: prepared.renderedAs
|
|
56403
|
+
};
|
|
56249
56404
|
let plan;
|
|
56250
56405
|
try {
|
|
56251
56406
|
plan = adapter.buildRequest(target.config, readyPrepared);
|
|
@@ -56613,11 +56768,12 @@ var TargetStore = class {
|
|
|
56613
56768
|
* Notifiers addon — a system `notification-output` collection provider.
|
|
56614
56769
|
*
|
|
56615
56770
|
* It hosts per-kind `NotifierAdapter`s (ntfy, pushover, gotify, telegram,
|
|
56616
|
-
* discord, webhook, zentik, whatsapp) behind one uniform cap surface.
|
|
56617
|
-
* persist via the `settings-store` cap; the canonical notification is
|
|
56618
|
-
* per each target's declared capabilities by the pure
|
|
56619
|
-
* engine in `@camstack/types`. The catalog is
|
|
56620
|
-
* (`adapters/registry.ts`)
|
|
56771
|
+
* discord, webhook, zentik, whatsapp, email) behind one uniform cap surface.
|
|
56772
|
+
* Targets persist via the `settings-store` cap; the canonical notification is
|
|
56773
|
+
* degraded per each target's declared capabilities by the pure
|
|
56774
|
+
* `prepareNotification` engine in `@camstack/types`. The catalog is
|
|
56775
|
+
* `buildNotifierAdapters` (`adapters/registry.ts`); email uses `sendDirect` →
|
|
56776
|
+
* `smtp-provider`.
|
|
56621
56777
|
*/
|
|
56622
56778
|
var NotifiersAddon = class extends BaseAddon {
|
|
56623
56779
|
constructor() {
|
|
@@ -56629,9 +56785,19 @@ var NotifiersAddon = class extends BaseAddon {
|
|
|
56629
56785
|
if (!api) this.ctx.logger.warn("notifiers: no ctx.api — targets persist in-memory only");
|
|
56630
56786
|
const store = new TargetStore(port);
|
|
56631
56787
|
await store.init();
|
|
56788
|
+
const adapters = buildNotifierAdapters(api !== void 0 ? { sendEmail: async (input) => {
|
|
56789
|
+
return { messageId: (await api.smtpProvider.sendEmail({
|
|
56790
|
+
to: input.to,
|
|
56791
|
+
subject: input.subject,
|
|
56792
|
+
text: input.text,
|
|
56793
|
+
...input.html !== void 0 ? { html: input.html } : {},
|
|
56794
|
+
...input.from !== void 0 ? { from: input.from } : {},
|
|
56795
|
+
...input.addonId !== void 0 ? { addonId: input.addonId } : {}
|
|
56796
|
+
})).messageId };
|
|
56797
|
+
} } : void 0);
|
|
56632
56798
|
const provider = createNotificationOutputProvider({
|
|
56633
56799
|
store,
|
|
56634
|
-
adapters
|
|
56800
|
+
adapters,
|
|
56635
56801
|
logger: this.ctx.logger.child("notification-output"),
|
|
56636
56802
|
iconUrlFor,
|
|
56637
56803
|
iconMediaTypeFor
|
|
@@ -56641,7 +56807,7 @@ var NotifiersAddon = class extends BaseAddon {
|
|
|
56641
56807
|
provider,
|
|
56642
56808
|
logger: this.ctx.logger.child("nc-target-actions")
|
|
56643
56809
|
});
|
|
56644
|
-
this.ctx.logger.info("NotifiersAddon initialized", { meta: { adapterCount:
|
|
56810
|
+
this.ctx.logger.info("NotifiersAddon initialized", { meta: { adapterCount: adapters.length } });
|
|
56645
56811
|
return {
|
|
56646
56812
|
providers: [{
|
|
56647
56813
|
capability: notificationOutputCapability,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-notifiers",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.131",
|
|
4
4
|
"description": "System notifiers addon for CamStack — a `notification-output` collection provider hosting per-kind notifier adapters (ntfy, pushover, gotify, telegram, discord, webhook, zentik).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|