Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/admin-ui/src/__mock-backend__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ export const otherCalls: Omit<Routes, keyof typeof namedSpacedCalls> = {
}),
getIsConnectedToInternet: async () => false,
getCoreVersion: async () => "0.2.92",
notificationsGetEndpoints: async () => {
notificationsGetAllEndpoints: async () => {
return { "geth.dnp.dappnode.eth": { endpoints: [], customEndpoints: [], isCore: false } };
},
notificationsUpdateEndpoints: async () => {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface EndpointsData {
export function NotificationsSettings() {
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
const [endpointsData, setEndpointsData] = useState<EndpointsData | undefined>();
const endpointsCall = useApi.notificationsGetEndpoints();
const endpointsCall = useApi.notificationsGetAllEndpoints();

useEffect(() => {
// Fetch the latest endpoints data when the component is mounted
Expand Down
6 changes: 3 additions & 3 deletions packages/daemons/src/autoUpdates/sendUpdateNotification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ export async function sendUpdatePackageNotificationMaybe({
newVersion: string;
}): Promise<void> {
// Check if auto-update notifications are enabled
const dappmanagerCustomEndpoint = (await notifications.getEndpoints())[
params.dappmanagerDnpName
].customEndpoints.find((customEndpoint) => customEndpoint.name === "auto-updates");
const dappmanagerCustomEndpoint = notifications
.getEndpointsIfExists(params.dappmanagerDnpName, true)
?.customEndpoints?.find((customEndpoint) => customEndpoint.name === "auto-updates");

if (!dappmanagerCustomEndpoint || !dappmanagerCustomEndpoint.enabled) return;

Expand Down
2 changes: 1 addition & 1 deletion packages/dappmanager/src/calls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export { getCoreVersion } from "./getCoreVersion.js";
export { getUserActionLogs } from "./getUserActionLogs.js";
export { getHostUptime } from "./getHostUptime.js";
export { getIsConnectedToInternet } from "./getIsConnectedToInternet.js";
export { notificationsGetEndpoints, notificationsUpdateEndpoints, notificationsGetAll } from "./notifications.js";
export { notificationsGetAllEndpoints, notificationsUpdateEndpoints, notificationsGetAll } from "./notifications.js";
export * from "./httpsPortal.js";
export { ipfsTest } from "./ipfsTest.js";
export { ipfsClientTargetSet } from "./ipfsClientTargetSet.js";
Expand Down
4 changes: 2 additions & 2 deletions packages/dappmanager/src/calls/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ export async function notificationsGetAll(): Promise<Notification[]> {
/**
* Get gatus and custom endpoints indexed by dnpName
*/
export async function notificationsGetEndpoints(): Promise<{
export async function notificationsGetAllEndpoints(): Promise<{
[dnpName: string]: { endpoints: GatusEndpoint[]; customEndpoints: CustomEndpoint[]; isCore: boolean };
}> {
return await notifications.getEndpoints();
return await notifications.getAllEndpoints();
}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/installer/src/installer/getInstallerPackageData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { getBackupPath, getDockerComposePath, getImagePath, getManifestPath } from "@dappnode/utils";
import { gt } from "semver";
import { logs } from "@dappnode/logger";
import { notifications } from "@dappnode/notifications";

interface GetInstallerPackageDataArg {
releases: PackageRelease[];
Expand Down Expand Up @@ -96,6 +97,13 @@ function getInstallerPackageData(
imagePath,
// Data to write
compose: compose.output(),
manifest: release.manifest.notifications
? {
...release.manifest,
// Apply notitications user settings if any
notifications: notifications.applyPreviousEndpoints(dnpName, isCore, release.manifest.notifications)
}
: release.manifest,
// User settings to be applied by the installer
fileUploads: userSettings?.fileUploads,
dockerTimeout,
Expand Down
22 changes: 20 additions & 2 deletions packages/notifications/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,28 @@ class Notifications {
/**
* Get gatus and custom endpoints indexed by dnpName
*/
async getEndpoints(): Promise<{
async getAllEndpoints(): Promise<{
[dnpName: string]: { endpoints: GatusEndpoint[]; customEndpoints: CustomEndpoint[]; isCore: boolean };
}> {
return await this.manifest.getEndpoints();
return await this.manifest.getAllEndpoints();
}

/**
* Get package endpoints (if exists) properties
*/
getEndpointsIfExists(dnpName: string, isCore: boolean): NotificationsConfig | null {
return this.manifest.getEndpointsIfExists(dnpName, isCore);
}

/**
* Joins new endpoints with previous ones
*/
applyPreviousEndpoints(
dnpName: string,
isCore: boolean,
newNotificationsConfig: NotificationsConfig
): NotificationsConfig {
return this.manifest.applyPreviousEndpoints(dnpName, isCore, newNotificationsConfig);
}

/**
Expand Down
70 changes: 69 additions & 1 deletion packages/notifications/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class NotificationsManifest {
/**
* Get gatus and custom endpoints indexed by dnpName from filesystem
*/
async getEndpoints(): Promise<{
async getAllEndpoints(): Promise<{
[dnpName: string]: { endpoints: GatusEndpoint[]; customEndpoints: CustomEndpoint[]; isCore: boolean };
}> {
const packages = await listPackages();
Expand All @@ -30,6 +30,74 @@ export class NotificationsManifest {
return notificationsEndpoints;
}

/**
* Get package endpoints (if exists) properties from filesystem
*/
getEndpointsIfExists(dnpName: string, isCore: boolean): NotificationsConfig | null {
const manifestPath = getManifestPath(dnpName, isCore);
if (!fs.existsSync(manifestPath)) return { endpoints: [], customEndpoints: [] };

const manifest: Manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
if (!manifest.notifications) return { endpoints: [], customEndpoints: [] };

const { endpoints, customEndpoints } = manifest.notifications;
return { endpoints: endpoints || [], customEndpoints: customEndpoints || [] };
}

/**
* Joins new endpoints with previous ones. If there are repeated endpoints:
* - GatusEndpoint: iterate over the conditions properties and split with regex matching any operator. If the right side is a number or a string, keep the old one.
* - CustomEndpoint: check the metric.treshold and keep the old one.
*
* Do not keep old endpoints that are not present in the new ones.
*/
applyPreviousEndpoints(
dnpName: string,
isCore: boolean,
newNotificationsConfig: NotificationsConfig
): NotificationsConfig {
const oldNotificationsConfig = this.getEndpointsIfExists(dnpName, isCore);
if (!oldNotificationsConfig) return newNotificationsConfig;

const { endpoints: oldEndpoints, customEndpoints: oldCustomEndpoints } = oldNotificationsConfig;
const { endpoints: newEndpoints, customEndpoints: newCustomEndpoints } = newNotificationsConfig;

const mergedEndpoints = oldEndpoints
?.map((oldEndpoint) => {
const newEndpoint = newEndpoints?.find((newEndpoint) => newEndpoint.name === oldEndpoint.name);
if (!newEndpoint) return null;

const mergedEndpoint = { ...oldEndpoint, ...newEndpoint };
if (mergedEndpoint.conditions) {
mergedEndpoint.conditions = mergedEndpoint.conditions.map((condition) => {
const [left, operator, right] = condition.split(/([=<>]+)/);
if (left && operator && right && (Number(right) || right === "0")) return condition;
return condition;
});
}

return mergedEndpoint;
})
.filter((endpoint) => endpoint !== null);

const mergedCustomEndpoints = oldCustomEndpoints
?.map((oldCustomEndpoint) => {
const newCustomEndpoint = newCustomEndpoints?.find(
(newCustomEndpoint) => newCustomEndpoint.name === oldCustomEndpoint.name
);
if (!newCustomEndpoint) return null;

const mergedCustomEndpoint = { ...oldCustomEndpoint, ...newCustomEndpoint };
if (mergedCustomEndpoint.metric && oldCustomEndpoint.metric)
mergedCustomEndpoint.metric.treshold = oldCustomEndpoint.metric.treshold;

return mergedCustomEndpoint;
})
.filter((customEndpoint) => customEndpoint !== null);

return { endpoints: mergedEndpoints, customEndpoints: mergedCustomEndpoints };
}

/**
* Update endpoint properties in filesystem
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/types/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export interface Routes {
/**
* Gatus get endpoints
*/
notificationsGetEndpoints(): Promise<{
notificationsGetAllEndpoints(): Promise<{
[dnpName: string]: { endpoints: GatusEndpoint[]; customEndpoints: CustomEndpoint[]; isCore: boolean };
}>;

Expand Down Expand Up @@ -713,7 +713,7 @@ export const routesData: { [P in keyof Routes]: RouteData } = {
fetchRegistry: {},
fetchDnpRequest: {},
notificationsGetAll: { log: true },
notificationsGetEndpoints: { log: true },
notificationsGetAllEndpoints: { log: true },
notificationsUpdateEndpoints: { log: true },
getUserActionLogs: {},
getHostUptime: {},
Expand Down
Loading