Files
pangolin/server/routers/newt/targets.ts
2026-01-12 20:48:18 -08:00

140 lines
4.2 KiB
TypeScript

import { Target, TargetHealthCheck, db, targetHealthCheck } from "@server/db";
import { sendToClient } from "#dynamic/routers/ws";
import logger from "@server/logger";
import { eq, inArray } from "drizzle-orm";
export async function addTargets(
newtId: string,
targets: Target[],
healthCheckData: TargetHealthCheck[],
protocol: string,
port: number | null = null
) {
//create a list of udp and tcp targets
const payloadTargets = targets.map((target) => {
return `${target.internalPort ? target.internalPort + ":" : ""}${
target.ip
}:${target.port}`;
});
await sendToClient(newtId, {
type: `newt/${protocol}/add`,
data: {
targets: payloadTargets
}
}, { incrementConfigVersion: true });
// Create a map for quick lookup
const healthCheckMap = new Map<number, TargetHealthCheck>();
healthCheckData.forEach((hc) => {
healthCheckMap.set(hc.targetId, hc);
});
const healthCheckTargets = targets.map((target) => {
const hc = healthCheckMap.get(target.targetId);
// If no health check data found, skip this target
if (!hc) {
logger.warn(
`No health check configuration found for target ${target.targetId}`
);
return null;
}
// Ensure all necessary fields are present
if (
!hc.hcPath ||
!hc.hcHostname ||
!hc.hcPort ||
!hc.hcInterval ||
!hc.hcMethod
) {
logger.debug(
`Skipping target ${target.targetId} due to missing health check fields`
);
return null; // Skip targets with missing health check fields
}
const hcHeadersParse = hc.hcHeaders ? JSON.parse(hc.hcHeaders) : null;
const hcHeadersSend: { [key: string]: string } = {};
if (hcHeadersParse) {
// transform
hcHeadersParse.forEach(
(header: { name: string; value: string }) => {
hcHeadersSend[header.name] = header.value;
}
);
}
// try to parse the hcStatus into a int and if not possible set to undefined
let hcStatus: number | undefined = undefined;
if (hc.hcStatus) {
const parsedStatus = parseInt(hc.hcStatus.toString());
if (!isNaN(parsedStatus)) {
hcStatus = parsedStatus;
}
}
return {
id: target.targetId,
hcEnabled: hc.hcEnabled,
hcPath: hc.hcPath,
hcScheme: hc.hcScheme,
hcMode: hc.hcMode,
hcHostname: hc.hcHostname,
hcPort: hc.hcPort,
hcInterval: hc.hcInterval, // in seconds
hcUnhealthyInterval: hc.hcUnhealthyInterval, // in seconds
hcTimeout: hc.hcTimeout, // in seconds
hcHeaders: hcHeadersSend,
hcMethod: hc.hcMethod,
hcStatus: hcStatus,
hcTlsServerName: hc.hcTlsServerName
};
});
// Filter out any null values from health check targets
const validHealthCheckTargets = healthCheckTargets.filter(
(target) => target !== null
);
await sendToClient(newtId, {
type: `newt/healthcheck/add`,
data: {
targets: validHealthCheckTargets
}
}, { incrementConfigVersion: true });
}
export async function removeTargets(
newtId: string,
targets: Target[],
protocol: string,
port: number | null = null
) {
//create a list of udp and tcp targets
const payloadTargets = targets.map((target) => {
return `${target.internalPort ? target.internalPort + ":" : ""}${
target.ip
}:${target.port}`;
});
await sendToClient(newtId, {
type: `newt/${protocol}/remove`,
data: {
targets: payloadTargets
}
}, { incrementConfigVersion: true });
const healthCheckTargets = targets.map((target) => {
return target.targetId;
});
await sendToClient(newtId, {
type: `newt/healthcheck/remove`,
data: {
ids: healthCheckTargets
}
}, { incrementConfigVersion: true });
}