Merge pull request 'style-fixes' (#26) from mstoeck3/forensic-pathways:style-fixes into main
Reviewed-on: mstoeck3/cc24-hub#26
This commit is contained in:
commit
e94c3a0f47
@ -88,6 +88,8 @@ npm install
|
||||
cp .env.example .env
|
||||
# .env bearbeiten (siehe Konfiguration unten)
|
||||
|
||||
npm run astro build
|
||||
|
||||
# Development Server starten
|
||||
npm run dev
|
||||
```
|
||||
@ -125,6 +127,7 @@ sudo npm install
|
||||
|
||||
# Production-Build erstellen
|
||||
sudo npm run build
|
||||
npm run astro build
|
||||
|
||||
# Berechtigungen setzen
|
||||
sudo chown -R www-data:www-data /opt/forensic-pathways
|
||||
|
@ -8,9 +8,9 @@ const phases = data.phases;
|
||||
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
||||
---
|
||||
|
||||
<section id="ai-interface" class="ai-interface" style="display: none;">
|
||||
<section id="ai-interface" class="ai-interface hidden">
|
||||
<div class="ai-query-section">
|
||||
<div style="text-align: center; margin-bottom: 2rem;">
|
||||
<div class="content-center-lg">
|
||||
<h2 style="margin-bottom: 1rem; color: var(--color-primary);">
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.75rem; vertical-align: middle;">
|
||||
<path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
|
||||
@ -27,7 +27,7 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
||||
<div class="ai-input-container" style="max-width: 1000px; margin: 0 auto;">
|
||||
<div class="ai-mode-toggle" style="display: flex; align-items: center; justify-content: center; gap: 1rem; margin-bottom: 1.5rem; padding: 1rem; background-color: var(--color-bg-secondary); border-radius: 0.75rem; border: 1px solid var(--color-border);">
|
||||
<span id="workflow-label" class="toggle-label active" style="font-weight: 500; color: var(--color-primary); cursor: pointer; transition: var(--transition-fast);">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2 align-middle">
|
||||
<polyline points="9,11 12,14 22,4"/>
|
||||
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
|
||||
</svg>
|
||||
@ -39,7 +39,7 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
||||
</div>
|
||||
|
||||
<span id="tool-label" class="toggle-label" style="font-weight: 500; color: var(--color-text-secondary); cursor: pointer; transition: var(--transition-fast);">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2 align-middle">
|
||||
<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>
|
||||
Spezifische Software oder Methode
|
||||
@ -78,19 +78,19 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="smart-prompting-container" class="smart-prompting-container" style="display: none;">
|
||||
<div id="smart-prompting-container" class="smart-prompting-container hidden">
|
||||
<div class="prompting-card">
|
||||
<div id="prompting-status" class="prompting-status">
|
||||
<div class="status-icon">💡</div>
|
||||
<span class="status-text">Analysiere Eingabe...</span>
|
||||
<div id="prompting-spinner" class="prompting-spinner" style="display: none;">
|
||||
<div id="prompting-spinner" class="prompting-spinner hidden">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--color-accent)" stroke-width="2">
|
||||
<path d="M21 12a9 9 0 11-6.219-8.56"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="suggested-questions" class="suggested-questions" style="display: none;">
|
||||
<div id="suggested-questions" class="suggested-questions hidden">
|
||||
<div class="suggestions-header">
|
||||
<span class="suggestions-label">Zur besseren Analyse:</span>
|
||||
</div>
|
||||
@ -139,9 +139,9 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
||||
<path d="M12 17h.01"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p id="loading-text" style="color: var(--color-text-secondary);">Analysiere Szenario und generiere Empfehlungen...</p>
|
||||
<p id="loading-text" class="text-secondary">Analysiere Szenario und generiere Empfehlungen...</p>
|
||||
|
||||
<div id="queue-status" class="queue-status-card" style="display: none;">
|
||||
<div id="queue-status" class="queue-status-card hidden">
|
||||
<div class="queue-header">
|
||||
<div class="queue-position-display">
|
||||
<div id="queue-position-badge" class="position-badge">1</div>
|
||||
@ -180,7 +180,7 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div id="ai-results" class="ai-results" style="display: none;">
|
||||
<div id="ai-results" class="ai-results hidden">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -812,7 +812,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
<div class="tool-results-container">
|
||||
<div style="text-align: center; margin-bottom: 2rem; padding: 1.5rem; background: linear-gradient(135deg, var(--color-primary) 0%, #525252 100%); color: white; border-radius: 0.75rem;">
|
||||
<h3 style="margin: 0 0 0.75rem 0; font-size: 1.5rem;">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2 align-middle">
|
||||
<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>
|
||||
Passende Empfehlungen
|
||||
@ -887,7 +887,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
</div>
|
||||
<div class="tool-detailed-explanation" style="margin-bottom: 1.5rem;">
|
||||
<h4 style="margin: 0.8rem 0 0.75rem 0; color: var(--color-accent); font-size: 1rem;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2 align-middle">
|
||||
<path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
|
||||
<path d="M9 11V7a3 3 0 0 1 6 0v4"/>
|
||||
</svg>
|
||||
@ -896,7 +896,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
${formatWorkflowSuggestion(toolRec.detailed_explanation)}
|
||||
${toolRec.implementation_approach ? `
|
||||
<h4 style="margin: 0.8rem 0 0.75rem 0; color: var(--color-primary); font-size: 1rem;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2 align-middle">
|
||||
<polyline points="9,11 12,14 22,4"/>
|
||||
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
|
||||
</svg>
|
||||
|
@ -65,7 +65,7 @@ const sortedTags = Object.entries(tagFrequency)
|
||||
<label for="include-proprietary">Proprietäre Software mit einschließen</label>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<div class="mb-4">
|
||||
<div class="tag-header">
|
||||
<label style="font-weight: 500;">
|
||||
Nach Tags filtern
|
||||
@ -95,8 +95,8 @@ const sortedTags = Object.entries(tagFrequency)
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 1rem; margin-bottom: 1.5rem; align-items: center;">
|
||||
<button class="btn btn-secondary view-toggle active" style="height:50px" data-view="grid">Kachelansicht</button>
|
||||
<button class="btn btn-secondary view-toggle" style="height:50px" data-view="matrix">Matrix-Ansicht</button>
|
||||
<button class="btn btn-secondary view-toggle active h-12" data-view="grid">Kachelansicht</button>
|
||||
<button class="btn btn-secondary view-toggle h-12" data-view="matrix">Matrix-Ansicht</button>
|
||||
|
||||
<button
|
||||
id="ai-view-toggle"
|
||||
|
@ -29,7 +29,7 @@ domains.forEach((domain: any) => {
|
||||
});
|
||||
---
|
||||
|
||||
<div id="matrix-container" class="matrix-wrapper" style="display: none;">
|
||||
<div id="matrix-container" class="matrix-wrapper hidden">
|
||||
{domainAgnosticTools.map((sectionData: any, index: number) => (
|
||||
<div id={`domain-agnostic-section-${sectionData.section.id}`} class="card collaboration-section-collapsed mb-6 border-l-4" style="border-left-color: var(--color-accent);">
|
||||
<div class="collaboration-header cursor-pointer flex items-center gap-3" onclick={`toggleDomainAgnosticSection('${sectionData.section.id}')`} style="margin-bottom: 0.1rem;">
|
||||
@ -39,7 +39,7 @@ domains.forEach((domain: any) => {
|
||||
<line x1="20" y1="8" x2="20" y2="14"/>
|
||||
<line x1="23" y1="11" x2="17" y2="11"/>
|
||||
</svg>
|
||||
<h3 class="m-0 text-lg" style="color: var(--color-accent);">
|
||||
<h3 class="m-0 text-lg text-accent">
|
||||
{sectionData.section.name}
|
||||
<span id={`count-${sectionData.section.id}`} class="badge text-xs" style="background-color: var(--color-text-secondary); color: var(--color-bg); margin-left: 0.5rem;">
|
||||
{sectionData.tools.length}
|
||||
@ -51,7 +51,7 @@ domains.forEach((domain: any) => {
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collaboration-content" style="display: none;">
|
||||
<div class="collaboration-content hidden">
|
||||
<div class="collaboration-tools-compact" id={`domain-agnostic-tools-${sectionData.section.id}`}>
|
||||
{sectionData.tools.map((tool: any) => {
|
||||
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||
@ -74,7 +74,7 @@ domains.forEach((domain: any) => {
|
||||
<p class="text-muted">
|
||||
{tool.description}
|
||||
</p>
|
||||
<div class="flex gap-3 text-xs" style="color: var(--color-text-secondary);">
|
||||
<div class="flex gap-3 text-xs text-secondary">
|
||||
<span>{tool.platforms.join(', ')}</span>
|
||||
<span>•</span>
|
||||
<span>{tool.skillLevel}</span>
|
||||
@ -140,9 +140,9 @@ domains.forEach((domain: any) => {
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<h2 id="tool-name-primary" class="m-0">Tool Name</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<div id="share-button-primary" style="display: none;">
|
||||
<div id="share-button-primary" class="hidden">
|
||||
</div>
|
||||
<div id="contribute-button-primary" style="display: none;">
|
||||
<div id="contribute-button-primary" class="hidden">
|
||||
</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">
|
||||
@ -168,9 +168,9 @@ domains.forEach((domain: any) => {
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<h2 id="tool-name-secondary" class="m-0">Tool Name</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<div id="share-button-secondary" style="display: none;">
|
||||
<div id="share-button-secondary" class="hidden">
|
||||
</div>
|
||||
<div id="contribute-button-secondary" style="display: none;">
|
||||
<div id="contribute-button-secondary" class="hidden">
|
||||
</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">
|
||||
@ -549,7 +549,7 @@ domains.forEach((domain: any) => {
|
||||
</button>
|
||||
` : ''}
|
||||
</div>
|
||||
<div ${collapseOnMobile ? 'style="display: none;"' : ''} class="flex flex-wrap gap-1">
|
||||
<div ${collapseOnMobile ? 'class="hidden"' : ''} class="flex flex-wrap gap-1">
|
||||
${conceptLinks}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -20,219 +20,224 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
|
||||
<title>{title} - ForensicPathways</title>
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const THEME_KEY = 'dfir-theme';
|
||||
<script>
|
||||
// Move utility functions OUTSIDE DOMContentLoaded to avoid race conditions
|
||||
function createToolSlug(toolName) {
|
||||
if (!toolName || typeof toolName !== 'string') {
|
||||
console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
|
||||
return '';
|
||||
}
|
||||
|
||||
return toolName.toLowerCase()
|
||||
.replace(/[^a-z0-9\s-]/g, '') // Remove special characters
|
||||
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
||||
.replace(/-+/g, '-') // Remove duplicate hyphens
|
||||
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
||||
}
|
||||
|
||||
function getSystemTheme() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
function findToolByIdentifier(tools, identifier) {
|
||||
if (!identifier || !Array.isArray(tools)) return undefined;
|
||||
|
||||
return tools.find(tool =>
|
||||
tool.name === identifier ||
|
||||
createToolSlug(tool.name) === identifier.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
function getStoredTheme() {
|
||||
return localStorage.getItem(THEME_KEY) || 'auto';
|
||||
}
|
||||
function isToolHosted(tool) {
|
||||
return tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
}
|
||||
|
||||
function applyTheme(theme) {
|
||||
const effectiveTheme = theme === 'auto' ? getSystemTheme() : theme;
|
||||
document.documentElement.setAttribute('data-theme', effectiveTheme);
|
||||
}
|
||||
|
||||
function updateThemeToggle(theme) {
|
||||
document.querySelectorAll('[data-theme-toggle]').forEach(button => {
|
||||
button.setAttribute('data-current-theme', theme);
|
||||
});
|
||||
}
|
||||
|
||||
function initTheme() {
|
||||
const storedTheme = getStoredTheme();
|
||||
applyTheme(storedTheme);
|
||||
updateThemeToggle(storedTheme);
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const current = getStoredTheme();
|
||||
const themes = ['light', 'dark', 'auto'];
|
||||
const currentIndex = themes.indexOf(current);
|
||||
const nextIndex = (currentIndex + 1) % themes.length;
|
||||
const nextTheme = themes[nextIndex];
|
||||
|
||||
localStorage.setItem(THEME_KEY, nextTheme);
|
||||
applyTheme(nextTheme);
|
||||
updateThemeToggle(nextTheme);
|
||||
}
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (getStoredTheme() === 'auto') {
|
||||
applyTheme('auto');
|
||||
}
|
||||
// Consolidated scrolling utility - also moved outside DOMContentLoaded
|
||||
function scrollToElement(element, options = {}) {
|
||||
if (!element) return;
|
||||
|
||||
// Calculate target position manually to avoid double-scroll
|
||||
setTimeout(() => {
|
||||
const headerHeight = document.querySelector('nav')?.offsetHeight || 80;
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
const absoluteElementTop = elementRect.top + window.pageYOffset;
|
||||
const targetPosition = absoluteElementTop - headerHeight - 20; // Adjust this 20 as needed
|
||||
|
||||
window.scrollTo({
|
||||
top: targetPosition,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
(window as any).themeUtils = {
|
||||
initTheme,
|
||||
toggleTheme,
|
||||
getStoredTheme
|
||||
};
|
||||
// Convenience functions for common scroll targets
|
||||
function scrollToElementById(elementId, options = {}) {
|
||||
const element = document.getElementById(elementId);
|
||||
scrollToElement(element, options);
|
||||
}
|
||||
|
||||
// Consolidated scrolling utility
|
||||
(window as any).scrollToElement = function(element, options = {}) {
|
||||
if (!element) return;
|
||||
|
||||
// Calculate target position manually to avoid double-scroll
|
||||
setTimeout(() => {
|
||||
const headerHeight = document.querySelector('nav')?.offsetHeight || 80;
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
const absoluteElementTop = elementRect.top + window.pageYOffset;
|
||||
const targetPosition = absoluteElementTop - headerHeight - 20; // Adjust this 20 as needed
|
||||
|
||||
window.scrollTo({
|
||||
top: targetPosition,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
function scrollToElementBySelector(selector, options = {}) {
|
||||
const element = document.querySelector(selector);
|
||||
scrollToElement(element, options);
|
||||
}
|
||||
|
||||
// Convenience functions for common scroll targets
|
||||
(window as any).scrollToElementById = function(elementId, options = {}) {
|
||||
const element = document.getElementById(elementId);
|
||||
(window as any).scrollToElement(element, options);
|
||||
};
|
||||
// Attach to window immediately - BEFORE DOMContentLoaded
|
||||
(window as any).createToolSlug = createToolSlug;
|
||||
(window as any).findToolByIdentifier = findToolByIdentifier;
|
||||
(window as any).isToolHosted = isToolHosted;
|
||||
(window as any).scrollToElement = scrollToElement;
|
||||
(window as any).scrollToElementById = scrollToElementById;
|
||||
(window as any).scrollToElementBySelector = scrollToElementBySelector;
|
||||
|
||||
(window as any).scrollToElementBySelector = function(selector, options = {}) {
|
||||
const element = document.querySelector(selector);
|
||||
(window as any).scrollToElement(element, options);
|
||||
};
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const THEME_KEY = 'dfir-theme';
|
||||
|
||||
function createToolSlug(toolName) {
|
||||
if (!toolName || typeof toolName !== 'string') {
|
||||
console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
|
||||
return '';
|
||||
}
|
||||
|
||||
return toolName.toLowerCase()
|
||||
.replace(/[^a-z0-9\s-]/g, '') // Remove special characters
|
||||
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
||||
.replace(/-+/g, '-') // Remove duplicate hyphens
|
||||
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
||||
}
|
||||
function getSystemTheme() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
function findToolByIdentifier(tools, identifier) {
|
||||
if (!identifier || !Array.isArray(tools)) return undefined;
|
||||
|
||||
return tools.find(tool =>
|
||||
tool.name === identifier ||
|
||||
createToolSlug(tool.name) === identifier.toLowerCase()
|
||||
);
|
||||
}
|
||||
function getStoredTheme() {
|
||||
return localStorage.getItem(THEME_KEY) || 'auto';
|
||||
}
|
||||
|
||||
function isToolHosted(tool) {
|
||||
return tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
}
|
||||
function applyTheme(theme) {
|
||||
const effectiveTheme = theme === 'auto' ? getSystemTheme() : theme;
|
||||
document.documentElement.setAttribute('data-theme', effectiveTheme);
|
||||
}
|
||||
|
||||
(window as any).createToolSlug = createToolSlug;
|
||||
(window as any).findToolByIdentifier = findToolByIdentifier;
|
||||
(window as any).isToolHosted = isToolHosted;
|
||||
function updateThemeToggle(theme) {
|
||||
document.querySelectorAll('[data-theme-toggle]').forEach(button => {
|
||||
button.setAttribute('data-current-theme', theme);
|
||||
});
|
||||
}
|
||||
|
||||
async function checkClientAuth(context = 'general') {
|
||||
try {
|
||||
const response = await fetch('/api/auth/status');
|
||||
const data = await response.json();
|
||||
|
||||
switch (context) {
|
||||
case 'contributions':
|
||||
return {
|
||||
authenticated: data.contributionAuthenticated,
|
||||
authRequired: data.contributionAuthRequired,
|
||||
expires: data.expires
|
||||
};
|
||||
case 'ai':
|
||||
return {
|
||||
authenticated: data.aiAuthenticated,
|
||||
authRequired: data.aiAuthRequired,
|
||||
expires: data.expires
|
||||
};
|
||||
default:
|
||||
return {
|
||||
authenticated: data.authenticated,
|
||||
authRequired: data.contributionAuthRequired || data.aiAuthRequired,
|
||||
expires: data.expires
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth check failed:', error);
|
||||
return {
|
||||
authenticated: false,
|
||||
authRequired: true
|
||||
};
|
||||
}
|
||||
}
|
||||
function initTheme() {
|
||||
const storedTheme = getStoredTheme();
|
||||
applyTheme(storedTheme);
|
||||
updateThemeToggle(storedTheme);
|
||||
}
|
||||
|
||||
async function requireClientAuth(callback, returnUrl, context = 'general') {
|
||||
const authStatus = await checkClientAuth(context);
|
||||
|
||||
if (authStatus.authRequired && !authStatus.authenticated) {
|
||||
const targetUrl = returnUrl || window.location.href;
|
||||
window.location.href = `/api/auth/login?returnTo=${encodeURIComponent(targetUrl)}`;
|
||||
return false;
|
||||
} else {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async function showIfAuthenticated(selector, context = 'general') {
|
||||
const authStatus = await checkClientAuth(context);
|
||||
const element = document.querySelector(selector);
|
||||
|
||||
if (element) {
|
||||
element.style.display = (!authStatus.authRequired || authStatus.authenticated)
|
||||
? 'inline-flex'
|
||||
: 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function setupAuthButtons(selector = '[data-contribute-button]') {
|
||||
document.addEventListener('click', async (e) => {
|
||||
if (!e.target) return;
|
||||
|
||||
const button = (e.target as Element).closest(selector);
|
||||
if (!button) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
console.log('[AUTH] Contribute button clicked:', button.getAttribute('data-contribute-button'));
|
||||
|
||||
await requireClientAuth(() => {
|
||||
console.log('[AUTH] Navigation approved, redirecting to:', (button as HTMLAnchorElement).href);
|
||||
window.location.href = (button as HTMLAnchorElement).href;
|
||||
}, (button as HTMLAnchorElement).href, 'contributions');
|
||||
});
|
||||
}
|
||||
|
||||
(window as any).checkClientAuth = checkClientAuth;
|
||||
(window as any).requireClientAuth = requireClientAuth;
|
||||
(window as any).showIfAuthenticated = showIfAuthenticated;
|
||||
(window as any).setupAuthButtons = setupAuthButtons;
|
||||
|
||||
initTheme();
|
||||
setupAuthButtons('[data-contribute-button]');
|
||||
function toggleTheme() {
|
||||
const current = getStoredTheme();
|
||||
const themes = ['light', 'dark', 'auto'];
|
||||
const currentIndex = themes.indexOf(current);
|
||||
const nextIndex = (currentIndex + 1) % themes.length;
|
||||
const nextTheme = themes[nextIndex];
|
||||
|
||||
const initAIButton = async () => {
|
||||
await showIfAuthenticated('#ai-view-toggle', 'ai');
|
||||
};
|
||||
initAIButton();
|
||||
|
||||
console.log('[CONSOLIDATED] All utilities loaded and initialized');
|
||||
localStorage.setItem(THEME_KEY, nextTheme);
|
||||
applyTheme(nextTheme);
|
||||
updateThemeToggle(nextTheme);
|
||||
}
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (getStoredTheme() === 'auto') {
|
||||
applyTheme('auto');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
(window as any).themeUtils = {
|
||||
initTheme,
|
||||
toggleTheme,
|
||||
getStoredTheme
|
||||
};
|
||||
|
||||
async function checkClientAuth(context = 'general') {
|
||||
try {
|
||||
const response = await fetch('/api/auth/status');
|
||||
const data = await response.json();
|
||||
|
||||
switch (context) {
|
||||
case 'contributions':
|
||||
return {
|
||||
authenticated: data.contributionAuthenticated,
|
||||
authRequired: data.contributionAuthRequired,
|
||||
expires: data.expires
|
||||
};
|
||||
case 'ai':
|
||||
return {
|
||||
authenticated: data.aiAuthenticated,
|
||||
authRequired: data.aiAuthRequired,
|
||||
expires: data.expires
|
||||
};
|
||||
default:
|
||||
return {
|
||||
authenticated: data.authenticated,
|
||||
authRequired: data.contributionAuthRequired || data.aiAuthRequired,
|
||||
expires: data.expires
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth check failed:', error);
|
||||
return {
|
||||
authenticated: false,
|
||||
authRequired: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function requireClientAuth(callback, returnUrl, context = 'general') {
|
||||
const authStatus = await checkClientAuth(context);
|
||||
|
||||
if (authStatus.authRequired && !authStatus.authenticated) {
|
||||
const targetUrl = returnUrl || window.location.href;
|
||||
window.location.href = `/api/auth/login?returnTo=${encodeURIComponent(targetUrl)}`;
|
||||
return false;
|
||||
} else {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
async function showIfAuthenticated(selector, context = 'general') {
|
||||
const authStatus = await checkClientAuth(context);
|
||||
const element = document.querySelector(selector);
|
||||
|
||||
if (element) {
|
||||
element.style.display = (!authStatus.authRequired || authStatus.authenticated)
|
||||
? 'inline-flex'
|
||||
: 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function setupAuthButtons(selector = '[data-contribute-button]') {
|
||||
document.addEventListener('click', async (e) => {
|
||||
if (!e.target) return;
|
||||
|
||||
const button = (e.target as Element).closest(selector);
|
||||
if (!button) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
console.log('[AUTH] Contribute button clicked:', button.getAttribute('data-contribute-button'));
|
||||
|
||||
await requireClientAuth(() => {
|
||||
console.log('[AUTH] Navigation approved, redirecting to:', (button as HTMLAnchorElement).href);
|
||||
window.location.href = (button as HTMLAnchorElement).href;
|
||||
}, (button as HTMLAnchorElement).href, 'contributions');
|
||||
});
|
||||
}
|
||||
|
||||
(window as any).checkClientAuth = checkClientAuth;
|
||||
(window as any).requireClientAuth = requireClientAuth;
|
||||
(window as any).showIfAuthenticated = showIfAuthenticated;
|
||||
(window as any).setupAuthButtons = setupAuthButtons;
|
||||
|
||||
initTheme();
|
||||
setupAuthButtons('[data-contribute-button]');
|
||||
|
||||
const initAIButton = async () => {
|
||||
await showIfAuthenticated('#ai-view-toggle', 'ai');
|
||||
};
|
||||
initAIButton();
|
||||
|
||||
console.log('[CONSOLIDATED] All utilities loaded and initialized');
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<Navigation />
|
||||
<main class="container" style="flex: 1; padding: 2rem 1rem;">
|
||||
<main class="container flex-1 py-8 px-4">
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
|
@ -3,36 +3,36 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
---
|
||||
|
||||
<BaseLayout title="Über das Projekt" description="ForensicPathways - Ein Projekt für die Seminargruppe CC24-w1">
|
||||
<section style="padding: 2rem 0; max-width: 900px; margin: 0 auto;">
|
||||
<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);">ForensicPathways</h1>
|
||||
<p style="font-size: 1.25rem; color: var(--color-text-secondary); margin-bottom: 0.5rem;">
|
||||
<section class="section container-narrow">
|
||||
<div class="header-center header-gradient mb-8">
|
||||
<h1 class="text-primary mb-4 text-2xl">ForensicPathways</h1>
|
||||
<p class="text-xl text-secondary mb-2">
|
||||
Forensik im Dienst der Transparenz
|
||||
</p>
|
||||
<p style="font-size: 1rem; color: var(--color-text-secondary);">
|
||||
<p class="text-base text-secondary">
|
||||
Ein Studienprojekt
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-primary);">
|
||||
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem;">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--color-primary)" stroke-width="2">
|
||||
<path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
|
||||
<path d="M9 11V7a3 3 0 0 1 6 0v4"/>
|
||||
</svg>
|
||||
<h2 style="margin: 0; color: var(--color-primary);">Das Ziel</h2>
|
||||
<h2 class="m-0 text-primary">Das Ziel</h2>
|
||||
</div>
|
||||
<p style="margin-bottom: 1rem; line-height: 1.7;">
|
||||
<p class="mb-4 leading-relaxed">
|
||||
Diese Seite soll meinen Kommilitonen der Seminargruppe CC24-w1 und anderen interessierten Forensikbegeisterten eine <strong>einfache und übersichtliche Orientierung</strong>
|
||||
bieten: Welches Werkzeug, welche Methode eignet sich für welchen Zweck in der digitalen Forensik?
|
||||
</p>
|
||||
<p style="margin-bottom: 1rem; line-height: 1.7;">
|
||||
<p class="mb-4 leading-relaxed">
|
||||
Ich stelle euch dafür <strong>Infrastruktur</strong> zur Verfügung, mit der ihr spezialisierte
|
||||
Forensik-Software direkt einsetzen und erforschen könnt – sei es für <em>Bildungszwecke</em> oder im
|
||||
<em>begrenzten Produktiveinsatz</em>.
|
||||
</p>
|
||||
<div style="background-color: var(--color-bg-secondary); padding: 1rem; border-radius: 0.5rem; margin-top: 1rem;">
|
||||
<p style="margin: 0; font-size: 0.875rem; color: var(--color-text-secondary);">
|
||||
<div class="bg-secondary p-4 rounded-lg mt-4">
|
||||
<p class="m-0 text-sm text-secondary">
|
||||
<strong>💡 Zugang:</strong> Die meisten Dienste sind bequem über das SSO der CC24-Cloud zugänglich,
|
||||
mit nur wenigen Ausnahmen. Der Zugang ist entsprechend vorbehaltlich Absprache auf diesen Personenkreis limitiert.
|
||||
</p>
|
||||
@ -40,42 +40,42 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-accent);">
|
||||
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem;">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--color-accent)" stroke-width="2">
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
||||
<polyline points="9,22 9,12 15,12 15,22"/>
|
||||
</svg>
|
||||
<h2 style="margin: 0; color: var(--color-accent);">Wissenschaftliche Grundlage</h2>
|
||||
</div>
|
||||
<p style="margin-bottom: 1rem; line-height: 1.7;">
|
||||
<p class="mb-4 leading-relaxed">
|
||||
Bei der vorgestellten Software handelt es sich um eine <strong>kuratierte Auswahl</strong> von Anwendungen,
|
||||
vor allem aus dem Bereich der Open-Source-Software.
|
||||
</p>
|
||||
<p style="margin-bottom: 1rem; line-height: 1.7;">
|
||||
<p class="mb-4 leading-relaxed">
|
||||
Das zugrunde liegende Modell folgt dem bewährten <strong>NIST-Framework</strong> nach Kent, Chevalier, Grance und Dang
|
||||
(NIST SP 800-86), das ich wegen seiner <em>Einfachheit</em> und <em>praktischen Adaptierbarkeit</em> schätze:
|
||||
</p>
|
||||
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem; margin: 1.5rem 0; text-align: center;">
|
||||
<div style="background-color: var(--color-bg-secondary); padding: 1rem; border-radius: 0.5rem; border: 2px solid var(--color-accent);">
|
||||
<strong style="color: var(--color-accent);">Datensammlung</strong>
|
||||
<div class="grid-4 gap-2 my-6 text-center">
|
||||
<div class="bg-secondary p-4 rounded-lg border-2 border-accent">
|
||||
<strong class="text-accent">Datensammlung</strong>
|
||||
</div>
|
||||
<div style="background-color: var(--color-bg-secondary); padding: 1rem; border-radius: 0.5rem; border: 2px solid var(--color-accent);">
|
||||
<strong style="color: var(--color-accent);">Auswertung</strong>
|
||||
<div class="bg-secondary p-4 rounded-lg border-2 border-accent">
|
||||
<strong class="text-accent">Auswertung</strong>
|
||||
</div>
|
||||
<div style="background-color: var(--color-bg-secondary); padding: 1rem; border-radius: 0.5rem; border: 2px solid var(--color-accent);">
|
||||
<strong style="color: var(--color-accent);">Analyse</strong>
|
||||
<div class="bg-secondary p-4 rounded-lg border-2 border-accent">
|
||||
<strong class="text-accent">Analyse</strong>
|
||||
</div>
|
||||
<div style="background-color: var(--color-bg-secondary); padding: 1rem; border-radius: 0.5rem; border: 2px solid var(--color-accent);">
|
||||
<strong style="color: var(--color-accent);">Bericht</strong>
|
||||
<div class="bg-secondary p-4 rounded-lg border-2 border-accent">
|
||||
<strong class="text-accent">Bericht</strong>
|
||||
</div>
|
||||
</div>
|
||||
<p style="margin: 0; font-size: 0.875rem; color: var(--color-text-secondary);">
|
||||
<p class="m-0 text-sm text-secondary">
|
||||
Diese Prozessphasen werden in der <strong>Matrix-Ansicht</strong> auf der Startseite mit forensischen Wissensdomänen verknüpft.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-warning);">
|
||||
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem;">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--color-warning)" stroke-width="2">
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||
<path d="M9 12l2 2 4-4"/>
|
||||
@ -83,9 +83,9 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
<h2 style="margin: 0; color: var(--color-warning);">Weshalb der Fokus auf Open Source?</h2>
|
||||
</div>
|
||||
|
||||
<div style="background: linear-gradient(135deg, var(--color-warning) 0%, var(--color-accent) 100%); color: white; padding: 1.5rem; border-radius: 0.75rem; margin-bottom: 1.5rem;">
|
||||
<h3 style="margin: 0 0 1rem 0; font-size: 1.125rem;">💡 Zentrale Hypothese</h3>
|
||||
<p style="margin: 0; line-height: 1.6; font-style: italic;">
|
||||
<div class="card-warning rounded-xl p-6 mb-6">
|
||||
<h3 class="m-0 mb-4 text-lg">💡 Zentrale Hypothese</h3>
|
||||
<p class="m-0 leading-normal italic">
|
||||
"Die IT-Forensik verlangt nach der Anwendung von wissenschaftlichen Methoden und Objektivität.
|
||||
Die wissenschaftliche Methode verlangt einen transparenten Untersuchungsprozess, der in der Hauptverhandlung
|
||||
bzw. dem Auftraggeber gegenüber stets nachvollziehbar und erklärbar sein muss.
|
||||
@ -127,13 +127,13 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
</div>
|
||||
|
||||
<div id= "support" class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-primary);">
|
||||
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem;">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--color-primary)" stroke-width="2">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
</svg>
|
||||
<h2 style="margin: 0; color: var(--color-primary);">Unterstützung und Kontakt</h2>
|
||||
<h2 class="m-0 text-primary">Unterstützung und Kontakt</h2>
|
||||
</div>
|
||||
<p style="margin-bottom: 1rem; line-height: 1.7;">
|
||||
<p class="mb-4 leading-relaxed">
|
||||
Falls eine Anwendung nicht wie vorgesehen funktioniert, ihr Unterstützung braucht, der Speicherplatz ausgeht
|
||||
oder sonstige Probleme auftreten: <strong>Schreibt mir einfach auf Signal</strong> oder an <a href="mailto:mstoeck3@hs-mittweida.de">mstoeck3@hs-mittweida.de</a>.
|
||||
</p>
|
||||
@ -172,7 +172,7 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-accent);">
|
||||
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem;">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--color-accent)" stroke-width="2">
|
||||
<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"/>
|
||||
|
@ -51,7 +51,7 @@
|
||||
<div class="spinner"></div>
|
||||
<h2>Processing authentication...</h2>
|
||||
<p>Please wait while we complete your login.</p>
|
||||
<div id="error-message" style="display: none;" class="error"></div>
|
||||
<div id="error-message" class="hidden error"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
@ -14,8 +14,8 @@ const { authenticated, userEmail, userId } = authResult;
|
||||
---
|
||||
|
||||
<BaseLayout title="Contribute" description="Inhalte zum ForensicPathways beitragen">
|
||||
<section style="padding: 2rem 0;">
|
||||
<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);">
|
||||
<section class="section-padding">
|
||||
<div class="header-center header-primary mb-8">
|
||||
<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"/>
|
||||
@ -49,7 +49,7 @@ const { authenticated, userEmail, userId } = authResult;
|
||||
<div class="card"
|
||||
style="padding: 2rem; border-left: 4px solid var(--color-primary); transition: var(--transition-fast);
|
||||
display:flex; flex-direction:column;">
|
||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<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"/>
|
||||
@ -108,7 +108,7 @@ const { authenticated, userEmail, userId } = authResult;
|
||||
style="padding: 2rem; border-left: 4px solid var(--color-accent); cursor: pointer; transition: var(--transition-fast);
|
||||
display:flex; flex-direction:column;"
|
||||
onclick="window.location.href='/contribute/knowledgebase'">
|
||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<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"/>
|
||||
@ -142,7 +142,7 @@ const { authenticated, userEmail, userId } = authResult;
|
||||
<div class="card"
|
||||
style="padding: 2rem; border-left: 4px solid var(--color-accent); cursor: pointer; transition: var(--transition-fast);
|
||||
display:flex; flex-direction:column;">
|
||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<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"/>
|
||||
|
@ -20,7 +20,7 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
|
||||
<BaseLayout title="Contribute Knowledge Base Article">
|
||||
<div class="container" style="max-width: 900px; margin: 0 auto; padding: 2rem 1rem;">
|
||||
|
||||
<div style="text-align: center; margin-bottom: 2rem; padding: 2rem; background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); color: white; border-radius: 1rem;">
|
||||
<div class="header-center header-primary rounded-xl">
|
||||
<h1 style="margin-bottom: 1rem; font-size: 2rem;">Knowledgebase-Artikel</h1>
|
||||
<p style="margin: 0.5rem 0; opacity: 0.9;">Danke für deinen Beitrag!</p>
|
||||
{userEmail && <p style="margin: 0.5rem 0; opacity: 0.8;"><strong>Eingeloggt als:</strong> {userEmail}</p>}
|
||||
@ -114,7 +114,7 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
|
||||
<div class="form-group">
|
||||
<label class="form-label">Dokumente, Bilder, Videos (Optional)</label>
|
||||
<div class="upload-area" id="upload-area">
|
||||
<input type="file" id="file-input" multiple accept=".pdf,.doc,.docx,.txt,.md,.zip,.png,.jpg,.jpeg,.gif,.mp4,.webm" style="display: none;">
|
||||
<input type="file" id="file-input" multiple accept=".pdf,.doc,.docx,.txt,.md,.zip,.png,.jpg,.jpeg,.gif,.mp4,.webm" class="hidden">
|
||||
<div class="upload-placeholder">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
@ -125,7 +125,7 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
|
||||
<small>Die Dateien landen in der CC24-Cloud. Keine Malware.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div id="file-list" class="file-list" style="display: none;">
|
||||
<div id="file-list" class="file-list hidden">
|
||||
<h5>Ausgewählte Dateien</h5>
|
||||
<div id="files-container"></div>
|
||||
</div>
|
||||
@ -177,7 +177,7 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
|
||||
<a href="/" class="btn btn-secondary">Abbruch</a>
|
||||
<button type="submit" id="submit-btn" class="btn btn-accent">
|
||||
<span id="submit-text">Abschicken</span>
|
||||
<span id="submit-spinner" style="display: none;">⏳</span>
|
||||
<span id="submit-spinner" class="hidden">⏳</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@ -193,7 +193,7 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
|
||||
Your knowledge‑base article has been submitted as an issue for review by maintainers.
|
||||
</p>
|
||||
<div style="display:flex; gap:1rem; justify-content:center;">
|
||||
<a id="issue-link" href="#" target="_blank" class="btn btn-primary" style="display:none;">View Issue</a>
|
||||
<a id="issue-link" href="#" target="_blank" class="btn btn-primary hidden">View Issue</a>
|
||||
<a href="/" class="btn btn-secondary">Back to Home</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,7 +27,7 @@ const isEdit = !!editTool;
|
||||
<BaseLayout title={isEdit ? `Edit ${editTool?.name}` : 'Contribute Tool'}>
|
||||
<div class="container" style="max-width: 900px; margin: 0 auto; padding: 2rem 1rem;">
|
||||
|
||||
<div style="text-align: center; margin-bottom: 2rem; padding: 2rem; background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); color: white; border-radius: 1rem;">
|
||||
<div class="header-center header-primary rounded-xl">
|
||||
<h1 style="margin-bottom: 1rem; font-size: 2rem;">{isEdit ? `Edit: ${editTool?.name}` : 'Tool / Methode / Konzept beitragen'}</h1>
|
||||
<p style="margin: 0.5rem 0; opacity: 0.9;">
|
||||
{isEdit
|
||||
@ -105,7 +105,7 @@ const isEdit = !!editTool;
|
||||
<div style="border: 1px solid var(--color-border); border-radius: 0.5rem; padding: 1.5rem; margin-bottom: 2rem;">
|
||||
<h3 style="margin: 0 0 1.5rem 0; color: var(--color-primary); border-bottom: 1px solid var(--color-border); padding-bottom: 0.5rem;">Kategorien</h3>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
|
||||
<div class="grid-2 gap-8">
|
||||
<div>
|
||||
<label style="display: block; margin-bottom: 0.75rem; font-weight: 600;">Forensische Domänen</label>
|
||||
<div style="display: grid; gap: 0.5rem;">
|
||||
@ -166,7 +166,7 @@ const isEdit = !!editTool;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
|
||||
<div class="grid-2 gap-8">
|
||||
<div>
|
||||
<label for="accessType" style="display: block; margin-bottom: 0.75rem; font-weight: 600;">Zugriff über:</label>
|
||||
<select id="accessType" name="accessType">
|
||||
@ -257,7 +257,7 @@ const isEdit = !!editTool;
|
||||
<h3 style="margin-bottom: 1rem;">Contribution Submitted!</h3>
|
||||
<p id="success-message" style="margin-bottom: 1.5rem;">Your contribution has been submitted successfully.</p>
|
||||
<div style="display: flex; gap: 1rem; justify-content: center;">
|
||||
<a id="pr-link" href="#" target="_blank" class="btn btn-primary" style="display: none;">View Pull Request</a>
|
||||
<a id="pr-link" href="#" target="_blank" class="btn btn-primary hidden">View Pull Request</a>
|
||||
<a href="/" class="btn btn-secondary">Back to Home</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,8 +3,8 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
---
|
||||
|
||||
<BaseLayout title="Impressum" description="ForensicPathways - Impressum">
|
||||
<section style="padding: 2rem 0; max-width: 900px; margin: 0 auto;">
|
||||
<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);">
|
||||
<section class="section container-narrow">
|
||||
<div class="header-center header-gradient mb-8">
|
||||
<p>Bei dieser Webseite handelt es sich um ein privates Werk. Keine Bildungseinrichtung, kein Unternehmen sind finanziell oder moderierend Teil dieses Projekts.</p>
|
||||
<h1>Impressum</h1>
|
||||
<p>Angaben gemäß § 5 DDG</p>
|
||||
@ -16,7 +16,7 @@ Mario Stöckl<br>
|
||||
</p>
|
||||
<p><strong>Kontakt:</strong> <br>
|
||||
Telefon: 01522-7086296<br>
|
||||
E-Mail: <a href='mailto:mario.stoeckl@posteo.de'>mario.stoeckl@posteo.de</a>
|
||||
E-Mail: <a href='mailto:mstoeck3@hs-mittweida.de'>mstoeck3@hs-mittweida.de</a>
|
||||
</p>
|
||||
<p><strong>PGP-Schlüssel:</strong><br>
|
||||
Zur sicheren Kommunikation per E-Mail können Sie folgenden PGP-Schlüssel verwenden:<br><br>
|
||||
|
@ -60,7 +60,7 @@ const phases = data.phases;
|
||||
<p class="approach-info">
|
||||
<span class="info-icon">ℹ️</span>
|
||||
Die lila gekennzeichneten Einträge sind über das Single-Sign-On der CC24-Cloud direkt zugänglich.
|
||||
Teilnehmer der Seminargruppe CC24-w1 können die gehostete Infrastruktur nutzen.
|
||||
Teilnehmer der Seminargruppe CC24-w1 (oder andere Berechtigte) können die gehostete Infrastruktur nutzen.
|
||||
<a href="/about#support">Kontakt bei Problemen</a>
|
||||
</p>
|
||||
|
||||
@ -143,8 +143,8 @@ const phases = data.phases;
|
||||
|
||||
<TargetedScenarios />
|
||||
|
||||
<section id="filters-section" style="padding: 2rem 0;">
|
||||
<div style="text-align: center; margin-bottom: 2rem;">
|
||||
<section id="filters-section" class="section">
|
||||
<div class="content-center-lg">
|
||||
<h3 style="color: var(--color-text); margin-bottom: 0.5rem;">Alle verfügbaren Werkzeuge durchsuchen</h3>
|
||||
<p style="color: var(--color-text-secondary); margin: 0;">
|
||||
Nutzen Sie die erweiterten Filter und Kategorien für eine detaillierte Suche
|
||||
|
@ -42,7 +42,7 @@ knowledgebaseEntries.sort((a: any, b: any) => a.title.localeCompare(b.title));
|
||||
---
|
||||
|
||||
<BaseLayout title="Knowledgebase" description="Extended documentation and insights for DFIR tools">
|
||||
<section style="padding: 2rem 0;">
|
||||
<section class="section-padding">
|
||||
<div class="text-center mb-8 p-8 bg-secondary rounded-lg border">
|
||||
<h1 class="mb-4 text-2xl text-primary">Knowledgebase</h1>
|
||||
<p class="text-lg text-secondary mb-4">
|
||||
@ -209,13 +209,13 @@ knowledgebaseEntries.sort((a: any, b: any) => a.title.localeCompare(b.title));
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div id="no-kb-results" class="card text-center p-8" style="display: none;">
|
||||
<div id="no-kb-results" class="card text-center p-8 hidden">
|
||||
<h3 class="text-secondary mb-2">Keine Ergebnisse gefunden</h3>
|
||||
<p class="text-secondary">Versuchen Sie es mit anderen Suchbegriffen.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="fab-container" class="fixed bottom-8 right-8 z-50" style="display: none;">
|
||||
<div id="fab-container" class="fixed bottom-8 right-8 z-50 hidden">
|
||||
<ContributionButton
|
||||
type="write"
|
||||
variant="primary"
|
||||
|
@ -14,8 +14,8 @@ const hostedServices = data.tools.filter((tool: any) => {
|
||||
---
|
||||
|
||||
<BaseLayout title="Service Status">
|
||||
<section style="padding: 2rem 0;">
|
||||
<h1 style="text-align: center; margin-bottom: 1rem;">Service Status</h1>
|
||||
<section class="section-padding">
|
||||
<h1 class="content-center">Service Status</h1>
|
||||
<p class="text-muted" style="text-align: center; max-width: 600px; margin: 0 auto 3rem;">
|
||||
Live-Monitoring zum Onlinestatus der Dienste.
|
||||
</p>
|
||||
|
2920
src/styles/global copy.css
Normal file
2920
src/styles/global copy.css
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user