diff --git a/.gitignore b/.gitignore index e2b5290..d470200 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,12 @@ -# Created by https://www.toptal.com/developers/gitignore/api/go -# Edit at https://www.toptal.com/developers/gitignore?templates=go +# Created by https://www.toptal.com/developers/gitignore/api/go,nextjs +# Edit at https://www.toptal.com/developers/gitignore?templates=go,nextjs ### Go ### # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore # # Binaries for programs and plugins +*.exe *.exe~ *.dll *.so @@ -23,4 +24,40 @@ # Go workspace file go.work -# End of https://www.toptal.com/developers/gitignore/api/go +### NextJS ### +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# End of https://www.toptal.com/developers/gitignore/api/go,nextjs diff --git a/console/app/globals.css b/console/app/globals.css new file mode 100644 index 0000000..02687bd --- /dev/null +++ b/console/app/globals.css @@ -0,0 +1,123 @@ +@import 'tailwindcss'; +@import 'tw-animate-css'; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/console/app/layout.tsx b/console/app/layout.tsx new file mode 100644 index 0000000..a262171 --- /dev/null +++ b/console/app/layout.tsx @@ -0,0 +1,31 @@ +import type { Metadata } from 'next' +import { GeistSans } from 'geist/font/sans' +import { GeistMono } from 'geist/font/mono' +import './globals.css' + +export const metadata: Metadata = { + title: 'v0 App', + description: 'Created with v0', + generator: 'v0.app', +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + + + + {children} + + ) +} diff --git a/console/app/page.tsx b/console/app/page.tsx new file mode 100644 index 0000000..d5ed09f --- /dev/null +++ b/console/app/page.tsx @@ -0,0 +1,5 @@ +import { HomecloudConsole } from "@/components/homecloud-console" + +export default function HomePage() { + return +} diff --git a/console/components.json b/console/components.json new file mode 100644 index 0000000..335484f --- /dev/null +++ b/console/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/console/components/homecloud-console.tsx b/console/components/homecloud-console.tsx new file mode 100644 index 0000000..7b8b1e1 --- /dev/null +++ b/console/components/homecloud-console.tsx @@ -0,0 +1,504 @@ +"use client" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + Search, + Bell, + HelpCircle, + Settings, + User, + ChevronDown, + Cloud, + Plus, + MoreHorizontal, + Database, + Server, + Shield, + Zap, + BarChart3, + ExternalLink, + Network, + Users, +} from "lucide-react" + +import { S3ServicePage } from "./services/s3-service" +import { EC2ServicePage } from "./services/ec2-service" +import { VPCServicePage } from "./services/vpc-service" +import { RDSServicePage } from "./services/rds-service" +import { LambdaServicePage } from "./services/lambda-service" +import { CloudWatchServicePage } from "./services/cloudwatch-service" +import { IAMServicePage } from "./services/iam-service" + +export function HomecloudConsole() { + const [searchQuery, setSearchQuery] = useState("") + const [currentView, setCurrentView] = useState("home") + + const accountData = { + accountId: "123456789012", + accountName: "Production Account", + userName: "john.smith@company.com", + region: "us-east-1", + regionName: "US East (N. Virginia)", + organizationId: "o-abc123def456", + billingCurrency: "USD", + } + + const recentlyVisited = [ + { name: "S3", icon: Database, color: "bg-green-500", id: "s3" }, + { name: "VPC", icon: Shield, color: "bg-purple-500", id: "vpc" }, + { name: "EC2", icon: Server, color: "bg-orange-500", id: "ec2" }, + { name: "RDS", icon: Database, color: "bg-blue-500", id: "rds" }, + { name: "Lambda", icon: Zap, color: "bg-yellow-500", id: "lambda" }, + { name: "CloudWatch", icon: BarChart3, color: "bg-indigo-500", id: "cloudwatch" }, + { name: "IAM", icon: Users, color: "bg-red-500", id: "iam" }, + ] + + const serviceCategories = [ + { + category: "Compute", + services: [ + { name: "EC2", description: "Virtual Servers in the Cloud", id: "ec2", icon: Server }, + { name: "Lambda", description: "Run Code without Thinking about Servers", id: "lambda", icon: Zap }, + ], + }, + { + category: "Storage", + services: [{ name: "S3", description: "Scalable Storage in the Cloud", id: "s3", icon: Database }], + }, + { + category: "Database", + services: [{ name: "RDS", description: "Managed Relational Database Service", id: "rds", icon: Database }], + }, + { + category: "Networking & Content Delivery", + services: [{ name: "VPC", description: "Isolated Cloud Resources", id: "vpc", icon: Network }], + }, + { + category: "Management & Governance", + services: [ + { name: "CloudWatch", description: "Monitoring and Observability", id: "cloudwatch", icon: BarChart3 }, + ], + }, + { + category: "Security, Identity, & Compliance", + services: [{ name: "IAM", description: "Identity and Access Management", id: "iam", icon: Users }], + }, + ] + + const handleServiceClick = (serviceId: string) => { + setCurrentView(serviceId) + } + + const handleBackToHome = () => { + setCurrentView("home") + } + + const renderCurrentView = () => { + switch (currentView) { + case "s3": + return + case "ec2": + return + case "vpc": + return + case "rds": + return + case "lambda": + return + case "cloudwatch": + return + case "iam": + return + default: + return renderDashboard() + } + } + + const renderDashboard = () => ( + <> + {/* Console Home Header */} +
+
+
+

