overcuriousity 47eb5ad72a progress
2025-07-14 15:15:57 +02:00

171 lines
6.4 KiB
Plaintext

---
import BaseLayout from '../layouts/BaseLayout.astro';
import ToolCard from '../components/ToolCard.astro';
import ToolFilters from '../components/ToolFilters.astro';
import ToolMatrix from '../components/ToolMatrix.astro';
import { promises as fs } from 'fs';
import { load } from 'js-yaml';
import path from 'path';
// Load tools data
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
const yamlContent = await fs.readFile(yamlPath, 'utf8');
const data = load(yamlContent) as any;
const tools = data.tools;
---
<BaseLayout title="Home">
<!-- Hero Section -->
<section style="text-align: center; padding: 3rem 0; border-bottom: 1px solid var(--color-border);">
<h1 style="margin-bottom: 1rem;">DFIR Tools Hub</h1>
<p class="text-muted" style="font-size: 1.125rem; max-width: 800px; margin: 0 auto;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.
</p>
</section>
<!-- Filters Section -->
<section style="padding: 2rem 0;">
<ToolFilters />
</section>
<!-- Tools Grid -->
<section id="tools-grid" style="padding-bottom: 2rem;">
<div class="grid grid-cols-3 gap-4" id="tools-container">
{tools.map((tool: any) => (
<ToolCard tool={tool} />
))}
</div>
<!-- No results message -->
<div id="no-results" style="display: none; text-align: center; padding: 4rem 0;">
<p class="text-muted" style="font-size: 1.125rem;">No tools found matching your criteria.</p>
</div>
</section>
<!-- Matrix View -->
<ToolMatrix />
</BaseLayout>
<script>
// Handle view changes and filtering
document.addEventListener('DOMContentLoaded', () => {
const toolsContainer = document.getElementById('tools-container');
const toolsGrid = document.getElementById('tools-grid');
const matrixContainer = document.getElementById('matrix-container');
const noResults = document.getElementById('no-results');
// Guard against null elements
if (!toolsContainer || !toolsGrid || !matrixContainer || !noResults) {
console.error('Required DOM elements not found');
return;
}
// Initial tools HTML
const initialToolsHTML = toolsContainer.innerHTML;
// Handle filtered results
window.addEventListener('toolsFiltered', (event: Event) => {
const customEvent = event as CustomEvent;
const filtered = customEvent.detail;
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
if (currentView === 'matrix') {
// Matrix view handles its own rendering
return;
}
// Clear container
toolsContainer.innerHTML = '';
if (filtered.length === 0) {
noResults.style.display = 'block';
} else {
noResults.style.display = 'none';
// Render filtered tools
filtered.forEach((tool: any) => {
const toolCard = createToolCard(tool);
toolsContainer.appendChild(toolCard);
});
}
});
// Handle view changes
window.addEventListener('viewChanged', (event: Event) => {
const customEvent = event as CustomEvent;
const view = customEvent.detail;
if (view === 'matrix') {
toolsGrid.style.display = 'none';
matrixContainer.style.display = 'block';
} else {
toolsGrid.style.display = 'block';
matrixContainer.style.display = 'none';
}
});
// Create tool card element
function createToolCard(tool: any): HTMLElement {
const cardDiv = document.createElement('div');
const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Proprietary' ? 'card card-oss' : 'card');
cardDiv.className = cardClass;
cardDiv.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 0.75rem;">
<h3 style="margin: 0;">${tool.name}</h3>
<div style="display: flex; gap: 0.5rem;">
${tool.isHosted ? '<span class="badge badge-primary">Self-Hosted</span>' : ''}
${tool.license !== 'Proprietary' ? '<span class="badge badge-success">Open Source</span>' : ''}
</div>
</div>
<p class="text-muted" style="font-size: 0.875rem; margin-bottom: 1rem;">
${tool.description}
</p>
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1rem;">
<div style="display: flex; align-items: center; gap: 0.25rem;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<line x1="9" y1="9" x2="15" y2="9"></line>
<line x1="9" y1="15" x2="15" y2="15"></line>
</svg>
<span class="text-muted" style="font-size: 0.75rem;">
${tool.platforms.join(', ')}
</span>
</div>
<div style="display: flex; align-items: center; gap: 0.25rem;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="M12 6v6l4 2"></path>
</svg>
<span class="text-muted" style="font-size: 0.75rem;">
${tool.skillLevel}
</span>
</div>
<div style="display: flex; align-items: center; gap: 0.25rem;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
</svg>
<span class="text-muted" style="font-size: 0.75rem;">
${tool.license}
</span>
</div>
</div>
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem; margin-bottom: 1rem;">
${tool.tags.map((tag: string) => `<span class="tag">${tag}</span>`).join('')}
</div>
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
${tool.isHosted ? 'Access Service' : 'Visit Website'}
</a>
`;
return cardDiv;
}
});
</script>