214 lines
6.2 KiB
JavaScript
214 lines
6.2 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
|
import { useToast } from "../ui/toast";
|
|
import Loader from "../ui/loader";
|
|
import { Button } from "../ui/button";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
|
|
import Table from "../ui/table"; // Import your custom Table component
|
|
import { useIsLoggedIn } from '../../lib/isLoggedIn';
|
|
|
|
export default function HetznerServicesList() {
|
|
const { isLoggedIn, loading, error, sessionData } = useIsLoggedIn();
|
|
const { showToast } = useToast();
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [servers, setServers] = useState([]);
|
|
const PUBLIC_HETZNER_API_KEY = import.meta.env.PUBLIC_HETZNER_API_KEY;
|
|
|
|
// Define fetchServers outside useEffect so it can be reused
|
|
const fetchServers = async () => {
|
|
try {
|
|
const response = await fetch("https://api.hetzner.cloud/v1/servers", {
|
|
headers: { "Authorization": `Bearer ${PUBLIC_HETZNER_API_KEY}` }
|
|
});
|
|
const data = await response.json();
|
|
setServers(data.servers || []);
|
|
} catch (error) {
|
|
showToast({
|
|
title: "Error",
|
|
description: "Failed to load servers",
|
|
variant: "destructive"
|
|
});
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchServers();
|
|
}, []);
|
|
|
|
const handlePowerAction = async (serverId, action) => {
|
|
try {
|
|
setIsLoading(true);
|
|
const response = await fetch(`https://api.hetzner.cloud/v1/servers/${serverId}/actions/${action}`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Authorization": `Bearer ${PUBLIC_HETZNER_API_KEY}`,
|
|
"Content-Type": "application/json"
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.action) {
|
|
showToast({
|
|
title: "Success",
|
|
description: `Server ${action} action initiated`,
|
|
variant: "success"
|
|
});
|
|
// Refresh server list after a short delay
|
|
setTimeout(() => {
|
|
fetchServers();
|
|
}, 3000);
|
|
}
|
|
} catch (error) {
|
|
showToast({
|
|
title: "Error",
|
|
description: `Failed to ${action} server`,
|
|
variant: "destructive"
|
|
});
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleDeleteServer = async (serverId) => {
|
|
try {
|
|
setIsLoading(true);
|
|
const response = await fetch(`https://api.hetzner.cloud/v1/servers/${serverId}`, {
|
|
method: "DELETE",
|
|
headers: {
|
|
"Authorization": `Bearer ${PUBLIC_HETZNER_API_KEY}`,
|
|
"Content-Type": "application/json"
|
|
}
|
|
});
|
|
|
|
if (response.ok) {
|
|
showToast({
|
|
title: "Success",
|
|
description: "Server deleted successfully",
|
|
variant: "success"
|
|
});
|
|
// Remove the server from local state
|
|
setServers(prev => prev.filter(server => server.id !== serverId));
|
|
}
|
|
} catch (error) {
|
|
showToast({
|
|
title: "Error",
|
|
description: "Failed to delete server",
|
|
variant: "destructive"
|
|
});
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const getStatusBadge = (status) => {
|
|
let colorClass = "";
|
|
switch (status) {
|
|
case "running":
|
|
colorClass = "bg-green-100 text-green-800";
|
|
break;
|
|
case "off":
|
|
colorClass = "bg-gray-100 text-gray-800";
|
|
break;
|
|
case "starting":
|
|
case "stopping":
|
|
colorClass = "bg-yellow-100 text-yellow-800";
|
|
break;
|
|
default:
|
|
colorClass = "bg-blue-100 text-blue-800";
|
|
}
|
|
|
|
return (
|
|
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${colorClass}`}>
|
|
{status}
|
|
</span>
|
|
);
|
|
};
|
|
|
|
// Prepare data for your Table component
|
|
const tableData = servers.map(server => ({
|
|
name: server.name,
|
|
status: getStatusBadge(server.status),
|
|
type: server.server_type.name,
|
|
location: server.datacenter.location.city,
|
|
ip: server.public_net.ipv4 ? server.public_net.ipv4.ip : "None",
|
|
backups: server.backup_window ? "Enabled" : "Disabled",
|
|
actions: (
|
|
<div className="flex gap-2">
|
|
{server.status === "running" ? (
|
|
<>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => handlePowerAction(server.id, "shutdown")}
|
|
disabled={isLoading}
|
|
>
|
|
Stop
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => handlePowerAction(server.id, "reboot")}
|
|
disabled={isLoading}
|
|
>
|
|
Reboot
|
|
</Button>
|
|
</>
|
|
) : (
|
|
<Button
|
|
className=""
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => handlePowerAction(server.id, "poweron")}
|
|
disabled={isLoading}
|
|
>
|
|
Start
|
|
</Button>
|
|
)}
|
|
<Button
|
|
className="bg-red-500 hover:bg-red-600 transition duration-500"
|
|
size="sm"
|
|
onClick={() => handleDeleteServer(server.id)}
|
|
disabled={isLoading}
|
|
>
|
|
Delete
|
|
</Button>
|
|
</div>
|
|
)
|
|
}));
|
|
|
|
const tableHeaders = ["Name", "Status", "Type", "Location", "IPv4", "Backups", "Actions"];
|
|
|
|
|
|
if (loading || (isLoggedIn && sessionData?.user_type === 'admin' && isLoading && servers.length === 0)) {
|
|
return <Loader />;
|
|
}
|
|
if (!isLoggedIn || sessionData?.user_type !== 'admin') {
|
|
return <p className="text-center mt-8">You are not authorized to view this page. <a href="/" className="text-[#6d9e37]">Click Here</a> to go to the homepage.</p>;
|
|
}
|
|
|
|
return (
|
|
<Card className="container mx-auto my-4">
|
|
<CardHeader>
|
|
<CardTitle>Your Hetzner Cloud Servers</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{servers.length === 0 ? (
|
|
<div className="text-center py-8">
|
|
<p className="text-gray-500">No servers found. Create your first server to get started.</p>
|
|
</div>
|
|
) : (
|
|
<Table
|
|
headers={tableHeaders}
|
|
data={tableData}
|
|
striped
|
|
hover
|
|
caption="A list of your Hetzner Cloud servers"
|
|
className="mt-4"
|
|
/>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
} |