feat: unify alerts into one plugin, channels, rules and inbox

This commit is contained in:
LukeGus committed 2026-09-25 16:26:37 -05:00
1 parent fbe736bc20
commit 7c02daccf4
129 files changed
+20957 -2687

No files matched your search

+1
View File
@@ -20,6 +20,7 @@
"secrets:own",
"network:outbound",
"network:serve",
"notify:send",
"ui:surface"
],
"contributes": {
+12 -1
View File
@@ -134,10 +134,21 @@ export function createAcmeRunner(
await runner.issue();
return true;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
ctx.log.error(
"Certificate renewal failed",
error instanceof Error ? error : new Error(String(error)),
error instanceof Error ? error : new Error(message),
);
await ctx.notify
.send({
title: `Certificate renewal failed for ${settings.domain}`,
body: message,
severity: "critical",
category: "acme-ssl.renewal_failed",
audience: "admins",
dedupeKey: `acme-ssl.renewal_failed:${settings.domain}`,
})
.catch(() => {});
return false;
}
},
@@ -176,6 +176,23 @@ describe("scheduled renewal", () => {
await vi.waitFor(() => expect(mock.tls.renewers).toBe(0));
});
it("alerts every admin when a renewal fails", async () => {
const { mock, runner } = await setup({ settings: CONFIGURED });
issue.mockRejectedValueOnce(new Error("rate limited"));
expect(await runner.check()).toBe(false);
expect(mock.notifications).toEqual([
{
actor: undefined,
notification: expect.objectContaining({
body: "rate limited",
severity: "critical",
category: "acme-ssl.renewal_failed",
audience: "admins",
}),
},
]);
});
it("waits before retrying after a failed attempt", async () => {
const { mock, runner } = await setup({ settings: CONFIGURED });
issue.mockRejectedValueOnce(new Error("rate limited"));