mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-02 16:56:39 +00:00
Filter out duplicates
This commit is contained in:
@@ -105,117 +105,115 @@ export async function getTraefikConfig(
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get resources with their targets and sites in a single optimized query
|
// Get resources with their targets and sites in a single optimized query
|
||||||
// Start from sites on this exit node, then join to targets and resources
|
// Start from sites on this exit node, then join to targets and resources
|
||||||
const resourcesWithTargetsAndSites = await db
|
const resourcesWithTargetsAndSites = await db
|
||||||
.select({
|
.select({
|
||||||
// Resource fields
|
// Resource fields
|
||||||
resourceId: resources.resourceId,
|
resourceId: resources.resourceId,
|
||||||
fullDomain: resources.fullDomain,
|
fullDomain: resources.fullDomain,
|
||||||
ssl: resources.ssl,
|
ssl: resources.ssl,
|
||||||
http: resources.http,
|
http: resources.http,
|
||||||
proxyPort: resources.proxyPort,
|
proxyPort: resources.proxyPort,
|
||||||
protocol: resources.protocol,
|
protocol: resources.protocol,
|
||||||
subdomain: resources.subdomain,
|
subdomain: resources.subdomain,
|
||||||
domainId: resources.domainId,
|
domainId: resources.domainId,
|
||||||
enabled: resources.enabled,
|
enabled: resources.enabled,
|
||||||
stickySession: resources.stickySession,
|
stickySession: resources.stickySession,
|
||||||
tlsServerName: resources.tlsServerName,
|
tlsServerName: resources.tlsServerName,
|
||||||
setHostHeader: resources.setHostHeader,
|
setHostHeader: resources.setHostHeader,
|
||||||
enableProxy: resources.enableProxy,
|
enableProxy: resources.enableProxy,
|
||||||
headers: resources.headers,
|
headers: resources.headers,
|
||||||
// Target fields
|
// Target fields
|
||||||
targetId: targets.targetId,
|
targetId: targets.targetId,
|
||||||
targetEnabled: targets.enabled,
|
targetEnabled: targets.enabled,
|
||||||
ip: targets.ip,
|
ip: targets.ip,
|
||||||
method: targets.method,
|
method: targets.method,
|
||||||
port: targets.port,
|
port: targets.port,
|
||||||
internalPort: targets.internalPort,
|
internalPort: targets.internalPort,
|
||||||
path: targets.path,
|
path: targets.path,
|
||||||
pathMatchType: targets.pathMatchType,
|
pathMatchType: targets.pathMatchType,
|
||||||
|
|
||||||
// Site fields
|
// Site fields
|
||||||
siteId: sites.siteId,
|
siteId: sites.siteId,
|
||||||
siteType: sites.type,
|
siteType: sites.type,
|
||||||
siteOnline: sites.online,
|
siteOnline: sites.online,
|
||||||
subnet: sites.subnet,
|
subnet: sites.subnet,
|
||||||
exitNodeId: sites.exitNodeId
|
exitNodeId: sites.exitNodeId
|
||||||
})
|
})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
.innerJoin(targets, eq(targets.siteId, sites.siteId))
|
||||||
.innerJoin(resources, eq(resources.resourceId, targets.resourceId))
|
.innerJoin(resources, eq(resources.resourceId, targets.resourceId))
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(targets.enabled, true),
|
eq(targets.enabled, true),
|
||||||
eq(resources.enabled, true),
|
eq(resources.enabled, true),
|
||||||
or(
|
or(eq(sites.exitNodeId, exitNodeId), isNull(sites.exitNodeId)),
|
||||||
eq(sites.exitNodeId, exitNodeId),
|
inArray(sites.type, siteTypes),
|
||||||
isNull(sites.exitNodeId)
|
config.getRawConfig().traefik.allow_raw_resources
|
||||||
),
|
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
|
||||||
inArray(sites.type, siteTypes),
|
: eq(resources.http, true)
|
||||||
config.getRawConfig().traefik.allow_raw_resources
|
)
|
||||||
? isNotNull(resources.http) // ignore the http check if allow_raw_resources is true
|
);
|
||||||
: eq(resources.http, true)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Group by resource and include targets with their unique site data
|
// Group by resource and include targets with their unique site data
|
||||||
const resourcesMap = new Map();
|
const resourcesMap = new Map();
|
||||||
|
|
||||||
resourcesWithTargetsAndSites.forEach((row) => {
|
resourcesWithTargetsAndSites.forEach((row) => {
|
||||||
const resourceId = row.resourceId;
|
const resourceId = row.resourceId;
|
||||||
const targetPath = sanitizePath(row.path) || ""; // Handle null/undefined paths
|
const targetPath = sanitizePath(row.path) || ""; // Handle null/undefined paths
|
||||||
const pathMatchType = row.pathMatchType || "";
|
const pathMatchType = row.pathMatchType || "";
|
||||||
|
|
||||||
// Create a unique key combining resourceId and path+pathMatchType
|
// Create a unique key combining resourceId and path+pathMatchType
|
||||||
const pathKey = [targetPath, pathMatchType].filter(Boolean).join("-");
|
const pathKey = [targetPath, pathMatchType].filter(Boolean).join("-");
|
||||||
const mapKey = [resourceId, pathKey].filter(Boolean).join("-");
|
const mapKey = [resourceId, pathKey].filter(Boolean).join("-");
|
||||||
|
|
||||||
if (!resourcesMap.has(mapKey)) {
|
if (!resourcesMap.has(mapKey)) {
|
||||||
resourcesMap.set(mapKey, {
|
resourcesMap.set(mapKey, {
|
||||||
resourceId: row.resourceId,
|
|
||||||
fullDomain: row.fullDomain,
|
|
||||||
ssl: row.ssl,
|
|
||||||
http: row.http,
|
|
||||||
proxyPort: row.proxyPort,
|
|
||||||
protocol: row.protocol,
|
|
||||||
subdomain: row.subdomain,
|
|
||||||
domainId: row.domainId,
|
|
||||||
enabled: row.enabled,
|
|
||||||
stickySession: row.stickySession,
|
|
||||||
tlsServerName: row.tlsServerName,
|
|
||||||
setHostHeader: row.setHostHeader,
|
|
||||||
enableProxy: row.enableProxy,
|
|
||||||
targets: [],
|
|
||||||
headers: row.headers,
|
|
||||||
path: row.path, // the targets will all have the same path
|
|
||||||
pathMatchType: row.pathMatchType // the targets will all have the same pathMatchType
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add target with its associated site data
|
|
||||||
resourcesMap.get(mapKey).targets.push({
|
|
||||||
resourceId: row.resourceId,
|
resourceId: row.resourceId,
|
||||||
targetId: row.targetId,
|
fullDomain: row.fullDomain,
|
||||||
ip: row.ip,
|
ssl: row.ssl,
|
||||||
method: row.method,
|
http: row.http,
|
||||||
port: row.port,
|
proxyPort: row.proxyPort,
|
||||||
internalPort: row.internalPort,
|
protocol: row.protocol,
|
||||||
enabled: row.targetEnabled,
|
subdomain: row.subdomain,
|
||||||
site: {
|
domainId: row.domainId,
|
||||||
siteId: row.siteId,
|
enabled: row.enabled,
|
||||||
type: row.siteType,
|
stickySession: row.stickySession,
|
||||||
subnet: row.subnet,
|
tlsServerName: row.tlsServerName,
|
||||||
exitNodeId: row.exitNodeId,
|
setHostHeader: row.setHostHeader,
|
||||||
online: row.siteOnline
|
enableProxy: row.enableProxy,
|
||||||
}
|
targets: [],
|
||||||
|
headers: row.headers,
|
||||||
|
path: row.path, // the targets will all have the same path
|
||||||
|
pathMatchType: row.pathMatchType // the targets will all have the same pathMatchType
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Add target with its associated site data
|
||||||
|
resourcesMap.get(mapKey).targets.push({
|
||||||
|
resourceId: row.resourceId,
|
||||||
|
targetId: row.targetId,
|
||||||
|
ip: row.ip,
|
||||||
|
method: row.method,
|
||||||
|
port: row.port,
|
||||||
|
internalPort: row.internalPort,
|
||||||
|
enabled: row.targetEnabled,
|
||||||
|
site: {
|
||||||
|
siteId: row.siteId,
|
||||||
|
type: row.siteType,
|
||||||
|
subnet: row.subnet,
|
||||||
|
exitNodeId: row.exitNodeId,
|
||||||
|
online: row.siteOnline
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// convert the map to an object for printing
|
// convert the map to an object for printing
|
||||||
|
|
||||||
logger.debug(`Resources: ${JSON.stringify(Object.fromEntries(resourcesMap), null, 2)}`);
|
logger.debug(
|
||||||
|
`Resources: ${JSON.stringify(Object.fromEntries(resourcesMap), null, 2)}`
|
||||||
|
);
|
||||||
|
|
||||||
// make sure we have at least one resource
|
// make sure we have at least one resource
|
||||||
if (resourcesMap.size === 0) {
|
if (resourcesMap.size === 0) {
|
||||||
@@ -399,55 +397,64 @@ export async function getTraefikConfig(
|
|||||||
targets as TargetWithSite[]
|
targets as TargetWithSite[]
|
||||||
).some((target: TargetWithSite) => target.site.online);
|
).some((target: TargetWithSite) => target.site.online);
|
||||||
|
|
||||||
return (targets as TargetWithSite[])
|
return (
|
||||||
.filter((target: TargetWithSite) => {
|
(targets as TargetWithSite[])
|
||||||
if (!target.enabled) {
|
.filter((target: TargetWithSite) => {
|
||||||
return false;
|
if (!target.enabled) {
|
||||||
}
|
|
||||||
|
|
||||||
// If any sites are online, exclude offline sites
|
|
||||||
if (anySitesOnline && !target.site.online) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
target.site.type === "local" ||
|
|
||||||
target.site.type === "wireguard"
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
!target.ip ||
|
|
||||||
!target.port ||
|
|
||||||
!target.method
|
|
||||||
) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (target.site.type === "newt") {
|
|
||||||
if (
|
// If any sites are online, exclude offline sites
|
||||||
!target.internalPort ||
|
if (anySitesOnline && !target.site.online) {
|
||||||
!target.method ||
|
|
||||||
!target.site.subnet
|
|
||||||
) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true;
|
if (
|
||||||
})
|
target.site.type === "local" ||
|
||||||
.map((target: TargetWithSite) => {
|
target.site.type === "wireguard"
|
||||||
if (
|
) {
|
||||||
target.site.type === "local" ||
|
if (
|
||||||
target.site.type === "wireguard"
|
!target.ip ||
|
||||||
) {
|
!target.port ||
|
||||||
return {
|
!target.method
|
||||||
url: `${target.method}://${target.ip}:${target.port}`
|
) {
|
||||||
};
|
return false;
|
||||||
} else if (target.site.type === "newt") {
|
}
|
||||||
const ip =
|
} else if (target.site.type === "newt") {
|
||||||
target.site.subnet!.split("/")[0];
|
if (
|
||||||
return {
|
!target.internalPort ||
|
||||||
url: `${target.method}://${ip}:${target.internalPort}`
|
!target.method ||
|
||||||
};
|
!target.site.subnet
|
||||||
}
|
) {
|
||||||
});
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((target: TargetWithSite) => {
|
||||||
|
if (
|
||||||
|
target.site.type === "local" ||
|
||||||
|
target.site.type === "wireguard"
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
url: `${target.method}://${target.ip}:${target.port}`
|
||||||
|
};
|
||||||
|
} else if (target.site.type === "newt") {
|
||||||
|
const ip =
|
||||||
|
target.site.subnet!.split("/")[0];
|
||||||
|
return {
|
||||||
|
url: `${target.method}://${ip}:${target.internalPort}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// filter out duplicates
|
||||||
|
.filter(
|
||||||
|
(v, i, a) =>
|
||||||
|
a.findIndex(
|
||||||
|
(t) => t && v && t.url === v.url
|
||||||
|
) === i
|
||||||
|
)
|
||||||
|
);
|
||||||
})(),
|
})(),
|
||||||
...(resource.stickySession
|
...(resource.stickySession
|
||||||
? {
|
? {
|
||||||
|
|||||||
Reference in New Issue
Block a user