Files
Charon/frontend/src/components/CertificateList.tsx
Wikid82 945b18ab3e feat: Implement User Authentication and Fix Frontend Startup
- Implemented Issue #9: User Authentication & Authorization
  - Added User model fields (FailedLoginAttempts, LockedUntil, LastLogin)
  - Created AuthService with JWT support, bcrypt hashing, and account lockout
  - Added AuthMiddleware and AuthHandler
  - Registered auth routes in backend
  - Created AuthContext and RequireAuth component in frontend
  - Implemented Login page and integrated with backend
- Fixed 'Blank Page' issue in local Docker environment
  - Added QueryClientProvider to main.tsx
  - Installed missing lucide-react dependency
  - Fixed TypeScript linting errors in SetupGuard.tsx
- Updated docker-entrypoint.sh to use 127.0.0.1 for reliable Caddy checks
- Verified with local Docker build
2025-11-19 19:44:22 -05:00

72 lines
2.4 KiB
TypeScript

import { useCertificates } from '../hooks/useCertificates'
import { LoadingSpinner } from './LoadingStates'
export default function CertificateList() {
const { certificates, isLoading, error } = useCertificates()
if (isLoading) return <LoadingSpinner />
if (error) return <div className="text-red-500">Failed to load certificates</div>
return (
<div className="bg-dark-card rounded-lg border border-gray-800 overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full text-left text-sm text-gray-400">
<thead className="bg-gray-900 text-gray-200 uppercase font-medium">
<tr>
<th className="px-6 py-3">Domain</th>
<th className="px-6 py-3">Issuer</th>
<th className="px-6 py-3">Expires</th>
<th className="px-6 py-3">Status</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-800">
{certificates.length === 0 ? (
<tr>
<td colSpan={4} className="px-6 py-8 text-center text-gray-500">
No certificates found.
</td>
</tr>
) : (
certificates.map((cert) => (
<tr key={cert.domain} className="hover:bg-gray-800/50 transition-colors">
<td className="px-6 py-4 font-medium text-white">{cert.domain}</td>
<td className="px-6 py-4">{cert.issuer}</td>
<td className="px-6 py-4">
{new Date(cert.expires_at).toLocaleDateString()}
</td>
<td className="px-6 py-4">
<StatusBadge status={cert.status} />
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
)
}
function StatusBadge({ status }: { status: string }) {
const styles = {
valid: 'bg-green-900/30 text-green-400 border-green-800',
expiring: 'bg-yellow-900/30 text-yellow-400 border-yellow-800',
expired: 'bg-red-900/30 text-red-400 border-red-800',
}
const labels = {
valid: 'Valid',
expiring: 'Expiring Soon',
expired: 'Expired',
}
const style = styles[status as keyof typeof styles] || styles.valid
const label = labels[status as keyof typeof labels] || status
return (
<span className={`px-2.5 py-0.5 rounded-full text-xs font-medium border ${style}`}>
{label}
</span>
)
}