sp/src/components/BuyServices/NewUthoCloudInstance.jsx

698 lines
22 KiB
JavaScript

import React, { useState, useEffect } from "react";
import { Button } from '../ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card";
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "../ui/select";
import { Input } from "../ui/input";
import { Label } from "../ui/label";
import Loader from "../ui/loader";
import { useToast } from "../ui/toast";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "../ui/tabs";
import { Textarea } from "../ui/textarea";
import { Switch } from "../ui/switch";
import { useIsLoggedIn } from '../../lib/isLoggedIn';
export default function NewCloudInstance() {
const PUBLIC_UTHO_API_KEY = import.meta.env.PUBLIC_UTHO_API_KEY;
const { isLoggedIn, loading, error, sessionData } = useIsLoggedIn();
const { showToast } = useToast();
const [isLoading, setIsLoading] = useState(false);
const [isFetchingData, setIsFetchingData] = useState(true);
const [deployError, setDeployError] = useState()
const [deployStatus, setDeployStatus] = useState({});
const [copied, setCopied] = useState(false);
const [plans, setPlans] = useState([
{
"id": "10027",
"type": "cloud",
"slug": "dedicated-cpu",
"disk": "80",
"ram": "4096",
"cpu": "2",
"bandwidth": "1000",
"dedicated_vcore": "1",
"is_available": "YES",
"price": 26.83,
"price_currency": 26.83,
"price_cur": "$26.8"
},
{
"id": "10028",
"type": "cloud",
"slug": "dedicated-cpu",
"disk": "160",
"ram": "8192",
"cpu": "4",
"bandwidth": "2000",
"dedicated_vcore": "1",
"is_available": "YES",
"price": 53.72,
"price_currency": 53.72,
"price_cur": "$53.7"
},
{
"id": "10029",
"type": "cloud",
"slug": "dedicated-cpu",
"disk": "480",
"ram": "32768",
"cpu": "8",
"bandwidth": "3000",
"dedicated_vcore": "1",
"is_available": "YES",
"price": 151.91,
"price_currency": 151.91,
"price_cur": "$151.9"
},
{
"id": "10030",
"type": "cloud",
"slug": "dedicated-cpu",
"disk": "960",
"ram": "65536",
"cpu": "16",
"bandwidth": "4000",
"dedicated_vcore": "1",
"is_available": "YES",
"price": 292.19,
"price_currency": 292.19,
"price_cur": "$292.2"
},
]);
// const [sshKeys, setSshKeys] = useState([]);
const [showAddSshKey, setShowAddSshKey] = useState(false);
const [vpcs, setVpcs] = useState();
const [images, setImages] = useState([
{
"distro": null,
"distribution": "Alma Linux",
"version": "9.2 x86_64",
"image": "almalinux-9.2-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Alma Linux",
"version": "8.4 x86_64",
"image": "AlmaLinux_8.4_x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Centos",
"version": "7.3 x86_64",
"image": "centos-7.3-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Centos",
"version": "8 x86_64",
"image": "centos-8-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Centos",
"version": "7.9 x86_64",
"image": "centos-7.9-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Centos",
"version": "Ameyo x86_64",
"image": "AmeyoCentos7",
"cost": 0
},
{
"distro": null,
"distribution": "Debian",
"version": "9.4 x86_64",
"image": "debian-9.4-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Debian",
"version": "9.13 x86_64",
"image": "debian-9.13-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Debian",
"version": "10 x86_64",
"image": "debian-10-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Debian",
"version": "11 x86_64",
"image": "debian-11-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Debian",
"version": "12 x86_64",
"image": "debian-12-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Fedora",
"version": "34 x86_64",
"image": "fedora-34-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Fedora",
"version": "32 x86_64",
"image": "fedora-32-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Fedora",
"version": "33 x86_64",
"image": "fedora-33-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "FortiOS",
"version": "7.6.2 ",
"image": "fortios-762",
"cost": 0
},
{
"distro": null,
"distribution": "Rocky Linux",
"version": "8.7 x86_64",
"image": "rocky-8.7-86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Rocky Linux",
"version": "8.8 x86_64",
"image": "rocky-8.8-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Rocky Linux",
"version": "9.1 x86_64",
"image": "rocky-9.1-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Rocky Linux",
"version": "9.2 x86_64",
"image": "rocky-9.2-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Ubuntu",
"version": "20.04 x86_64",
"image": "ubuntu-20.04-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Ubuntu",
"version": "18.04 x86_64",
"image": "ubuntu-18.10-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Ubuntu",
"version": "22.04 x86_64",
"image": "ubuntu-22.04-x86_64",
"cost": 0
},
{
"distro": null,
"distribution": "Windows",
"version": "Server 2012R2 x86_64",
"image": "windows-server2012r2-x86_64",
"cost": 1200
},
{
"distro": null,
"distribution": "Windows",
"version": "Server 2016 x86_64",
"image": "windows-2016",
"cost": 1200
},
{
"distro": null,
"distribution": "Windows",
"version": "Server 2019 x86_64",
"image": "windows2019-10nov2020",
"cost": 1200
},
{
"distro": null,
"distribution": "Windows",
"version": "Server 2022 x86_64",
"image": "windows-2022",
"cost": 1200
}
])
const [formData, setFormData] = useState({
dcslug: '',
planid: '',
billingcycle: "hourly",
auth: "option2",
enable_publicip: '',
subnetRequired: '',
firewall: "23434645",
cpumodel: "intel",
enablebackup: '',
root_password: '',
support: "unmanaged",
vpc: '',
cloud: [
{
hostname: ''
}
],
image: '',
sshkeys: ''
});
useEffect(() => {
const fetchData = async () => {
try {
const vpcResponse = await fetch('https://api.utho.com/v2/vpc', {
headers: { "Authorization": `Bearer ${PUBLIC_UTHO_API_KEY}` }
});
const vpcsData = await vpcResponse.json();
setVpcs(vpcsData.vpc || []);
} catch (error) {
showToast({
title: "Error",
description: "Failed to load configuration data",
variant: "destructive"
});
} finally {
setIsFetchingData(false);
}
};
fetchData();
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
try {
const payload = {
dcslug: formData.dcslug,
planid: formData.planid,
billingcycle: "hourly",
auth: "option2",
enable_publicip: formData.enable_publicip ? true : false,
subnetRequired: false,
firewall: "23434645",
cpumodel: "intel",
enablebackup: formData.enablebackup ? true : false,
root_password: formData.rootPassword,
support: "unmanaged",
vpc: formData.dcslug === 'inmumbaizone2' ? '68c36d65-bda8-43cc-94b5-ae28bb2c3306' : formData.dcslug === 'inbangalore' ? 'c17032c9-3cfd-4028-8f2a-f3f5aa8c2976' : '',
cloud: [
{
hostname: formData.hostname
}
],
image: formData.image,
sshkeys: formData.sshkeys
};
// inbangalore = c17032c9-3cfd-4028-8f2a-f3f5aa8c2976
// inmumbai = 68c36d65-bda8-43cc-94b5-ae28bb2c3306
// Add authentication based on selected method
if (formData.auth === "ssh_key") {
payload.sshkeys = formData.sshkeys;
} else {
payload.password = formData.password;
}
console.log('payload', payload)
const response = await fetch("https://host-api.cs1.hz.siliconpin.com/v1/vps/?query=deploy&source=chanel_1", {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
const data = await response.json();
if (data.status === "success") {
setDeployStatus({status : data.status, ip : data.ipv4})
// showToast({
// title: "Deployment Started",
// description: `Server ${data.cloudid} is being deployed with IP ${data.ipv4}`,
// variant: "success"
// });
// Reset form after successful deployment
setFormData(prev => ({
...prev,
hostname: "",
planid: ""
}));
} else {
throw new Error(data.message || "Failed to deploy instance");
}
} catch (error) {
setDeployError(error.message)
} finally {
setIsLoading(false);
}
};
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === "checkbox" ? checked : value
}));
};
const handleAddSshKey = async () => {
try {
const response = await fetch('https://api.utho.com/v2/key/import', {
method: "POST",
headers: {
"Authorization": `Bearer ${PUBLIC_UTHO_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
name: formData.newSshKeyName,
sshkey: formData.newSshKeyValue
})
});
const data = await response.json();
if (data.status === 'success') {
// Refresh SSH keys list
const sshResponse = await fetch("https://api.utho.com/v2/key", {
headers: { "Authorization": `Bearer ${PUBLIC_UTHO_API_KEY}` }
});
const sshData = await sshResponse.json();
setSshKeys(sshData.key || []);
showToast({
title: "SSH Key Added",
description: "Your SSH key has been successfully added",
variant: "success"
});
setShowAddSshKey(false);
}
} catch (error) {
showToast({
title: "Error",
description: "Failed to add SSH key",
variant: "destructive"
});
}
};
const handleCopy = (text) => {
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 1000); // revert after 1 second
};
if (loading || (isLoggedIn && isFetchingData)) {
return <Loader />;
}
if (deployError) return <p>Error: {deployError?.message}</p>;
if (!isLoggedIn) {
return <p className="text-center mt-8">
You are not Logged in. <a href="/login" className="text-[#6d9e37]">Click Here</a> to login first then you can add this service.
</p>;
}
if(deployStatus.status === 'success'){
return (
<>
<Card className="w-full max-w-2xl mx-auto my-4">
<CardHeader>
<CardTitle>Create New Vertual Private Server (VPS)</CardTitle>
<CardDescription>Provide the root password then you can access vps using the password. <br /> later on you can setup ssh key</CardDescription>
</CardHeader>
<CardContent>
<p>Your New VPS</p>
<div className="flex flex-col my-2">
<Label>IP Address:</Label>
<div className="flex">
<Input className="rounded-l-md rounded-r-none text-xl font-bold border-[2px] border-[#6d9e37]" type="text" readOnly value={deployStatus.ip} />
<Button className="rounded-l-none rounded-r-md" onClick={() => handleCopy(deployStatus.ip)}>{copied ? 'Copied!' : 'Copy' }</Button>
</div>
</div>
<div className="flex justify-center place-items-center">
<Button className="" onClick={() => window.location.href = '/services/cloud-instance'}>Back</Button>
</div>
</CardContent>
</Card>
</>
)
}
return (
<Card className="w-full max-w-2xl mx-auto my-4">
<CardHeader>
<CardTitle>Create New Vertual Private Server (VPS)</CardTitle>
<CardDescription>Provide the root password then you can access vps using the password. <br /> later on you can setup ssh key</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="hostname">Hostname *</Label>
<Input
id="hostname"
name="hostname"
value={formData.hostname}
onChange={handleChange}
placeholder="server1.example.com"
required
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="dcslug">Data Center *</Label>
<Select
name="dcslug"
value={formData.dcslug}
onValueChange={(value) => setFormData({...formData, dcslug: value})}
>
<SelectTrigger>
<SelectValue placeholder="Select location" />
</SelectTrigger>
<SelectContent>
<SelectItem value="inmumbaizone2">Mumbai</SelectItem>
<SelectItem value="inbangalore">Bangalore</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="planid">Plan *</Label>
<Select
name="planid"
value={formData.planid}
onValueChange={(value) => setFormData({...formData, planid: value})}
required
>
<SelectTrigger>
<SelectValue placeholder="Select a plan" />
</SelectTrigger>
<SelectContent>
{plans.map(plan => (
<SelectItem key={plan.id} value={plan.id}>
{`${plan.cpu} vCPU, ${plan.ram}MB RAM - ${plan.price_cur}/mo`}
</SelectItem>
))}
{/* <SelectItem key={plans[0].id} value={plans[0].id}>
{`${plans[0].cpu} vCPU, ${plans[0].ram}MB RAM - ${plans[0].price_cur}/mo`}
</SelectItem> */}
</SelectContent>
</Select>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="image">Operating System *</Label>
<Select
name="image"
value={formData.image}
onValueChange={(value) => setFormData({...formData, image: value})}>
<SelectTrigger>
<SelectValue placeholder="Select OS" />
</SelectTrigger>
<SelectContent>
{
images.map((image, i) => (
<SelectItem key={i} value={image.image}>{image.image.split('-x86_64')[0]}</SelectItem>
))
}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="password">Root Password *</Label>
<Input
id="password"
name="password"
type="password"
value={formData.password}
onChange={handleChange}
placeholder="Enter root password"
required={formData.auth === "password"}
/>
</div>
</div>
{/* <div className="space-y-4">
<h3 className="text-lg font-medium">Authentication</h3>
<Tabs
value={formData.auth}
onValueChange={(value) => setFormData({...formData, auth: value})}
className="w-full">
<TabsList className="grid w-full grid-cols-2 gap-4">
<TabsTrigger className={`p-2 rounded-md ${formData.auth === 'ssh_key' ? 'bg-[#6d9e37] text-[#FFF]' : 'border border-[#6d9e37]'}`} value="ssh_key">SSH Key</TabsTrigger>
<TabsTrigger className={`p-2 rounded-md ${formData.auth === 'password' ? 'bg-[#6d9e37] text-[#FFF]' : 'border border-[#6d9e37]'}`} value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="ssh_key" className="space-y-4">
<div className="space-y-2">
<Label>Select SSH Key</Label>
<div className="flex gap-2">
<Select
value={formData.sshkeys}
onValueChange={(value) => setFormData({...formData, sshkeys: value})}
>
<SelectTrigger>
<SelectValue placeholder="Select SSH key" />
</SelectTrigger>
<SelectContent>
{sshKeys.map(key => (
<SelectItem key={key.id} value={key.id}>{key.name}</SelectItem>
))}
</SelectContent>
</Select>
<Button
type="button"
variant="outline"
className="whitespace-nowrap"
onClick={() => setShowAddSshKey(!showAddSshKey)}
>
{showAddSshKey ? "Cancel" : "Add New"}
</Button>
</div>
</div>
{showAddSshKey && (
<div className="space-y-4 p-4 border rounded-lg">
<div className="space-y-2">
<Label htmlFor="newSshKeyName">Key Name</Label>
<Input
id="newSshKeyName"
name="newSshKeyName"
value={formData.newSshKeyName}
onChange={handleChange}
placeholder="My Laptop Key"
/>
</div>
<div className="space-y-2">
<Label htmlFor="newSshKeyValue">Public Key</Label>
<Textarea
id="newSshKeyValue"
name="newSshKeyValue"
value={formData.newSshKeyValue}
onChange={handleChange}
placeholder="ssh-rsa AAAAB3NzaC1yc2E..."
rows={4}
/>
</div>
<Button
type="button"
onClick={handleAddSshKey}
disabled={!formData.newSshKeyName || !formData.newSshKeyValue}
>
Add SSH Key
</Button>
</div>
)}
</TabsContent>
<TabsContent value="password" className="space-y-2">
<div className="space-y-2">
<Label htmlFor="password">Root Password *</Label>
<Input
id="password"
name="password"
type="password"
value={formData.password}
onChange={handleChange}
placeholder="Enter root password"
required={formData.auth === "password"}
/>
</div>
</TabsContent>
</Tabs>
</div> */}
<div className="space-y-4">
<h3 className="text-lg font-medium">Options</h3>
<div className="flex flex-col gap-4">
<div className="flex items-center space-x-2">
<Switch
id="enable_publicip"
checked={formData.enable_publicip}
onCheckedChange={(checked) => setFormData({...formData, enable_publicip: checked})}
/>
<Label htmlFor="enable_publicip">Enable Public IP</Label>
</div>
<div className="flex items-center space-x-2">
<Switch
id="enablebackup"
checked={formData.enablebackup}
onCheckedChange={(checked) => setFormData({...formData, enablebackup: checked})}
/>
<Label htmlFor="enablebackup">Enable Weekly Backups (+20%)</Label>
</div>
</div>
</div>
<div className="flex justify-end pt-4">
<Button type="submit" disabled={isLoading} className="w-full md:w-auto">
{isLoading ? "Deploying..." : "Deploy Instance"}
</Button>
</div>
</form>
</CardContent>
</Card>
);
}