fix: Restore all files lost during destructive rebase

A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-02-09 09:51:32 +01:00
parent f7487ee240
commit bfdaf63ba9
2009 changed files with 749983 additions and 1731 deletions

View File

@@ -0,0 +1,129 @@
import { NextRequest, NextResponse } from 'next/server'
/**
* POST /api/admin/companion/feedback
* Submit feedback (bug report, feature request, general feedback)
* Proxy to backend /api/feedback
*/
export async function POST(request: NextRequest) {
try {
const body = await request.json()
// Validate required fields
if (!body.type || !body.title || !body.description) {
return NextResponse.json(
{
success: false,
error: 'Missing required fields: type, title, description',
},
{ status: 400 }
)
}
// Validate feedback type
const validTypes = ['bug', 'feature', 'feedback']
if (!validTypes.includes(body.type)) {
return NextResponse.json(
{
success: false,
error: 'Invalid feedback type. Must be: bug, feature, or feedback',
},
{ status: 400 }
)
}
// TODO: Replace with actual backend call
// const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000'
// const response = await fetch(`${backendUrl}/api/feedback`, {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// // Add auth headers
// },
// body: JSON.stringify({
// type: body.type,
// title: body.title,
// description: body.description,
// screenshot: body.screenshot,
// sessionId: body.sessionId,
// metadata: {
// ...body.metadata,
// source: 'companion',
// timestamp: new Date().toISOString(),
// userAgent: request.headers.get('user-agent'),
// },
// }),
// })
//
// if (!response.ok) {
// throw new Error(`Backend responded with ${response.status}`)
// }
//
// const data = await response.json()
// return NextResponse.json(data)
// Mock response - just acknowledge the submission
const feedbackId = `fb-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
console.log('Feedback received:', {
id: feedbackId,
type: body.type,
title: body.title,
description: body.description.substring(0, 100) + '...',
hasScreenshot: !!body.screenshot,
sessionId: body.sessionId,
})
return NextResponse.json({
success: true,
message: 'Feedback submitted successfully',
data: {
feedbackId,
submittedAt: new Date().toISOString(),
},
})
} catch (error) {
console.error('Submit feedback error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}
/**
* GET /api/admin/companion/feedback
* Get feedback history (admin only)
*/
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const type = searchParams.get('type')
const limit = parseInt(searchParams.get('limit') || '10')
// TODO: Replace with actual backend call
// Mock response - empty list for now
return NextResponse.json({
success: true,
data: {
feedback: [],
total: 0,
page: 1,
limit,
},
})
} catch (error) {
console.error('Get feedback error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,194 @@
import { NextRequest, NextResponse } from 'next/server'
/**
* POST /api/admin/companion/lesson
* Start a new lesson session
* Proxy to backend /api/classroom/sessions
*/
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000'
// TODO: Replace with actual backend call
// const response = await fetch(`${backendUrl}/api/classroom/sessions`, {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify(body),
// })
//
// if (!response.ok) {
// throw new Error(`Backend responded with ${response.status}`)
// }
//
// const data = await response.json()
// return NextResponse.json(data)
// Mock response - create a new session
const sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
const mockSession = {
success: true,
data: {
sessionId,
classId: body.classId,
className: body.className || body.classId,
subject: body.subject,
topic: body.topic,
startTime: new Date().toISOString(),
phases: [
{ phase: 'einstieg', duration: 8, status: 'active', actualTime: 0 },
{ phase: 'erarbeitung', duration: 20, status: 'planned', actualTime: 0 },
{ phase: 'sicherung', duration: 10, status: 'planned', actualTime: 0 },
{ phase: 'transfer', duration: 7, status: 'planned', actualTime: 0 },
{ phase: 'reflexion', duration: 5, status: 'planned', actualTime: 0 },
],
totalPlannedDuration: 50,
currentPhaseIndex: 0,
elapsedTime: 0,
isPaused: false,
pauseDuration: 0,
overtimeMinutes: 0,
status: 'in_progress',
homeworkList: [],
materials: [],
},
}
return NextResponse.json(mockSession)
} catch (error) {
console.error('Start lesson error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}
/**
* GET /api/admin/companion/lesson
* Get current lesson session or list of recent sessions
*/
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const sessionId = searchParams.get('sessionId')
// TODO: Replace with actual backend call
// const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000'
// const url = sessionId
// ? `${backendUrl}/api/classroom/sessions/${sessionId}`
// : `${backendUrl}/api/classroom/sessions`
//
// const response = await fetch(url)
// const data = await response.json()
// return NextResponse.json(data)
// Mock response
if (sessionId) {
return NextResponse.json({
success: true,
data: null, // No active session stored on server in mock
})
}
return NextResponse.json({
success: true,
data: {
sessions: [], // Empty list for now
},
})
} catch (error) {
console.error('Get lesson error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}
/**
* PATCH /api/admin/companion/lesson
* Update lesson session (timer state, phase changes, etc.)
*/
export async function PATCH(request: NextRequest) {
try {
const body = await request.json()
const { sessionId, ...updates } = body
if (!sessionId) {
return NextResponse.json(
{ success: false, error: 'Session ID required' },
{ status: 400 }
)
}
// TODO: Replace with actual backend call
// const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000'
// const response = await fetch(`${backendUrl}/api/classroom/sessions/${sessionId}`, {
// method: 'PATCH',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(updates),
// })
//
// const data = await response.json()
// return NextResponse.json(data)
// Mock response - just acknowledge the update
return NextResponse.json({
success: true,
message: 'Session updated',
})
} catch (error) {
console.error('Update lesson error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}
/**
* DELETE /api/admin/companion/lesson
* End/delete a lesson session
*/
export async function DELETE(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const sessionId = searchParams.get('sessionId')
if (!sessionId) {
return NextResponse.json(
{ success: false, error: 'Session ID required' },
{ status: 400 }
)
}
// TODO: Replace with actual backend call
return NextResponse.json({
success: true,
message: 'Session ended',
})
} catch (error) {
console.error('End lesson error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,102 @@
import { NextResponse } from 'next/server'
/**
* GET /api/admin/companion
* Proxy to backend /api/state/dashboard for companion dashboard data
*/
export async function GET() {
try {
const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000'
// TODO: Replace with actual backend call when endpoint is available
// const response = await fetch(`${backendUrl}/api/state/dashboard`, {
// method: 'GET',
// headers: {
// 'Content-Type': 'application/json',
// },
// })
//
// if (!response.ok) {
// throw new Error(`Backend responded with ${response.status}`)
// }
//
// const data = await response.json()
// return NextResponse.json(data)
// Mock response for development
const mockData = {
success: true,
data: {
context: {
currentPhase: 'erarbeitung',
phaseDisplayName: 'Erarbeitung',
},
stats: {
classesCount: 4,
studentsCount: 96,
learningUnitsCreated: 23,
gradesEntered: 156,
},
phases: [
{ id: 'einstieg', shortName: 'E', displayName: 'Einstieg', duration: 8, status: 'completed', color: '#4A90E2' },
{ id: 'erarbeitung', shortName: 'A', displayName: 'Erarbeitung', duration: 20, status: 'active', color: '#F5A623' },
{ id: 'sicherung', shortName: 'S', displayName: 'Sicherung', duration: 10, status: 'planned', color: '#7ED321' },
{ id: 'transfer', shortName: 'T', displayName: 'Transfer', duration: 7, status: 'planned', color: '#9013FE' },
{ id: 'reflexion', shortName: 'R', displayName: 'Reflexion', duration: 5, status: 'planned', color: '#6B7280' },
],
progress: {
percentage: 65,
completed: 13,
total: 20,
},
suggestions: [
{
id: '1',
title: 'Klausuren korrigieren',
description: 'Deutsch LK - 12 unkorrigierte Arbeiten warten',
priority: 'urgent',
icon: 'ClipboardCheck',
actionTarget: '/ai/klausur-korrektur',
estimatedTime: 120,
},
{
id: '2',
title: 'Elternsprechtag vorbereiten',
description: 'Notenuebersicht fuer 8b erstellen',
priority: 'high',
icon: 'Users',
actionTarget: '/education/grades',
estimatedTime: 30,
},
],
upcomingEvents: [
{
id: 'e1',
title: 'Mathe-Test 9b',
date: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString(),
type: 'exam',
inDays: 2,
},
{
id: 'e2',
title: 'Elternsprechtag',
date: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(),
type: 'parent_meeting',
inDays: 5,
},
],
},
}
return NextResponse.json(mockData)
} catch (error) {
console.error('Companion dashboard error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,137 @@
import { NextRequest, NextResponse } from 'next/server'
const DEFAULT_SETTINGS = {
defaultPhaseDurations: {
einstieg: 8,
erarbeitung: 20,
sicherung: 10,
transfer: 7,
reflexion: 5,
},
preferredLessonLength: 45,
autoAdvancePhases: true,
soundNotifications: true,
showKeyboardShortcuts: true,
highContrastMode: false,
onboardingCompleted: false,
}
/**
* GET /api/admin/companion/settings
* Get teacher settings
* Proxy to backend /api/teacher/settings
*/
export async function GET() {
try {
// TODO: Replace with actual backend call
// const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000'
// const response = await fetch(`${backendUrl}/api/teacher/settings`, {
// method: 'GET',
// headers: {
// 'Content-Type': 'application/json',
// // Add auth headers
// },
// })
//
// if (!response.ok) {
// throw new Error(`Backend responded with ${response.status}`)
// }
//
// const data = await response.json()
// return NextResponse.json(data)
// Mock response - return default settings
return NextResponse.json({
success: true,
data: DEFAULT_SETTINGS,
})
} catch (error) {
console.error('Get settings error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}
/**
* PUT /api/admin/companion/settings
* Update teacher settings
*/
export async function PUT(request: NextRequest) {
try {
const body = await request.json()
// Validate the settings structure
if (!body || typeof body !== 'object') {
return NextResponse.json(
{ success: false, error: 'Invalid settings data' },
{ status: 400 }
)
}
// TODO: Replace with actual backend call
// const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000'
// const response = await fetch(`${backendUrl}/api/teacher/settings`, {
// method: 'PUT',
// headers: {
// 'Content-Type': 'application/json',
// // Add auth headers
// },
// body: JSON.stringify(body),
// })
//
// if (!response.ok) {
// throw new Error(`Backend responded with ${response.status}`)
// }
//
// const data = await response.json()
// return NextResponse.json(data)
// Mock response - just acknowledge the save
return NextResponse.json({
success: true,
message: 'Settings saved',
data: body,
})
} catch (error) {
console.error('Save settings error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}
/**
* PATCH /api/admin/companion/settings
* Partially update teacher settings
*/
export async function PATCH(request: NextRequest) {
try {
const body = await request.json()
// TODO: Replace with actual backend call
return NextResponse.json({
success: true,
message: 'Settings updated',
data: body,
})
} catch (error) {
console.error('Update settings error:', error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}