Spaces:
No application file

OSINT / src /frontend /index.html
abello's picture
Upload 28 files
839a850 verified
Raw
History Blame
15.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OSINT Early Warning System</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #1a1a1a;
color: #e0e0e0;
line-height: 1.6;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 40px;
border-bottom: 2px solid #333;
padding-bottom: 20px;
}
.header h1 {
color: #006aff;
font-size: 2.5rem;
font-weight: 300;
margin-bottom: 10px;
}
.header p {
color: #888;
font-size: 1.1rem;
}
.search-section {
background: #2a2a2a;
border-radius: 8px;
padding: 30px;
margin-bottom: 30px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}
.search-form {
display: flex;
gap: 15px;
margin-bottom: 20px;
}
.search-input {
flex: 1;
padding: 15px;
background: #1a1a1a;
border: 2px solid #444;
border-radius: 6px;
color: #e0e0e0;
font-size: 1rem;
transition: border-color 0.3s ease;
}
.search-input:focus {
outline: none;
border-color: #00ff88;
}
.search-input::placeholder {
color: #666;
}
.search-btn {
padding: 15px 30px;
background: #00ff88;
color: #1a1a1a;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
transition: all 0.3s ease;
}
.search-btn:hover:not(:disabled) {
background: #00cc6a;
transform: translateY(-2px);
}
.search-btn:disabled {
background: #555;
color: #888;
cursor: not-allowed;
transform: none;
}
.status-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 30px;
}
.status-card {
background: #2a2a2a;
border-radius: 6px;
padding: 20px;
text-align: center;
border-left: 4px solid #00ff88;
}
.status-card h3 {
color: #00ff88;
font-size: 0.9rem;
margin-bottom: 5px;
font-weight: 500;
}
.status-card p {
font-size: 1.2rem;
font-weight: 600;
}
.loading {
display: none;
text-align: center;
padding: 40px;
color: #00ff88;
}
.loading.active {
display: block;
}
.spinner {
border: 4px solid #333;
border-top: 4px solid #00ff88;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.results-section {
background: #2a2a2a;
border-radius: 8px;
padding: 30px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
display: none;
}
.results-section.active {
display: block;
}
.results-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
padding-bottom: 15px;
border-bottom: 1px solid #444;
}
.results-title {
color: #00ff88;
font-size: 1.5rem;
font-weight: 600;
}
.risk-badge {
padding: 8px 16px;
border-radius: 20px;
font-size: 0.9rem;
font-weight: 600;
text-transform: uppercase;
}
.risk-low { background: #28a745; color: white; }
.risk-medium { background: #ffc107; color: black; }
.risk-high { background: #fd7e14; color: white; }
.risk-critical { background: #dc3545; color: white; }
.risk-unknown { background: #6c757d; color: white; }
.results-content {
white-space: pre-wrap;
line-height: 1.8;
font-size: 1rem;
}
.results-meta {
margin-top: 25px;
padding-top: 20px;
border-top: 1px solid #444;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
font-size: 0.9rem;
color: #888;
}
.meta-item {
display: flex;
justify-content: space-between;
}
.meta-label {
font-weight: 600;
}
.error {
background: #dc3545;
color: white;
padding: 15px;
border-radius: 6px;
margin: 20px 0;
display: none;
}
.error.active {
display: block;
}
.examples {
margin-top: 15px;
color: #666;
font-size: 0.9rem;
}
.examples strong {
color: #888;
}
.example-btn {
background: none;
border: 1px solid #444;
color: #888;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
margin: 2px;
font-size: 0.8rem;
transition: all 0.3s ease;
}
.example-btn:hover {
border-color: #00ff88;
color: #00ff88;
}
@media (max-width: 768px) {
.container {
padding: 15px;
}
.search-form {
flex-direction: column;
}
.header h1 {
font-size: 2rem;
}
.status-section {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<header class="header">
<h1>OSINT Early Warning</h1>
<p>Comprehensive threat intelligence analysis</p>
</header>
<section class="search-section">
<form class="search-form" id="searchForm">
<input
type="text"
class="search-input"
id="entityInput"
placeholder="Enter IP, domain, hash, CVE, or other entity..."
required
>
<button type="submit" class="search-btn" id="searchBtn">
Analyze
</button>
</form>
<div class="examples">
<strong>Examples:</strong>
<button class="example-btn" onclick="setExample('8.8.8.8')">IP Address</button>
<button class="example-btn" onclick="setExample('google.com')">Domain</button>
<button class="example-btn" onclick="setExample('CVE-2024-0001')">CVE</button>
<button class="example-btn" onclick="setExample('44d88612fea8a8f36de82e1278abb02f')">Hash</button>
</div>
</section>
<section class="status-section" id="statusSection">
<div class="status-card">
<h3>System Status</h3>
<p id="systemStatus">Ready</p>
</div>
<div class="status-card">
<h3>Active Sources</h3>
<p id="activeSources">Loading...</p>
</div>
<div class="status-card">
<h3>Last Analysis</h3>
<p id="lastAnalysis">None</p>
</div>
<div class="status-card">
<h3>Response Time</h3>
<p id="responseTime">-</p>
</div>
</section>
<div class="loading" id="loadingSection">
<div class="spinner"></div>
<p>Analyzing entity across multiple OSINT sources...</p>
<p><small>This may take 30-60 seconds</small></p>
</div>
<div class="error" id="errorSection"></div>
<section class="results-section" id="resultsSection">
<div class="results-header">
<h2 class="results-title" id="resultsTitle">Analysis Results</h2>
<span class="risk-badge" id="riskBadge">Unknown</span>
</div>
<div class="results-content" id="resultsContent"></div>
<div class="results-meta" id="resultsMeta"></div>
</section>
</div>
<script>
let analysisStartTime;
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
loadSourcesStatus();
setupEventListeners();
});
function setupEventListeners() {
const form = document.getElementById('searchForm');
form.addEventListener('submit', handleSearch);
}
async function handleSearch(e) {
e.preventDefault();
const entity = document.getElementById('entityInput').value.trim();
if (!entity) return;
showLoading();
hideError();
hideResults();
analysisStartTime = Date.now();
updateSystemStatus('Analyzing...');
try {
const response = await fetch(`/api/osint?entity=${encodeURIComponent(entity)}`);
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Analysis failed');
}
displayResults(entity, data);
updateSystemStatus('Complete');
updateLastAnalysis(entity);
updateResponseTime();
} catch (error) {
showError(error.message);
updateSystemStatus('Error');
console.error('Analysis error:', error);
} finally {
hideLoading();
}
}
function showLoading() {
document.getElementById('loadingSection').classList.add('active');
document.getElementById('searchBtn').disabled = true;
document.getElementById('searchBtn').textContent = 'Analyzing...';
}
function hideLoading() {
document.getElementById('loadingSection').classList.remove('active');
document.getElementById('searchBtn').disabled = false;
document.getElementById('searchBtn').textContent = 'Analyze';
}
function showError(message) {
const errorSection = document.getElementById('errorSection');
errorSection.textContent = message;
errorSection.classList.add('active');
}
function hideError() {
document.getElementById('errorSection').classList.remove('active');
}
function hideResults() {
document.getElementById('resultsSection').classList.remove('active');
}
function displayResults(entity, data) {
const resultsSection = document.getElementById('resultsSection');
const resultsTitle = document.getElementById('resultsTitle');
const riskBadge = document.getElementById('riskBadge');
const resultsContent = document.getElementById('resultsContent');
const resultsMeta = document.getElementById('resultsMeta');
// Set title and risk badge
resultsTitle.textContent = `Analysis: ${entity}`;
riskBadge.textContent = data.risk_level || 'Unknown';
riskBadge.className = `risk-badge risk-${(data.risk_level || 'unknown').toLowerCase()}`;
// Set content
resultsContent.textContent = data.summary || 'No analysis available';
// Set metadata
resultsMeta.innerHTML = `
<div class="meta-item">
<span class="meta-label">Confidence:</span>
<span>${(data.confidence * 100).toFixed(1)}%</span>
</div>
<div class="meta-item">
<span class="meta-label">Sources:</span>
<span>${data.sources_used ? data.sources_used.join(', ') : 'None'}</span>
</div>
<div class="meta-item">
<span class="meta-label">Analysis Time:</span>
<span>${data.timestamp ? new Date(data.timestamp).toLocaleString() : 'Unknown'}</span>
</div>
<div class="meta-item">
<span class="meta-label">Response Time:</span>
<span>${((Date.now() - analysisStartTime) / 1000).toFixed(1)}s</span>
</div>
`;
resultsSection.classList.add('active');
}
async function loadSourcesStatus() {
try {
const response = await fetch('/api/sources');
const data = await response.json();
const activeSources = data.sources.filter(s => s.status === 'active').length;
document.getElementById('activeSources').textContent = activeSources;
} catch (error) {
document.getElementById('activeSources').textContent = 'Error';
console.error('Failed to load sources:', error);
}
}
function updateSystemStatus(status) {
document.getElementById('systemStatus').textContent = status;
}
function updateLastAnalysis(entity) {
const now = new Date();
const timeStr = now.toLocaleTimeString();
document.getElementById('lastAnalysis').textContent = `${entity} (${timeStr})`;
}
function updateResponseTime() {
if (analysisStartTime) {
const responseTime = ((Date.now() - analysisStartTime) / 1000).toFixed(1);
document.getElementById('responseTime').textContent = `${responseTime}s`;
}
}
function setExample(value) {
document.getElementById('entityInput').value = value;
document.getElementById('entityInput').focus();
}
// Health check
setInterval(async () => {
try {
const response = await fetch('/api/health');
if (response.ok) {
updateSystemStatus('Ready');
} else {
updateSystemStatus('Warning');
}
} catch (error) {
updateSystemStatus('Offline');
}
}, 30000); // Check every 30 seconds
</script>
</body>
</html>