Initial commit: breakpilot-compliance - Compliance SDK Platform
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>
This commit is contained in:
284
admin-compliance/lib/content.ts
Normal file
284
admin-compliance/lib/content.ts
Normal file
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
Reference in New Issue
Block a user