mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
Merge pull request #4 from prowler-cloud/PRWLR-4069-remove-be-code
Clean-up repo
This commit is contained in:
@@ -24,4 +24,4 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
- name: Run Healthcheck
|
||||
run: npm healthcheck
|
||||
run: npm run healthcheck
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
FROM python:3.11.9-slim
|
||||
|
||||
COPY requirements.txt /requirements.txt
|
||||
COPY be_poc /be_poc
|
||||
|
||||
RUN python -m pip install -r /requirements.txt
|
||||
WORKDIR /be_poc
|
||||
RUN python manage.py migrate && \
|
||||
python manage.py loaddata dummy_accounts
|
||||
|
||||
ENTRYPOINT ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
||||
@@ -51,41 +51,3 @@ After modifying the `.npmrc` file, you need to run `pnpm install` again to ensur
|
||||
## License
|
||||
|
||||
Licensed under the [MIT license](https://github.com/nextui-org/next-app-template/blob/main/LICENSE).
|
||||
|
||||
# Prowler Django REST API (PoC)
|
||||
|
||||
## Requirements
|
||||
|
||||
- Have `docker` and `docker compose` installed.
|
||||
|
||||
## How to run the REST API
|
||||
|
||||
### Build the service image
|
||||
|
||||
```
|
||||
docker compose build django-be-poc
|
||||
```
|
||||
|
||||
### Start the service
|
||||
|
||||
```
|
||||
docker compose up django-be-poc
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
The API will be accessible through HTTP, port `8080`. For instance, `http://localhost:8080/api/v1/`.
|
||||
|
||||
### Implemented endpoints for the PoC:
|
||||
|
||||
```
|
||||
/api/v1/providers/{provider_id}/accounts (the only available provider is 'aws')
|
||||
```
|
||||
|
||||
### Expected response with `curl`
|
||||
|
||||
```shellsession
|
||||
curl http://localhost:8080/api/v1/providers/aws/accounts
|
||||
|
||||
[{"id":1,"type":"Cloudy","enable":true,"provider_id":"aws","provider_data":{"test":"test value"},"inserted_at":"2024-06-24T10:20:18.309000Z","updated_at":"2024-06-24T10:20:18.309000Z","connected":true,"last_checked_at":"2024-06-24T10:20:04Z","alias":"dummy_alias","scanner_configuration":{"full_scan":true},"account_id":1}]%
|
||||
```
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
export default function AboutLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function AboutLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
|
||||
<div className="inline-block max-w-lg text-center justify-center">
|
||||
{children}
|
||||
</div>
|
||||
<div className="inline-block max-w-lg text-center justify-center">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
+2
-8
@@ -1,13 +1,7 @@
|
||||
export default function BlogLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function BlogLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
|
||||
<div className="inline-block max-w-lg text-center justify-center">
|
||||
{children}
|
||||
</div>
|
||||
<div className="inline-block max-w-lg text-center justify-center">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
export default function CloudsLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function CloudsLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
|
||||
<div className="inline-block text-center justify-center">{children}</div>
|
||||
|
||||
+20
-33
@@ -1,43 +1,34 @@
|
||||
"use client";
|
||||
|
||||
import useSWR from "swr";
|
||||
import React from "react";
|
||||
|
||||
// import { PencilSquareIcon } from "@heroicons/react/24/solid";
|
||||
// import { TrashIcon } from "@heroicons/react/24/solid";
|
||||
// import { EyeIcon } from "@heroicons/react/24/solid";
|
||||
import {
|
||||
// Chip,
|
||||
// getKeyValue,
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableColumn,
|
||||
TableRow,
|
||||
TableCell,
|
||||
User,
|
||||
Chip,
|
||||
Tooltip,
|
||||
getKeyValue,
|
||||
TableColumn,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
// Tooltip,
|
||||
// User,
|
||||
} from "@nextui-org/react";
|
||||
import { fetcher } from "@/utils/fetcher";
|
||||
import { title } from "@/components/primitives";
|
||||
import React from "react";
|
||||
import useSWR from "swr";
|
||||
|
||||
import { PencilSquareIcon } from "@heroicons/react/24/solid";
|
||||
import { TrashIcon } from "@heroicons/react/24/solid";
|
||||
import { EyeIcon } from "@heroicons/react/24/solid";
|
||||
import { title } from "@/components/primitives";
|
||||
import { fetcher } from "@/utils/fetcher";
|
||||
|
||||
export default function CloudsPage() {
|
||||
const getAccounts = useSWR(
|
||||
`http://localhost:8080/api/v1/providers/aws/accounts`,
|
||||
fetcher,
|
||||
);
|
||||
const getAccounts = useSWR("http://localhost:8080/api/v1/providers/aws/accounts", fetcher);
|
||||
|
||||
const getAudits = useSWR(
|
||||
`http://localhost:8080/api/v1/providers/aws/audits`,
|
||||
fetcher,
|
||||
);
|
||||
const getAudits = useSWR("http://localhost:8080/api/v1/providers/aws/audits", fetcher);
|
||||
|
||||
// TODO FIX TYPE CHECKING
|
||||
const getScanDetails = (account_id: Number, detail: String) => {
|
||||
const scan =
|
||||
getAudits.data &&
|
||||
getAudits.data.find((audit: any) => audit.account_id === account_id);
|
||||
const getScanDetails = (account_id: number, detail: string) => {
|
||||
const scan = getAudits.data && getAudits.data.find((audit: any) => audit.account_id === account_id);
|
||||
|
||||
if (detail === "status") {
|
||||
return scan?.audit_complete && "Completed";
|
||||
@@ -79,12 +70,8 @@ export default function CloudsPage() {
|
||||
<div>
|
||||
<h1 className={title()}>Cloud Accounts</h1>
|
||||
<p className="mt-10 text-left">
|
||||
{getAccounts.error && (
|
||||
<span className="text-red-400">Failed to load</span>
|
||||
)}
|
||||
{getAccounts.isLoading && (
|
||||
<span className="text-yellow-400">Loading</span>
|
||||
)}
|
||||
{getAccounts.error && <span className="text-red-400">Failed to load</span>}
|
||||
{getAccounts.isLoading && <span className="text-yellow-400">Loading</span>}
|
||||
</p>
|
||||
{getAccounts.data && (
|
||||
<Table aria-label="cloud accounts table" className="text-left mt-10">
|
||||
|
||||
+2
-8
@@ -1,13 +1,7 @@
|
||||
export default function DocsLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function DocsLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
|
||||
<div className="inline-block max-w-lg text-center justify-center">
|
||||
{children}
|
||||
</div>
|
||||
<div className="inline-block max-w-lg text-center justify-center">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
+1
-7
@@ -2,13 +2,7 @@
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error;
|
||||
reset: () => void;
|
||||
}) {
|
||||
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
||||
useEffect(() => {
|
||||
// Log the error to an error reporting service
|
||||
/* eslint-disable no-console */
|
||||
|
||||
+9
-19
@@ -1,10 +1,11 @@
|
||||
import { Metadata, Viewport } from "next";
|
||||
import clsx from "clsx";
|
||||
|
||||
import "@/styles/globals.css";
|
||||
import { siteConfig } from "@/config/site";
|
||||
import { fontSans } from "@/config/fonts";
|
||||
|
||||
import clsx from "clsx";
|
||||
import { Metadata, Viewport } from "next";
|
||||
|
||||
import { Navbar } from "@/components/navbar";
|
||||
import { fontSans } from "@/config/fonts";
|
||||
import { siteConfig } from "@/config/site";
|
||||
|
||||
import { Providers } from "./providers";
|
||||
|
||||
@@ -26,26 +27,15 @@ export const viewport: Viewport = {
|
||||
],
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html suppressHydrationWarning lang="en">
|
||||
<head />
|
||||
<body
|
||||
className={clsx(
|
||||
"min-h-screen bg-background font-sans antialiased",
|
||||
fontSans.variable,
|
||||
)}
|
||||
>
|
||||
<body className={clsx("min-h-screen bg-background font-sans antialiased", fontSans.variable)}>
|
||||
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
|
||||
<div className="relative flex flex-col h-screen">
|
||||
<Navbar />
|
||||
<main className="container mx-auto max-w-7xl pt-16 px-6 flex-grow">
|
||||
{children}
|
||||
</main>
|
||||
<main className="container mx-auto max-w-7xl pt-16 px-6 flex-grow">{children}</main>
|
||||
</div>
|
||||
</Providers>
|
||||
</body>
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
export default function LoginLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function LoginLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
|
||||
<div className="inline-block max-w-lg text-center justify-center">
|
||||
{children}
|
||||
</div>
|
||||
<div className="inline-block max-w-lg text-center justify-center">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
+4
-21
@@ -1,17 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardBody,
|
||||
CardFooter,
|
||||
Input,
|
||||
Link,
|
||||
Button,
|
||||
} from "@nextui-org/react";
|
||||
import { EyeIcon } from "@heroicons/react/24/solid";
|
||||
import { EyeSlashIcon } from "@heroicons/react/24/solid";
|
||||
import { Button, Card, CardBody, CardFooter, CardHeader, Input, Link } from "@nextui-org/react";
|
||||
import React from "react";
|
||||
|
||||
export default function LoginPage() {
|
||||
const [isVisible, setIsVisible] = React.useState(false);
|
||||
@@ -26,22 +18,13 @@ export default function LoginPage() {
|
||||
<h1 className="pl-1">Login</h1>
|
||||
</CardHeader>
|
||||
<CardBody className="w-72 pb-0">
|
||||
<Input
|
||||
label="Email"
|
||||
variant="bordered"
|
||||
placeholder="Enter your email"
|
||||
className="mb-2"
|
||||
/>
|
||||
<Input label="Email" variant="bordered" placeholder="Enter your email" className="mb-2" />
|
||||
<Input
|
||||
label="Password"
|
||||
variant="bordered"
|
||||
placeholder="Enter your password"
|
||||
endContent={
|
||||
<button
|
||||
className="focus:outline-none"
|
||||
type="button"
|
||||
onClick={toggleVisibility}
|
||||
>
|
||||
<button className="focus:outline-none" type="button" onClick={toggleVisibility}>
|
||||
{isVisible ? (
|
||||
<EyeSlashIcon className="text-2xl text-default-400 pointer-events-none w-5 h-5" />
|
||||
) : (
|
||||
|
||||
+4
-21
@@ -1,17 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardBody,
|
||||
CardFooter,
|
||||
Input,
|
||||
Link,
|
||||
Button,
|
||||
} from "@nextui-org/react";
|
||||
import { EyeIcon } from "@heroicons/react/24/solid";
|
||||
import { EyeSlashIcon } from "@heroicons/react/24/solid";
|
||||
import { Button, Card, CardBody, CardFooter, CardHeader, Input, Link } from "@nextui-org/react";
|
||||
import React from "react";
|
||||
|
||||
export default function Home() {
|
||||
const [isVisible, setIsVisible] = React.useState(false);
|
||||
@@ -26,22 +18,13 @@ export default function Home() {
|
||||
<h1 className="pl-1">Login</h1>
|
||||
</CardHeader>
|
||||
<CardBody className="w-72 pb-0">
|
||||
<Input
|
||||
label="Email"
|
||||
variant="bordered"
|
||||
placeholder="Enter your email"
|
||||
className="mb-2"
|
||||
/>
|
||||
<Input label="Email" variant="bordered" placeholder="Enter your email" className="mb-2" />
|
||||
<Input
|
||||
label="Password"
|
||||
variant="bordered"
|
||||
placeholder="Enter your password"
|
||||
endContent={
|
||||
<button
|
||||
className="focus:outline-none"
|
||||
type="button"
|
||||
onClick={toggleVisibility}
|
||||
>
|
||||
<button className="focus:outline-none" type="button" onClick={toggleVisibility}>
|
||||
{isVisible ? (
|
||||
<EyeSlashIcon className="text-2xl text-default-400 pointer-events-none w-5 h-5" />
|
||||
) : (
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
export default function PricingLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function PricingLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
|
||||
<div className="inline-block max-w-lg text-center justify-center">
|
||||
{children}
|
||||
</div>
|
||||
<div className="inline-block max-w-lg text-center justify-center">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { NextUIProvider } from "@nextui-org/system";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
import { ThemeProviderProps } from "next-themes/dist/types";
|
||||
import * as React from "react";
|
||||
|
||||
export interface ProvidersProps {
|
||||
children: React.ReactNode;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from .models import Account, CloudAccount, Audit
|
||||
|
||||
admin.site.register(Account)
|
||||
admin.site.register(CloudAccount)
|
||||
admin.site.register(Audit)
|
||||
@@ -1,6 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ApiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'api'
|
||||
@@ -1,203 +0,0 @@
|
||||
[
|
||||
{
|
||||
"model": "api.account",
|
||||
"pk": "c0bbdcd2-c69e-4bdf-bea7-8db6524fd205",
|
||||
"fields": {
|
||||
"name": "prod",
|
||||
"inserted_at": "2024-06-25T09:03:20.273Z",
|
||||
"updated_at": "2024-06-25T09:03:20.273Z",
|
||||
"aws_account_id": "232136659152",
|
||||
"scan_window_start_at": "2024-06-29T09:03:17Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.account",
|
||||
"pk": "cf51b7a0-687e-4a2a-bf3d-14f98dcb4c3f",
|
||||
"fields": {
|
||||
"name": "platform",
|
||||
"inserted_at": "2024-06-25T09:03:08.847Z",
|
||||
"updated_at": "2024-06-25T09:03:08.847Z",
|
||||
"aws_account_id": "714274078102",
|
||||
"scan_window_start_at": "2024-06-28T09:03:08Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.account",
|
||||
"pk": "cf9d2400-9a10-4c7b-a3be-6215c41b619e",
|
||||
"fields": {
|
||||
"name": "dev",
|
||||
"inserted_at": "2024-06-25T09:02:34.188Z",
|
||||
"updated_at": "2024-06-25T09:02:34.188Z",
|
||||
"aws_account_id": "106908755756",
|
||||
"scan_window_start_at": "2024-06-29T09:02:27Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.account",
|
||||
"pk": "dbc452bd-b51c-4199-8b42-c1bc8164d45d",
|
||||
"fields": {
|
||||
"name": "marketplace",
|
||||
"inserted_at": "2024-06-25T09:02:58.412Z",
|
||||
"updated_at": "2024-06-25T09:02:58.412Z",
|
||||
"aws_account_id": "879304169158",
|
||||
"scan_window_start_at": "2024-06-27T09:02:57Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.account",
|
||||
"pk": "fabc5b58-901b-4ba6-9901-4b2349acf492",
|
||||
"fields": {
|
||||
"name": "management",
|
||||
"inserted_at": "2024-06-25T09:02:46.950Z",
|
||||
"updated_at": "2024-06-25T09:02:46.950Z",
|
||||
"aws_account_id": "741399645537",
|
||||
"scan_window_start_at": "2024-06-27T09:02:46Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.cloudaccount",
|
||||
"pk": "1f1dd221-6d49-4413-a8b7-e10ca4b0660a",
|
||||
"fields": {
|
||||
"account_id": "cf9d2400-9a10-4c7b-a3be-6215c41b619e",
|
||||
"type": "test",
|
||||
"groups": [
|
||||
"Operation"
|
||||
],
|
||||
"resources": 644,
|
||||
"enable": true,
|
||||
"provider_id": "aws",
|
||||
"inserted_at": "2024-06-25T09:03:57.233Z",
|
||||
"updated_at": "2024-06-25T09:03:57.233Z",
|
||||
"alias": "dev",
|
||||
"connected": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.cloudaccount",
|
||||
"pk": "4997b2eb-a9f9-4d12-800b-1a6394728510",
|
||||
"fields": {
|
||||
"account_id": "c0bbdcd2-c69e-4bdf-bea7-8db6524fd205",
|
||||
"type": "test",
|
||||
"groups": [
|
||||
"Production"
|
||||
],
|
||||
"resources": 564,
|
||||
"enable": true,
|
||||
"provider_id": "aws",
|
||||
"inserted_at": "2024-06-25T09:04:56.941Z",
|
||||
"updated_at": "2024-06-25T09:04:56.941Z",
|
||||
"alias": "prod",
|
||||
"connected": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.cloudaccount",
|
||||
"pk": "668bb77d-cfb4-4f4a-8c51-45beb16af4e9",
|
||||
"fields": {
|
||||
"account_id": "dbc452bd-b51c-4199-8b42-c1bc8164d45d",
|
||||
"type": "test",
|
||||
"groups": [
|
||||
"Production"
|
||||
],
|
||||
"resources": 148,
|
||||
"enable": true,
|
||||
"provider_id": "aws",
|
||||
"inserted_at": "2024-06-25T09:04:31.753Z",
|
||||
"updated_at": "2024-06-25T09:04:31.753Z",
|
||||
"alias": "marketplace",
|
||||
"connected": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.cloudaccount",
|
||||
"pk": "a50d3d9d-ac2d-47d1-ac6c-29c8d304072e",
|
||||
"fields": {
|
||||
"account_id": "fabc5b58-901b-4ba6-9901-4b2349acf492",
|
||||
"type": "test",
|
||||
"groups": [
|
||||
"Production",
|
||||
"Operation"
|
||||
],
|
||||
"resources": 356,
|
||||
"enable": true,
|
||||
"provider_id": "aws",
|
||||
"inserted_at": "2024-06-25T09:04:14.567Z",
|
||||
"updated_at": "2024-06-25T09:04:14.567Z",
|
||||
"alias": "management",
|
||||
"connected": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.cloudaccount",
|
||||
"pk": "d71c8ac6-074d-45a3-a7a7-0595ab5440bc",
|
||||
"fields": {
|
||||
"account_id": "cf51b7a0-687e-4a2a-bf3d-14f98dcb4c3f",
|
||||
"type": "test",
|
||||
"groups": [
|
||||
"Production"
|
||||
],
|
||||
"resources": 112,
|
||||
"enable": true,
|
||||
"provider_id": "aws",
|
||||
"inserted_at": "2024-06-25T09:04:44.431Z",
|
||||
"updated_at": "2024-06-25T09:04:44.431Z",
|
||||
"alias": "platform",
|
||||
"connected": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.audit",
|
||||
"pk": "0f712e8e-b419-40b4-9d85-65ef723fc1b9",
|
||||
"fields": {
|
||||
"aws_account_id": "fabc5b58-901b-4ba6-9901-4b2349acf492",
|
||||
"audit_complete": true,
|
||||
"inserted_at": "2024-06-25T09:05:37.352Z",
|
||||
"updated_at": "2024-06-25T09:05:37.352Z",
|
||||
"audit_duration": "00:06:30"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.audit",
|
||||
"pk": "1bf18315-34e6-4912-8e8e-3e4650e4653e",
|
||||
"fields": {
|
||||
"aws_account_id": "dbc452bd-b51c-4199-8b42-c1bc8164d45d",
|
||||
"audit_complete": true,
|
||||
"inserted_at": "2024-06-25T09:05:43.601Z",
|
||||
"updated_at": "2024-06-25T09:05:43.601Z",
|
||||
"audit_duration": "00:08:00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.audit",
|
||||
"pk": "48605239-58ee-4dbf-8a46-3ef42b270a19",
|
||||
"fields": {
|
||||
"aws_account_id": "cf9d2400-9a10-4c7b-a3be-6215c41b619e",
|
||||
"audit_complete": true,
|
||||
"inserted_at": "2024-06-25T09:05:29.533Z",
|
||||
"updated_at": "2024-06-25T09:05:29.533Z",
|
||||
"audit_duration": "00:06:12"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.audit",
|
||||
"pk": "955c1a4f-496a-4df5-96c8-578280b7a914",
|
||||
"fields": {
|
||||
"aws_account_id": "cf51b7a0-687e-4a2a-bf3d-14f98dcb4c3f",
|
||||
"audit_complete": true,
|
||||
"inserted_at": "2024-06-25T09:05:49.405Z",
|
||||
"updated_at": "2024-06-25T09:05:49.405Z",
|
||||
"audit_duration": "00:06:40"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "api.audit",
|
||||
"pk": "ccbedd87-d64e-42dd-86d0-15ac60ade56d",
|
||||
"fields": {
|
||||
"aws_account_id": "c0bbdcd2-c69e-4bdf-bea7-8db6524fd205",
|
||||
"audit_complete": true,
|
||||
"inserted_at": "2024-06-25T09:06:00.463Z",
|
||||
"updated_at": "2024-06-25T09:06:00.463Z",
|
||||
"audit_duration": "00:07:12"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,65 +0,0 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-24 10:11
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Account',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('token', models.CharField(max_length=255)),
|
||||
('inserted_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('card_brand', models.CharField(max_length=255)),
|
||||
('card_exp_month', models.IntegerField()),
|
||||
('card_exp_year', models.IntegerField()),
|
||||
('card_last4', models.CharField(max_length=4)),
|
||||
('onboarding_required', models.BooleanField()),
|
||||
('onboarding_step', models.CharField(max_length=255)),
|
||||
('onboarding_completed_at', models.DateTimeField()),
|
||||
('aws_account_id', models.CharField(max_length=255)),
|
||||
('off_boarded', models.BooleanField()),
|
||||
('billing_type', models.CharField(max_length=255)),
|
||||
('billing_cancel_at', models.DateTimeField()),
|
||||
('billing_current_period_end_at', models.DateTimeField()),
|
||||
('billing_free_resource_count', models.IntegerField()),
|
||||
('grafana_org_name', models.CharField(max_length=255)),
|
||||
('billing_email', models.CharField(max_length=255)),
|
||||
('scan_window_start_at', models.DateTimeField()),
|
||||
('company_name', models.CharField(max_length=255)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'accounts',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CloudAccount',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type', models.CharField(max_length=255)),
|
||||
('enable', models.BooleanField()),
|
||||
('provider_id', models.CharField(max_length=255)),
|
||||
('provider_data', models.JSONField()),
|
||||
('inserted_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('connected', models.BooleanField()),
|
||||
('last_checked_at', models.DateTimeField()),
|
||||
('alias', models.CharField(max_length=255)),
|
||||
('scanner_configuration', models.JSONField()),
|
||||
('account_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.account')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'cloud_accounts',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,54 +0,0 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-25 08:56
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='cloudaccount',
|
||||
name='groups',
|
||||
field=models.JSONField(default=list),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cloudaccount',
|
||||
name='resources',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='account',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cloudaccount',
|
||||
name='account_id',
|
||||
field=models.ForeignKey(db_column='account_id', on_delete=django.db.models.deletion.CASCADE, to='api.account', unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cloudaccount',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Audit',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('audit_complete', models.BooleanField(default=True)),
|
||||
('inserted_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('audit_duration', models.DurationField()),
|
||||
('aws_account_id', models.ForeignKey(db_column='aws_account_id', on_delete=django.db.models.deletion.CASCADE, to='api.cloudaccount', to_field='account_id', unique=True)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'audits',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-25 08:57
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0002_cloudaccount_groups_cloudaccount_resources_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='audit',
|
||||
name='aws_account_id',
|
||||
field=models.OneToOneField(db_column='aws_account_id', on_delete=django.db.models.deletion.CASCADE, to='api.cloudaccount', to_field='account_id'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cloudaccount',
|
||||
name='account_id',
|
||||
field=models.OneToOneField(db_column='account_id', on_delete=django.db.models.deletion.CASCADE, to='api.account'),
|
||||
),
|
||||
]
|
||||
@@ -1,97 +0,0 @@
|
||||
# Generated by Django 5.0.6 on 2024-06-25 09:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0003_alter_audit_aws_account_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='billing_cancel_at',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='billing_current_period_end_at',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='billing_email',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='billing_free_resource_count',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='billing_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='card_brand',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='card_exp_month',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='card_exp_year',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='card_last4',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='company_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='grafana_org_name',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='off_boarded',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='onboarding_completed_at',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='onboarding_required',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='onboarding_step',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='account',
|
||||
name='token',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cloudaccount',
|
||||
name='alias',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cloudaccount',
|
||||
name='connected',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cloudaccount',
|
||||
name='last_checked_at',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cloudaccount',
|
||||
name='provider_data',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cloudaccount',
|
||||
name='scanner_configuration',
|
||||
),
|
||||
]
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 4.2.13 on 2024-06-26 07:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0004_remove_account_billing_cancel_at_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='cloudaccount',
|
||||
name='alias',
|
||||
field=models.CharField(max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cloudaccount',
|
||||
name='connected',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -1,57 +0,0 @@
|
||||
import uuid
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Account(models.Model):
|
||||
class Meta:
|
||||
db_table = "accounts"
|
||||
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
name = models.CharField(max_length=255)
|
||||
inserted_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
aws_account_id = models.CharField(max_length=255)
|
||||
scan_window_start_at = models.DateTimeField()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} - {self.id}"
|
||||
|
||||
|
||||
class CloudAccount(models.Model):
|
||||
class Meta:
|
||||
db_table = "cloud_accounts"
|
||||
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
account_id = models.OneToOneField(
|
||||
Account, on_delete=models.CASCADE, db_column="account_id", to_field="id"
|
||||
)
|
||||
type = models.CharField(max_length=255)
|
||||
groups = models.JSONField(default=list)
|
||||
resources = models.IntegerField(default=0)
|
||||
enable = models.BooleanField()
|
||||
alias = models.CharField(max_length=255, null=True)
|
||||
connected = models.BooleanField(default=False)
|
||||
provider_id = models.CharField(max_length=255)
|
||||
inserted_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.id} ({self.provider_id})"
|
||||
|
||||
|
||||
class Audit(models.Model):
|
||||
class Meta:
|
||||
db_table = "audits"
|
||||
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
aws_account_id = models.OneToOneField(
|
||||
CloudAccount, on_delete=models.CASCADE, db_column="aws_account_id", to_field="account_id"
|
||||
)
|
||||
audit_complete = models.BooleanField(default=True)
|
||||
inserted_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
audit_duration = models.DurationField()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.id} - {self.aws_account_id}"
|
||||
@@ -1,31 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import Account, CloudAccount, Audit
|
||||
|
||||
|
||||
class AccountSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Account
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class CloudAccountSerializer(serializers.ModelSerializer):
|
||||
aws_account_id = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = CloudAccount
|
||||
fields = "__all__"
|
||||
extra_fields = ["aws_account_id"]
|
||||
|
||||
def get_aws_account_id(self, obj):
|
||||
return obj.account_id.aws_account_id
|
||||
|
||||
|
||||
class AuditSerializer(serializers.ModelSerializer):
|
||||
account_id = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Audit
|
||||
fields = ['id', 'audit_complete', 'inserted_at', 'updated_at', 'audit_duration', 'account_id']
|
||||
|
||||
def get_account_id(self, obj):
|
||||
return obj.aws_account_id.account_id.id
|
||||
@@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -1,8 +0,0 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import AuditListView, CloudAccountListView
|
||||
|
||||
urlpatterns = [
|
||||
path('providers/<str:provider_id>/accounts', CloudAccountListView.as_view(), name='cloud-account-list'),
|
||||
path('providers/aws/audits', AuditListView.as_view(), name='aws-audit-list'),
|
||||
]
|
||||
@@ -1,27 +0,0 @@
|
||||
from rest_framework import generics
|
||||
from .models import Account, CloudAccount, Audit
|
||||
from .serializers import AccountSerializer, CloudAccountSerializer, AuditSerializer
|
||||
|
||||
|
||||
class AccountListView(generics.ListAPIView):
|
||||
queryset = Account.objects.all()
|
||||
serializer_class = AccountSerializer
|
||||
|
||||
|
||||
class CloudAccountListView(generics.ListAPIView):
|
||||
serializer_class = CloudAccountSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
provider_id = self.kwargs.get('provider_id')
|
||||
return CloudAccount.objects.filter(provider_id=provider_id)
|
||||
|
||||
|
||||
class AuditListView(generics.ListAPIView):
|
||||
serializer_class = AuditSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Audit.objects.all()
|
||||
account_id = self.request.query_params.get('account_id')
|
||||
if account_id:
|
||||
queryset = queryset.filter(aws_account_id__account_id=account_id)
|
||||
return queryset
|
||||
@@ -1,7 +0,0 @@
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'be_poc.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
@@ -1,119 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-h72a3mgf$l$1uqnjf0bbbz9fbmuotlff7ya50+hlao)fga=3dp'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['localhost', 'steady-separately-marlin.ngrok-free.app']
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
'rest_framework',
|
||||
'api',
|
||||
'corsheaders',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
|
||||
'corsheaders.middleware.CorsMiddleware'
|
||||
]
|
||||
|
||||
CORS_ALLOW_ALL_ORIGINS = True
|
||||
|
||||
ROOT_URLCONF = 'be_poc.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'be_poc.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
@@ -1,7 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/v1/', include('api.urls_v1')),
|
||||
]
|
||||
@@ -1,7 +0,0 @@
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'be_poc.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'be_poc.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Button } from "@nextui-org/react";
|
||||
import { useState } from "react";
|
||||
|
||||
export const Counter = () => {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
+13
-82
@@ -2,19 +2,8 @@ import * as React from "react";
|
||||
|
||||
import { IconSvgProps } from "@/types";
|
||||
|
||||
export const Logo: React.FC<IconSvgProps> = ({
|
||||
size = 36,
|
||||
width,
|
||||
height,
|
||||
...props
|
||||
}) => (
|
||||
<svg
|
||||
fill="none"
|
||||
height={size || height}
|
||||
viewBox="0 0 32 32"
|
||||
width={size || width}
|
||||
{...props}
|
||||
>
|
||||
export const Logo: React.FC<IconSvgProps> = ({ size = 36, width, height, ...props }) => (
|
||||
<svg fill="none" height={size || height} viewBox="0 0 32 32" width={size || width} {...props}>
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
d="M17.6482 10.1305L15.8785 7.02583L7.02979 22.5499H10.5278L17.6482 10.1305ZM19.8798 14.0457L18.11 17.1983L19.394 19.4511H16.8453L15.1056 22.5499H24.7272L19.8798 14.0457Z"
|
||||
@@ -24,19 +13,9 @@ export const Logo: React.FC<IconSvgProps> = ({
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const DiscordIcon: React.FC<IconSvgProps> = ({
|
||||
size = 24,
|
||||
width,
|
||||
height,
|
||||
...props
|
||||
}) => {
|
||||
export const DiscordIcon: React.FC<IconSvgProps> = ({ size = 24, width, height, ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
height={size || height}
|
||||
viewBox="0 0 24 24"
|
||||
width={size || width}
|
||||
{...props}
|
||||
>
|
||||
<svg height={size || height} viewBox="0 0 24 24" width={size || width} {...props}>
|
||||
<path
|
||||
d="M14.82 4.26a10.14 10.14 0 0 0-.53 1.1 14.66 14.66 0 0 0-4.58 0 10.14 10.14 0 0 0-.53-1.1 16 16 0 0 0-4.13 1.3 17.33 17.33 0 0 0-3 11.59 16.6 16.6 0 0 0 5.07 2.59A12.89 12.89 0 0 0 8.23 18a9.65 9.65 0 0 1-1.71-.83 3.39 3.39 0 0 0 .42-.33 11.66 11.66 0 0 0 10.12 0q.21.18.42.33a10.84 10.84 0 0 1-1.71.84 12.41 12.41 0 0 0 1.08 1.78 16.44 16.44 0 0 0 5.06-2.59 17.22 17.22 0 0 0-3-11.59 16.09 16.09 0 0 0-4.09-1.35zM8.68 14.81a1.94 1.94 0 0 1-1.8-2 1.93 1.93 0 0 1 1.8-2 1.93 1.93 0 0 1 1.8 2 1.93 1.93 0 0 1-1.8 2zm6.64 0a1.94 1.94 0 0 1-1.8-2 1.93 1.93 0 0 1 1.8-2 1.92 1.92 0 0 1 1.8 2 1.92 1.92 0 0 1-1.8 2z"
|
||||
fill="currentColor"
|
||||
@@ -45,19 +24,9 @@ export const DiscordIcon: React.FC<IconSvgProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const TwitterIcon: React.FC<IconSvgProps> = ({
|
||||
size = 24,
|
||||
width,
|
||||
height,
|
||||
...props
|
||||
}) => {
|
||||
export const TwitterIcon: React.FC<IconSvgProps> = ({ size = 24, width, height, ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
height={size || height}
|
||||
viewBox="0 0 24 24"
|
||||
width={size || width}
|
||||
{...props}
|
||||
>
|
||||
<svg height={size || height} viewBox="0 0 24 24" width={size || width} {...props}>
|
||||
<path
|
||||
d="M19.633 7.997c.013.175.013.349.013.523 0 5.325-4.053 11.461-11.46 11.461-2.282 0-4.402-.661-6.186-1.809.324.037.636.05.973.05a8.07 8.07 0 0 0 5.001-1.721 4.036 4.036 0 0 1-3.767-2.793c.249.037.499.062.761.062.361 0 .724-.05 1.061-.137a4.027 4.027 0 0 1-3.23-3.953v-.05c.537.299 1.16.486 1.82.511a4.022 4.022 0 0 1-1.796-3.354c0-.748.199-1.434.548-2.032a11.457 11.457 0 0 0 8.306 4.215c-.062-.3-.1-.611-.1-.923a4.026 4.026 0 0 1 4.028-4.028c1.16 0 2.207.486 2.943 1.272a7.957 7.957 0 0 0 2.556-.973 4.02 4.02 0 0 1-1.771 2.22 8.073 8.073 0 0 0 2.319-.624 8.645 8.645 0 0 1-2.019 2.083z"
|
||||
fill="currentColor"
|
||||
@@ -66,19 +35,9 @@ export const TwitterIcon: React.FC<IconSvgProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const GithubIcon: React.FC<IconSvgProps> = ({
|
||||
size = 24,
|
||||
width,
|
||||
height,
|
||||
...props
|
||||
}) => {
|
||||
export const GithubIcon: React.FC<IconSvgProps> = ({ size = 24, width, height, ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
height={size || height}
|
||||
viewBox="0 0 24 24"
|
||||
width={size || width}
|
||||
{...props}
|
||||
>
|
||||
<svg height={size || height} viewBox="0 0 24 24" width={size || width} {...props}>
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
d="M12.026 2c-5.509 0-9.974 4.465-9.974 9.974 0 4.406 2.857 8.145 6.821 9.465.499.09.679-.217.679-.481 0-.237-.008-.865-.011-1.696-2.775.602-3.361-1.338-3.361-1.338-.452-1.152-1.107-1.459-1.107-1.459-.905-.619.069-.605.069-.605 1.002.07 1.527 1.028 1.527 1.028.89 1.524 2.336 1.084 2.902.829.091-.645.351-1.085.635-1.334-2.214-.251-4.542-1.107-4.542-4.93 0-1.087.389-1.979 1.024-2.675-.101-.253-.446-1.268.099-2.64 0 0 .837-.269 2.742 1.021a9.582 9.582 0 0 1 2.496-.336 9.554 9.554 0 0 1 2.496.336c1.906-1.291 2.742-1.021 2.742-1.021.545 1.372.203 2.387.099 2.64.64.696 1.024 1.587 1.024 2.675 0 3.833-2.33 4.675-4.552 4.922.355.308.675.916.675 1.846 0 1.334-.012 2.41-.012 2.737 0 .267.178.577.687.479C19.146 20.115 22 16.379 22 11.974 22 6.465 17.535 2 12.026 2z"
|
||||
@@ -89,12 +48,7 @@ export const GithubIcon: React.FC<IconSvgProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const MoonFilledIcon = ({
|
||||
size = 24,
|
||||
width,
|
||||
height,
|
||||
...props
|
||||
}: IconSvgProps) => (
|
||||
export const MoonFilledIcon = ({ size = 24, width, height, ...props }: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
@@ -111,12 +65,7 @@ export const MoonFilledIcon = ({
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const SunFilledIcon = ({
|
||||
size = 24,
|
||||
width,
|
||||
height,
|
||||
...props
|
||||
}: IconSvgProps) => (
|
||||
export const SunFilledIcon = ({ size = 24, width, height, ...props }: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
@@ -133,12 +82,7 @@ export const SunFilledIcon = ({
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const HeartFilledIcon = ({
|
||||
size = 24,
|
||||
width,
|
||||
height,
|
||||
...props
|
||||
}: IconSvgProps) => (
|
||||
export const HeartFilledIcon = ({ size = 24, width, height, ...props }: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
@@ -176,13 +120,7 @@ export const SearchIcon = (props: IconSvgProps) => (
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<path
|
||||
d="M22 22L20 20"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<path d="M22 22L20 20" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -190,14 +128,7 @@ export const NextUILogo: React.FC<IconSvgProps> = (props) => {
|
||||
const { width, height = 40 } = props;
|
||||
|
||||
return (
|
||||
<svg
|
||||
fill="none"
|
||||
height={height}
|
||||
viewBox="0 0 161 32"
|
||||
width={width}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<svg fill="none" height={height} viewBox="0 0 161 32" width={width} xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
className="fill-black dark:fill-white"
|
||||
d="M55.6827 5V26.6275H53.7794L41.1235 8.51665H40.9563V26.6275H39V5H40.89L53.5911 23.1323H53.7555V5H55.6827ZM67.4831 26.9663C66.1109 27.0019 64.7581 26.6329 63.5903 25.9044C62.4852 25.185 61.6054 24.1633 61.0537 22.9582C60.4354 21.5961 60.1298 20.1106 60.1598 18.6126C60.132 17.1113 60.4375 15.6228 61.0537 14.2563C61.5954 13.0511 62.4525 12.0179 63.5326 11.268C64.6166 10.5379 65.8958 10.16 67.1986 10.1852C68.0611 10.1837 68.9162 10.3468 69.7187 10.666C70.5398 10.9946 71.2829 11.4948 71.8992 12.1337C72.5764 12.8435 73.0985 13.6889 73.4318 14.6152C73.8311 15.7483 74.0226 16.9455 73.9968 18.1479V19.0773H63.2262V17.4194H72.0935C72.1083 16.4456 71.8952 15.4821 71.4714 14.6072C71.083 13.803 70.4874 13.1191 69.7472 12.6272C68.9887 12.1348 68.1022 11.8812 67.2006 11.8987C66.2411 11.8807 65.3005 12.1689 64.5128 12.7223C63.7332 13.2783 63.1083 14.0275 62.6984 14.8978C62.2582 15.8199 62.0314 16.831 62.0352 17.8546V18.8476C62.009 20.0078 62.2354 21.1595 62.6984 22.2217C63.1005 23.1349 63.7564 23.9108 64.5864 24.4554C65.4554 24.9973 66.4621 25.2717 67.4831 25.2448C68.1676 25.2588 68.848 25.1368 69.4859 24.8859C70.0301 24.6666 70.5242 24.3376 70.9382 23.919C71.3183 23.5345 71.6217 23.0799 71.8322 22.5799L73.5995 23.1604C73.3388 23.8697 72.9304 24.5143 72.4019 25.0506C71.8132 25.6529 71.1086 26.1269 70.3314 26.4434C69.4258 26.8068 68.4575 26.9846 67.4831 26.9663V26.9663ZM78.8233 10.4075L82.9655 17.325L87.1076 10.4075H89.2683L84.1008 18.5175L89.2683 26.6275H87.103L82.9608 19.9317L78.8193 26.6275H76.6647L81.7711 18.5169L76.6647 10.4062L78.8233 10.4075ZM99.5142 10.4075V12.0447H91.8413V10.4075H99.5142ZM94.2427 6.52397H96.1148V22.3931C96.086 22.9446 96.2051 23.4938 96.4597 23.9827C96.6652 24.344 96.9805 24.629 97.3589 24.7955C97.7328 24.9548 98.1349 25.0357 98.5407 25.0332C98.7508 25.0359 98.9607 25.02 99.168 24.9857C99.3422 24.954 99.4956 24.9205 99.6283 24.8853L100.026 26.5853C99.8062 26.6672 99.5805 26.7327 99.3511 26.7815C99.0274 26.847 98.6977 26.8771 98.3676 26.8712C97.6854 26.871 97.0119 26.7156 96.3973 26.4166C95.7683 26.1156 95.2317 25.6485 94.8442 25.0647C94.4214 24.4018 94.2097 23.6242 94.2374 22.8363L94.2427 6.52397ZM118.398 5H120.354V19.3204C120.376 20.7052 120.022 22.0697 119.328 23.2649C118.644 24.4235 117.658 25.3698 116.477 26.0001C115.168 26.6879 113.708 27.0311 112.232 26.9978C110.759 27.029 109.302 26.6835 107.996 25.9934C106.815 25.362 105.827 24.4161 105.141 23.2582C104.447 22.0651 104.092 20.7022 104.115 19.319V5H106.08V19.1831C106.061 20.2559 106.324 21.3147 106.843 22.2511C107.349 23.1459 108.094 23.8795 108.992 24.3683C109.993 24.9011 111.111 25.1664 112.242 25.139C113.373 25.1656 114.493 24.9003 115.495 24.3683C116.395 23.8815 117.14 23.1475 117.644 22.2511C118.16 21.3136 118.421 20.2553 118.402 19.1831L118.398 5ZM128 5V26.6275H126.041V5H128Z"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Link,
|
||||
Navbar as NextUINavbar,
|
||||
NavbarBrand,
|
||||
NavbarContent,
|
||||
NavbarItem,
|
||||
NavbarMenu,
|
||||
NavbarMenuToggle,
|
||||
NavbarBrand,
|
||||
NavbarMenuItem,
|
||||
Link,
|
||||
NavbarMenuToggle,
|
||||
} from "@nextui-org/react";
|
||||
import React from "react";
|
||||
|
||||
export const Navbar = () => {
|
||||
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
|
||||
@@ -20,9 +20,7 @@ export const Navbar = () => {
|
||||
return (
|
||||
<NextUINavbar onMenuOpenChange={setIsMenuOpen}>
|
||||
<NavbarContent>
|
||||
<NavbarMenuToggle
|
||||
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
|
||||
/>
|
||||
<NavbarMenuToggle aria-label={isMenuOpen ? "Close menu" : "Open menu"} />
|
||||
<NavbarBrand>
|
||||
<p className="font-bold text-inherit">PROWLER</p>
|
||||
</NavbarBrand>
|
||||
|
||||
@@ -26,15 +26,7 @@ export const title = tv({
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
color: [
|
||||
"violet",
|
||||
"yellow",
|
||||
"blue",
|
||||
"cyan",
|
||||
"green",
|
||||
"pink",
|
||||
"foreground",
|
||||
],
|
||||
color: ["violet", "yellow", "blue", "cyan", "green", "pink", "foreground"],
|
||||
class: "bg-clip-text text-transparent bg-gradient-to-b",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import { VisuallyHidden } from "@react-aria/visually-hidden";
|
||||
import { SwitchProps, useSwitch } from "@nextui-org/react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useIsSSR } from "@react-aria/ssr";
|
||||
import { VisuallyHidden } from "@react-aria/visually-hidden";
|
||||
import clsx from "clsx";
|
||||
import { useTheme } from "next-themes";
|
||||
import { FC } from "react";
|
||||
|
||||
import { SunFilledIcon, MoonFilledIcon } from "@/components/icons";
|
||||
import { MoonFilledIcon, SunFilledIcon } from "@/components/icons";
|
||||
|
||||
export interface ThemeSwitchProps {
|
||||
className?: string;
|
||||
classNames?: SwitchProps["classNames"];
|
||||
}
|
||||
|
||||
export const ThemeSwitch: FC<ThemeSwitchProps> = ({
|
||||
className,
|
||||
classNames,
|
||||
}) => {
|
||||
export const ThemeSwitch: FC<ThemeSwitchProps> = ({ className, classNames }) => {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const isSSR = useIsSSR();
|
||||
|
||||
@@ -25,14 +22,7 @@ export const ThemeSwitch: FC<ThemeSwitchProps> = ({
|
||||
theme === "light" ? setTheme("dark") : setTheme("light");
|
||||
};
|
||||
|
||||
const {
|
||||
Component,
|
||||
slots,
|
||||
isSelected,
|
||||
getBaseProps,
|
||||
getInputProps,
|
||||
getWrapperProps,
|
||||
} = useSwitch({
|
||||
const { Component, slots, isSelected, getBaseProps, getInputProps, getWrapperProps } = useSwitch({
|
||||
isSelected: theme === "light" || isSSR,
|
||||
"aria-label": `Switch to ${theme === "light" || isSSR ? "dark" : "light"} mode`,
|
||||
onChange,
|
||||
@@ -41,11 +31,7 @@ export const ThemeSwitch: FC<ThemeSwitchProps> = ({
|
||||
return (
|
||||
<Component
|
||||
{...getBaseProps({
|
||||
className: clsx(
|
||||
"px-px transition-opacity hover:opacity-80 cursor-pointer",
|
||||
className,
|
||||
classNames?.base,
|
||||
),
|
||||
className: clsx("px-px transition-opacity hover:opacity-80 cursor-pointer", className, classNames?.base),
|
||||
})}
|
||||
>
|
||||
<VisuallyHidden>
|
||||
@@ -70,11 +56,7 @@ export const ThemeSwitch: FC<ThemeSwitchProps> = ({
|
||||
),
|
||||
})}
|
||||
>
|
||||
{!isSelected || isSSR ? (
|
||||
<SunFilledIcon size={22} />
|
||||
) : (
|
||||
<MoonFilledIcon size={22} />
|
||||
)}
|
||||
{!isSelected || isSSR ? <SunFilledIcon size={22} /> : <MoonFilledIcon size={22} />}
|
||||
</div>
|
||||
</Component>
|
||||
);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
services:
|
||||
django-be-poc:
|
||||
image: django-be-poc
|
||||
build:
|
||||
context: "./"
|
||||
ports:
|
||||
- "8080:8000"
|
||||
Reference in New Issue
Block a user