added required code changes, components, and packages for login feature
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Search, FileText, AlertCircle, Download, Upload, Eye, Filter, CheckCircle, XCircle, Loader, Trash2, Plus } from 'lucide-react';
|
||||
import { useAuth } from './contexts/AuthContext';
|
||||
import LoginForm from './components/LoginForm';
|
||||
import UserMenu from './components/UserMenu';
|
||||
import UserManagement from './components/UserManagement';
|
||||
|
||||
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
|
||||
const API_HOST = process.env.REACT_APP_API_HOST || 'http://localhost:3001';
|
||||
@@ -7,6 +11,7 @@ const API_HOST = process.env.REACT_APP_API_HOST || 'http://localhost:3001';
|
||||
const severityLevels = ['All Severities', 'Critical', 'High', 'Medium', 'Low'];
|
||||
|
||||
export default function App() {
|
||||
const { isAuthenticated, loading: authLoading, canWrite, isAdmin } = useAuth();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedVendor, setSelectedVendor] = useState('All Vendors');
|
||||
const [selectedSeverity, setSelectedSeverity] = useState('All Severities');
|
||||
@@ -21,6 +26,7 @@ export default function App() {
|
||||
const [quickCheckCVE, setQuickCheckCVE] = useState('');
|
||||
const [quickCheckResult, setQuickCheckResult] = useState(null);
|
||||
const [showAddCVE, setShowAddCVE] = useState(false);
|
||||
const [showUserManagement, setShowUserManagement] = useState(false);
|
||||
const [newCVE, setNewCVE] = useState({
|
||||
cve_id: '',
|
||||
vendor: '',
|
||||
@@ -30,19 +36,6 @@ export default function App() {
|
||||
});
|
||||
const [uploadingFile, setUploadingFile] = useState(false);
|
||||
|
||||
// Fetch CVEs from API
|
||||
useEffect(() => {
|
||||
fetchCVEs();
|
||||
fetchVendors();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Refetch when filters change
|
||||
useEffect(() => {
|
||||
fetchCVEs();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchQuery, selectedVendor, selectedSeverity]);
|
||||
|
||||
const fetchCVEs = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
@@ -52,7 +45,9 @@ export default function App() {
|
||||
if (selectedVendor !== 'All Vendors') params.append('vendor', selectedVendor);
|
||||
if (selectedSeverity !== 'All Severities') params.append('severity', selectedSeverity);
|
||||
|
||||
const response = await fetch(`${API_BASE}/cves?${params}`);
|
||||
const response = await fetch(`${API_BASE}/cves?${params}`, {
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to fetch CVEs');
|
||||
const data = await response.json();
|
||||
setCves(data);
|
||||
@@ -66,7 +61,9 @@ export default function App() {
|
||||
|
||||
const fetchVendors = async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/vendors`);
|
||||
const response = await fetch(`${API_BASE}/vendors`, {
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to fetch vendors');
|
||||
const data = await response.json();
|
||||
setVendors(['All Vendors', ...data]);
|
||||
@@ -78,9 +75,11 @@ export default function App() {
|
||||
const fetchDocuments = async (cveId, vendor) => {
|
||||
const key = `${cveId}-${vendor}`;
|
||||
if (cveDocuments[key]) return;
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/cves/${cveId}/documents?vendor=${vendor}`);
|
||||
const response = await fetch(`${API_BASE}/cves/${cveId}/documents?vendor=${vendor}`, {
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to fetch documents');
|
||||
const data = await response.json();
|
||||
setCveDocuments(prev => ({ ...prev, [key]: data }));
|
||||
@@ -91,9 +90,11 @@ export default function App() {
|
||||
|
||||
const quickCheckCVEStatus = async () => {
|
||||
if (!quickCheckCVE.trim()) return;
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/cves/check/${quickCheckCVE.trim()}`);
|
||||
const response = await fetch(`${API_BASE}/cves/check/${quickCheckCVE.trim()}`, {
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to check CVE');
|
||||
const data = await response.json();
|
||||
setQuickCheckResult(data);
|
||||
@@ -104,7 +105,6 @@ export default function App() {
|
||||
};
|
||||
|
||||
const handleViewDocuments = async (cveId, vendor) => {
|
||||
const key = `${cveId}-${vendor}`;
|
||||
if (selectedCVE === cveId && selectedVendorView === vendor) {
|
||||
setSelectedCVE(null);
|
||||
setSelectedVendorView(null);
|
||||
@@ -143,6 +143,7 @@ export default function App() {
|
||||
const response = await fetch(`${API_BASE}/cves`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(newCVE)
|
||||
});
|
||||
|
||||
@@ -195,6 +196,7 @@ export default function App() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/cves/${cveId}/documents`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: formData
|
||||
});
|
||||
|
||||
@@ -219,14 +221,15 @@ export default function App() {
|
||||
if (!window.confirm('Are you sure you want to delete this document?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/documents/${docId}`, {
|
||||
method: 'DELETE'
|
||||
method: 'DELETE',
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
|
||||
if (!response.ok) throw new Error('Failed to delete document');
|
||||
|
||||
|
||||
alert('Document deleted successfully!');
|
||||
const key = `${cveId}-${vendor}`;
|
||||
delete cveDocuments[key];
|
||||
@@ -237,6 +240,40 @@ export default function App() {
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch CVEs from API when authenticated
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
fetchCVEs();
|
||||
fetchVendors();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isAuthenticated]);
|
||||
|
||||
// Refetch when filters change
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
fetchCVEs();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchQuery, selectedVendor, selectedSeverity]);
|
||||
|
||||
// Show loading while checking auth
|
||||
if (authLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-100 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<Loader className="w-12 h-12 text-[#0476D9] mx-auto animate-spin" />
|
||||
<p className="text-gray-600 mt-4">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Show login if not authenticated
|
||||
if (!isAuthenticated) {
|
||||
return <LoginForm />;
|
||||
}
|
||||
|
||||
// Group CVEs by CVE ID
|
||||
const groupedCVEs = cves.reduce((acc, cve) => {
|
||||
if (!acc[cve.cve_id]) {
|
||||
@@ -257,15 +294,25 @@ export default function App() {
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">CVE Dashboard</h1>
|
||||
<p className="text-gray-600">Query vulnerabilities, manage vendors, and attach documentation</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowAddCVE(true)}
|
||||
className="px-4 py-2 bg-[#0476D9] text-white rounded-lg hover:bg-[#0360B8] transition-colors flex items-center gap-2 shadow-md"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
Add CVE/Vendor
|
||||
</button>
|
||||
<div className="flex items-center gap-4">
|
||||
{canWrite() && (
|
||||
<button
|
||||
onClick={() => setShowAddCVE(true)}
|
||||
className="px-4 py-2 bg-[#0476D9] text-white rounded-lg hover:bg-[#0360B8] transition-colors flex items-center gap-2 shadow-md"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
Add CVE/Vendor
|
||||
</button>
|
||||
)}
|
||||
<UserMenu onManageUsers={() => setShowUserManagement(true)} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* User Management Modal */}
|
||||
{showUserManagement && (
|
||||
<UserManagement onClose={() => setShowUserManagement(false)} />
|
||||
)}
|
||||
|
||||
{/* Add CVE Modal */}
|
||||
{showAddCVE && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
@@ -634,6 +681,7 @@ export default function App() {
|
||||
>
|
||||
View
|
||||
</a>
|
||||
{isAdmin() && (
|
||||
<button
|
||||
onClick={() => handleDeleteDocument(doc.id, cve.cve_id, cve.vendor)}
|
||||
className="px-3 py-1 text-sm text-red-600 hover:bg-red-50 rounded transition-colors border border-red-600 flex items-center gap-1"
|
||||
@@ -641,6 +689,7 @@ export default function App() {
|
||||
<Trash2 className="w-3 h-3" />
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -648,14 +697,16 @@ export default function App() {
|
||||
) : (
|
||||
<p className="text-sm text-gray-500 italic">No documents attached yet</p>
|
||||
)}
|
||||
<button
|
||||
onClick={() => handleFileUpload(cve.cve_id, cve.vendor)}
|
||||
disabled={uploadingFile}
|
||||
className="mt-3 px-4 py-2 text-sm text-gray-600 hover:bg-gray-100 rounded-lg transition-colors flex items-center gap-2 disabled:opacity-50 border border-gray-300"
|
||||
>
|
||||
<Upload className="w-4 h-4" />
|
||||
{uploadingFile ? 'Uploading...' : 'Upload Document'}
|
||||
</button>
|
||||
{canWrite() && (
|
||||
<button
|
||||
onClick={() => handleFileUpload(cve.cve_id, cve.vendor)}
|
||||
disabled={uploadingFile}
|
||||
className="mt-3 px-4 py-2 text-sm text-gray-600 hover:bg-gray-100 rounded-lg transition-colors flex items-center gap-2 disabled:opacity-50 border border-gray-300"
|
||||
>
|
||||
<Upload className="w-4 h-4" />
|
||||
{uploadingFile ? 'Uploading...' : 'Upload Document'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user