Console Home

+ + beta + +
+
+ + +
+
+
+ + {/* Main Dashboard Grid */} +
+
+ {/* Recently visited */} + + +
+
+ Recently visited + + info + +
+ +
+ + {recentlyVisited.slice(0, 4).map((service, index) => ( +
handleServiceClick(service.id)} + > +
+ +
+ {service.name} +
+ ))} +
+ +
+
+
+ + {/* Applications */} + + +
+
+ Applications (3) + + info + +
+
+ + +
+
+ +
+
+
+
web-app-prod
+
Running • 3 resources
+
+ + Healthy + +
+
+
+
api-service
+
Running • 5 resources
+
+ + Healthy + +
+
+
+
data-pipeline
+
Stopped • 2 resources
+
+ + Stopped + +
+
+
+ +
+
+
+ + {/* Quick Start */} + + +
+
+ Quick Start +
+ +
+ +
+ +
+

Launch a virtual server

+

Get started with EC2 instances for your applications.

+
+
+
+ +
+

Store and retrieve data

+

Use S3 for scalable object storage solutions.

+
+
+
+
+ + {/* System Health */} + + +
+
+ System Health + + info + +
+ +
+ +
+
+ Open issues + Past 7 days +
+
0
+
+
+
+ Scheduled maintenance + Upcoming +
+
1
+

Network upgrade - Dec 15, 2024

+
+
+ +
+
+
+ + {/* Cost and usage */} + + +
+
+ Cost and usage + + info + +
+ +
+ +
+
+
+ This month + Dec 2024 +
+
$247.83
+

↓ 12% from last month

