diff --git a/ui/actions/scans/scans.ts b/ui/actions/scans/scans.ts index 74b3c96268..7d3647f1cf 100644 --- a/ui/actions/scans/scans.ts +++ b/ui/actions/scans/scans.ts @@ -16,6 +16,12 @@ export const getScans = async ({ sort = "", filters = {}, pageSize = 10, +}: { + page?: number; + query?: string; + sort?: string; + filters?: Record; + pageSize?: number; }) => { const headers = await getAuthHeaders({ contentType: false }); @@ -28,23 +34,18 @@ export const getScans = async ({ if (query) url.searchParams.append("filter[search]", query); if (sort) url.searchParams.append("sort", sort); - // Handle multiple filters + // Add dynamic filters (e.g., "filter[state]", "fields[scans]") Object.entries(filters).forEach(([key, value]) => { - if (key !== "filter[search]") { - url.searchParams.append(key, String(value)); - } + url.searchParams.append(key, String(value)); }); try { - const scans = await fetch(url.toString(), { - headers, - }); - const data = await scans.json(); + const response = await fetch(url.toString(), { headers }); + const data = await response.json(); const parsedData = parseStringify(data); revalidatePath("/scans"); return parsedData; } catch (error) { - // eslint-disable-next-line no-console console.error("Error fetching scans:", error); return undefined; } @@ -260,45 +261,6 @@ export const getExportsZip = async (scanId: string) => { } }; -export const getScansByFields = async ( - fields: string = "state", - filters = {}, -) => { - const headers = await getAuthHeaders({ contentType: false }); - - const url = new URL(`${apiBaseUrl}/scans`); - - // Request only the necessary fields to optimize the response - url.searchParams.append("fields[scans]", fields); - - Object.entries(filters).forEach(([key, value]) => { - url.searchParams.append(key, String(value)); - }); - - try { - const response = await fetch(url.toString(), { - headers, - }); - - if (!response.ok) { - try { - const errorData = await response.json(); - throw new Error(errorData?.message || "Failed to fetch scans by state"); - } catch { - throw new Error("Failed to fetch scans by state"); - } - } - - const data = await response.json(); - - return parseStringify(data); - } catch (error) { - // eslint-disable-next-line no-console - console.error("Error fetching scans by state:", error); - return undefined; - } -}; - export const getComplianceCsv = async ( scanId: string, complianceId: string, diff --git a/ui/app/(prowler)/resources/page.tsx b/ui/app/(prowler)/resources/page.tsx index bfc0e86e18..a1cd98416d 100644 --- a/ui/app/(prowler)/resources/page.tsx +++ b/ui/app/(prowler)/resources/page.tsx @@ -3,7 +3,7 @@ import { format, parseISO } from "date-fns"; import { Suspense } from "react"; import { getResourceFields, getResources } from "@/actions/resources"; -import { getScansByFields } from "@/actions/scans"; +import { getScans } from "@/actions/scans"; import { FilterControls } from "@/components/filters"; import { SkeletonTableResources } from "@/components/resources/skeleton/skeleton-table-resources"; import { ColumnResources } from "@/components/resources/table/column-resources"; @@ -41,9 +41,11 @@ export default async function Resources({ ), }; - // Fetch scans data latest date not fully done - const scansData = await getScansByFields("inserted_at", { - "filter[state]": "completed", + const scansData = await getScans({ + filters: { + "filter[state]": "completed", + "fields[scans]": "inserted_at", + }, }); if (scansData.data?.length !== 0) { @@ -161,8 +163,11 @@ const SSRDataTable = async ({ }; // Fetch scans data latest date - const scansData = await getScansByFields("inserted_at", { - "filter[state]": "completed", + const scansData = await getScans({ + filters: { + "filter[state]": "completed", + "fields[scans]": "inserted_at", + }, }); if (scansData.data?.length !== 0) { diff --git a/ui/app/(prowler)/scans/page.tsx b/ui/app/(prowler)/scans/page.tsx index 737d7f284b..30c1af6f2f 100644 --- a/ui/app/(prowler)/scans/page.tsx +++ b/ui/app/(prowler)/scans/page.tsx @@ -123,7 +123,13 @@ const SSRDataTableScans = async ({ const query = (filters["filter[search]"] as string) || ""; // Fetch scans data - const scansData = await getScans({ query, page, sort, filters, pageSize }); + const scansData = await getScans({ + query, + page, + sort, + filters: filters as Record, + pageSize, + }); // Handle expanded scans data const expandedScansData = await Promise.all( diff --git a/ui/components/resources/table/resource-detail.tsx b/ui/components/resources/table/resource-detail.tsx index 710df9290d..6093d99748 100644 --- a/ui/components/resources/table/resource-detail.tsx +++ b/ui/components/resources/table/resource-detail.tsx @@ -131,10 +131,10 @@ export const ResourceDetail = ({ - {/* Finding Details section */} + {/* Finding associated with this resource section */}
-

- Findings Details +

+ Findings associated with this resource

{isLoading ? ( diff --git a/ui/types/components.ts b/ui/types/components.ts index 69c83c6d73..874c57e99c 100644 --- a/ui/types/components.ts +++ b/ui/types/components.ts @@ -1,7 +1,7 @@ import { LucideIcon } from "lucide-react"; import { SVGProps } from "react"; -import { ProviderType } from "./providers"; +import { ProviderProps, ProviderType } from "./providers"; export type IconSvgProps = SVGProps & { size?: number; @@ -704,145 +704,3 @@ export interface UserProps { dateAdded: string; status: "active" | "inactive"; } - -export interface ResourceProps { - type: "resources"; - id: string; - attributes: { - inserted_at: string; - updated_at: string; - uid: string; - name: string; - region: string; - service: string; - tags: Record; - type: string; - }; - relationships: { - provider: { - data: { - type: "providers"; - id: string; - attributes: { - inserted_at: string; - updated_at: string; - provider: string; - uid: string; - alias: string | null; - connection: { - connected: boolean; - last_checked_at: string; - }; - }; - relationships: { - secret: { - data: { - type: "provider-secrets"; - id: string; - }; - }; - }; - links: { - self: string; - }; - }; - }; - findings: { - meta: { - count: number; - }; - data: { - type: "findings"; - id: string; - attributes: { status: string }; - }[]; - }; - }; - links: { - self: string; - }; -} -interface Provider { - type: "providers" | "findings"; - id: string; - attributes: { - uid: string; - delta: string; - status: "PASS" | "FAIL" | "MANUAL"; - status_extended: string; - severity: "informational" | "low" | "medium" | "high" | "critical"; - check_id: string; - check_metadata: CheckMetadata; - raw_result: Record; - inserted_at: string; - updated_at: string; - first_seen_at: string; - muted: boolean; - }; - relationships: { - secret: { - data: { - type: string; - id: string; - }; - }; - scan: { - data: { - type: string; - id: string; - }; - }; - provider_groups: { - meta: { - count: number; - }; - data: []; - }; - }; - links: { - self: string; - }; -} - -interface CheckMetadata { - risk: string; - notes: string; - checkid: string; - provider: string; - severity: string; - checktype: string[]; - dependson: string[]; - relatedto: string[]; - categories: string[]; - checktitle: string; - compliance: any; - relatedurl: string; - description: string; - remediation: { - code: { - cli: string; - other: string; - nativeiac: string; - terraform: string; - }; - recommendation: { - url: string; - text: string; - }; - }; - servicename: string; - checkaliases: string[]; - resourcetype: string; - subservicename: string; - resourceidtemplate: string; -} - -interface Meta { - version: string; -} - -export interface ResourceApiResponse { - data: ResourceProps; - included: Provider[]; - meta: Meta; -} diff --git a/ui/types/index.ts b/ui/types/index.ts index e35a2815da..40d4d9a1ef 100644 --- a/ui/types/index.ts +++ b/ui/types/index.ts @@ -3,3 +3,4 @@ export * from "./components"; export * from "./filters"; export * from "./formSchemas"; export * from "./providers"; +export * from "./resources"; diff --git a/ui/types/resources.ts b/ui/types/resources.ts new file mode 100644 index 0000000000..6d23b5bc51 --- /dev/null +++ b/ui/types/resources.ts @@ -0,0 +1,142 @@ +export interface ResourceProps { + type: "resources"; + id: string; + attributes: { + inserted_at: string; + updated_at: string; + uid: string; + name: string; + region: string; + service: string; + tags: Record; + type: string; + }; + relationships: { + provider: { + data: { + type: "providers"; + id: string; + attributes: { + inserted_at: string; + updated_at: string; + provider: string; + uid: string; + alias: string | null; + connection: { + connected: boolean; + last_checked_at: string; + }; + }; + relationships: { + secret: { + data: { + type: "provider-secrets"; + id: string; + }; + }; + }; + links: { + self: string; + }; + }; + }; + findings: { + meta: { + count: number; + }; + data: { + type: "findings"; + id: string; + attributes: { status: string }; + }[]; + }; + }; + links: { + self: string; + }; +} + +interface ResourceItemProps { + type: "providers" | "findings"; + id: string; + attributes: { + uid: string; + delta: string; + status: "PASS" | "FAIL" | "MANUAL"; + status_extended: string; + severity: "informational" | "low" | "medium" | "high" | "critical"; + check_id: string; + check_metadata: CheckMetadataProps; + raw_result: Record; + inserted_at: string; + updated_at: string; + first_seen_at: string; + muted: boolean; + }; + relationships: { + secret: { + data: { + type: string; + id: string; + }; + }; + scan: { + data: { + type: string; + id: string; + }; + }; + provider_groups: { + meta: { + count: number; + }; + data: []; + }; + }; + links: { + self: string; + }; +} + +interface CheckMetadataProps { + risk: string; + notes: string; + checkid: string; + provider: string; + severity: string; + checktype: string[]; + dependson: string[]; + relatedto: string[]; + categories: string[]; + checktitle: string; + compliance: any; + relatedurl: string; + description: string; + remediation: { + code: { + cli: string; + other: string; + nativeiac: string; + terraform: string; + }; + recommendation: { + url: string; + text: string; + }; + }; + servicename: string; + checkaliases: string[]; + resourcetype: string; + subservicename: string; + resourceidtemplate: string; +} + +interface Meta { + version: string; +} + +export interface ResourceApiResponse { + data: ResourceProps; + included: ResourceItemProps[]; + meta: Meta; +}