Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion messages/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1519,5 +1519,20 @@
"domainPickerSubdomainSanitized": "Subdomain sanitized",
"domainPickerSubdomainCorrected": "\"{sub}\" was corrected to \"{sanitized}\"",
"resourceAddEntrypointsEditFile": "Edit file: config/traefik/traefik_config.yml",
"resourceExposePortsEditFile": "Edit file: docker-compose.yml"
"resourceExposePortsEditFile": "Edit file: docker-compose.yml",
"target": "Target",
"showColumns": "Show Columns",
"hideColumns": "Hide Columns",
"columnVisibility": "Column Visibility",
"toggleColumn": "Toggle {columnName} column",
"allColumns": "All Columns",
"defaultColumns": "Default Columns",
"customizeView": "Customize View",
"viewOptions": "View Options",
"selectAll": "Select All",
"selectNone": "Select None",
"selectedResources": "Selected Resources",
"enableSelected": "Enable Selected",
"disableSelected": "Disable Selected",
"checkSelectedStatus": "Check Status of Selected"
}
107 changes: 101 additions & 6 deletions server/routers/resource/listResources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
userResources,
roleResources,
resourcePassword,
resourcePincode
resourcePincode,
targets,
} from "@server/db";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
Expand Down Expand Up @@ -40,6 +41,53 @@ const listResourcesSchema = z.object({
.pipe(z.number().int().nonnegative())
});

// (resource fields + a single joined target)
type JoinedRow = {
resourceId: number;
name: string;
ssl: boolean;
niceId: string;
fullDomain: string | null;
passwordId: number | null;
sso: boolean;
pincodeId: number | null;
whitelist: boolean;
http: boolean;
protocol: string;
proxyPort: number | null;
enabled: boolean;
domainId: string | null;

targetId: number | null;
targetIp: string | null;
targetPort: number | null;
targetEnabled: boolean | null;
};

// grouped by resource with targets[])
export type ResourceWithTargets = {
resourceId: number;
name: string;
ssl: boolean;
fullDomain: string | null;
passwordId: number | null;
sso: boolean;
pincodeId: number | null;
whitelist: boolean;
http: boolean;
protocol: string;
proxyPort: number | null;
enabled: boolean;
domainId: string | null;
niceId: string;
targets: Array<{
targetId: number;
ip: string;
port: number;
enabled: boolean;
}>;
};

function queryResources(accessibleResourceIds: number[], orgId: string) {
return db
.select({
Expand All @@ -56,7 +104,13 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
proxyPort: resources.proxyPort,
enabled: resources.enabled,
domainId: resources.domainId,
niceId: resources.niceId
niceId: resources.niceId,

targetId: targets.targetId,
targetIp: targets.ip,
targetPort: targets.port,
targetEnabled: targets.enabled,

})
.from(resources)
.leftJoin(
Expand All @@ -67,6 +121,7 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
resourcePincode,
eq(resourcePincode.resourceId, resources.resourceId)
)
.leftJoin(targets, eq(targets.resourceId, resources.resourceId))
.where(
and(
inArray(resources.resourceId, accessibleResourceIds),
Expand All @@ -76,7 +131,7 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
}

export type ListResourcesResponse = {
resources: NonNullable<Awaited<ReturnType<typeof queryResources>>>;
resources: ResourceWithTargets[];
pagination: { total: number; limit: number; offset: number };
};

Expand Down Expand Up @@ -141,7 +196,7 @@ export async function listResources(
);
}

let accessibleResources;
let accessibleResources: Array<{ resourceId: number }>;
if (req.user) {
accessibleResources = await db
.select({
Expand Down Expand Up @@ -178,9 +233,49 @@ export async function listResources(

const baseQuery = queryResources(accessibleResourceIds, orgId);

const resourcesList = await baseQuery!.limit(limit).offset(offset);
const rows: JoinedRow[] = await baseQuery.limit(limit).offset(offset);

// avoids TS issues with reduce/never[]
const map = new Map<number, ResourceWithTargets>();

for (const row of rows) {
let entry = map.get(row.resourceId);
if (!entry) {
entry = {
resourceId: row.resourceId,
name: row.name,
ssl: row.ssl,
fullDomain: row.fullDomain,
passwordId: row.passwordId,
sso: row.sso,
pincodeId: row.pincodeId,
whitelist: row.whitelist,
http: row.http,
protocol: row.protocol,
proxyPort: row.proxyPort,
enabled: row.enabled,
domainId: row.domainId,
niceId: row.niceId,
targets: [],
};
map.set(row.resourceId, entry);
}

// Push target if present (left join can be null)
if (row.targetId != null && row.targetIp && row.targetPort != null && row.targetEnabled != null) {
entry.targets.push({
targetId: row.targetId,
ip: row.targetIp,
port: row.targetPort,
enabled: row.targetEnabled,
});
}
}

const resourcesList: ResourceWithTargets[] = Array.from(map.values());

const totalCountResult = await countQuery;
const totalCount = totalCountResult[0].count;
const totalCount = totalCountResult[0]?.count ?? 0;

return response<ListResourcesResponse>(res, {
data: {
Expand Down
Loading