Services: Admin-Compliance, Backend-Compliance, AI-Compliance-SDK, Consent-SDK, Developer-Portal, PCA-Platform, DSMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
285 lines
8.5 KiB
TypeScript
285 lines
8.5 KiB
TypeScript
/**
|
|
* Website Content Management (Server-only)
|
|
*
|
|
* Loads website texts from JSON files.
|
|
* Admin can edit texts via /development/content
|
|
*
|
|
* IMPORTANT: This file may only be used in Server Components!
|
|
* For Client Components use @/lib/content-types
|
|
*/
|
|
|
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, accessSync, constants } from 'fs'
|
|
import { join, dirname } from 'path'
|
|
|
|
// Re-export types from content-types for backward compatibility
|
|
export type {
|
|
HeroContent,
|
|
FeatureContent,
|
|
FAQItem,
|
|
PricingPlan,
|
|
WebsiteContent,
|
|
} from './content-types'
|
|
|
|
import type { WebsiteContent } from './content-types'
|
|
|
|
// Content directory - use environment variable or relative path
|
|
function getContentDir(): string {
|
|
// Check environment variable first
|
|
if (process.env.CONTENT_DIR) {
|
|
return process.env.CONTENT_DIR
|
|
}
|
|
|
|
// Try various possible paths
|
|
const possiblePaths = [
|
|
join(process.cwd(), 'content'), // Standard: CWD/content
|
|
join(process.cwd(), 'admin-v2', 'content'), // If CWD is project root
|
|
'/app/content', // Docker container
|
|
join(dirname(__filename), '..', 'content'), // Relative to this file
|
|
]
|
|
|
|
// Check if any path exists and is writable
|
|
for (const path of possiblePaths) {
|
|
try {
|
|
if (existsSync(path)) {
|
|
accessSync(path, constants.W_OK)
|
|
return path
|
|
}
|
|
} catch {
|
|
// Path not writable, try next
|
|
}
|
|
}
|
|
|
|
// Fallback: Create in CWD
|
|
const fallbackPath = join(process.cwd(), 'content')
|
|
try {
|
|
mkdirSync(fallbackPath, { recursive: true, mode: 0o755 })
|
|
console.log(`[Content] Created content directory at: ${fallbackPath}`)
|
|
} catch (err) {
|
|
console.error(`[Content] Failed to create content directory: ${err}`)
|
|
}
|
|
return fallbackPath
|
|
}
|
|
|
|
const CONTENT_DIR = getContentDir()
|
|
|
|
// Default Content
|
|
const defaultContent: WebsiteContent = {
|
|
hero: {
|
|
badge: 'Entwickelt fuer deutsche Lehrkraefte',
|
|
title: 'Korrigieren Sie',
|
|
titleHighlight1: 'schneller',
|
|
titleHighlight2: 'besser',
|
|
subtitle: 'BreakPilot unterstuetzt Lehrkraefte mit intelligenter KI bei der Bewertung von Aufgaben. Sparen Sie bis zu 50% Ihrer Korrekturzeit und geben Sie besseres Feedback.',
|
|
ctaPrimary: '7 Tage kostenlos testen',
|
|
ctaSecondary: 'Mehr erfahren',
|
|
ctaHint: 'Keine Kreditkarte fuer den Start erforderlich',
|
|
},
|
|
features: [
|
|
{
|
|
id: 'ai-correction',
|
|
icon: '✍️',
|
|
title: 'KI-gestuetzte Korrektur',
|
|
description: 'Intelligente Analyse von Schuelerantworten mit Verbesserungsvorschlaegen und automatischer Bewertung nach Ihren Kriterien.',
|
|
},
|
|
{
|
|
id: 'templates',
|
|
icon: '📋',
|
|
title: 'Dokumentvorlagen',
|
|
description: 'Erstellen und verwalten Sie Ihre eigenen Arbeitsblatt-Vorlagen. Wiederverwendbar fuer verschiedene Klassen und Jahrgaenge.',
|
|
},
|
|
{
|
|
id: 'analytics',
|
|
icon: '📊',
|
|
title: 'Fortschrittsanalyse',
|
|
description: 'Verfolgen Sie die Entwicklung Ihrer Schueler ueber Zeit. Erkennen Sie Staerken und Schwaechen fruehzeitig.',
|
|
},
|
|
{
|
|
id: 'gdpr',
|
|
icon: '🔒',
|
|
title: 'DSGVO-konform',
|
|
description: 'Hosting in Deutschland, volle Datenschutzkonformitaet. Ihre Daten und die Ihrer Schueler sind sicher.',
|
|
},
|
|
{
|
|
id: 'team',
|
|
icon: '👥',
|
|
title: 'Team-Funktionen',
|
|
description: 'Arbeiten Sie im Fachbereich zusammen. Teilen Sie Vorlagen, Bewertungskriterien und Best Practices.',
|
|
},
|
|
{
|
|
id: 'mobile',
|
|
icon: '📱',
|
|
title: 'Ueberall verfuegbar',
|
|
description: 'Browserbasiert und responsive. Funktioniert auf Desktop, Tablet und Smartphone - ohne Installation.',
|
|
},
|
|
],
|
|
faq: [
|
|
{
|
|
question: 'Was ist bei Breakpilot eine „Aufgabe"?',
|
|
answer: [
|
|
'Eine Aufgabe ist ein abgeschlossener Arbeitsauftrag, den du mit Breakpilot erledigst.',
|
|
'Typische Beispiele:',
|
|
'• eine Klassenarbeit korrigieren (egal wie viele Seiten)',
|
|
'• mehrere Klassenarbeiten in einer Serie korrigieren',
|
|
'• einen Elternbrief erstellen',
|
|
'Wichtig: Die Anzahl der Seiten, Dateien oder Uploads spielt dabei keine Rolle.',
|
|
],
|
|
},
|
|
{
|
|
question: 'Kann ich Breakpilot kostenlos testen?',
|
|
answer: [
|
|
'Ja.',
|
|
'• Du kannst Breakpilot 7 Tage kostenlos testen',
|
|
'• Dafuer ist eine Kreditkarte erforderlich',
|
|
'• Wenn du innerhalb der Testphase kuendigst, entstehen keine Kosten',
|
|
],
|
|
},
|
|
{
|
|
question: 'Werden meine Daten fuer KI-Training verwendet?',
|
|
answer: [
|
|
'Nein.',
|
|
'• Deine Inhalte werden nicht fuer das Training oeffentlicher KI-Modelle genutzt',
|
|
'• Die Verarbeitung erfolgt DSGVO-konform',
|
|
'• Deine Daten bleiben unter deiner Kontrolle',
|
|
],
|
|
},
|
|
{
|
|
question: 'Kann ich meinen Tarif jederzeit aendern oder kuendigen?',
|
|
answer: [
|
|
'Ja.',
|
|
'• Upgrades sind jederzeit moeglich',
|
|
'• Downgrades greifen zum naechsten Abrechnungszeitraum',
|
|
'• Kuendigungen sind jederzeit moeglich',
|
|
],
|
|
},
|
|
],
|
|
pricing: [
|
|
{
|
|
id: 'basic',
|
|
name: 'Basic',
|
|
description: 'Perfekt fuer den Einstieg',
|
|
price: 9.90,
|
|
currency: 'EUR',
|
|
interval: 'Monat',
|
|
features: {
|
|
tasks: '30 Aufgaben',
|
|
taskDescription: 'pro Monat',
|
|
included: [
|
|
'KI-gestuetzte Korrektur',
|
|
'Basis-Dokumentvorlagen',
|
|
'E-Mail Support',
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: 'standard',
|
|
name: 'Standard',
|
|
description: 'Fuer regelmaessige Nutzer',
|
|
price: 19.90,
|
|
currency: 'EUR',
|
|
interval: 'Monat',
|
|
popular: true,
|
|
features: {
|
|
tasks: '100 Aufgaben',
|
|
taskDescription: 'pro Monat',
|
|
included: [
|
|
'Alles aus Basic',
|
|
'Eigene Vorlagen erstellen',
|
|
'Batch-Verarbeitung',
|
|
'Bis zu 3 Teammitglieder',
|
|
'Prioritaets-Support',
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: 'premium',
|
|
name: 'Premium',
|
|
description: 'Sorglos-Tarif fuer Vielnutzer',
|
|
price: 39.90,
|
|
currency: 'EUR',
|
|
interval: 'Monat',
|
|
features: {
|
|
tasks: 'Unbegrenzt',
|
|
taskDescription: 'Fair Use',
|
|
included: [
|
|
'Alles aus Standard',
|
|
'Unbegrenzte Aufgaben (Fair Use)',
|
|
'Bis zu 10 Teammitglieder',
|
|
'Admin-Panel & Audit-Log',
|
|
'API-Zugang',
|
|
'Eigenes Branding',
|
|
'Dedizierter Support',
|
|
],
|
|
},
|
|
},
|
|
],
|
|
trust: {
|
|
item1: { value: 'DSGVO', label: 'Konform & sicher' },
|
|
item2: { value: '7 Tage', label: 'Kostenlos testen' },
|
|
item3: { value: '100%', label: 'Made in Germany' },
|
|
},
|
|
testimonial: {
|
|
quote: 'BreakPilot hat meine Korrekturzeit halbiert. Ich habe endlich wieder Zeit fuer das Wesentliche: meine Schueler.',
|
|
author: 'Maria S.',
|
|
role: 'Deutschlehrerin, Gymnasium',
|
|
},
|
|
}
|
|
|
|
/**
|
|
* Loads content from JSON file or returns default
|
|
*/
|
|
export function getContent(): WebsiteContent {
|
|
const contentPath = join(CONTENT_DIR, 'website.json')
|
|
|
|
try {
|
|
if (existsSync(contentPath)) {
|
|
const fileContent = readFileSync(contentPath, 'utf-8')
|
|
return JSON.parse(fileContent) as WebsiteContent
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading content:', error)
|
|
}
|
|
|
|
return defaultContent
|
|
}
|
|
|
|
/**
|
|
* Saves content to JSON file
|
|
* @returns Object with success status and optional error message
|
|
*/
|
|
export function saveContent(content: WebsiteContent): { success: boolean; error?: string } {
|
|
const contentPath = join(CONTENT_DIR, 'website.json')
|
|
|
|
try {
|
|
// Ensure directory exists
|
|
if (!existsSync(CONTENT_DIR)) {
|
|
console.log(`[Content] Creating directory: ${CONTENT_DIR}`)
|
|
mkdirSync(CONTENT_DIR, { recursive: true, mode: 0o755 })
|
|
}
|
|
|
|
// Check write permissions
|
|
try {
|
|
accessSync(CONTENT_DIR, constants.W_OK)
|
|
} catch {
|
|
const error = `Directory not writable: ${CONTENT_DIR}`
|
|
console.error(`[Content] ${error}`)
|
|
return { success: false, error }
|
|
}
|
|
|
|
// Write file
|
|
writeFileSync(contentPath, JSON.stringify(content, null, 2), 'utf-8')
|
|
console.log(`[Content] Saved successfully to: ${contentPath}`)
|
|
return { success: true }
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
console.error(`[Content] Error saving: ${errorMessage}`)
|
|
return { success: false, error: errorMessage }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns default content (for reset)
|
|
*/
|
|
export function getDefaultContent(): WebsiteContent {
|
|
return defaultContent
|
|
}
|