ai-wpa/app/services/cloud-instance/page.tsx

720 lines
26 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { Header } from '@/components/header'
import { Footer } from '@/components/footer'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Checkbox } from '@/components/ui/checkbox'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import {
Cloud,
Server,
HardDrive,
Shield,
AlertCircle,
Check,
Cpu,
MapPin,
ExternalLink,
Terminal,
} from 'lucide-react'
import { useAuth } from '@/contexts/AuthContext'
import { checkSufficientBalance, formatCurrency } from '@/lib/balance-service'
interface Plan {
id: string
name: string
price: {
daily: number
monthly: number
}
}
interface OSImage {
id: string
name: string
price?: {
daily: number
monthly: number
}
}
interface DataCenter {
id: string
name: string
}
interface DeploymentResult {
status: string
cloudid?: string
message?: string
password?: string
ipv4?: string
server_id?: string
error?: string
[key: string]: any
}
export default function CloudInstancePage() {
const router = useRouter()
const { user } = useAuth()
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState('')
const [deploymentResult, setDeploymentResult] = useState<DeploymentResult | null>(null)
const [billingCycle, setBillingCycle] = useState<'daily' | 'monthly'>('monthly')
// Form state
const [formData, setFormData] = useState({
hostname: '',
planId: '',
image: '',
password: '',
dcslug: '',
enableBackup: false,
enablePublicIp: true,
})
// Available plans
const plans: Plan[] = [
{ id: '10027', name: '2 vCPU, 4GB RAM, 80GB SSD', price: { daily: 60, monthly: 1600 } },
{ id: '10028', name: '4 vCPU, 8GB RAM, 160GB SSD', price: { daily: 80, monthly: 2200 } },
{ id: '10029', name: '8 vCPU, 32GB RAM, 480GB SSD', price: { daily: 100, monthly: 2800 } },
{ id: '10030', name: '16 vCPU, 64GB RAM, 960GB SSD', price: { daily: 120, monthly: 3200 } },
]
// Available OS images
const images: OSImage[] = [
{ id: 'almalinux-9.2-x86_64', name: 'Alma Linux 9.2' },
{ id: 'centos-7.9-x86_64', name: 'CentOS 7.9' },
{ id: 'debian-12-x86_64', name: 'Debian 12' },
{ id: 'ubuntu-22.04-x86_64', name: 'Ubuntu 22.04 LTS' },
{ id: 'windows-2022', name: 'Windows Server 2022', price: { daily: 200, monthly: 5000 } },
]
// Data centers
const dataCenters: DataCenter[] = [
{ id: 'inmumbaizone2', name: 'Mumbai, India' },
{ id: 'inbangalore', name: 'Bangalore, India' },
]
// Get user balance from auth context
const userBalance = user?.balance || 0
// Calculate total price
const calculatePrice = () => {
const selectedPlan = plans.find((p) => p.id === formData.planId)
const selectedImage = images.find((i) => i.id === formData.image)
if (!selectedPlan) return 0
let total = selectedPlan.price[billingCycle]
// Add Windows license cost if applicable
if (selectedImage?.price) {
total += selectedImage.price[billingCycle]
}
// Add backup cost (20% of plan price)
if (formData.enableBackup) {
total += selectedPlan.price[billingCycle] * 0.2
}
return total
}
const handleDeploy = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
setSuccess('')
setDeploymentResult(null)
if (!user) {
router.push('/auth?redirect=/services/cloud-instance')
return
}
// Validate form
if (
!formData.hostname ||
!formData.planId ||
!formData.image ||
!formData.password ||
!formData.dcslug
) {
setError('All fields are required')
return
}
// Check password strength
if (formData.password.length < 8) {
setError('Password must be at least 8 characters long')
return
}
// Check balance
const totalPrice = calculatePrice()
const balanceCheck = await checkSufficientBalance(totalPrice)
if (!balanceCheck.sufficient) {
setError(
balanceCheck.error ||
`Insufficient balance. You need ${formatCurrency(totalPrice)} but only have ${formatCurrency(balanceCheck.currentBalance || 0)}`
)
return
}
setLoading(true)
try {
const response = await fetch('/api/services/deploy-cloude', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
hostname: formData.hostname,
planid: formData.planId,
image: formData.image,
dclocation: formData.dcslug,
password: formData.password,
cycle: billingCycle,
amount: totalPrice,
backup: formData.enableBackup,
publicip: formData.enablePublicIp,
}),
})
const result: DeploymentResult = await response.json()
if (!response.ok) {
throw new Error(result.message || `Deployment failed with status ${response.status}`)
}
if (result.status === 'success') {
setDeploymentResult(result)
// Create success message with server details
let successMessage = 'Cloud instance deployed successfully!'
if (result.cloudid) {
successMessage += ` Server ID: ${result.cloudid}`
}
if (result.ipv4) {
successMessage += ` | IP: ${result.ipv4}`
}
setSuccess(successMessage)
// Reset form after successful deployment
setFormData({
hostname: '',
planId: '',
image: '',
password: '',
dcslug: '',
enableBackup: false,
enablePublicIp: true,
})
} else {
throw new Error(result.message || 'Deployment failed')
}
} catch (err) {
console.error('Deployment error:', err)
setError(err instanceof Error ? err.message : 'Failed to deploy cloud instance')
} finally {
setLoading(false)
}
}
const resetForm = () => {
setDeploymentResult(null)
setSuccess('')
setError('')
}
return (
<div className="min-h-screen bg-background">
<Header />
<div className="container mx-auto px-4 pt-24 pb-16">
<div className="max-w-4xl mx-auto">
{/* Page Header */}
<div className="text-center mb-12">
<div className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full mb-4">
<Cloud className="w-8 h-8 text-white" />
</div>
<h1 className="text-4xl font-bold mb-4">Deploy Cloud Instance</h1>
<p className="text-xl text-muted-foreground">
High-performance cloud servers with instant deployment
</p>
</div>
{!user && (
<Alert className="mb-8">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
You need to be logged in to deploy a cloud instance.{' '}
<a
href="/auth?redirect=/services/cloud-instance"
className="text-primary underline"
>
Click here to login
</a>
</AlertDescription>
</Alert>
)}
{success && (
<Alert className="mb-8 bg-green-50 border-green-200">
<Check className="h-4 w-4 text-green-600" />
<AlertDescription className="text-green-800">
<div className="space-y-3">
<div className="font-semibold">{success}</div>
{deploymentResult?.ipv4 && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-3">
<div>
<Label className="text-sm font-medium text-green-700">
Server IP Address
</Label>
<div className="flex items-center mt-1">
<Input
className="font-mono text-sm bg-green-100 border-green-300"
value={deploymentResult.ipv4}
readOnly
/>
<Button
variant="outline"
size="sm"
className="ml-2"
onClick={() => {
navigator.clipboard.writeText(deploymentResult.ipv4!)
// You can add a toast notification here
}}
>
<ExternalLink className="h-4 w-4" />
</Button>
</div>
</div>
<div>
<Label className="text-sm font-medium text-green-700">Server ID</Label>
<p className="text-sm font-mono bg-green-100 p-2 rounded border border-green-300 mt-1">
{deploymentResult.cloudid || deploymentResult.server_id}
</p>
</div>
</div>
)}
{deploymentResult?.password && (
<div className="mt-3">
<Label className="text-sm font-medium text-green-700">Root Password</Label>
<div className="flex items-center mt-1">
<Input
type="password"
className="font-mono text-sm bg-green-100 border-green-300"
value={deploymentResult.password}
readOnly
/>
<Button
variant="outline"
size="sm"
className="ml-2"
onClick={() => {
navigator.clipboard.writeText(deploymentResult.password!)
// You can add a toast notification here
}}
>
Copy
</Button>
</div>
<p className="text-xs text-green-600 mt-1">
Please change this password after first login
</p>
</div>
)}
{deploymentResult?.message && (
<div className="mt-3 p-3 bg-green-100 rounded-md">
<p className="text-sm text-green-700">{deploymentResult.message}</p>
</div>
)}
<div className="flex gap-3 mt-4 pt-3 border-t border-green-200">
<Button
onClick={() => router.push('/dashboard/instances')}
className="bg-green-600 hover:bg-green-700"
>
<Server className="w-4 h-4 mr-2" />
View All Instances
</Button>
<Button
variant="outline"
onClick={() => {
// SSH connection button - you can implement this
if (deploymentResult?.ipv4) {
const sshCommand = `ssh root@${deploymentResult.ipv4}`
navigator.clipboard.writeText(sshCommand)
// Show toast notification
}
}}
>
<Terminal className="w-4 h-4 mr-2" />
Copy SSH Command
</Button>
<Button variant="outline" onClick={resetForm}>
Deploy Another
</Button>
</div>
<div className="mt-4 text-xs text-green-600">
<p> Deployment successful! Your server is being provisioned.</p>
{/* <p>📧 Login details will be sent to your email once ready.</p> */}
</div>
</div>
</AlertDescription>
</Alert>
)}
{deploymentResult ? (
<Card>
<CardHeader>
<CardTitle>Deployment Successful!</CardTitle>
<CardDescription>
Your cloud instance has been deployed successfully.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<Label>Server ID</Label>
<p className="text-sm font-mono bg-muted p-2 rounded">
{deploymentResult.server_id}
</p>
</div>
<div>
<Label>Status</Label>
<p className="text-sm capitalize">{deploymentResult.status}</p>
</div>
</div>
<div className="flex gap-4">
<Button onClick={() => router.push('/dashboard/instances')}>
View Instances
</Button>
<Button variant="outline" onClick={resetForm}>
Deploy Another
</Button>
</div>
</CardContent>
</Card>
) : (
<form onSubmit={handleDeploy}>
<div className="space-y-8">
{/* Billing Cycle Selection */}
<Card>
<CardHeader>
<CardTitle>Billing Cycle</CardTitle>
<CardDescription>Choose your preferred billing period</CardDescription>
</CardHeader>
<CardContent>
<Tabs
value={billingCycle}
onValueChange={(v) => setBillingCycle(v as 'daily' | 'monthly')}
>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="daily">Daily</TabsTrigger>
<TabsTrigger value="monthly">Monthly (Save 20%)</TabsTrigger>
</TabsList>
</Tabs>
</CardContent>
</Card>
{/* Server Configuration */}
<Card>
<CardHeader>
<CardTitle>Server Configuration</CardTitle>
<CardDescription>Configure your cloud instance specifications</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="hostname">Hostname</Label>
<Input
id="hostname"
placeholder="my-server.example.com"
value={formData.hostname}
onChange={(e) => setFormData({ ...formData, hostname: e.target.value })}
required
/>
</div>
<div>
<Label htmlFor="plan">Server Plan</Label>
<Select
value={formData.planId}
onValueChange={(value) => setFormData({ ...formData, planId: value })}
>
<SelectTrigger>
<SelectValue placeholder="Select a plan" />
</SelectTrigger>
<SelectContent>
{plans.map((plan) => (
<SelectItem key={plan.id} value={plan.id}>
<div className="flex items-center justify-between w-full">
<span>{plan.name}</span>
<span className="ml-4 text-primary">
{plan.price[billingCycle]}/
{billingCycle === 'daily' ? 'day' : 'month'}
</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="image">Operating System</Label>
<Select
value={formData.image}
onValueChange={(value) => setFormData({ ...formData, image: value })}
>
<SelectTrigger>
<SelectValue placeholder="Select an OS" />
</SelectTrigger>
<SelectContent>
{images.map((image) => (
<SelectItem key={image.id} value={image.id}>
<div className="flex items-center justify-between w-full">
<span>{image.name}</span>
{image.price && (
<span className="ml-4 text-primary">
+{image.price[billingCycle]}/
{billingCycle === 'daily' ? 'day' : 'month'}
</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="datacenter">Data Center</Label>
<Select
value={formData.dcslug}
onValueChange={(value) => setFormData({ ...formData, dcslug: value })}
>
<SelectTrigger>
<SelectValue placeholder="Select location" />
</SelectTrigger>
<SelectContent>
{dataCenters.map((dc) => (
<SelectItem key={dc.id} value={dc.id}>
<div className="flex items-center gap-2">
<MapPin className="w-4 h-4" />
{dc.name}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="password">Root Password</Label>
<Input
id="password"
type="password"
placeholder="Strong password (min 8 characters)"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
required
/>
<p className="text-xs text-muted-foreground mt-1">
Minimum 8 characters required
</p>
</div>
<div className="space-y-3">
<div className="flex items-center space-x-2">
<Checkbox
id="backup"
checked={formData.enableBackup}
onCheckedChange={(checked) =>
setFormData({ ...formData, enableBackup: checked as boolean })
}
/>
<Label htmlFor="backup" className="cursor-pointer">
Enable automatic backups (+20% of plan cost)
</Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="publicip"
checked={formData.enablePublicIp}
onCheckedChange={(checked) =>
setFormData({ ...formData, enablePublicIp: checked as boolean })
}
/>
<Label htmlFor="publicip" className="cursor-pointer">
Enable public IP address
</Label>
</div>
</div>
</CardContent>
</Card>
{/* Pricing Summary */}
<Card>
<CardHeader>
<CardTitle>Order Summary</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-muted-foreground">Base Plan</span>
<span>
{plans.find((p) => p.id === formData.planId)?.price[billingCycle] || 0}
</span>
</div>
{images.find((i) => i.id === formData.image)?.price && (
<div className="flex justify-between">
<span className="text-muted-foreground">Windows License</span>
<span>
{images.find((i) => i.id === formData.image)?.price?.[billingCycle] ||
0}
</span>
</div>
)}
{formData.enableBackup && (
<div className="flex justify-between">
<span className="text-muted-foreground">Backup Service</span>
<span>
{(plans.find((p) => p.id === formData.planId)?.price[billingCycle] ||
0) * 0.2}
</span>
</div>
)}
<div className="border-t pt-2">
<div className="flex justify-between font-semibold">
<span>Total</span>
<span className="text-primary">
{calculatePrice()}/{billingCycle === 'daily' ? 'day' : 'month'}
</span>
</div>
</div>
<div className="pt-2">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Your Balance</span>
<span
className={
userBalance >= calculatePrice() ? 'text-green-600' : 'text-red-600'
}
>
{userBalance.toFixed(2)}
</span>
</div>
</div>
</div>
</CardContent>
</Card>
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* Deploy Button */}
<Button type="submit" className="w-full" size="lg" disabled={loading || !user}>
{loading ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Deploying...
</>
) : (
'Deploy Cloud Instance'
)}
</Button>
</div>
</form>
)}
{/* Features */}
{!deploymentResult && (
<div className="mt-16 grid md:grid-cols-3 gap-8">
<Card>
<CardHeader>
<Server className="w-8 h-8 text-primary mb-2" />
<CardTitle>High Performance</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
Enterprise-grade hardware with NVMe SSD storage
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<Shield className="w-8 h-8 text-primary mb-2" />
<CardTitle>Secure & Reliable</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
DDoS protection and 99.99% uptime SLA
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<HardDrive className="w-8 h-8 text-primary mb-2" />
<CardTitle>Instant Deployment</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
Your server ready in less than 60 seconds
</p>
</CardContent>
</Card>
</div>
)}
</div>
</div>
<Footer />
</div>
)
}
// jRIfuQspDJlWdgXxLmqVFSUthwBOkezibPCyoTvHYcEANraGKMnZ
// {
// "status": "success",
// "cloudid": "1645972",
// "message": "Cloud Server deploy in process and as soon it get ready to use system will send you login detail over the email.",
// "password": "00000000",
// "ipv4": "103.189.89.32"
// }