diff --git a/server/services/monitoringService.ts b/server/services/monitoringService.ts index 2446693..4e44acb 100644 --- a/server/services/monitoringService.ts +++ b/server/services/monitoringService.ts @@ -18,6 +18,7 @@ import { forConstraint } from "../shared/validation/validator"; import { Domain, DurationSeconds, + filterUndefinedFromJSON, Hostname, isBoolean, isDomain, @@ -438,7 +439,7 @@ async function sendMonitoringMailsBatched( node.nickname + " <" + node.email + ">", mailType, { - node: node, + node: filterUndefinedFromJSON(node), lastSeen: nodeState.last_seen, disableUrl: monitoringDisableUrl(monitoringToken), } diff --git a/server/services/nodeService.ts b/server/services/nodeService.ts index 8cb4750..b931c76 100644 --- a/server/services/nodeService.ts +++ b/server/services/nodeService.ts @@ -19,6 +19,7 @@ import { CreateOrUpdateNode, EmailAddress, FastdKey, + filterUndefinedFromJSON, Hostname, isFastdKey, isHostname, @@ -504,7 +505,7 @@ async function sendMonitoringConfirmationMail( node.nickname + " <" + node.email + ">", MailType.MONITORING_CONFIRMATION, { - node: node, + node: filterUndefinedFromJSON(node), confirmUrl: confirmUrl, disableUrl: disableUrl, } diff --git a/server/shared/types/index.ts b/server/shared/types/index.ts index e7e9895..faa8fe8 100644 --- a/server/shared/types/index.ts +++ b/server/shared/types/index.ts @@ -11,6 +11,19 @@ export function parseJSON(str: string): JSONValue { return json; } +export function filterUndefinedFromJSON(obj: { + [key: string]: JSONValue | undefined; +}): JSONObject { + const result: JSONObject = {}; + for (const [key, value] of Object.entries(obj)) { + if (value !== undefined) { + result[key] = value; + } + } + + return result; +} + export type JSONValue = | null | string @@ -403,8 +416,8 @@ export type BaseNode = { nickname: Nickname; email: EmailAddress; hostname: Hostname; - coords?: Coordinates; - key?: FastdKey; + coords: Coordinates | undefined; + key: FastdKey | undefined; mac: MAC; }; @@ -506,12 +519,11 @@ export const isDomain = toIsNewtype(isString, "" as Domain); /** * Represents a node in the context of a Freifunk site and domain. */ -export type DomainSpecificNodeResponse = Record & - NodeResponse & { - site?: Site; - domain?: Domain; - onlineState?: OnlineState; - }; +export type DomainSpecificNodeResponse = NodeResponse & { + site: Site | undefined; + domain: Domain | undefined; + onlineState: OnlineState | undefined; +}; export function isDomainSpecificNodeResponse( arg: unknown @@ -549,7 +561,8 @@ export function isMonitoringResponse(arg: unknown): arg is MonitoringResponse { ); } -export enum NodeSortField { +// noinspection JSUnusedGlobalSymbols +enum NodeSortFieldEnum { HOSTNAME = "hostname", NICKNAME = "nickname", EMAIL = "email", @@ -563,7 +576,17 @@ export enum NodeSortField { MONITORING_STATE = "monitoringState", } -export const isNodeSortField = toIsEnum(NodeSortField); +export type NodeSortField = keyof Pick< + DomainSpecificNodeResponse, + NodeSortFieldEnum +>; + +export function isNodeSortField(arg: unknown): arg is NodeSortField { + if (!isString(arg)) { + return false; + } + return Object.values(NodeSortFieldEnum).includes(arg as NodeSortField); +} export type NodesFilter = { hasKey?: boolean; diff --git a/server/utils/resources.ts b/server/utils/resources.ts index e36cbd5..3ef0335 100644 --- a/server/utils/resources.ts +++ b/server/utils/resources.ts @@ -286,12 +286,15 @@ export function filter( ); } -export function sort, S extends string>( - entities: T[], - isSortField: TypeGuard, +export function sort< + Type extends { [Key in SortField]: unknown }, + SortField extends string +>( + entities: Type[], + isSortField: TypeGuard, restParams: RestParams -): T[] { - const sortField: S | undefined = isSortField(restParams._sortField) +): Type[] { + const sortField: SortField | undefined = isSortField(restParams._sortField) ? restParams._sortField : undefined; if (!sortField) {