+
+
+
+ Forecasted + End of month +
+
$312.45
+
+
+ +
+
+
+
+ + {/* Security Overview */} + + +
+
+ Security Overview + + info + +
+ +
+ +
+ Security alerts + + All clear + +
+
+ Active policies + 12 +
+
+ MFA enabled users + 8/10 +
+
+ +
+
+
+
+
+ + ) + + return ( +
+ {/* Top Navigation Bar */} +
+
+
+
+ + homecloud +
+ + + + + + All services + + {serviceCategories.map((category, categoryIndex) => ( +
+ + {category.category} + + {category.services.map((service, serviceIndex) => ( + handleServiceClick(service.id)} + > + +
+
{service.name}
+
{service.description}
+
+
+ ))} + {categoryIndex < serviceCategories.length - 1 && } +
+ ))} +
+
+
+ + setSearchQuery(e.target.value)} + className="pl-10 w-80 bg-gray-800 border-gray-700 text-white placeholder-gray-400" + /> +
[Alt+S]
+
+
+ +
+ + + + + + + + + Select Region + + US East (N. Virginia) - us-east-1 + US West (Oregon) - us-west-2 + Europe (Ireland) - eu-west-1 + Asia Pacific (Tokyo) - ap-northeast-1 + + + + + + + + Account Information + +
+
{accountData.accountName}
+
Account ID: {accountData.accountId}
+
User: {accountData.userName}
+
Organization: {accountData.organizationId}
+
+ + Switch Account + Account Settings + Sign Out +
+
+ +
+
+
+ + {renderCurrentView()} +
+ ) +} diff --git a/console/components/services/cloudwatch-service.tsx b/console/components/services/cloudwatch-service.tsx new file mode 100644 index 0000000..53bc65a --- /dev/null +++ b/console/components/services/cloudwatch-service.tsx @@ -0,0 +1,272 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { + ArrowLeft, + Search, + Plus, + MoreHorizontal, + BarChart3, + Bell, + AlertTriangle, + Activity, + RefreshCw, + Eye, + Settings, +} from "lucide-react" + +interface CloudWatchServicePageProps { + onBack: () => void +} + +export function CloudWatchServicePage({ onBack }: CloudWatchServicePageProps) { + const alarms = [ + { + name: "High CPU Usage", + status: "OK", + metric: "EC2 CPU Utilization", + threshold: "> 80%", + state: "OK", + updated: "2 hours ago", + }, + { + name: "Database Connections", + status: "ALARM", + metric: "RDS DatabaseConnections", + threshold: "> 90", + state: "ALARM", + updated: "15 minutes ago", + }, + { + name: "Lambda Errors", + status: "OK", + metric: "Lambda Errors", + threshold: "> 5", + state: "OK", + updated: "1 hour ago", + }, + ] + + const getStatusColor = (status: string) => { + switch (status) { + case "OK": + return "bg-green-100 text-green-800" + case "ALARM": + return "bg-red-100 text-red-800" + case "INSUFFICIENT_DATA": + return "bg-yellow-100 text-yellow-800" + default: + return "bg-gray-100 text-gray-800" + } + } + + return ( + <> + {/* Service Header */} +
+
+
+ +
+
+ +

CloudWatch

+
+
+
+ + +
+
+
+ + {/* CloudWatch Dashboard */} +
+ {/* Quick Actions */} +
+ + + +

Create alarm

+

Monitor metrics

+
+
+ + + +

Create dashboard

+

Visualize metrics

+
+
+ + + +

View logs

+

Browse log groups

+
+
+ + + +

Insights

+

Query and analyze

+
+
+
+ + {/* Alarms List */} + + +
+ Alarms +
+
+ + +
+ +
+
+
+ +
+ + + + + + + + + + + + + {alarms.map((alarm, index) => ( + + + + + + + + + ))} + +
Alarm nameStatusMetricThresholdUpdatedActions
+ {alarm.name} + + {alarm.status} + {alarm.metric}{alarm.threshold}{alarm.updated} +
+ + + +
+
+
+
+
+ + {/* Monitoring Summary */} +
+ + + + + Alarm Summary + + + +
+
+ Total alarms + 3 +
+
+ In alarm + 1 +
+
+ OK + 2 +
+
+
+
+ + + + + + Dashboards + + + +
+
+ Custom dashboards + 2 +
+
+ Widgets + 12 +
+
+ Automatic dashboards + 5 +
+
+
+
+ + + + + + Logs + + + +
+
+ Log groups + 8 +
+
+ Log streams + 24 +
+
+ Stored data + 2.1 GB +
+
+
+
+
+
+ + ) +} diff --git a/console/components/services/ec2-service.tsx b/console/components/services/ec2-service.tsx new file mode 100644 index 0000000..5aaa995 --- /dev/null +++ b/console/components/services/ec2-service.tsx @@ -0,0 +1,289 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { + ArrowLeft, + Search, + Plus, + Server, + Play, + Square, + RotateCcw, + Settings, + RefreshCw, + Monitor, + HardDrive, + Cpu, +} from "lucide-react" + +interface EC2ServicePageProps { + onBack: () => void +} + +export function EC2ServicePage({ onBack }: EC2ServicePageProps) { + const instances = [ + { + id: "i-1234567890abcdef0", + name: "Web Server 1", + type: "t3.medium", + state: "running", + az: "ap-northeast-1a", + publicIp: "54.123.45.67", + privateIp: "10.0.1.15", + launched: "2024-01-15 10:30", + }, + { + id: "i-0987654321fedcba0", + name: "Database Server", + type: "r5.large", + state: "stopped", + az: "ap-northeast-1b", + publicIp: "-", + privateIp: "10.0.2.20", + launched: "2024-01-10 14:20", + }, + { + id: "i-abcdef1234567890", + name: "Load Balancer", + type: "t3.small", + state: "running", + az: "ap-northeast-1c", + publicIp: "52.198.76.54", + privateIp: "10.0.3.10", + launched: "2024-01-20 09:15", + }, + ] + + const getStateColor = (state: string) => { + switch (state) { + case "running": + return "bg-green-100 text-green-800" + case "stopped": + return "bg-red-100 text-red-800" + case "pending": + return "bg-yellow-100 text-yellow-800" + default: + return "bg-gray-100 text-gray-800" + } + } + + return ( + <> + {/* Service Header */} +
+
+
+ +
+
+ +

EC2

+
+
+
+ + +
+
+
+ + {/* EC2 Dashboard */} +
+ {/* Quick Actions */} +
+ + + +

Launch instance

+

Create a new EC2 instance

+
+
+ + + +

Create volume

+

Create EBS volume

+
+
+ + + +

View metrics

+

Monitor performance

+
+
+ + + +

Security groups

+

Manage firewall rules

+
+
+
+ + {/* Instances List */} + + +
+ Instances +
+
+ + +
+ +
+
+
+ +
+ + + + + + + + + + + + + + {instances.map((instance, index) => ( + + + + + + + + + + ))} + +
Instance IDNameInstance typeInstance stateAvailability ZonePublic IPv4Actions
+ + {instance.id} + + {instance.name}{instance.type} + {instance.state} + {instance.az}{instance.publicIp} +
+ {instance.state === "running" ? ( + + ) : ( + + )} + + +
+
+
+
+
+ + {/* Resource Summary */} +
+ + + + + Instance Summary + + + +
+
+ Running instances + 2 +
+
+ Stopped instances + 1 +
+
+ Total instances + 3 +
+
+
+
+ + + + + + Storage + + + +
+
+ EBS volumes + 5 +
+
+ Total storage + 250 GB +
+
+ Snapshots + 12 +
+
+
+
+ + + + + + Monitoring + + + +
+
+ CPU utilization + 23% +
+
+ Network in + 1.2 MB/s +
+
+ Network out + 0.8 MB/s +
+
+
+
+
+
+ + ) +} diff --git a/console/components/services/iam-service.tsx b/console/components/services/iam-service.tsx new file mode 100644 index 0000000..03a183a --- /dev/null +++ b/console/components/services/iam-service.tsx @@ -0,0 +1,324 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { + ArrowLeft, + Search, + Plus, + MoreHorizontal, + Shield, + Users, + Key, + FileText, + RefreshCw, + Settings, + Lock, + UserCheck, +} from "lucide-react" + +interface IAMServicePageProps { + onBack: () => void +} + +export function IAMServicePage({ onBack }: IAMServicePageProps) { + const users = [ + { + name: "admin-user", + groups: ["Administrators"], + policies: 3, + accessKeys: 1, + lastActivity: "2024-01-15 14:30", + mfa: true, + }, + { + name: "developer-john", + groups: ["Developers"], + policies: 2, + accessKeys: 2, + lastActivity: "2024-01-14 16:45", + mfa: false, + }, + { + name: "readonly-user", + groups: ["ReadOnly"], + policies: 1, + accessKeys: 0, + lastActivity: "2024-01-10 09:20", + mfa: true, + }, + ] + + const roles = [ + { name: "EC2-S3-Access-Role", type: "Service Role", policies: 2, lastUsed: "2024-01-15" }, + { name: "Lambda-Execution-Role", type: "Service Role", policies: 1, lastUsed: "2024-01-14" }, + { name: "Cross-Account-Role", type: "Cross-account", policies: 3, lastUsed: "2024-01-12" }, + ] + + return ( + <> + {/* Service Header */} +
+
+
+ +
+
+ +

IAM

+
+
+
+ + +
+
+
+ + {/* IAM Dashboard */} +
+ {/* Quick Actions */} +
+ + + +

Create user

+

Add new IAM user

+
+
+ + + +

Create role

+

Define service permissions

+
+
+ + + +

Create policy

+

Define permissions

+
+
+ + + +

Access analyzer

+

Review permissions

+
+
+
+ + {/* Users List */} + + +
+ Users +
+
+ + +
+ +
+
+
+ +
+ + + + + + + + + + + + + + {users.map((user, index) => ( + + + + + + + + + + ))} + +
User nameGroupsPoliciesAccess keysMFALast activityActions
+ {user.name} + {user.groups.join(", ")}{user.policies}{user.accessKeys} + + {user.mfa ? "Enabled" : "Disabled"} + + {user.lastActivity} +
+ + + +
+
+
+
+
+ + {/* Roles List */} + + +
+ Roles + +
+
+ +
+ + + + + + + + + + + + {roles.map((role, index) => ( + + + + + + + + ))} + +
Role nameTypePoliciesLast usedActions
+ {role.name} + {role.type}{role.policies}{role.lastUsed} +
+ + +
+
+
+
+
+ + {/* IAM Summary */} +
+ + + + + Users & Groups + + + +
+
+ Total users + 3 +
+
+ Groups + 3 +
+
+ MFA enabled + 2/3 +
+
+
+
+ + + + + + Roles & Policies + + + +
+
+ Total roles + 3 +
+
+ Custom policies + 8 +
+
+ AWS managed + 12 +
+
+
+
+ + + + + + Security + + + +
+
+ Access keys + 3 +
+
+ Unused keys + 0 +
+
+ Password policy + Strong +
+
+
+
+
+
+ + ) +} diff --git a/console/components/services/lambda-service.tsx b/console/components/services/lambda-service.tsx new file mode 100644 index 0000000..8ec5fb2 --- /dev/null +++ b/console/components/services/lambda-service.tsx @@ -0,0 +1,278 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { + ArrowLeft, + Search, + Plus, + MoreHorizontal, + Zap, + Play, + Settings, + RefreshCw, + Code, + Clock, + Activity, + FileText, +} from "lucide-react" + +interface LambdaServicePageProps { + onBack: () => void +} + +export function LambdaServicePage({ onBack }: LambdaServicePageProps) { + const functions = [ + { + name: "user-authentication", + runtime: "Node.js 18.x", + status: "Active", + lastModified: "2024-01-15 14:30", + timeout: "30s", + memory: "256 MB", + invocations: "1,247", + }, + { + name: "image-processor", + runtime: "Python 3.11", + status: "Active", + lastModified: "2024-01-12 09:15", + timeout: "5m", + memory: "1024 MB", + invocations: "89", + }, + { + name: "data-sync", + runtime: "Node.js 18.x", + status: "Inactive", + lastModified: "2024-01-08 16:45", + timeout: "15m", + memory: "512 MB", + invocations: "0", + }, + ] + + const getStatusColor = (status: string) => { + switch (status) { + case "Active": + return "bg-green-100 text-green-800" + case "Inactive": + return "bg-gray-100 text-gray-800" + case "Failed": + return "bg-red-100 text-red-800" + default: + return "bg-gray-100 text-gray-800" + } + } + + return ( + <> + {/* Service Header */} +
+
+
+ +
+
+ +

Lambda

+
+
+
+ + +
+
+
+ + {/* Lambda Dashboard */} +
+ {/* Quick Actions */} +
+ + + +

Create function

+

Build serverless function

+
+
+ + + +

Browse blueprints

+

Use pre-built templates

+
+
+ + + +

View metrics

+

Monitor performance

+
+
+ + + +

Layers

+

Manage shared code

+
+
+
+ + {/* Functions List */} + + +
+ Functions +
+
+ + +
+ +
+
+
+ +
+ + + + + + + + + + + + + + {functions.map((func, index) => ( + + + + + + + + + + ))} + +
Function nameRuntimeStatusLast modifiedMemoryInvocationsActions
+ {func.name} + {func.runtime} + {func.status} + {func.lastModified}{func.memory}{func.invocations} +
+ + + +
+
+
+
+
+ + {/* Lambda Summary */} +
+ + + + + Function Summary + + + +
+
+ Total functions + 3 +
+
+ Active functions + 2 +
+
+ Total invocations + 1,336 +
+
+
+
+ + + + + + Performance + + + +
+
+ Avg duration + 245ms +
+
+ Error rate + 0.2% +
+
+ Throttles + 0 +
+
+
+
+ + + + + + Resources + + + +
+
+ Layers + 2 +
+
+ Event sources + 5 +
+
+ Destinations + 1 +
+
+
+
+
+
+ + ) +} diff --git a/console/components/services/rds-service.tsx b/console/components/services/rds-service.tsx new file mode 100644 index 0000000..6ecffb2 --- /dev/null +++ b/console/components/services/rds-service.tsx @@ -0,0 +1,286 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { + ArrowLeft, + Search, + Plus, + Database, + Play, + Square, + RotateCcw, + Settings, + RefreshCw, + Activity, + HardDrive, +} from "lucide-react" + +interface RDSServicePageProps { + onBack: () => void +} + +export function RDSServicePage({ onBack }: RDSServicePageProps) { + const databases = [ + { + id: "myapp-prod-db", + engine: "MySQL", + version: "8.0.35", + status: "available", + instanceClass: "db.t3.medium", + storage: "100 GB", + endpoint: "myapp-prod-db.cluster-xyz.ap-northeast-1.rds.amazonaws.com", + created: "2024-01-15", + }, + { + id: "analytics-db", + engine: "PostgreSQL", + version: "15.4", + status: "stopped", + instanceClass: "db.r5.large", + storage: "500 GB", + endpoint: "analytics-db.xyz.ap-northeast-1.rds.amazonaws.com", + created: "2024-01-10", + }, + { + id: "cache-cluster", + engine: "Redis", + version: "7.0", + status: "available", + instanceClass: "cache.t3.micro", + storage: "1 GB", + endpoint: "cache-cluster.xyz.cache.amazonaws.com", + created: "2024-01-20", + }, + ] + + const getStatusColor = (status: string) => { + switch (status) { + case "available": + return "bg-green-100 text-green-800" + case "stopped": + return "bg-red-100 text-red-800" + case "creating": + return "bg-yellow-100 text-yellow-800" + default: + return "bg-gray-100 text-gray-800" + } + } + + return ( + <> + {/* Service Header */} +
+
+
+ +
+
+ +

RDS

+
+
+
+ + +
+
+
+ + {/* RDS Dashboard */} +
+ {/* Quick Actions */} +
+ + + +

Create database

+

Launch new RDS instance

+
+
+ + + +

Create snapshot

+

Backup your database

+
+
+ + + +

Performance insights

+

Monitor performance

+
+
+ + + +

Parameter groups

+

Manage configurations

+
+
+
+ + {/* Databases List */} + + +
+ Databases +
+
+ + +
+ +
+
+
+ +
+ + + + + + + + + + + + + {databases.map((db, index) => ( + + + + + + + + + ))} + +
DB identifierEngineStatusInstance classStorageActions
+ {db.id} + + {db.engine} {db.version} + + {db.status} + {db.instanceClass}{db.storage} +
+ {db.status === "available" ? ( + + ) : ( + + )} + + +
+
+
+
+
+ + {/* Database Summary */} +
+ + + + + Database Summary + + + +
+
+ Running databases + 2 +
+
+ Stopped databases + 1 +
+
+ Total storage + 601 GB +
+
+
+
+ + + + + + Backups + + + +
+
+ Automated backups + Enabled +
+
+ Manual snapshots + 5 +
+
+ Backup retention + 7 days +
+
+
+
+ + + + + + Performance + + + +
+
+ CPU utilization + 15% +
+
+ Connections + 12/100 +
+
+ Read IOPS + 245/sec +
+
+
+
+
+
+ + ) +} diff --git a/console/components/services/s3-service.tsx b/console/components/services/s3-service.tsx new file mode 100644 index 0000000..427c431 --- /dev/null +++ b/console/components/services/s3-service.tsx @@ -0,0 +1,212 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { + ArrowLeft, + Search, + Plus, + Database, + Folder, + FileText, + Download, + Upload, + Trash2, + Settings, + RefreshCw, +} from "lucide-react" + +interface S3ServicePageProps { + onBack: () => void +} + +export function S3ServicePage({ onBack }: S3ServicePageProps) { + const buckets = [ + { name: "my-app-assets", region: "ap-northeast-1", created: "2024-01-15", objects: 1247, size: "2.3 GB" }, + { name: "backup-storage", region: "ap-northeast-1", created: "2024-02-01", objects: 89, size: "456 MB" }, + { name: "logs-archive", region: "ap-northeast-1", created: "2024-01-20", objects: 3421, size: "8.7 GB" }, + ] + + return ( + <> + {/* Service Header */} +
+
+
+ +
+
+ +

S3

+
+
+
+ + +
+
+
+ + {/* S3 Dashboard */} +
+ {/* Quick Actions */} +
+ + + +

Upload files

+

Upload files to S3

+
+
+ + + +

Create bucket

+

Create a new S3 bucket

+
+
+ + + +

Bucket policies

+

Manage access policies

+
+
+ + + +

Access logs

+

View access logs

+
+
+
+ + {/* Buckets List */} + + +
+ Buckets +
+
+ + +
+ +
+
+
+ +
+ + + + + + + + + + + + + {buckets.map((bucket, index) => ( + + + + + + + + + ))} + +
NameRegionDate createdObjectsSizeActions
+
+ + + {bucket.name} + +
+
{bucket.region}{bucket.created}{bucket.objects.toLocaleString()}{bucket.size} +
+ + + +
+
+
+
+
+ + {/* Storage Analytics */} +
+ + + Storage usage + + +
+
+ Total storage + 11.5 GB +
+
+ Number of objects + 4,757 +
+
+ Number of buckets + 3 +
+
+
+
+ + + + Recent activity + + +
+
+
+ Uploaded 15 files to my-app-assets + 2h ago +
+
+
+ Created bucket backup-storage + 1d ago +
+
+
+ Updated bucket policy for logs-archive + 3d ago +
+
+
+
+
+
+ + ) +} diff --git a/console/components/services/vpc-service.tsx b/console/components/services/vpc-service.tsx new file mode 100644 index 0000000..be32764 --- /dev/null +++ b/console/components/services/vpc-service.tsx @@ -0,0 +1,342 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { + ArrowLeft, + Search, + Plus, + MoreHorizontal, + Shield, + Network, + Router, + Lock, + Settings, + RefreshCw, +} from "lucide-react" + +interface VPCServicePageProps { + onBack: () => void +} + +export function VPCServicePage({ onBack }: VPCServicePageProps) { + const vpcs = [ + { + id: "vpc-1234567890abcdef0", + name: "Production VPC", + cidr: "10.0.0.0/16", + state: "available", + subnets: 4, + routeTables: 2, + igw: "Yes", + }, + { + id: "vpc-0987654321fedcba0", + name: "Development VPC", + cidr: "172.16.0.0/16", + state: "available", + subnets: 2, + routeTables: 1, + igw: "Yes", + }, + { + id: "vpc-abcdef1234567890", + name: "Testing VPC", + cidr: "192.168.0.0/16", + state: "available", + subnets: 3, + routeTables: 1, + igw: "No", + }, + ] + + const subnets = [ + { + id: "subnet-1a2b3c4d", + name: "Public Subnet 1", + vpc: "Production VPC", + cidr: "10.0.1.0/24", + az: "ap-northeast-1a", + type: "Public", + }, + { + id: "subnet-5e6f7g8h", + name: "Private Subnet 1", + vpc: "Production VPC", + cidr: "10.0.2.0/24", + az: "ap-northeast-1b", + type: "Private", + }, + { + id: "subnet-9i0j1k2l", + name: "Public Subnet 2", + vpc: "Development VPC", + cidr: "172.16.1.0/24", + az: "ap-northeast-1a", + type: "Public", + }, + ] + + return ( + <> + {/* Service Header */} +
+
+
+ +
+
+ +

VPC

+
+
+
+ + +
+
+
+ + {/* VPC Dashboard */} +
+ {/* Quick Actions */} +
+ + + +

Create VPC

+

Create a new VPC

+
+
+ + + +

Create subnet

+

Add subnet to VPC

+
+
+ + + +

Route tables

+

Manage routing

+
+
+ + + +

Security groups

+

Configure firewall

+
+
+
+ + {/* VPCs List */} + + +
+ Your VPCs +
+
+ + +
+ +
+
+
+ +
+ + + + + + + + + + + + + + {vpcs.map((vpc, index) => ( + + + + + + + + + + ))} + +
VPC IDNameIPv4 CIDRStateSubnetsInternet GatewayActions
+ {vpc.id} + {vpc.name}{vpc.cidr} + {vpc.state} + {vpc.subnets}{vpc.igw} +
+ + +
+
+
+
+
+ + {/* Subnets List */} + + +
+ Subnets + +
+
+ +
+ + + + + + + + + + + + + {subnets.map((subnet, index) => ( + + + + + + + + + ))} + +
Subnet IDNameVPCIPv4 CIDRAvailability ZoneType
+ + {subnet.id} + + {subnet.name}{subnet.vpc}{subnet.cidr}{subnet.az} + + {subnet.type} + +
+
+
+
+ + {/* Network Summary */} +
+ + + + + VPC Summary + + + +
+
+ Total VPCs + 3 +
+
+ Total subnets + 9 +
+
+ Internet gateways + 2 +
+
+
+
+ + + + + + Security + + + +
+
+ Security groups + 8 +
+
+ Network ACLs + 3 +
+
+ NAT gateways + 2 +
+
+
+
+ + + + + + Routing + + + +
+
+ Route tables + 4 +
+
+ VPC endpoints + 1 +
+
+ Peering connections + 0 +
+
+
+
+
+
+ + ) +} diff --git a/console/components/theme-provider.tsx b/console/components/theme-provider.tsx new file mode 100644 index 0000000..55c2f6e --- /dev/null +++ b/console/components/theme-provider.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as React from 'react' +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, +} from 'next-themes' + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/console/components/ui/accordion.tsx b/console/components/ui/accordion.tsx new file mode 100644 index 0000000..4a8cca4 --- /dev/null +++ b/console/components/ui/accordion.tsx @@ -0,0 +1,66 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDownIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Accordion({ + ...props +}: React.ComponentProps) { + return +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/console/components/ui/alert-dialog.tsx b/console/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..0863e40 --- /dev/null +++ b/console/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/console/components/ui/alert.tsx b/console/components/ui/alert.tsx new file mode 100644 index 0000000..1421354 --- /dev/null +++ b/console/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/console/components/ui/aspect-ratio.tsx b/console/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..3df3fd0 --- /dev/null +++ b/console/components/ui/aspect-ratio.tsx @@ -0,0 +1,11 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return +} + +export { AspectRatio } diff --git a/console/components/ui/avatar.tsx b/console/components/ui/avatar.tsx new file mode 100644 index 0000000..71e428b --- /dev/null +++ b/console/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/console/components/ui/badge.tsx b/console/components/ui/badge.tsx new file mode 100644 index 0000000..0205413 --- /dev/null +++ b/console/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/console/components/ui/breadcrumb.tsx b/console/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..eb88f32 --- /dev/null +++ b/console/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return