2025-12-06 11:53:45 +05:30

132 lines
3.8 KiB
TypeScript

"use client";
import React, { useEffect, useState } from "react";
import axios from "axios";
interface DomainStatus {
domain: string;
validTo: string;
expired: boolean;
}
const SSLChecker: React.FC = () => {
const [data, setData] = useState<DomainStatus[]>([]);
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState("");
const [lastChecked, setLastChecked] = useState<string>("");
// Fetch SSL Data
const loadSSL = async () => {
try {
setLoading(true);
const response = await axios.get("http://localhost:3010/api/ssl/status");
setData(response.data);
const now = new Date().toLocaleString();
setLastChecked(now);
} catch (error) {
console.error("SSL API Error:", error);
} finally {
setLoading(false);
}
};
// Auto load on mount
useEffect(() => {
loadSSL();
}, []);
// Filter domains
const filteredData = data.filter((item) =>
item.domain.toLowerCase().includes(search.toLowerCase())
);
return (
<div className="min-h-screen bg-gray-50 p-8 text-gray-900">
<h1 className="text-3xl font-bold text-center mb-6">
SSL Expiry Status Checker
</h1>
{/* Search + Refresh */}
<div className="flex items-center justify-between mb-5">
<input
type="text"
placeholder="Search domain..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-1/3 p-2 border border-gray-300 rounded-md focus:ring focus:ring-blue-300"
/>
<button
onClick={loadSSL}
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition"
>
Refresh
</button>
</div>
{/* Last checked time */}
<p className="mb-4 text-sm text-gray-600">
Last checked: <span className="font-semibold">{lastChecked}</span>
</p>
{/* Loading Shimmer */}
{loading ? (
<div className="p-10 bg-white rounded-xl shadow-md animate-pulse text-center text-gray-700">
Checking SSL certificates...
</div>
) : (
<div className="overflow-x-auto bg-white rounded-xl shadow-lg border">
<table className="w-full">
<thead>
<tr className="bg-gray-100 border-b">
<th className="px-6 py-3 text-left font-semibold text-gray-700">
Domain
</th>
<th className="px-6 py-3 text-left font-semibold text-gray-700">
Valid Until
</th>
<th className="px-6 py-3 text-left font-semibold text-gray-700">
Status
</th>
</tr>
</thead>
<tbody>
{filteredData.map((d) => (
<tr
key={d.domain}
className="border-b hover:bg-gray-50 transition"
>
<td className="px-6 py-4">{d.domain}</td>
<td className="px-6 py-4">{d.validTo}</td>
<td className="px-6 py-4">
<span
className={`px-3 py-1 rounded-full text-sm font-semibold ${
d.expired
? "bg-red-100 text-red-700"
: "bg-green-100 text-green-700"
}`}
>
{d.expired ? "Expired" : "Valid"}
</span>
</td>
</tr>
))}
</tbody>
</table>
{filteredData.length === 0 && (
<p className="text-center py-6 text-gray-500">
No matching domains found.
</p>
)}
</div>
)}
</div>
);
};
export default SSLChecker;