import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Plus, Pencil, Trash2, Shield, Copy, Eye, Info } from 'lucide-react'; import { useSecurityHeaderProfiles, useCreateSecurityHeaderProfile, useUpdateSecurityHeaderProfile, useDeleteSecurityHeaderProfile, } from '../hooks/useSecurityHeaders'; import { SecurityHeaderProfileForm } from '../components/SecurityHeaderProfileForm'; import { SecurityScoreDisplay } from '../components/SecurityScoreDisplay'; import type { SecurityHeaderProfile, CreateProfileRequest } from '../api/securityHeaders'; import { createBackup } from '../api/backups'; import toast from 'react-hot-toast'; import { PageShell } from '../components/layout/PageShell'; import { Button, Alert, Card, EmptyState, SkeletonTable, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, } from '../components/ui'; export default function SecurityHeaders() { const { t } = useTranslation(); const { data: profiles, isLoading } = useSecurityHeaderProfiles(); const createMutation = useCreateSecurityHeaderProfile(); const updateMutation = useUpdateSecurityHeaderProfile(); const deleteMutation = useDeleteSecurityHeaderProfile(); const [showCreateForm, setShowCreateForm] = useState(false); const [editingProfile, setEditingProfile] = useState(null); const [showDeleteConfirm, setShowDeleteConfirm] = useState(null); const [isDeleting, setIsDeleting] = useState(false); const handleCreate = (data: CreateProfileRequest) => { createMutation.mutate(data, { onSuccess: () => setShowCreateForm(false), }); }; const handleUpdate = (data: CreateProfileRequest) => { if (!editingProfile) return; updateMutation.mutate( { id: editingProfile.id, data }, { onSuccess: () => setEditingProfile(null), } ); }; const handleDeleteWithBackup = async (profile: SecurityHeaderProfile) => { setIsDeleting(true); try { toast.loading(t('securityHeaders.creatingBackup'), { id: 'backup-toast' }); await createBackup(); toast.success(t('securityHeaders.backupCreated'), { id: 'backup-toast' }); deleteMutation.mutate(profile.id, { onSuccess: () => { setShowDeleteConfirm(null); setEditingProfile(null); toast.success(t('securityHeaders.deleteSuccess', { name: profile.name })); }, onError: (error: Error) => { toast.error(t('securityHeaders.deleteFailed', { error: error.message })); }, onSettled: () => { setIsDeleting(false); }, }); } catch { toast.error(t('securityHeaders.backupFailed'), { id: 'backup-toast' }); setIsDeleting(false); } }; const handleCloneProfile = (profile: SecurityHeaderProfile) => { const clonedData: CreateProfileRequest = { name: `${profile.name} (Copy)`, description: profile.description, hsts_enabled: profile.hsts_enabled, hsts_max_age: profile.hsts_max_age, hsts_include_subdomains: profile.hsts_include_subdomains, hsts_preload: profile.hsts_preload, csp_enabled: profile.csp_enabled, csp_directives: profile.csp_directives, csp_report_only: profile.csp_report_only, csp_report_uri: profile.csp_report_uri, x_frame_options: profile.x_frame_options, x_content_type_options: profile.x_content_type_options, referrer_policy: profile.referrer_policy, permissions_policy: profile.permissions_policy, cross_origin_opener_policy: profile.cross_origin_opener_policy, cross_origin_resource_policy: profile.cross_origin_resource_policy, cross_origin_embedder_policy: profile.cross_origin_embedder_policy, xss_protection: profile.xss_protection, cache_control_no_store: profile.cache_control_no_store, }; createMutation.mutate(clonedData); }; const customProfiles = profiles?.filter((p: SecurityHeaderProfile) => !p.is_preset) || []; const presetProfiles = (profiles?.filter((p: SecurityHeaderProfile) => p.is_preset) || []) .sort((a, b) => a.security_score - b.security_score); // Get tooltip content for preset types const getPresetTooltip = (presetType: string): string => { switch (presetType) { case 'basic': return t('securityHeaders.presets.basic'); case 'api-friendly': return t('securityHeaders.presets.apiFriendly'); case 'strict': return t('securityHeaders.presets.strict'); case 'paranoid': return t('securityHeaders.presets.paranoid'); default: return ''; } }; return ( setShowCreateForm(true)}> {t('securityHeaders.createProfile')} } > {/* Info Alert */}

{t('securityHeaders.alertTitle')}

{t('securityHeaders.alertDescription')}

{/* Quick Presets (Read-Only) */} {presetProfiles.length > 0 && (

{t('securityHeaders.systemProfiles')}

{t('securityHeaders.systemProfilesDescription')}

{presetProfiles.map((profile: SecurityHeaderProfile) => (

{profile.name}

{profile.preset_type && getPresetTooltip(profile.preset_type) && ( {getPresetTooltip(profile.preset_type)} )}
{profile.description && (

{profile.description}

)}
))}
)} {/* Custom Profiles Section */}

{t('securityHeaders.customProfiles')}

{isLoading ? ( ) : customProfiles.length === 0 ? ( } title={t('securityHeaders.noCustomProfiles')} description={t('securityHeaders.noCustomProfilesDescription')} action={{ label: t('securityHeaders.createProfile'), onClick: () => setShowCreateForm(true), }} /> ) : (
{customProfiles.map((profile: SecurityHeaderProfile) => (

{profile.name}

{t('securityHeaders.updated')} {new Date(profile.updated_at).toLocaleDateString()}

{profile.description && (

{profile.description}

)}
))}
)}
{/* Create/Edit Dialog */} { if (!open) { setShowCreateForm(false); setEditingProfile(null); } }}> {editingProfile ? (editingProfile.is_preset ? t('securityHeaders.viewProfile') : t('securityHeaders.editProfile')) : t('securityHeaders.createProfileTitle')} { setShowCreateForm(false); setEditingProfile(null); }} onDelete={editingProfile && !editingProfile.is_preset ? () => setShowDeleteConfirm(editingProfile) : undefined} isLoading={createMutation.isPending || updateMutation.isPending} isDeleting={isDeleting} /> {/* Delete Confirmation Dialog */} !open && setShowDeleteConfirm(null)}> {t('securityHeaders.confirmDeletion')}

{t('securityHeaders.deleteConfirmMessage', { name: showDeleteConfirm?.name })}

); }