feat: mock the API for services page and creating components

This commit is contained in:
Pablo Lara
2024-08-14 15:21:52 +02:00
parent 4c0a14b96c
commit ebd3bb386c
9 changed files with 733 additions and 63 deletions
+39
View File
@@ -0,0 +1,39 @@
"use server";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { parseStringify } from "@/lib";
export const getService = async ({ page = 1 }) => {
if (isNaN(Number(page)) || page < 1) redirect("/services");
const keyServer = process.env.LOCAL_SITE_URL;
try {
const services = await fetch(
`${keyServer}/api/services?page%5Bnumber%5D=${page}`,
);
const data = await services.json();
const parsedData = parseStringify(data);
revalidatePath("/services");
return parsedData;
} catch (error) {
console.error("Error fetching services:", error);
return undefined;
}
};
export const getErrorMessage = (error: unknown): string => {
let message: string;
if (error instanceof Error) {
message = error.message;
} else if (error && typeof error === "object" && "message" in error) {
message = String(error.message);
} else if (typeof error === "string") {
message = error;
} else {
message = "Wops! Something when wrong.";
}
return message;
};
+1 -1
View File
@@ -16,7 +16,7 @@ export default async function Providers({ searchParams }: searchParamsProps) {
return (
<>
<Header title="Providers" icon="fluent:cloud-sync-24-regular" />
<Spacer />
<Spacer y={4} />
<div className="flex flex-col items-end w-full">
<div className="flex space-x-6">
<AddProviderModal />
+36 -62
View File
@@ -1,73 +1,47 @@
import { FilterControls } from "@/components/filters";
import {
AmazonEC2Icon,
AmazonEMRIcon,
AmazonGuardDutyIcon,
AmazonInspectorIcon,
AmazonMacieIcon,
AmazonRDSIcon,
AmazonRoute53Icon,
AmazonS3Icon,
AmazonSNSIcon,
AmazonVPCIcon,
AWSAccountIcon,
AWSAthenaIcon,
AWSCertificateManagerIcon,
AWSCloudFormationIcon,
AWSCloudTrailIcon,
AWSCloudWatchIcon,
AWSConfigIcon,
AWSDatabaseMigrationServiceIcon,
AWSGlueIcon,
AWSIAMIcon,
AWSLambdaIcon,
AWSNetworkFirewallIcon,
AWSOrganizationsIcon,
AWSResourceExplorerIcon,
AWSSecurityHubIcon,
AWSSystemsManagerIncidentManagerIcon,
AWSTrustedAdvisorIcon,
IAMAccessAnalyzerIcon,
} from "@/components/icons";
import { Header } from "@/components/ui";
import { Spacer } from "@nextui-org/react";
import { redirect } from "next/navigation";
import { Suspense } from "react";
export default function Services() {
import { getService } from "@/actions/services";
import { FilterControls } from "@/components/filters";
import { SkeletonTableProvider } from "@/components/providers";
import { CardService } from "@/components/services";
import { Header } from "@/components/ui";
import { searchParamsProps } from "@/types";
export default async function Services({ searchParams }: searchParamsProps) {
return (
<>
<Header
title="Services"
icon="material-symbols:linked-services-outline"
/>
<Spacer y={4} />
<FilterControls />
<IAMAccessAnalyzerIcon />
<AWSAccountIcon />
<AWSCertificateManagerIcon />
<AWSAthenaIcon />
<AWSLambdaIcon />
<AWSCloudFormationIcon />
<AWSCloudTrailIcon />
<AWSCloudWatchIcon />
<AWSConfigIcon />
<AWSDatabaseMigrationServiceIcon />
<AmazonEC2Icon />
<AmazonEMRIcon />
<AWSGlueIcon />
<AmazonGuardDutyIcon />
<AmazonInspectorIcon />
<AWSIAMIcon />
<AmazonMacieIcon />
<AWSNetworkFirewallIcon />
<AWSOrganizationsIcon />
<AmazonRDSIcon />
<AWSResourceExplorerIcon />
<AmazonRoute53Icon />
<AmazonS3Icon />
<AWSSecurityHubIcon />
<AmazonSNSIcon />
<AWSSystemsManagerIncidentManagerIcon />
<AWSTrustedAdvisorIcon />
<AmazonVPCIcon />
<Spacer y={4} />
<Suspense key={searchParams.page} fallback={<SkeletonTableProvider />}>
<SSRServiceGrid searchParams={searchParams} />
</Suspense>
</>
);
}
const SSRServiceGrid = async ({ searchParams }: searchParamsProps) => {
const page = searchParams.page ? parseInt(searchParams.page) : 1;
const servicesData = await getService({ page });
const [services] = await Promise.all([servicesData]);
if (services?.errors) redirect("/services");
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
{services.services?.data.map((service: any) => (
<CardService
key={service.id}
fidingsFailed={service.attributes.findings.failed}
serviceAlias={service.attributes.alias}
/>
))}
</div>
);
};
+10
View File
@@ -0,0 +1,10 @@
import { NextResponse } from "next/server";
import data from "../../../dataServices.json";
export async function GET() {
// Simulate fetching data with a delay
await new Promise((resolve) => setTimeout(resolve, 2000));
return NextResponse.json({ services: data });
}
+44
View File
@@ -440,3 +440,47 @@ export const AlertIcon: React.FC<IconSvgProps> = ({
</g>
</svg>
);
export const NotificationIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
fill="none"
height={size || height || 24}
viewBox="0 0 24 24"
width={size || width || 24}
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
clipRule="evenodd"
d="M18.707 8.796c0 1.256.332 1.997 1.063 2.85.553.628.73 1.435.73 2.31 0 .874-.287 1.704-.863 2.378a4.537 4.537 0 01-2.9 1.413c-1.571.134-3.143.247-4.736.247-1.595 0-3.166-.068-4.737-.247a4.532 4.532 0 01-2.9-1.413 3.616 3.616 0 01-.864-2.378c0-.875.178-1.682.73-2.31.754-.854 1.064-1.594 1.064-2.85V8.37c0-1.682.42-2.781 1.283-3.858C7.861 2.942 9.919 2 11.956 2h.09c2.08 0 4.204.987 5.466 2.625.82 1.054 1.195 2.108 1.195 3.745v.426zM9.074 20.061c0-.504.462-.734.89-.833.5-.106 3.545-.106 4.045 0 .428.099.89.33.89.833-.025.48-.306.904-.695 1.174a3.635 3.635 0 01-1.713.731 3.795 3.795 0 01-1.008 0 3.618 3.618 0 01-1.714-.732c-.39-.269-.67-.694-.695-1.173z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
);
export const SuccessIcon: React.FC<IconSvgProps> = ({
size = 24,
width,
height,
...props
}) => (
<svg
width={size || width || 24}
height={size || height || 24}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M12 2C6.49 2 2 6.49 2 12C2 17.51 6.49 22 12 22C17.51 22 22 17.51 22 12C22 6.49 17.51 2 12 2ZM16.78 9.7L11.11 15.37C10.97 15.51 10.78 15.59 10.58 15.59C10.38 15.59 10.19 15.51 10.05 15.37L7.22 12.54C6.93 12.25 6.93 11.77 7.22 11.48C7.51 11.19 7.99 11.19 8.28 11.48L10.58 13.78L15.72 8.64C16.01 8.35 16.49 8.35 16.78 8.64C17.07 8.93 17.07 9.4 16.78 9.7Z"
fill="currentColor"
/>
</svg>
);
@@ -858,3 +858,66 @@ export const AmazonVPCIcon: React.FC<IconSvgProps> = ({
</g>
</svg>
);
export const getAWSIcon = (serviceAlias: string) => {
switch (serviceAlias) {
case "Amazon EC2":
return <AmazonEC2Icon />;
case "Amazon EMR":
return <AmazonEMRIcon />;
case "Amazon GuardDuty":
return <AmazonGuardDutyIcon />;
case "Amazon Inspector":
return <AmazonInspectorIcon />;
case "Amazon Macie":
return <AmazonMacieIcon />;
case "Amazon RDS":
return <AmazonRDSIcon />;
case "Amazon Route 53":
return <AmazonRoute53Icon />;
case "Amazon S3":
return <AmazonS3Icon />;
case "Amazon SNS":
return <AmazonSNSIcon />;
case "Amazon VPC":
return <AmazonVPCIcon />;
case "AWS Account":
return <AWSAccountIcon />;
case "AWS Athena":
return <AWSAthenaIcon />;
case "AWS Certificate Manager":
return <AWSCertificateManagerIcon />;
case "AWS CloudFormation":
return <AWSCloudFormationIcon />;
case "AWS CloudTrail":
return <AWSCloudTrailIcon />;
case "AWS CloudWatch":
return <AWSCloudWatchIcon />;
case "AWS Config":
return <AWSConfigIcon />;
case "AWS Database Migration Service":
return <AWSDatabaseMigrationServiceIcon />;
case "AWS Glue":
return <AWSGlueIcon />;
case "AWS IAM":
return <AWSIAMIcon />;
case "AWS Lambda":
return <AWSLambdaIcon />;
case "AWS Network Firewall":
return <AWSNetworkFirewallIcon />;
case "AWS Organizations":
return <AWSOrganizationsIcon />;
case "AWS Resource Explorer":
return <AWSResourceExplorerIcon />;
case "AWS Security Hub":
return <AWSSecurityHubIcon />;
case "AWS Systems Manager Incident Manager":
return <AWSSystemsManagerIncidentManagerIcon />;
case "AWS Trusted Advisor":
return <AWSTrustedAdvisorIcon />;
case "IAM Access Analyzer":
return <IAMAccessAnalyzerIcon />;
default:
return null;
}
};
+45
View File
@@ -0,0 +1,45 @@
import { Card, CardBody, Chip } from "@nextui-org/react";
import { getAWSIcon, NotificationIcon, SuccessIcon } from "../icons";
interface CardServiceProps {
fidingsFailed: number;
serviceAlias: string;
}
export const CardService: React.FC<CardServiceProps> = ({
fidingsFailed,
serviceAlias,
}) => {
return (
<Card fullWidth isPressable isHoverable shadow="sm">
<CardBody className="flex flex-row items-center space-x-4">
{getAWSIcon(serviceAlias)}
<div className="flex flex-col">
<h4 className="font-bold text-large">{serviceAlias}</h4>
<small className="text-default-500">
{fidingsFailed > 0
? `${fidingsFailed} Failed Findings`
: "All findings passed"}
</small>
</div>
<Chip
className="h-10"
variant="flat"
startContent={
fidingsFailed > 0 ? (
<NotificationIcon size={18} />
) : (
<SuccessIcon size={18} />
)
}
color={fidingsFailed > 0 ? "danger" : "success"}
radius="full"
size="md"
>
{fidingsFailed > 0 ? fidingsFailed : "All passed"}
</Chip>
</CardBody>
</Card>
);
};
+1
View File
@@ -0,0 +1 @@
export * from "./CardService";
+494
View File
@@ -0,0 +1,494 @@
{
"links": {
"first": "http://localhost:8080/api/v1/services?page%5Bnumber%5D=1",
"last": "http://localhost:8080/api/v1/services?page%5Bnumber%5D=1",
"next": null,
"prev": null
},
"data": [
{
"type": "Service",
"id": "001",
"attributes": {
"inserted_at": "2024-08-14T10:00:00.000000Z",
"updated_at": "2024-08-14T10:00:00.000000Z",
"provider": "aws",
"provider_id": "001",
"alias": "Amazon EC2",
"regions": ["us-east-1", "us-east-2"],
"findings": {
"failed": 3,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "002",
"attributes": {
"inserted_at": "2024-08-14T10:01:00.000000Z",
"updated_at": "2024-08-14T10:01:00.000000Z",
"provider": "aws",
"provider_id": "002",
"alias": "Amazon EMR",
"regions": ["us-west-1", "us-west-2"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "003",
"attributes": {
"inserted_at": "2024-08-14T10:02:00.000000Z",
"updated_at": "2024-08-14T10:02:00.000000Z",
"provider": "aws",
"provider_id": "003",
"alias": "Amazon GuardDuty",
"regions": ["eu-west-1", "eu-west-2"],
"findings": {
"failed": 5,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "004",
"attributes": {
"inserted_at": "2024-08-14T10:03:00.000000Z",
"updated_at": "2024-08-14T10:03:00.000000Z",
"provider": "aws",
"provider_id": "004",
"alias": "Amazon Inspector",
"regions": ["ap-southeast-1", "ap-southeast-2"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "005",
"attributes": {
"inserted_at": "2024-08-14T10:04:00.000000Z",
"updated_at": "2024-08-14T10:04:00.000000Z",
"provider": "aws",
"provider_id": "005",
"alias": "Amazon Macie",
"regions": ["eu-north-1", "eu-north-2"],
"findings": {
"failed": 2,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "006",
"attributes": {
"inserted_at": "2024-08-14T10:05:00.000000Z",
"updated_at": "2024-08-14T10:05:00.000000Z",
"provider": "aws",
"provider_id": "006",
"alias": "Amazon RDS",
"regions": ["sa-east-1", "sa-east-2"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "007",
"attributes": {
"inserted_at": "2024-08-14T10:06:00.000000Z",
"updated_at": "2024-08-14T10:06:00.000000Z",
"provider": "aws",
"provider_id": "007",
"alias": "Amazon Route 53",
"regions": ["af-south-1", "af-south-2"],
"findings": {
"failed": 1,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "008",
"attributes": {
"inserted_at": "2024-08-14T10:07:00.000000Z",
"updated_at": "2024-08-14T10:07:00.000000Z",
"provider": "aws",
"provider_id": "008",
"alias": "Amazon S3",
"regions": ["us-east-1", "us-west-1"],
"findings": {
"failed": 3,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "009",
"attributes": {
"inserted_at": "2024-08-14T10:08:00.000000Z",
"updated_at": "2024-08-14T10:08:00.000000Z",
"provider": "aws",
"provider_id": "009",
"alias": "Amazon SNS",
"regions": ["eu-central-1", "eu-central-2"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "010",
"attributes": {
"inserted_at": "2024-08-14T10:09:00.000000Z",
"updated_at": "2024-08-14T10:09:00.000000Z",
"provider": "aws",
"provider_id": "010",
"alias": "Amazon VPC",
"regions": ["us-west-1", "us-west-2"],
"findings": {
"failed": 4,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "011",
"attributes": {
"inserted_at": "2024-08-14T10:10:00.000000Z",
"updated_at": "2024-08-14T10:10:00.000000Z",
"provider": "aws",
"provider_id": "011",
"alias": "AWS Account",
"regions": ["eu-west-1", "eu-west-3"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "012",
"attributes": {
"inserted_at": "2024-08-14T10:11:00.000000Z",
"updated_at": "2024-08-14T10:11:00.000000Z",
"provider": "aws",
"provider_id": "012",
"alias": "AWS Athena",
"regions": ["us-east-1", "us-east-2"],
"findings": {
"failed": 5,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "013",
"attributes": {
"inserted_at": "2024-08-14T10:12:00.000000Z",
"updated_at": "2024-08-14T10:12:00.000000Z",
"provider": "aws",
"provider_id": "013",
"alias": "AWS Certificate Manager",
"regions": ["us-west-1", "us-west-2"],
"findings": {
"failed": 1,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "014",
"attributes": {
"inserted_at": "2024-08-14T10:13:00.000000Z",
"updated_at": "2024-08-14T10:13:00.000000Z",
"provider": "aws",
"provider_id": "014",
"alias": "AWS CloudFormation",
"regions": ["eu-central-1", "eu-central-2"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "015",
"attributes": {
"inserted_at": "2024-08-14T10:14:00.000000Z",
"updated_at": "2024-08-14T10:14:00.000000Z",
"provider": "aws",
"provider_id": "015",
"alias": "AWS CloudTrail",
"regions": ["us-east-1", "us-east-2"],
"findings": {
"failed": 4,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "016",
"attributes": {
"inserted_at": "2024-08-14T10:15:00.000000Z",
"updated_at": "2024-08-14T10:15:00.000000Z",
"provider": "aws",
"provider_id": "016",
"alias": "AWS CloudWatch",
"regions": ["us-west-1", "us-west-2"],
"findings": {
"failed": 2,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "017",
"attributes": {
"inserted_at": "2024-08-14T10:16:00.000000Z",
"updated_at": "2024-08-14T10:16:00.000000Z",
"provider": "aws",
"provider_id": "017",
"alias": "AWS Config",
"regions": ["eu-west-1", "eu-west-3"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "018",
"attributes": {
"inserted_at": "2024-08-14T10:17:00.000000Z",
"updated_at": "2024-08-14T10:17:00.000000Z",
"provider": "aws",
"provider_id": "018",
"alias": "AWS Database Migration Service",
"regions": ["us-east-1", "us-east-2"],
"findings": {
"failed": 5,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "019",
"attributes": {
"inserted_at": "2024-08-14T10:18:00.000000Z",
"updated_at": "2024-08-14T10:18:00.000000Z",
"provider": "aws",
"provider_id": "019",
"alias": "AWS Glue",
"regions": ["us-west-1", "us-west-2"],
"findings": {
"failed": 1,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "020",
"attributes": {
"inserted_at": "2024-08-14T10:19:00.000000Z",
"updated_at": "2024-08-14T10:19:00.000000Z",
"provider": "aws",
"provider_id": "020",
"alias": "AWS IAM",
"regions": ["eu-central-1", "eu-central-2"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "021",
"attributes": {
"inserted_at": "2024-08-14T10:20:00.000000Z",
"updated_at": "2024-08-14T10:20:00.000000Z",
"provider": "aws",
"provider_id": "021",
"alias": "AWS Lambda",
"regions": ["us-east-1", "us-east-2"],
"findings": {
"failed": 4,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "022",
"attributes": {
"inserted_at": "2024-08-14T10:21:00.000000Z",
"updated_at": "2024-08-14T10:21:00.000000Z",
"provider": "aws",
"provider_id": "022",
"alias": "AWS Network Firewall",
"regions": ["us-west-1", "us-west-2"],
"findings": {
"failed": 2,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "023",
"attributes": {
"inserted_at": "2024-08-14T10:22:00.000000Z",
"updated_at": "2024-08-14T10:22:00.000000Z",
"provider": "aws",
"provider_id": "023",
"alias": "AWS Organizations",
"regions": ["eu-west-1", "eu-west-3"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "024",
"attributes": {
"inserted_at": "2024-08-14T10:23:00.000000Z",
"updated_at": "2024-08-14T10:23:00.000000Z",
"provider": "aws",
"provider_id": "024",
"alias": "AWS Resource Explorer",
"regions": ["us-east-1", "us-east-2"],
"findings": {
"failed": 3,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "025",
"attributes": {
"inserted_at": "2024-08-14T10:24:00.000000Z",
"updated_at": "2024-08-14T10:24:00.000000Z",
"provider": "aws",
"provider_id": "025",
"alias": "AWS Security Hub",
"regions": ["us-west-1", "us-west-2"],
"findings": {
"failed": 5,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "026",
"attributes": {
"inserted_at": "2024-08-14T10:25:00.000000Z",
"updated_at": "2024-08-14T10:25:00.000000Z",
"provider": "aws",
"provider_id": "026",
"alias": "AWS Systems Manager Incident Manager",
"regions": ["eu-west-1", "eu-west-3"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "027",
"attributes": {
"inserted_at": "2024-08-14T10:26:00.000000Z",
"updated_at": "2024-08-14T10:26:00.000000Z",
"provider": "aws",
"provider_id": "027",
"alias": "AWS Trusted Advisor",
"regions": ["us-east-1", "us-east-2"],
"findings": {
"failed": 2,
"last_checked_at": null
},
"scanner_args": {}
}
},
{
"type": "Service",
"id": "028",
"attributes": {
"inserted_at": "2024-08-14T10:27:00.000000Z",
"updated_at": "2024-08-14T10:27:00.000000Z",
"provider": "aws",
"provider_id": "028",
"alias": "IAM Access Analyzer",
"regions": ["eu-west-1", "eu-west-3"],
"findings": {
"failed": 0,
"last_checked_at": null
},
"scanner_args": {}
}
}
],
"meta": {
"pagination": {
"page": 1,
"pages": 1,
"count": 28
},
"version": "v1"
}
}