mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
5b9824c379
Co-authored-by: Pablo F.G <pablo.fernandez@prowler.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
289 lines
7.8 KiB
TypeScript
289 lines
7.8 KiB
TypeScript
"use server";
|
||
|
||
import { revalidatePath } from "next/cache";
|
||
import { redirect } from "next/navigation";
|
||
|
||
import { apiBaseUrl, getAuthHeaders, getErrorMessage } from "@/lib";
|
||
import { handleApiError, handleApiResponse } from "@/lib/server-actions-helper";
|
||
import { ManageGroupPayload, ProviderGroupsResponse } from "@/types/components";
|
||
|
||
export const getProviderGroups = async ({
|
||
page = 1,
|
||
query = "",
|
||
sort = "",
|
||
filters = {},
|
||
pageSize = 10,
|
||
}: {
|
||
page?: number;
|
||
query?: string;
|
||
sort?: string;
|
||
filters?: Record<string, string | number>;
|
||
pageSize?: number;
|
||
}): Promise<ProviderGroupsResponse | undefined> => {
|
||
const headers = await getAuthHeaders({ contentType: false });
|
||
|
||
if (isNaN(Number(page)) || page < 1)
|
||
redirect("/providers?tab=provider-groups");
|
||
|
||
const url = new URL(`${apiBaseUrl}/provider-groups`);
|
||
|
||
if (page) url.searchParams.append("page[number]", page.toString());
|
||
if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
|
||
if (query) url.searchParams.append("filter[search]", query);
|
||
if (sort) url.searchParams.append("sort", sort);
|
||
|
||
// Handle multiple filters
|
||
Object.entries(filters).forEach(([key, value]) => {
|
||
if (key !== "filter[search]") {
|
||
url.searchParams.append(key, String(value));
|
||
}
|
||
});
|
||
|
||
try {
|
||
const response = await fetch(url.toString(), {
|
||
headers,
|
||
});
|
||
|
||
return await handleApiResponse(response);
|
||
} catch (error) {
|
||
console.error("Error fetching provider groups:", error);
|
||
return undefined;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Fetches all provider groups by iterating through every page.
|
||
* Used to populate filter dropdowns (e.g. the Provider Group selector) without
|
||
* the pagination cap that `getProviderGroups` applies for the management table.
|
||
*/
|
||
export const getAllProviderGroups = async (): Promise<
|
||
ProviderGroupsResponse | undefined
|
||
> => {
|
||
const pageSize = 100; // Larger page size to minimize API calls
|
||
const maxPages = 50; // Safety limit: 50 pages × 100 = 5000 groups max
|
||
let currentPage = 1;
|
||
const allGroups: ProviderGroupsResponse["data"] = [];
|
||
let lastResponse: ProviderGroupsResponse | undefined;
|
||
let hasMorePages = true;
|
||
|
||
try {
|
||
const headers = await getAuthHeaders({ contentType: false });
|
||
while (hasMorePages && currentPage <= maxPages) {
|
||
const url = new URL(`${apiBaseUrl}/provider-groups`);
|
||
url.searchParams.append("page[number]", currentPage.toString());
|
||
url.searchParams.append("page[size]", pageSize.toString());
|
||
|
||
const response = await fetch(url.toString(), { headers });
|
||
const data = (await handleApiResponse(response)) as
|
||
| ProviderGroupsResponse
|
||
| { error: string; status?: number }
|
||
| undefined;
|
||
|
||
// A later page resolving to an API error payload must abort rather than
|
||
// be treated as "no more pages", which would silently truncate groups.
|
||
if (data && "error" in data) {
|
||
console.error("Error fetching all provider groups:", data.error);
|
||
return undefined;
|
||
}
|
||
|
||
if (!data?.data || data.data.length === 0) {
|
||
hasMorePages = false;
|
||
continue;
|
||
}
|
||
|
||
allGroups.push(...data.data);
|
||
lastResponse = data;
|
||
|
||
const totalPages = data.meta?.pagination?.pages || 1;
|
||
if (currentPage >= totalPages) {
|
||
hasMorePages = false;
|
||
} else {
|
||
currentPage++;
|
||
}
|
||
}
|
||
|
||
if (hasMorePages && currentPage > maxPages) {
|
||
console.error(
|
||
`Error fetching all provider groups: exceeded max page limit (${maxPages})`,
|
||
);
|
||
return undefined;
|
||
}
|
||
|
||
if (lastResponse) {
|
||
return {
|
||
...lastResponse,
|
||
data: allGroups,
|
||
meta: {
|
||
...lastResponse.meta,
|
||
pagination: {
|
||
...lastResponse.meta?.pagination,
|
||
page: 1,
|
||
pages: 1,
|
||
count: allGroups.length,
|
||
},
|
||
},
|
||
};
|
||
}
|
||
|
||
return undefined;
|
||
} catch (error) {
|
||
console.error("Error fetching all provider groups:", error);
|
||
return undefined;
|
||
}
|
||
};
|
||
|
||
export const getProviderGroupInfoById = async (providerGroupId: string) => {
|
||
const headers = await getAuthHeaders({ contentType: false });
|
||
const url = new URL(`${apiBaseUrl}/provider-groups/${providerGroupId}`);
|
||
|
||
try {
|
||
const response = await fetch(url.toString(), {
|
||
method: "GET",
|
||
headers,
|
||
});
|
||
|
||
return await handleApiResponse(response);
|
||
} catch (error) {
|
||
handleApiError(error);
|
||
}
|
||
};
|
||
|
||
export const createProviderGroup = async (formData: FormData) => {
|
||
const headers = await getAuthHeaders({ contentType: true });
|
||
|
||
const name = formData.get("name") as string;
|
||
const providersJson = formData.get("providers") as string;
|
||
const rolesJson = formData.get("roles") as string;
|
||
|
||
// Parse JSON strings and handle empty cases
|
||
const providers = providersJson ? JSON.parse(providersJson) : [];
|
||
const roles = rolesJson ? JSON.parse(rolesJson) : [];
|
||
|
||
// Prepare base payload
|
||
const payload: any = {
|
||
data: {
|
||
type: "provider-groups",
|
||
attributes: {
|
||
name,
|
||
},
|
||
relationships: {},
|
||
},
|
||
};
|
||
|
||
// Add relationships only if there are items
|
||
if (providers.length > 0) {
|
||
payload.data.relationships.providers = {
|
||
data: providers,
|
||
};
|
||
}
|
||
|
||
if (roles.length > 0) {
|
||
payload.data.relationships.roles = {
|
||
data: roles,
|
||
};
|
||
}
|
||
|
||
const body = JSON.stringify(payload);
|
||
|
||
try {
|
||
const url = new URL(`${apiBaseUrl}/provider-groups`);
|
||
const response = await fetch(url.toString(), {
|
||
method: "POST",
|
||
headers,
|
||
body,
|
||
});
|
||
|
||
return await handleApiResponse(response, "/providers?tab=provider-groups");
|
||
} catch (error) {
|
||
handleApiError(error);
|
||
}
|
||
};
|
||
|
||
export const updateProviderGroup = async (
|
||
providerGroupId: string,
|
||
formData: FormData,
|
||
) => {
|
||
const headers = await getAuthHeaders({ contentType: true });
|
||
|
||
const name = formData.get("name") as string;
|
||
const providersJson = formData.get("providers") as string;
|
||
const rolesJson = formData.get("roles") as string;
|
||
|
||
const providers = providersJson ? JSON.parse(providersJson) : null;
|
||
const roles = rolesJson ? JSON.parse(rolesJson) : null;
|
||
|
||
const payload: Partial<ManageGroupPayload> = {
|
||
data: {
|
||
type: "provider-groups",
|
||
id: providerGroupId,
|
||
attributes: name ? { name } : undefined,
|
||
relationships: {},
|
||
},
|
||
};
|
||
|
||
// Add relationships only if changes are detected
|
||
if (providers) {
|
||
payload.data!.relationships!.providers = { data: providers };
|
||
}
|
||
|
||
if (roles) {
|
||
payload.data!.relationships!.roles = { data: roles };
|
||
}
|
||
|
||
try {
|
||
const url = `${apiBaseUrl}/provider-groups/${providerGroupId}`;
|
||
const response = await fetch(url, {
|
||
method: "PATCH",
|
||
headers,
|
||
body: JSON.stringify(payload),
|
||
});
|
||
|
||
return await handleApiResponse(response);
|
||
} catch (error) {
|
||
handleApiError(error);
|
||
}
|
||
};
|
||
|
||
export const deleteProviderGroup = async (formData: FormData) => {
|
||
const headers = await getAuthHeaders({ contentType: false });
|
||
const providerGroupId = formData.get("id");
|
||
|
||
if (!providerGroupId) {
|
||
return {
|
||
errors: [{ detail: "Provider Group ID is required." }],
|
||
};
|
||
}
|
||
|
||
const url = new URL(`${apiBaseUrl}/provider-groups/${providerGroupId}`);
|
||
|
||
try {
|
||
const response = await fetch(url.toString(), {
|
||
method: "DELETE",
|
||
headers,
|
||
});
|
||
|
||
if (!response.ok) {
|
||
try {
|
||
const errorData = await response.json();
|
||
throw new Error(
|
||
errorData?.message || "Failed to delete the provider group",
|
||
);
|
||
} catch {
|
||
throw new Error("Failed to delete the provider group");
|
||
}
|
||
}
|
||
|
||
let data = null;
|
||
if (response.status !== 204) {
|
||
data = await response.json();
|
||
}
|
||
|
||
revalidatePath("/providers");
|
||
return data || { success: true };
|
||
} catch (error) {
|
||
console.error("Error deleting provider group:", error);
|
||
const message = getErrorMessage(error);
|
||
return { errors: [{ detail: message }] };
|
||
}
|
||
};
|