Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions packages/brain/src/modules/apiClients/dappmanager/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
import logger from "../../logger/index.js";
import { StandardApi } from "../standard.js";
import { NotificationType } from "./types.js";
import { Manifest } from "./types.js";

export class DappmanagerApi extends StandardApi {
/**
* Triggers a notification in the dappmanager.
* Fetches the notification settings from the dappmanager.
*/
public async sendDappmanagerNotification({
notificationType,
title,
body
}: {
notificationType: NotificationType;
title: string;
body: string;
}): Promise<void> {
public async getPackageManifest({ dnpName }: { dnpName: string }): Promise<Manifest> {
try {
await this.request({
method: "POST",
endpoint: `/notification-send?type=${encodeURIComponent(notificationType)}&title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`
const response = await this.request({
method: "GET",
endpoint: `/package-manifest/${encodeURIComponent(dnpName)}`,
headers: {
Accept: "application/json"
}
});
return response;
} catch (error) {
logger.error("Failed to send notification to dappmanager", error);
logger.error("Failed to fetch notification settings from dappmanager", error);
throw error;
}
}
Expand Down
22 changes: 16 additions & 6 deletions packages/brain/src/modules/apiClients/dappmanager/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
// Must be in sync with dappmanager
export enum NotificationType {
Success = "success",
Info = "info",
Warning = "warning",
Danger = "danger"
export interface Manifest {
notifications?: {
customEndpoints?: CustomEndpoint[];
};
}

export interface CustomEndpoint {
name: string;
enabled: boolean;
description: string;
metric?: {
treshold: number;
min: number;
max: number;
unit: string;
};
}
6 changes: 5 additions & 1 deletion packages/brain/src/modules/apiClients/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { Web3SignerApi } from "./signer/index.js";
import { PostgresClient } from "./postgres/index.js";
import { BrainConfig } from "../config/types.js";
import { DappnodeSignatureVerifier } from "./signatureVerifier/index.js";
import { NotificationsApi } from "./notifications/index.js";

export {
BlockExplorerApi,
DappmanagerApi,
NotificationsApi,
PrometheusApi,
BeaconchainApi,
ValidatorApi,
Expand All @@ -27,6 +29,7 @@ export const getApiClients = (brainConfig: BrainConfig) => {
validatorUrl,
beaconchainUrl,
dappmanagerUrl,
notificationsUrl,
postgresUrl,
token,
tlsCert,
Expand Down Expand Up @@ -60,6 +63,7 @@ export const getApiClients = (brainConfig: BrainConfig) => {
),
beaconchainApi: new BeaconchainApi({ baseUrl: beaconchainUrl }, network),
dappmanagerApi: new DappmanagerApi({ baseUrl: dappmanagerUrl }, network),
postgresClient: new PostgresClient(postgresUrl)
postgresClient: new PostgresClient(postgresUrl),
notificationsApi: new NotificationsApi({ baseUrl: notificationsUrl }, network)
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ApiError } from "../error.js";

export class NotificationsApiError extends ApiError {
constructor(message: string) {
super(message);
this.name = "NotificationsApiError";
}
}
25 changes: 25 additions & 0 deletions packages/brain/src/modules/apiClients/notifications/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import logger from "../../logger/index.js";
import { StandardApi } from "../standard.js";
import { NotificationPayload } from "./types.js";

export class NotificationsApi extends StandardApi {
/**
* Sends a notification to the notifier service of the notifications pkg.
*/
public async sendNotification({ notificationPayload }: { notificationPayload: NotificationPayload }): Promise<void> {
try {
await this.request({
method: "POST",
endpoint: `/api/v1/notification`,
body: JSON.stringify(notificationPayload),
headers: {
"Content-Type": "application/json",
Accept: "application/json"
}
});
} catch (error) {
logger.error("Failed to send notification to dappmanager", error);
throw error;
}
}
}
11 changes: 11 additions & 0 deletions packages/brain/src/modules/apiClients/notifications/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface NotificationPayload {
title: string;
body: string;
category: string;
dnpName: string;
callToAction?: {
title: string;
url: string;
};
errors?: string;
}
1 change: 1 addition & 0 deletions packages/brain/src/modules/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface ApisConfig {
beaconchainUrl: string;
executionClientUrl: string;
dappmanagerUrl: string;
notificationsUrl: string;
postgresUrl: string;
token: string;
tlsCert: Buffer | null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Network } from "@stakingbrain/common";
import { BeaconchainApi, DappmanagerApi, NotificationsApi } from "../../apiClients/index.js";
import { getWeb3signerDnpName } from "./params.js";
import logger from "../../logger/index.js";
import { logPrefix } from "./logPrefix.js";
import { BrainDataBase } from "../../db/index.js";
import { sendValidatorsOfflineNotification } from "./sendValidatorsOfflineNotification.js";

export async function sendValidatorsNotifications({
network,
brainDb,
beaconchainApi,
dappmanagerApi,
notificationsApi
}: {
network: Network;
brainDb: BrainDataBase;
beaconchainApi: BeaconchainApi;
dappmanagerApi: DappmanagerApi;
notificationsApi: NotificationsApi;
}): Promise<void> {
// Get web3signer notifications settings from dappmanager API
const web3signerDnpName = getWeb3signerDnpName(network);
const manifest = await dappmanagerApi.getPackageManifest({ dnpName: web3signerDnpName });

const customEndpoints = manifest.notifications?.customEndpoints;

if (!customEndpoints?.length) {
logger.warn(`${logPrefix}No notification settings found for ${web3signerDnpName}. Notifications will not be sent.`);
return;
}

// Send notifications if any

// validator offline
const validatorOfflineNotificationEnabled = customEndpoints.some(
(endpoint) => endpoint.name === "Validator offline" && endpoint.enabled
);
if (validatorOfflineNotificationEnabled)
await sendValidatorsOfflineNotification(brainDb, beaconchainApi, notificationsApi);

// validator liveness
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const logPrefix = "[CRON - sendValidatorsNotification]: ";
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Network } from "@stakingbrain/common";

export const getWeb3signerDnpName = (network: Network): string => {
switch (network) {
case Network.Mainnet:
return "web3signer.dnp.dappnode.eth";
case Network.Gnosis:
return "web3signer-gnosis.dnp.dappnode.eth";
case Network.Holesky:
return "web3signer-holesky.dnp.dapopnode.eth";
case Network.Prater:
return "web3signer-prater.dnp.dappnode.eth";
case Network.Lukso:
return "web3signer-lukso.dnp.dappnode.eth";
case Network.Hoodi:
return "web3signer-hoodi.dnp.dappnode.eth";
default:
throw new Error("Invalid network");
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { BrainDataBase } from "../../db/index.js";
import { BeaconchainApi, NotificationsApi } from "../../apiClients/index.js";

export async function sendValidatorsOfflineNotification(
brainDb: BrainDataBase,

Check failure on line 5 in packages/brain/src/modules/cron/sendValidatorsNotifications/sendValidatorsOfflineNotification.ts

View workflow job for this annotation

GitHub Actions / build

'brainDb' is defined but never used. Allowed unused args must match /^_/u
beaconchainApi: BeaconchainApi,

Check failure on line 6 in packages/brain/src/modules/cron/sendValidatorsNotifications/sendValidatorsOfflineNotification.ts

View workflow job for this annotation

GitHub Actions / build

'beaconchainApi' is defined but never used. Allowed unused args must match /^_/u
notificationsApi: NotificationsApi

Check failure on line 7 in packages/brain/src/modules/cron/sendValidatorsNotifications/sendValidatorsOfflineNotification.ts

View workflow job for this annotation

GitHub Actions / build

'notificationsApi' is defined but never used. Allowed unused args must match /^_/u
): Promise<void> {}
Loading