progress
This commit is contained in:
parent
043a2d32ac
commit
f4acf39ca7
101
src/components/ContributionButton.astro
Normal file
101
src/components/ContributionButton.astro
Normal file
@ -0,0 +1,101 @@
|
||||
---
|
||||
// src/components/ContributionButton.astro
|
||||
export interface Props {
|
||||
type: 'edit' | 'new' | 'write';
|
||||
toolName?: string;
|
||||
variant?: 'primary' | 'secondary' | 'small';
|
||||
text?: string;
|
||||
className?: string;
|
||||
style?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
type,
|
||||
toolName,
|
||||
variant = 'secondary',
|
||||
text,
|
||||
className = '',
|
||||
style = ''
|
||||
} = Astro.props;
|
||||
|
||||
// Generate appropriate URLs and text based on type
|
||||
let href: string;
|
||||
let defaultText: string;
|
||||
let icon: string;
|
||||
|
||||
switch (type) {
|
||||
case 'edit':
|
||||
href = `/contribute/tool?edit=${encodeURIComponent(toolName || '')}`;
|
||||
defaultText = 'Edit';
|
||||
icon = `<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
||||
<path d="M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>`;
|
||||
break;
|
||||
case 'new':
|
||||
href = '/contribute/tool';
|
||||
defaultText = 'Add Tool';
|
||||
icon = `<line x1="12" y1="5" x2="12" y2="19"/>
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>`;
|
||||
break;
|
||||
case 'write':
|
||||
href = '/contribute/knowledgebase';
|
||||
defaultText = 'Write Article';
|
||||
icon = `<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
<polyline points="10 9 9 9 8 9"/>`;
|
||||
break;
|
||||
default:
|
||||
href = '/contribute';
|
||||
defaultText = 'Contribute';
|
||||
icon = `<line x1="12" y1="5" x2="12" y2="19"/>
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>`;
|
||||
}
|
||||
|
||||
const displayText = text || defaultText;
|
||||
const buttonClass = `btn btn-${variant} ${className}`.trim();
|
||||
const iconSize = variant === 'small' ? '14' : '16';
|
||||
---
|
||||
|
||||
<a
|
||||
href={href}
|
||||
class={buttonClass}
|
||||
style={style}
|
||||
data-contribute-button={type}
|
||||
data-tool-name={toolName}
|
||||
title={`${displayText}${toolName ? `: ${toolName}` : ''}`}
|
||||
>
|
||||
<svg width={iconSize} height={iconSize} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
|
||||
<Fragment set:html={icon} />
|
||||
</svg>
|
||||
{displayText}
|
||||
</a>
|
||||
|
||||
<script>
|
||||
// Check authentication status and redirect if needed
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const contributeButtons = document.querySelectorAll('[data-contribute-button]');
|
||||
|
||||
contributeButtons.forEach(button => {
|
||||
button.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/status');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.authRequired && !data.authenticated) {
|
||||
const returnUrl = (button as HTMLAnchorElement).href;
|
||||
window.location.href = `/api/auth/login?returnTo=${encodeURIComponent(returnUrl)}`;
|
||||
} else {
|
||||
window.location.href = (button as HTMLAnchorElement).href;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth check failed:', error);
|
||||
// Fallback - proceed anyway
|
||||
window.location.href = (button as HTMLAnchorElement).href;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
@ -1,4 +1,5 @@
|
||||
---
|
||||
// src/components/Navigation.astro
|
||||
import ThemeToggle from './ThemeToggle.astro';
|
||||
|
||||
const currentPath = Astro.url.pathname;
|
||||
@ -24,6 +25,11 @@ const currentPath = Astro.url.pathname;
|
||||
~/knowledgebase
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/contribute" class={`nav-link ${currentPath.startsWith('/contribute') ? 'active' : ''}`}>
|
||||
~/contribute
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/status" class={`nav-link ${currentPath === '/status' ? 'active' : ''}`}>
|
||||
~/status
|
||||
@ -41,34 +47,3 @@ const currentPath = Astro.url.pathname;
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
/* Logo theme switching */
|
||||
.nav-logo-light {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-logo-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-logo-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-logo-dark {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Make brand clickable */
|
||||
.nav-brand {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.nav-brand:hover {
|
||||
text-decoration: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
@ -1,4 +1,8 @@
|
||||
---
|
||||
// src/components/ToolCard.astro (Updated)
|
||||
import ContributionButton from './ContributionButton.astro';
|
||||
import ShareButton from './ShareButton.astro';
|
||||
|
||||
export interface Props {
|
||||
tool: {
|
||||
name: string;
|
||||
@ -52,6 +56,7 @@ const cardClass = isConcept ? 'card card-concept tool-card' :
|
||||
<!-- Only show CC24-Server and Knowledgebase badges -->
|
||||
{!isMethod && hasValidProjectUrl && <span class="badge badge-primary">CC24-Server</span>}
|
||||
{hasKnowledgebase && <span class="badge badge-error">📖</span>}
|
||||
<ShareButton toolName={tool.name} context="card" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -101,20 +106,27 @@ const cardClass = isConcept ? 'card card-concept tool-card' :
|
||||
))}
|
||||
</div>
|
||||
|
||||
<!-- Buttons - Fixed at Bottom -->
|
||||
<!-- Buttons - Fixed at Bottom with Contribution Button -->
|
||||
<div class="tool-card-buttons" onclick="event.stopPropagation();">
|
||||
{isConcept ? (
|
||||
<!-- Concept button -->
|
||||
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary single-button" style="background-color: var(--color-concept); border-color: var(--color-concept);">
|
||||
<!-- Concept buttons with edit -->
|
||||
<div class="button-row">
|
||||
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="background-color: var(--color-concept); border-color: var(--color-concept); flex: 2;">
|
||||
Mehr erfahren
|
||||
</a>
|
||||
<ContributionButton type="edit" toolName={tool.name} variant="secondary" text="Edit" style="flex: 1; font-size: 0.75rem;" />
|
||||
</div>
|
||||
) : isMethod ? (
|
||||
<!-- Method button -->
|
||||
<a href={tool.projectUrl || tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary single-button" style="background-color: var(--color-method); border-color: var(--color-method);">
|
||||
<!-- Method buttons with edit -->
|
||||
<div class="button-row">
|
||||
<a href={tool.projectUrl || tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="background-color: var(--color-method); border-color: var(--color-method); flex: 2;">
|
||||
Zur Methode
|
||||
</a>
|
||||
<ContributionButton type="edit" toolName={tool.name} variant="secondary" text="Edit" style="flex: 1; font-size: 0.75rem;" />
|
||||
</div>
|
||||
) : hasValidProjectUrl ? (
|
||||
<!-- Two buttons for hosted tools -->
|
||||
<!-- Three buttons for hosted tools -->
|
||||
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
<div class="button-row">
|
||||
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-secondary">
|
||||
Homepage
|
||||
@ -123,11 +135,16 @@ const cardClass = isConcept ? 'card card-concept tool-card' :
|
||||
Zugreifen
|
||||
</a>
|
||||
</div>
|
||||
<ContributionButton type="edit" toolName={tool.name} variant="secondary" text="Edit Entry" className="single-button" />
|
||||
</div>
|
||||
) : (
|
||||
<!-- Single button for external tools -->
|
||||
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary single-button">
|
||||
<!-- Two buttons for external tools -->
|
||||
<div class="button-row">
|
||||
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="flex: 2;">
|
||||
Software-Homepage
|
||||
</a>
|
||||
<ContributionButton type="edit" toolName={tool.name} variant="secondary" text="Edit" style="flex: 1; font-size: 0.75rem;" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
@ -152,6 +152,9 @@ domains.forEach((domain: any) => {
|
||||
<div id="share-button-primary" style="display: none;">
|
||||
<!-- Share button will be populated by JavaScript -->
|
||||
</div>
|
||||
<div id="contribute-button-primary" style="display: none;">
|
||||
<!-- Contribution button will be populated by JavaScript -->
|
||||
</div>
|
||||
<button class="btn-icon" onclick="window.hideToolDetails('primary')">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
@ -180,6 +183,9 @@ domains.forEach((domain: any) => {
|
||||
<div id="share-button-secondary" style="display: none;">
|
||||
<!-- Share button will be populated by JavaScript -->
|
||||
</div>
|
||||
<div id="contribute-button-secondary" style="display: none;">
|
||||
<!-- Contribution button will be populated by JavaScript -->
|
||||
</div>
|
||||
<button class="btn-icon" onclick="window.hideToolDetails('secondary')">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
@ -680,6 +686,26 @@ domains.forEach((domain: any) => {
|
||||
shareButtonContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
// ===== POPULATE CONTRIBUTION BUTTON =====
|
||||
const contributeButtonContainer = document.getElementById(`contribute-button-${modalType}`);
|
||||
if (contributeButtonContainer) {
|
||||
contributeButtonContainer.innerHTML = `
|
||||
<a href="/contribute/tool?edit=${encodeURIComponent(tool.name)}"
|
||||
class="btn-icon"
|
||||
data-contribute-button="edit"
|
||||
data-tool-name="${tool.name}"
|
||||
title="Edit ${tool.name}"
|
||||
aria-label="Edit ${tool.name}"
|
||||
onclick="event.stopPropagation();">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
||||
<path d="M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
||||
</svg>
|
||||
</a>
|
||||
`;
|
||||
contributeButtonContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
// Show modals and update layout
|
||||
const overlay = document.getElementById('modal-overlay');
|
||||
const primaryModal = document.getElementById('tool-details-primary');
|
||||
@ -703,29 +729,35 @@ domains.forEach((domain: any) => {
|
||||
const primaryModal = document.getElementById('tool-details-primary');
|
||||
const secondaryModal = document.getElementById('tool-details-secondary');
|
||||
|
||||
|
||||
if (modalType === 'both' || modalType === 'all') {
|
||||
if (primaryModal) {
|
||||
primaryModal.classList.remove('active');
|
||||
// Hide share button
|
||||
const shareButtonPrimary = document.getElementById('share-button-primary');
|
||||
const contributeButtonPrimary = document.getElementById('contribute-button-primary');
|
||||
if (shareButtonPrimary) shareButtonPrimary.style.display = 'none';
|
||||
if (contributeButtonPrimary) contributeButtonPrimary.style.display = 'none';
|
||||
}
|
||||
if (secondaryModal) {
|
||||
secondaryModal.classList.remove('active');
|
||||
// Hide share button
|
||||
const shareButtonSecondary = document.getElementById('share-button-secondary');
|
||||
const contributeButtonSecondary = document.getElementById('contribute-button-secondary');
|
||||
if (shareButtonSecondary) shareButtonSecondary.style.display = 'none';
|
||||
if (contributeButtonSecondary) contributeButtonSecondary.style.display = 'none';
|
||||
}
|
||||
if (overlay) overlay.classList.remove('active');
|
||||
document.body.classList.remove('modals-side-by-side');
|
||||
// ... rest of existing code
|
||||
} else if (modalType === 'primary' && primaryModal) {
|
||||
primaryModal.classList.remove('active');
|
||||
const shareButtonPrimary = document.getElementById('share-button-primary');
|
||||
const contributeButtonPrimary = document.getElementById('contribute-button-primary');
|
||||
if (shareButtonPrimary) shareButtonPrimary.style.display = 'none';
|
||||
if (contributeButtonPrimary) contributeButtonPrimary.style.display = 'none';
|
||||
} else if (modalType === 'secondary' && secondaryModal) {
|
||||
secondaryModal.classList.remove('active');
|
||||
const shareButtonSecondary = document.getElementById('share-button-secondary');
|
||||
const contributeButtonSecondary = document.getElementById('contribute-button-secondary');
|
||||
if (shareButtonSecondary) shareButtonSecondary.style.display = 'none';
|
||||
if (contributeButtonSecondary) contributeButtonSecondary.style.display = 'none';
|
||||
}
|
||||
|
||||
// Check if any modal is still active
|
||||
@ -820,4 +852,32 @@ domains.forEach((domain: any) => {
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Auth check for contribution buttons (similar to existing auth checking pattern)
|
||||
function setupContributionButtonAuth() {
|
||||
document.addEventListener('click', async (e) => {
|
||||
const contributeButton = e.target.closest('[data-contribute-button]');
|
||||
if (!contributeButton) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/status');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.authRequired && !data.authenticated) {
|
||||
const returnUrl = contributeButton.href;
|
||||
window.location.href = `/api/auth/login?returnTo=${encodeURIComponent(returnUrl)}`;
|
||||
} else {
|
||||
window.location.href = contributeButton.href;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth check failed:', error);
|
||||
window.location.href = contributeButton.href;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupContributionButtonAuth();
|
||||
});
|
||||
</script>
|
193
src/pages/contribute/index.astro
Normal file
193
src/pages/contribute/index.astro
Normal file
@ -0,0 +1,193 @@
|
||||
---
|
||||
// src/pages/contribute/index.astro
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getAuthContext, requireAuth } from '../../utils/serverAuth.js';
|
||||
|
||||
// Check authentication
|
||||
const authContext = await getAuthContext(Astro);
|
||||
const authRedirect = requireAuth(authContext, Astro.url.toString());
|
||||
if (authRedirect) return authRedirect;
|
||||
---
|
||||
|
||||
<BaseLayout title="Contribute" description="Contribute tools, methods, concepts, and knowledge articles to CC24-Guide">
|
||||
<section style="padding: 2rem 0;">
|
||||
<!-- Header -->
|
||||
<div style="text-align: center; margin-bottom: 3rem; padding: 2rem; background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); color: white; border-radius: 1rem; border: 1px solid var(--color-border);">
|
||||
<h1 style="margin-bottom: 1rem; font-size: 2.5rem;">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.75rem; vertical-align: middle;">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="8.5" cy="7" r="4"/>
|
||||
<line x1="20" y1="8" x2="20" y2="14"/>
|
||||
<line x1="23" y1="11" x2="17" y2="11"/>
|
||||
</svg>
|
||||
Contribute to CC24-Guide
|
||||
</h1>
|
||||
<p style="margin: 0; opacity: 0.9; line-height: 1.6; font-size: 1.125rem;">
|
||||
Help expand our DFIR knowledge base by contributing tools, methods, concepts, and detailed articles.
|
||||
All contributions are reviewed before being merged into the main database.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Contribution Options -->
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 2rem; margin-bottom: 3rem;">
|
||||
|
||||
<!-- Tools, Methods & Concepts -->
|
||||
<div class="card" style="padding: 2rem; border-left: 4px solid var(--color-primary); cursor: pointer; transition: var(--transition-fast);" onclick="window.location.href='/contribute/tool'">
|
||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
||||
<div style="width: 48px; height: 48px; background-color: var(--color-primary); border-radius: 0.5rem; display: flex; align-items: center; justify-content: center;">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
||||
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 style="margin: 0; color: var(--color-primary); font-size: 1.25rem;">Tools, Methods & Concepts</h3>
|
||||
</div>
|
||||
|
||||
<p style="margin-bottom: 1.5rem; line-height: 1.6;">
|
||||
Add new software tools, forensic methodologies, or fundamental concepts to our database.
|
||||
Includes detailed forms for metadata, licensing, platforms, and categorization.
|
||||
</p>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem;">
|
||||
<span class="badge" style="background-color: var(--color-primary); color: white;">Software Tools</span>
|
||||
<span class="badge" style="background-color: var(--color-method); color: white;">Methods</span>
|
||||
<span class="badge" style="background-color: var(--color-concept); color: white;">Concepts</span>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 1rem;">
|
||||
<a href="/contribute/tool" class="btn btn-primary" style="flex: 1;">Add New Entry</a>
|
||||
<a href="/contribute/tool?mode=browse" class="btn btn-secondary" style="flex: 1;">Edit Existing</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Knowledgebase Articles -->
|
||||
<div class="card" style="padding: 2rem; border-left: 4px solid var(--color-accent); cursor: pointer; transition: var(--transition-fast);" onclick="window.location.href='/contribute/knowledgebase'">
|
||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
||||
<div style="width: 48px; height: 48px; background-color: var(--color-accent); border-radius: 0.5rem; display: flex; align-items: center; justify-content: center;">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" 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"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
<polyline points="10 9 9 9 8 9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 style="margin: 0; color: var(--color-accent); font-size: 1.25rem;">Knowledgebase Articles</h3>
|
||||
</div>
|
||||
|
||||
<p style="margin-bottom: 1.5rem; line-height: 1.6;">
|
||||
Write detailed guides, tutorials, configuration instructions, and best practices.
|
||||
Features a markdown editor with live preview and media upload capabilities.
|
||||
</p>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem;">
|
||||
<span class="badge badge-secondary">Installation Guides</span>
|
||||
<span class="badge badge-secondary">Tutorials</span>
|
||||
<span class="badge badge-secondary">Best Practices</span>
|
||||
<span class="badge badge-secondary">Case Studies</span>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 1rem;">
|
||||
<a href="/contribute/knowledgebase" class="btn btn-accent" style="flex: 1;">Write Article</a>
|
||||
<a href="/knowledgebase" class="btn btn-secondary" style="flex: 1;">View Articles</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Issues & Improvements -->
|
||||
<div class="card" style="padding: 2rem; border-left: 4px solid var(--color-warning); grid-column: 1 / -1;">
|
||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
||||
<div style="width: 48px; height: 48px; background-color: var(--color-warning); border-radius: 0.5rem; display: flex; align-items: center; justify-content: center;">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="8" x2="12" y2="12"/>
|
||||
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 style="margin: 0; color: var(--color-warning); font-size: 1.25rem;">Issues & Improvements</h3>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 2fr 1fr; gap: 2rem; align-items: center;">
|
||||
<div>
|
||||
<p style="margin-bottom: 1rem; line-height: 1.6;">
|
||||
Found incorrect information, broken links, or have suggestions for improvements?
|
||||
Report issues directly in our Git repository or suggest enhancements to existing entries.
|
||||
</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
|
||||
<span class="badge" style="background-color: var(--color-warning); color: white;">Bug Reports</span>
|
||||
<span class="badge" style="background-color: var(--color-warning); color: white;">Corrections</span>
|
||||
<span class="badge" style="background-color: var(--color-warning); color: white;">Suggestions</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
||||
<a href="https://git.cc24.dev/mstoeck3/cc24-hub/issues/new" target="_blank" rel="noopener noreferrer" class="btn" style="background-color: var(--color-warning); color: white; border-color: var(--color-warning);">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" y1="14" x2="21" y2="3"/>
|
||||
</svg>
|
||||
Report Issue
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Guidelines -->
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h3 style="margin-bottom: 1.5rem; color: var(--color-text);">Contribution Guidelines</h3>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem;">
|
||||
<div>
|
||||
<h4 style="margin-bottom: 0.75rem; color: var(--color-primary);">Quality Standards</h4>
|
||||
<ul style="margin: 0; padding-left: 1.5rem; line-height: 1.6;">
|
||||
<li>Provide accurate and up-to-date information</li>
|
||||
<li>Use clear, professional language</li>
|
||||
<li>Include relevant tags and categorization</li>
|
||||
<li>Verify all URLs and links work correctly</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 style="margin-bottom: 0.75rem; color: var(--color-accent);">Review Process</h4>
|
||||
<ul style="margin: 0; padding-left: 1.5rem; line-height: 1.6;">
|
||||
<li>All contributions create pull requests</li>
|
||||
<li>Maintainers review within 48-72 hours</li>
|
||||
<li>Feedback provided for requested changes</li>
|
||||
<li>Approved changes merged automatically</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 style="margin-bottom: 0.75rem; color: var(--color-warning);">Best Practices</h4>
|
||||
<ul style="margin: 0; padding-left: 1.5rem; line-height: 1.6;">
|
||||
<li>Search existing entries before adding duplicates</li>
|
||||
<li>Include rationale for new additions</li>
|
||||
<li>Follow existing categorization patterns</li>
|
||||
<li>Test tools/methods before recommending</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Statistics -->
|
||||
<div style="text-align: center; padding: 1.5rem; background-color: var(--color-bg-secondary); border-radius: 0.75rem;">
|
||||
<p class="text-muted" style="margin: 0; font-size: 0.9375rem;">
|
||||
<strong>Community Contributions:</strong> Help us maintain the most comprehensive DFIR resource available.
|
||||
<br>
|
||||
Your contributions are credited and help the entire forensics community.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
div[style*="grid-template-columns: 2fr 1fr"] {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -53,6 +53,17 @@ const tools = data.tools;
|
||||
KI befragen
|
||||
</button>
|
||||
|
||||
<!-- NEW: Contribution Button -->
|
||||
<a href="/contribute" class="btn" style="padding: 0.75rem 1.5rem; background-color: var(--color-warning); color: white; border-color: var(--color-warning);" data-contribute-button="new">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="8.5" cy="7" r="4"/>
|
||||
<line x1="20" y1="8" x2="20" y2="14"/>
|
||||
<line x1="23" y1="11" x2="17" y2="11"/>
|
||||
</svg>
|
||||
Beitragen
|
||||
</a>
|
||||
|
||||
<a href="#filters-section" class="btn btn-secondary" style="padding: 0.75rem 1.5rem;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
|
||||
|
@ -1,6 +1,7 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { getToolsData } from '../utils/dataService.js';
|
||||
import ContributionButton from '../components/ContributionButton.astro';
|
||||
|
||||
// Load tools data
|
||||
const data = await getToolsData();
|
||||
@ -17,12 +18,24 @@ knowledgebaseTools.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||
<!-- Header -->
|
||||
<div style="text-align: center; margin-bottom: 3rem; padding: 2rem; background: linear-gradient(135deg, var(--color-bg-secondary) 0%, var(--color-bg-tertiary) 100%); border-radius: 1rem; border: 1px solid var(--color-border);">
|
||||
<h1 style="margin-bottom: 1rem; font-size: 2.5rem; color: var(--color-primary);">Knowledgebase</h1>
|
||||
<p style="font-size: 1.25rem; color: var(--color-text-secondary); margin-bottom: 0.5rem;">
|
||||
<p style="font-size: 1.25rem; color: var(--color-text-secondary); margin-bottom: 1.125rem;">
|
||||
Erweiterte Dokumentation und Erkenntnisse
|
||||
</p>
|
||||
<p style="font-size: 1rem; color: var(--color-text-secondary);">
|
||||
<p style="font-size: 1rem; color: var(--color-text-secondary); margin-bottom: 1.5rem;">
|
||||
Praktische Erfahrungen, Konfigurationshinweise und Lektionen aus der Praxis
|
||||
</p>
|
||||
|
||||
<!--contribution button -->
|
||||
<div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
|
||||
<ContributionButton type="write" variant="primary" text="Artikel schreiben" style="padding: 0.75rem 1.5rem;" />
|
||||
<a href="#kb-entries" class="btn btn-secondary" style="padding: 0.75rem 1.5rem;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
</svg>
|
||||
Artikel durchsuchen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search -->
|
||||
@ -100,7 +113,8 @@ knowledgebaseTools.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action button -->
|
||||
<!-- Action buttons -->
|
||||
<div style="display: flex; gap: 0.5rem; align-items: center; flex-shrink: 0;">
|
||||
<a href={`/knowledgebase/${toolSlug}`} class="btn btn-primary" style="font-size: 0.8125rem;">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
@ -109,8 +123,13 @@ knowledgebaseTools.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||
</svg>
|
||||
Artikel öffnen
|
||||
</a>
|
||||
|
||||
<!-- NEW: Edit button for existing knowledgebase articles -->
|
||||
<ContributionButton type="edit" toolName={tool.name} variant="secondary" text="Edit" style="font-size: 0.8125rem; padding: 0.5rem 0.75rem;" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rest of existing article content remains unchanged -->
|
||||
<!-- Description -->
|
||||
<p style="margin: 1rem 0; color: var(--color-text-secondary); line-height: 1.5;">
|
||||
{tool.description}
|
||||
@ -151,25 +170,17 @@ knowledgebaseTools.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||
<p class="text-muted">Versuchen Sie es mit anderen Suchbegriffen.</p>
|
||||
</div>
|
||||
</section>
|
||||
<div id="fab-container" style="position: fixed; bottom: 2rem; right: 2rem; z-index: 100; display: none;">
|
||||
<ContributionButton
|
||||
type="write"
|
||||
variant="primary"
|
||||
text="✍️"
|
||||
style="border-radius: 50%; width: 56px; height: 56px; display: flex; align-items: center; justify-content: center; box-shadow: var(--shadow-lg); font-size: 1.5rem; padding: 0;"
|
||||
className="fab-button"
|
||||
/>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
/* Simplified knowledgebase styles */
|
||||
.kb-entry {
|
||||
margin-bottom: 1.5rem;
|
||||
border-left: 4px solid var(--color-accent);
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.kb-entry:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.dark .kb-entry:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Enhanced knowledgebase functionality with search
|
||||
|
30
src/scripts/auth-utils.js
Normal file
30
src/scripts/auth-utils.js
Normal file
@ -0,0 +1,30 @@
|
||||
// src/scripts/auth-utils.js
|
||||
export async function checkAuthAndRedirect(targetUrl) {
|
||||
try {
|
||||
const response = await fetch('/api/auth/status');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.authRequired && !data.authenticated) {
|
||||
const returnUrl = encodeURIComponent(targetUrl);
|
||||
window.location.href = `/api/auth/login?returnTo=${returnUrl}`;
|
||||
return false;
|
||||
} else {
|
||||
window.location.href = targetUrl;
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth check failed:', error);
|
||||
window.location.href = targetUrl; // Fallback
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export function setupAuthButtons(selector = '[data-contribute-button]') {
|
||||
document.addEventListener('click', async (e) => {
|
||||
const button = e.target.closest(selector);
|
||||
if (!button) return;
|
||||
|
||||
e.preventDefault();
|
||||
await checkAuthAndRedirect(button.href);
|
||||
});
|
||||
}
|
@ -1282,6 +1282,16 @@ Collaboration Section Collapse */
|
||||
position: relative;
|
||||
transition: var(--transition-medium);
|
||||
}
|
||||
/*
|
||||
.kb-entry {
|
||||
margin-bottom: 1.5rem;
|
||||
border-left: 4px solid var(--color-accent);
|
||||
transition: var(--transition-fast);
|
||||
}*/
|
||||
.kb-entry:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.kb-entry:target { animation: highlight-flash 2s ease-out; }
|
||||
|
||||
@ -1296,6 +1306,9 @@ Collaboration Section Collapse */
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
.dark .kb-entry:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.kb-expand-icon svg { transition: var(--transition-medium); }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user