import { useState, useEffect } from 'react' import { useParams, useNavigate } from 'react-router-dom' import { useAuth } from '../contexts/AuthContext' import { usePermissions } from '../hooks/usePermissions' const SpaceDetail = () => { const { id } = useParams() const navigate = useNavigate() const { authFetch } = useAuth() const { canCreateFqdn, canDeleteFqdn, canSignCSR, canUploadCSR, refreshPermissions } = usePermissions() const [space, setSpace] = useState(null) const [fqdns, setFqdns] = useState([]) const [showForm, setShowForm] = useState(false) const [formData, setFormData] = useState({ fqdn: '', description: '' }) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [fetchError, setFetchError] = useState('') const [loadingSpace, setLoadingSpace] = useState(true) const [showDeleteModal, setShowDeleteModal] = useState(false) const [fqdnToDelete, setFqdnToDelete] = useState(null) const [confirmChecked, setConfirmChecked] = useState(false) const [selectedFqdn, setSelectedFqdn] = useState(null) const [csrData, setCsrData] = useState(null) const [csrHistory, setCsrHistory] = useState([]) const [uploadingCSR, setUploadingCSR] = useState(false) const [csrError, setCsrError] = useState('') const [showCSRModal, setShowCSRModal] = useState(false) const [showCSRDropdown, setShowCSRDropdown] = useState({}) const [copiedFqdnId, setCopiedFqdnId] = useState(null) const [showSignCSRModal, setShowSignCSRModal] = useState(false) const [signCSRStep, setSignCSRStep] = useState(1) const [selectedProvider, setSelectedProvider] = useState(null) const [providers, setProviders] = useState([]) const [providerTestResult, setProviderTestResult] = useState(null) const [signingCSR, setSigningCSR] = useState(false) const [signResult, setSignResult] = useState(null) const [showCertificatesModal, setShowCertificatesModal] = useState(false) const [certificates, setCertificates] = useState([]) const [loadingCertificates, setLoadingCertificates] = useState(false) const [refreshingCertificate, setRefreshingCertificate] = useState(null) useEffect(() => { fetchSpace() fetchFqdns() }, [id]) const fetchSpace = async () => { try { setLoadingSpace(true) const response = await authFetch('/api/spaces') if (response.ok) { const spaces = await response.json() const foundSpace = spaces.find(s => s.id === id) if (foundSpace) { setSpace(foundSpace) } else { setFetchError('Space nicht gefunden') } } else { setFetchError('Fehler beim Laden des Space') } } catch (err) { console.error('Error fetching space:', err) setFetchError('Fehler beim Laden des Space') } finally { setLoadingSpace(false) } } const fetchFqdns = async () => { try { setFetchError('') const response = await authFetch(`/api/spaces/${id}/fqdns`) if (response.ok) { const data = await response.json() setFqdns(Array.isArray(data) ? data : []) } else { if (response.status !== 404) { const errorText = `Fehler beim Abrufen der FQDNs: ${response.status}` console.error(errorText) setFetchError(errorText) } setFqdns([]) } } catch (err) { console.error('Error fetching fqdns:', err) setFqdns([]) } } const handleSubmit = async (e) => { e.preventDefault() setError('') setLoading(true) if (!formData.fqdn.trim()) { setError('Bitte geben Sie einen FQDN ein.') setLoading(false) return } // Einfache FQDN-Validierung const fqdnPattern = /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/ if (!fqdnPattern.test(formData.fqdn.trim())) { setError('Bitte geben Sie einen gültigen FQDN ein (z.B. example.com, subdomain.example.com)') setLoading(false) return } try { const response = await authFetch(`/api/spaces/${id}/fqdns`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData), }) if (response.ok) { const newFqdn = await response.json() setFqdns([...fqdns, newFqdn]) setFormData({ fqdn: '', description: '' }) setShowForm(false) // Aktualisiere Berechtigungen nach dem Erstellen eines FQDNs refreshPermissions() } else { let errorMessage = 'Fehler beim Erstellen des FQDN' try { const errorText = await response.text() if (errorText) { // Versuche JSON zu parsen try { const errorData = JSON.parse(errorText) errorMessage = errorData.error || errorText } catch { // Wenn kein JSON, verwende den Text direkt errorMessage = errorText } } else if (response.status === 409) { errorMessage = 'Dieser FQDN existiert bereits' } } catch (err) { // Fallback auf Standard-Fehlermeldung if (response.status === 409) { errorMessage = 'Dieser FQDN existiert bereits' } } setError(errorMessage) } } catch (err) { setError('Fehler beim Erstellen des FQDN') console.error('Error creating fqdn:', err) } finally { setLoading(false) } } const handleDelete = (fqdn) => { setFqdnToDelete(fqdn) setShowDeleteModal(true) setConfirmChecked(false) } const confirmDelete = async () => { if (!confirmChecked || !fqdnToDelete) { return } try { const response = await authFetch(`/api/spaces/${id}/fqdns/${fqdnToDelete.id}`, { method: 'DELETE', }) if (response.ok) { setFqdns(fqdns.filter(fqdn => fqdn.id !== fqdnToDelete.id)) setShowDeleteModal(false) setFqdnToDelete(null) setConfirmChecked(false) // Aktualisiere Berechtigungen nach dem Löschen eines FQDNs refreshPermissions() } else { const errorData = await response.json().catch(() => ({ error: 'Fehler beim Löschen' })) alert(errorData.error || 'Fehler beim Löschen des FQDN') } } catch (err) { console.error('Error deleting fqdn:', err) alert('Fehler beim Löschen des FQDN') } } const cancelDelete = () => { setShowDeleteModal(false) setFqdnToDelete(null) setConfirmChecked(false) } const copyFqdnIdToClipboard = async (fqdnId, e) => { e.stopPropagation() try { await navigator.clipboard.writeText(fqdnId) setCopiedFqdnId(fqdnId) setTimeout(() => setCopiedFqdnId(null), 2000) } catch (err) { console.error('Fehler beim Kopieren:', err) } } const handleCSRUpload = async (fqdn, file) => { if (!file) { setCsrError('Bitte wählen Sie eine Datei aus') return } setUploadingCSR(true) setCsrError('') const formData = new FormData() formData.append('csr', file) formData.append('spaceId', id) formData.append('fqdn', fqdn.fqdn) try { const response = await authFetch(`/api/spaces/${id}/fqdns/${fqdn.id}/csr`, { method: 'POST', body: formData, }) if (response.ok) { const csr = await response.json() // Füge den neuen CSR zur History hinzu (nur wenn der Bereich bereits geöffnet ist) if (showCSRDropdown[fqdn.id]) { const newCsrWithFqdnId = { ...csr, fqdnId: fqdn.id } setCsrHistory(prev => { const filtered = prev.filter(csrItem => csrItem.fqdnId !== fqdn.id) // Füge den neuen CSR am Anfang hinzu (neuester zuerst) return [newCsrWithFqdnId, ...filtered] }) } setCsrData(csr) setSelectedFqdn(fqdn) setShowCSRModal(true) // Aktualisiere die FQDN-Liste fetchFqdns() } else { const errorText = await response.text() setCsrError(errorText || 'Fehler beim Hochladen des CSR') } } catch (err) { console.error('Error uploading CSR:', err) setCsrError('Fehler beim Hochladen des CSR') } finally { setUploadingCSR(false) } } const fetchCSR = async (fqdn) => { try { const response = await authFetch(`/api/spaces/${id}/fqdns/${fqdn.id}/csr`) if (response.ok) { const csr = await response.json() if (csr) { setCsrData(csr) setSelectedFqdn(fqdn) } else { setCsrData(null) setSelectedFqdn(fqdn) } } } catch (err) { console.error('Error fetching CSR:', err) } } const handleViewCSR = async (fqdn) => { setSelectedFqdn(fqdn) setCsrError('') setShowCSRModal(true) // Lade neuesten CSR und alle CSRs für History try { // Lade neuesten CSR const latestResponse = await authFetch(`/api/spaces/${id}/fqdns/${fqdn.id}/csr?latest=true`) if (latestResponse.ok) { const csr = await latestResponse.json() setCsrData(csr || null) } else { setCsrData(null) } // Lade alle CSRs für History const historyResponse = await authFetch(`/api/spaces/${id}/fqdns/${fqdn.id}/csr`) if (historyResponse.ok) { const history = await historyResponse.json() setCsrHistory(Array.isArray(history) ? history : []) } else { setCsrHistory([]) } } catch (err) { console.error('Error fetching CSR:', err) setCsrData(null) setCsrHistory([]) } } const handleSelectCSR = (csr) => { setCsrData(csr) setShowCSRDropdown({}) } const closeCSRModal = () => { setShowCSRModal(false) setSelectedFqdn(null) setCsrData(null) setCsrError('') setCsrHistory([]) } const handleChange = (e) => { setFormData({ ...formData, [e.target.name]: e.target.value }) } const handleRequestSigning = async (fqdn) => { setSelectedFqdn(fqdn) setSignCSRStep(1) setSelectedProvider(null) setProviderTestResult(null) setSignResult(null) // Lade neuesten CSR try { const response = await authFetch(`/api/spaces/${id}/fqdns/${fqdn.id}/csr?latest=true`) if (response.ok) { const csr = await response.json() setCsrData(csr) } } catch (err) { console.error('Error fetching CSR:', err) } // Lade Provider try { const response = await authFetch('/api/providers') if (response.ok) { const providersData = await response.json() setProviders(providersData.filter(p => p.enabled)) } } catch (err) { console.error('Error fetching providers:', err) } setShowSignCSRModal(true) } const handleTestProvider = async (providerId) => { setProviderTestResult(null) try { const response = await authFetch(`/api/providers/${providerId}/test`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }) const result = await response.json() setProviderTestResult(result) } catch (err) { setProviderTestResult({ success: false, message: 'Fehler beim Testen des Providers' }) } } const handleSignCSR = async () => { if (!selectedProvider || !selectedFqdn) return setSigningCSR(true) setSignResult(null) try { const response = await authFetch(`/api/spaces/${id}/fqdns/${selectedFqdn.id}/csr/sign`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ providerId: selectedProvider.id }) }) const result = await response.json() setSignResult(result) if (result.success) { // Lade Zertifikate automatisch neu, um das neue Zertifikat anzuzeigen try { const certResponse = await authFetch(`/api/spaces/${id}/fqdns/${selectedFqdn.id}/certificates`) if (certResponse.ok) { const certs = await certResponse.json() setCertificates(certs) } } catch (err) { console.error('Fehler beim Laden der Zertifikate nach Signierung:', err) } } } catch (err) { setSignResult({ success: false, message: 'Fehler beim Signieren des CSR' }) } finally { setSigningCSR(false) } } const closeSignCSRModal = () => { setShowSignCSRModal(false) setSignCSRStep(1) setSelectedProvider(null) setProviderTestResult(null) setSignResult(null) setSelectedFqdn(null) } const handleViewCertificates = async (fqdn) => { setSelectedFqdn(fqdn) setLoadingCertificates(true) setCertificates([]) try { const response = await authFetch(`/api/spaces/${id}/fqdns/${fqdn.id}/certificates`) if (response.ok) { const certs = await response.json() setCertificates(certs) } else { console.error('Fehler beim Laden der Zertifikate') } } catch (err) { console.error('Error fetching certificates:', err) } finally { setLoadingCertificates(false) setShowCertificatesModal(true) } } const handleRefreshCertificate = async (cert) => { setRefreshingCertificate(cert.id) try { const response = await authFetch(`/api/spaces/${id}/fqdns/${selectedFqdn.id}/certificates/${cert.id}/refresh`, { method: 'POST' }) if (response.ok) { const result = await response.json() // Aktualisiere Zertifikat in der Liste setCertificates(prev => prev.map(c => c.id === cert.id ? { ...c, certificatePEM: result.certificatePEM } : c )) } } catch (err) { console.error('Error refreshing certificate:', err) } finally { setRefreshingCertificate(null) } } const closeCertificatesModal = () => { setShowCertificatesModal(false) setCertificates([]) setSelectedFqdn(null) } if (loadingSpace) { return (
Lade Space...
{fetchError || 'Space nicht gefunden'}
{space.description}
)}{fetchError}
Noch keine FQDNs vorhanden. Erstellen Sie Ihren ersten FQDN!
) : ({fqdn.description}
)}Erstellt: {fqdn.createdAt ? new Date(fqdn.createdAt).toLocaleString('de-DE') : 'Unbekannt'}
CSR wird hochgeladen...
{csrError}
{csrData.subject}
{csrData.publicKeyAlgorithm}
{csrData.signatureAlgorithm}
{csrData.keySize} bits
{new Date(csrData.createdAt).toLocaleString('de-DE')}
{csrData.csrPem}
Kein CSR für diesen FQDN vorhanden.
Laden Sie einen CSR über den Upload-Button hoch.
Möchten Sie den FQDN {fqdnToDelete.fqdn} wirklich löschen?
Diese Aktion kann nicht rückgängig gemacht werden.
{csrData.subject}
{csrData.publicKeyAlgorithm}
{csrData.keySize} bits
Kein CSR gefunden für diesen FQDN.
)}Keine aktivierten Provider verfügbar.
)}{selectedProvider.description}
{providerTestResult.message || (providerTestResult.success ? 'Verbindung erfolgreich' : 'Verbindung fehlgeschlagen')}
Bereit zum Einreichen:
{signResult.message || 'Fehler beim Signieren'}
Lade Zertifikate...
) : certificates.length === 0 ? (Keine Zertifikate vorhanden
) : ({certificates.length} {certificates.length === 1 ? 'Zertifikat' : 'Zertifikate'} gefunden
Interne UID:{' '} {cert.id}
Erstellt:{' '} {new Date(cert.createdAt).toLocaleString('de-DE')}
Status:{' '} {cert.status}
{cert.providerId && (Provider:{' '} {cert.providerId}
)}
{cert.certificatePEM}