Connection log page working

This commit is contained in:
Owen
2026-03-23 21:41:53 -07:00
parent 6471571bc6
commit 2c6e9507b5
5 changed files with 349 additions and 29 deletions

View File

@@ -581,6 +581,7 @@ export type SubnetProxyTargetV2 = {
max: number;
protocol: "tcp" | "udp";
}[];
resourceId?: number;
};
export function generateSubnetProxyTargetV2(
@@ -617,7 +618,8 @@ export function generateSubnetProxyTargetV2(
sourcePrefixes: [],
destPrefix: destination,
portRange,
disableIcmp
disableIcmp,
resourceId: siteResource.siteResourceId,
};
}
@@ -628,7 +630,8 @@ export function generateSubnetProxyTargetV2(
destPrefix: `${siteResource.aliasAddress}/32`,
rewriteTo: destination,
portRange,
disableIcmp
disableIcmp,
resourceId: siteResource.siteResourceId,
};
}
} else if (siteResource.mode == "cidr") {
@@ -636,7 +639,8 @@ export function generateSubnetProxyTargetV2(
sourcePrefixes: [],
destPrefix: siteResource.destination,
portRange,
disableIcmp
disableIcmp,
resourceId: siteResource.siteResourceId,
};
}

View File

@@ -17,6 +17,7 @@ import {
siteResources,
sites,
clients,
users,
primaryDb
} from "@server/db";
import { registry } from "@server/openApi";
@@ -193,6 +194,13 @@ async function enrichWithDetails(
.filter((id): id is number => id !== null && id !== undefined)
)
];
const userIds = [
...new Set(
logs
.map((log) => log.userId)
.filter((id): id is string => id !== null && id !== undefined)
)
];
// Fetch resource details from main database
const resourceMap = new Map<
@@ -235,18 +243,46 @@ async function enrichWithDetails(
}
// Fetch client details from main database
const clientMap = new Map<number, { name: string }>();
const clientMap = new Map<
number,
{ name: string; niceId: string; type: string }
>();
if (clientIds.length > 0) {
const clientDetails = await primaryDb
.select({
clientId: clients.clientId,
name: clients.name
name: clients.name,
niceId: clients.niceId,
type: clients.type
})
.from(clients)
.where(inArray(clients.clientId, clientIds));
for (const c of clientDetails) {
clientMap.set(c.clientId, { name: c.name });
clientMap.set(c.clientId, {
name: c.name,
niceId: c.niceId,
type: c.type
});
}
}
// Fetch user details from main database
const userMap = new Map<
string,
{ email: string | null }
>();
if (userIds.length > 0) {
const userDetails = await primaryDb
.select({
userId: users.userId,
email: users.email
})
.from(users)
.where(inArray(users.userId, userIds));
for (const u of userDetails) {
userMap.set(u.userId, { email: u.email });
}
}
@@ -267,6 +303,15 @@ async function enrichWithDetails(
: null,
clientName: log.clientId
? clientMap.get(log.clientId)?.name ?? null
: null,
clientNiceId: log.clientId
? clientMap.get(log.clientId)?.niceId ?? null
: null,
clientType: log.clientId
? clientMap.get(log.clientId)?.type ?? null
: null,
userEmail: log.userId
? userMap.get(log.userId)?.email ?? null
: null
}));
}
@@ -290,10 +335,111 @@ async function queryUniqueFilterAttributes(
.from(connectionAuditLog)
.where(baseConditions);
// Get unique destination addresses
const uniqueDestAddrs = await logsDb
.selectDistinct({
destAddr: connectionAuditLog.destAddr
})
.from(connectionAuditLog)
.where(baseConditions);
// Get unique client IDs
const uniqueClients = await logsDb
.selectDistinct({
clientId: connectionAuditLog.clientId
})
.from(connectionAuditLog)
.where(baseConditions);
// Get unique resource IDs
const uniqueResources = await logsDb
.selectDistinct({
siteResourceId: connectionAuditLog.siteResourceId
})
.from(connectionAuditLog)
.where(baseConditions);
// Get unique user IDs
const uniqueUsers = await logsDb
.selectDistinct({
userId: connectionAuditLog.userId
})
.from(connectionAuditLog)
.where(baseConditions);
// Enrich client IDs with names from main database
const clientIds = uniqueClients
.map((row) => row.clientId)
.filter((id): id is number => id !== null);
let clientsWithNames: Array<{ id: number; name: string }> = [];
if (clientIds.length > 0) {
const clientDetails = await primaryDb
.select({
clientId: clients.clientId,
name: clients.name
})
.from(clients)
.where(inArray(clients.clientId, clientIds));
clientsWithNames = clientDetails.map((c) => ({
id: c.clientId,
name: c.name
}));
}
// Enrich resource IDs with names from main database
const resourceIds = uniqueResources
.map((row) => row.siteResourceId)
.filter((id): id is number => id !== null);
let resourcesWithNames: Array<{ id: number; name: string | null }> = [];
if (resourceIds.length > 0) {
const resourceDetails = await primaryDb
.select({
siteResourceId: siteResources.siteResourceId,
name: siteResources.name
})
.from(siteResources)
.where(inArray(siteResources.siteResourceId, resourceIds));
resourcesWithNames = resourceDetails.map((r) => ({
id: r.siteResourceId,
name: r.name
}));
}
// Enrich user IDs with emails from main database
const userIdsList = uniqueUsers
.map((row) => row.userId)
.filter((id): id is string => id !== null);
let usersWithEmails: Array<{ id: string; email: string | null }> = [];
if (userIdsList.length > 0) {
const userDetails = await primaryDb
.select({
userId: users.userId,
email: users.email
})
.from(users)
.where(inArray(users.userId, userIdsList));
usersWithEmails = userDetails.map((u) => ({
id: u.userId,
email: u.email
}));
}
return {
protocols: uniqueProtocols
.map((row) => row.protocol)
.filter((protocol): protocol is string => protocol !== null)
.filter((protocol): protocol is string => protocol !== null),
destAddrs: uniqueDestAddrs
.map((row) => row.destAddr)
.filter((addr): addr is string => addr !== null),
clients: clientsWithNames,
resources: resourcesWithNames,
users: usersWithEmails
};
}
@@ -342,7 +488,7 @@ export async function queryConnectionAuditLogs(
const logsRaw = await baseQuery.limit(data.limit).offset(data.offset);
// Enrich with resource, site, and client details
// Enrich with resource, site, client, and user details
const log = await enrichWithDetails(logsRaw);
const totalCountResult = await countConnectionQuery(data);

View File

@@ -277,6 +277,8 @@ export const handleConnectionLogMessage: MessageHandler = async (context) => {
return;
}
logger.debug(`Sessions: ${JSON.stringify(sessions)}`)
// Build a map from sourceAddr → { clientId, userId } by querying clients
// whose subnet field matches exactly. Client subnets are stored with the
// org's CIDR suffix (e.g. "100.90.128.5/16"), so we reconstruct that from
@@ -295,9 +297,15 @@ export const handleConnectionLogMessage: MessageHandler = async (context) => {
if (uniqueSourceAddrs.size > 0) {
// Construct the exact subnet strings as stored in the DB
const subnetQueries = Array.from(uniqueSourceAddrs).map(
(addr) => `${addr}${cidrSuffix}`
(addr) => {
// Strip port if present (e.g. "100.90.128.1:38004" → "100.90.128.1")
const ip = addr.includes(":") ? addr.split(":")[0] : addr;
return `${ip}${cidrSuffix}`;
}
);
logger.debug(`Subnet queries: ${JSON.stringify(subnetQueries)}`);
const matchedClients = await db
.select({
clientId: clients.clientId,
@@ -314,6 +322,7 @@ export const handleConnectionLogMessage: MessageHandler = async (context) => {
for (const c of matchedClients) {
const ip = c.subnet.split("/")[0];
logger.debug(`Client ${c.clientId} subnet ${c.subnet} matches ${ip}`);
ipToClient.set(ip, { clientId: c.clientId, userId: c.userId });
}
}
@@ -346,7 +355,10 @@ export const handleConnectionLogMessage: MessageHandler = async (context) => {
// Match the source address to a client. The sourceAddr is the
// client's IP on the WireGuard network, which corresponds to the IP
// portion of the client's subnet CIDR (e.g. "100.90.128.5/24").
const clientInfo = ipToClient.get(session.sourceAddr) ?? null;
// Strip port if present (e.g. "100.90.128.1:38004" → "100.90.128.1")
const sourceIp = session.sourceAddr.includes(":") ? session.sourceAddr.split(":")[0] : session.sourceAddr;
const clientInfo = ipToClient.get(sourceIp) ?? null;
buffer.push({
sessionId: session.sessionId,

View File

@@ -112,6 +112,9 @@ export type QueryConnectionAuditLogResponse = {
siteName: string | null;
siteNiceId: string | null;
clientName: string | null;
clientNiceId: string | null;
clientType: string | null;
userEmail: string | null;
}[];
pagination: {
total: number;
@@ -120,5 +123,18 @@ export type QueryConnectionAuditLogResponse = {
};
filterAttributes: {
protocols: string[];
destAddrs: string[];
clients: {
id: number;
name: string;
}[];
resources: {
id: number;
name: string | null;
}[];
users: {
id: string;
email: string | null;
}[];
};
};