mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-31 23:10:00 +02:00
Remove json_group_array from queries
Also improve handling tcp/udp resources in newt function so it does not loop twice
This commit is contained in:
parent
212fff73c6
commit
1e78188c71
2 changed files with 148 additions and 105 deletions
|
@ -7,7 +7,7 @@ import {
|
||||||
Target,
|
Target,
|
||||||
targets
|
targets
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schema";
|
||||||
import { eq, and, sql } from "drizzle-orm";
|
import { eq, and, sql, inArray } from "drizzle-orm";
|
||||||
import { addPeer, deletePeer } from "../gerbil/peers";
|
import { addPeer, deletePeer } from "../gerbil/peers";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|
||||||
|
@ -75,68 +75,84 @@ export const handleRegisterMessage: MessageHandler = async (context) => {
|
||||||
allowedIps: [site.subnet]
|
allowedIps: [site.subnet]
|
||||||
});
|
});
|
||||||
|
|
||||||
const allResources = await db
|
// Improved version
|
||||||
.select({
|
const allResources = await db.transaction(async (tx) => {
|
||||||
// Resource fields
|
// First get all resources for the site
|
||||||
resourceId: resources.resourceId,
|
const resourcesList = await tx
|
||||||
subdomain: resources.subdomain,
|
.select({
|
||||||
fullDomain: resources.fullDomain,
|
resourceId: resources.resourceId,
|
||||||
ssl: resources.ssl,
|
subdomain: resources.subdomain,
|
||||||
blockAccess: resources.blockAccess,
|
fullDomain: resources.fullDomain,
|
||||||
sso: resources.sso,
|
ssl: resources.ssl,
|
||||||
emailWhitelistEnabled: resources.emailWhitelistEnabled,
|
blockAccess: resources.blockAccess,
|
||||||
http: resources.http,
|
sso: resources.sso,
|
||||||
proxyPort: resources.proxyPort,
|
emailWhitelistEnabled: resources.emailWhitelistEnabled,
|
||||||
protocol: resources.protocol,
|
http: resources.http,
|
||||||
// Targets as a subquery
|
proxyPort: resources.proxyPort,
|
||||||
targets: sql<string>`json_group_array(json_object(
|
protocol: resources.protocol
|
||||||
'targetId', ${targets.targetId},
|
})
|
||||||
'ip', ${targets.ip},
|
.from(resources)
|
||||||
'method', ${targets.method},
|
.where(eq(resources.siteId, siteId));
|
||||||
'port', ${targets.port},
|
|
||||||
'internalPort', ${targets.internalPort},
|
// Get all enabled targets for these resources in a single query
|
||||||
'enabled', ${targets.enabled}
|
const resourceIds = resourcesList.map((r) => r.resourceId);
|
||||||
))`.as("targets")
|
const allTargets =
|
||||||
})
|
resourceIds.length > 0
|
||||||
.from(resources)
|
? await tx
|
||||||
.leftJoin(
|
.select({
|
||||||
targets,
|
resourceId: targets.resourceId,
|
||||||
and(
|
targetId: targets.targetId,
|
||||||
eq(targets.resourceId, resources.resourceId),
|
ip: targets.ip,
|
||||||
eq(targets.enabled, true)
|
method: targets.method,
|
||||||
|
port: targets.port,
|
||||||
|
internalPort: targets.internalPort,
|
||||||
|
enabled: targets.enabled
|
||||||
|
})
|
||||||
|
.from(targets)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
inArray(targets.resourceId, resourceIds),
|
||||||
|
eq(targets.enabled, true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Combine the data in JS instead of using SQL for the JSON
|
||||||
|
return resourcesList.map((resource) => ({
|
||||||
|
...resource,
|
||||||
|
targets: allTargets.filter(
|
||||||
|
(target) => target.resourceId === resource.resourceId
|
||||||
)
|
)
|
||||||
)
|
}));
|
||||||
.where(eq(resources.siteId, siteId))
|
});
|
||||||
.groupBy(resources.resourceId);
|
|
||||||
|
|
||||||
let tcpTargets: string[] = [];
|
const { tcpTargets, udpTargets } = allResources.reduce(
|
||||||
let udpTargets: string[] = [];
|
(acc, resource) => {
|
||||||
|
// Skip resources with no targets
|
||||||
|
if (!resource.targets?.length) return acc;
|
||||||
|
|
||||||
for (const resource of allResources) {
|
// Format valid targets into strings
|
||||||
const targets = JSON.parse(resource.targets);
|
const formattedTargets = resource.targets
|
||||||
if (!targets || targets.length === 0) {
|
.filter(
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (resource.protocol === "tcp") {
|
|
||||||
tcpTargets = tcpTargets.concat(
|
|
||||||
targets.map(
|
|
||||||
(target: Target) =>
|
(target: Target) =>
|
||||||
`${
|
target?.internalPort && target?.ip && target?.port
|
||||||
target.internalPort ? target.internalPort + ":" : ""
|
|
||||||
}${target.ip}:${target.port}`
|
|
||||||
)
|
)
|
||||||
);
|
.map(
|
||||||
} else {
|
|
||||||
udpTargets = tcpTargets.concat(
|
|
||||||
targets.map(
|
|
||||||
(target: Target) =>
|
(target: Target) =>
|
||||||
`${
|
`${target.internalPort}:${target.ip}:${target.port}`
|
||||||
target.internalPort ? target.internalPort + ":" : ""
|
);
|
||||||
}${target.ip}:${target.port}`
|
|
||||||
)
|
// Add to the appropriate protocol array
|
||||||
);
|
if (resource.protocol === "tcp") {
|
||||||
}
|
acc.tcpTargets.push(...formattedTargets);
|
||||||
}
|
} else {
|
||||||
|
acc.udpTargets.push(...formattedTargets);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ tcpTargets: [] as string[], udpTargets: [] as string[] }
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: {
|
message: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq, inArray } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
@ -12,52 +12,79 @@ export async function traefikConfigProvider(
|
||||||
res: Response
|
res: Response
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const allResources = await db
|
// Get all resources with related data
|
||||||
.select({
|
const allResources = await db.transaction(async (tx) => {
|
||||||
// Resource fields
|
// First query to get resources with site and org info
|
||||||
resourceId: resources.resourceId,
|
const resourcesWithRelations = await tx
|
||||||
subdomain: resources.subdomain,
|
.select({
|
||||||
fullDomain: resources.fullDomain,
|
// Resource fields
|
||||||
ssl: resources.ssl,
|
resourceId: resources.resourceId,
|
||||||
blockAccess: resources.blockAccess,
|
subdomain: resources.subdomain,
|
||||||
sso: resources.sso,
|
fullDomain: resources.fullDomain,
|
||||||
emailWhitelistEnabled: resources.emailWhitelistEnabled,
|
ssl: resources.ssl,
|
||||||
http: resources.http,
|
blockAccess: resources.blockAccess,
|
||||||
proxyPort: resources.proxyPort,
|
sso: resources.sso,
|
||||||
protocol: resources.protocol,
|
emailWhitelistEnabled: resources.emailWhitelistEnabled,
|
||||||
isBaseDomain: resources.isBaseDomain,
|
http: resources.http,
|
||||||
domainId: resources.domainId,
|
proxyPort: resources.proxyPort,
|
||||||
// Site fields
|
protocol: resources.protocol,
|
||||||
site: {
|
isBaseDomain: resources.isBaseDomain,
|
||||||
siteId: sites.siteId,
|
domainId: resources.domainId,
|
||||||
type: sites.type,
|
// Site fields
|
||||||
subnet: sites.subnet
|
site: {
|
||||||
},
|
siteId: sites.siteId,
|
||||||
// Org fields
|
type: sites.type,
|
||||||
org: {
|
subnet: sites.subnet
|
||||||
orgId: orgs.orgId
|
},
|
||||||
},
|
// Org fields
|
||||||
// Targets as a subquery
|
org: {
|
||||||
targets: sql<string>`json_group_array(json_object(
|
orgId: orgs.orgId
|
||||||
'targetId', ${targets.targetId},
|
}
|
||||||
'ip', ${targets.ip},
|
})
|
||||||
'method', ${targets.method},
|
.from(resources)
|
||||||
'port', ${targets.port},
|
.innerJoin(sites, eq(sites.siteId, resources.siteId))
|
||||||
'internalPort', ${targets.internalPort},
|
.innerJoin(orgs, eq(resources.orgId, orgs.orgId));
|
||||||
'enabled', ${targets.enabled}
|
|
||||||
))`.as("targets")
|
// Get all resource IDs from the first query
|
||||||
})
|
const resourceIds = resourcesWithRelations.map((r) => r.resourceId);
|
||||||
.from(resources)
|
|
||||||
.innerJoin(sites, eq(sites.siteId, resources.siteId))
|
// Second query to get all enabled targets for these resources
|
||||||
.innerJoin(orgs, eq(resources.orgId, orgs.orgId))
|
const allTargets =
|
||||||
.leftJoin(
|
resourceIds.length > 0
|
||||||
targets,
|
? await tx
|
||||||
and(
|
.select({
|
||||||
eq(targets.resourceId, resources.resourceId),
|
resourceId: targets.resourceId,
|
||||||
eq(targets.enabled, true)
|
targetId: targets.targetId,
|
||||||
)
|
ip: targets.ip,
|
||||||
)
|
method: targets.method,
|
||||||
.groupBy(resources.resourceId);
|
port: targets.port,
|
||||||
|
internalPort: targets.internalPort,
|
||||||
|
enabled: targets.enabled
|
||||||
|
})
|
||||||
|
.from(targets)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
inArray(targets.resourceId, resourceIds),
|
||||||
|
eq(targets.enabled, true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Create a map for fast target lookup by resourceId
|
||||||
|
const targetsMap = allTargets.reduce((map, target) => {
|
||||||
|
if (!map.has(target.resourceId)) {
|
||||||
|
map.set(target.resourceId, []);
|
||||||
|
}
|
||||||
|
map.get(target.resourceId).push(target);
|
||||||
|
return map;
|
||||||
|
}, new Map());
|
||||||
|
|
||||||
|
// Combine the data
|
||||||
|
return resourcesWithRelations.map((resource) => ({
|
||||||
|
...resource,
|
||||||
|
targets: targetsMap.get(resource.resourceId) || []
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
if (!allResources.length) {
|
if (!allResources.length) {
|
||||||
return res.status(HttpCode.OK).json({});
|
return res.status(HttpCode.OK).json({});
|
||||||
|
@ -101,7 +128,7 @@ export async function traefikConfigProvider(
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const resource of allResources) {
|
for (const resource of allResources) {
|
||||||
const targets = JSON.parse(resource.targets);
|
const targets = resource.targets as Target[];
|
||||||
const site = resource.site;
|
const site = resource.site;
|
||||||
const org = resource.org;
|
const org = resource.org;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue