Fix: Remove broken getKlausurApiUrl and clean up empty lines
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
sed replacement left orphaned hostname references in story page and empty lines in getApiBase functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
244
admin-lehrer/app/(admin)/infrastructure/ci-cd/useCiCdData.ts
Normal file
244
admin-lehrer/app/(admin)/infrastructure/ci-cd/useCiCdData.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import type {
|
||||
PipelineStatus,
|
||||
PipelineRun,
|
||||
SystemStats,
|
||||
DockerStats,
|
||||
WoodpeckerStatus,
|
||||
TabType,
|
||||
ContainerFilter,
|
||||
ContainerInfo,
|
||||
} from './types'
|
||||
|
||||
export interface CiCdData {
|
||||
// Tab
|
||||
activeTab: TabType
|
||||
setActiveTab: (tab: TabType) => void
|
||||
|
||||
// Pipeline
|
||||
pipelineStatus: PipelineStatus | null
|
||||
pipelineHistory: PipelineRun[]
|
||||
triggeringPipeline: boolean
|
||||
triggerPipeline: () => Promise<void>
|
||||
|
||||
// Container
|
||||
systemStats: SystemStats | null
|
||||
dockerStats: DockerStats | null
|
||||
containerFilter: ContainerFilter
|
||||
setContainerFilter: (f: ContainerFilter) => void
|
||||
filteredContainers: ContainerInfo[]
|
||||
actionLoading: string | null
|
||||
containerAction: (containerId: string, action: 'start' | 'stop' | 'restart') => Promise<void>
|
||||
loadContainerData: () => Promise<void>
|
||||
|
||||
// Woodpecker
|
||||
woodpeckerStatus: WoodpeckerStatus | null
|
||||
triggeringWoodpecker: boolean
|
||||
triggerWoodpeckerPipeline: () => Promise<void>
|
||||
|
||||
// General
|
||||
loading: boolean
|
||||
error: string | null
|
||||
message: string | null
|
||||
}
|
||||
|
||||
export function useCiCdData(): CiCdData {
|
||||
const [activeTab, setActiveTab] = useState<TabType>('overview')
|
||||
|
||||
// Pipeline State
|
||||
const [pipelineStatus, setPipelineStatus] = useState<PipelineStatus | null>(null)
|
||||
const [pipelineHistory, setPipelineHistory] = useState<PipelineRun[]>([])
|
||||
const [triggeringPipeline, setTriggeringPipeline] = useState(false)
|
||||
|
||||
// Container State
|
||||
const [systemStats, setSystemStats] = useState<SystemStats | null>(null)
|
||||
const [dockerStats, setDockerStats] = useState<DockerStats | null>(null)
|
||||
const [containerFilter, setContainerFilter] = useState<ContainerFilter>('all')
|
||||
const [actionLoading, setActionLoading] = useState<string | null>(null)
|
||||
|
||||
// Woodpecker State
|
||||
const [woodpeckerStatus, setWoodpeckerStatus] = useState<WoodpeckerStatus | null>(null)
|
||||
const [triggeringWoodpecker, setTriggeringWoodpecker] = useState(false)
|
||||
|
||||
// General State
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [message, setMessage] = useState<string | null>(null)
|
||||
|
||||
const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL || ''
|
||||
|
||||
// ============================================================================
|
||||
// Data Loading
|
||||
// ============================================================================
|
||||
|
||||
const loadPipelineData = useCallback(async () => {
|
||||
try {
|
||||
const [statusRes, historyRes] = await Promise.all([
|
||||
fetch(`${BACKEND_URL}/api/v1/security/sbom/pipeline/status`),
|
||||
fetch(`${BACKEND_URL}/api/v1/security/sbom/pipeline/history`),
|
||||
])
|
||||
|
||||
if (statusRes.ok) {
|
||||
setPipelineStatus(await statusRes.json())
|
||||
}
|
||||
if (historyRes.ok) {
|
||||
setPipelineHistory(await historyRes.json())
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load pipeline data:', err)
|
||||
}
|
||||
}, [BACKEND_URL])
|
||||
|
||||
const loadContainerData = useCallback(async () => {
|
||||
try {
|
||||
const response = await fetch('/api/admin/infrastructure/mac-mini')
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setSystemStats(data.system)
|
||||
setDockerStats(data.docker)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load container data:', err)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const loadWoodpeckerData = useCallback(async () => {
|
||||
try {
|
||||
const response = await fetch('/api/admin/infrastructure/woodpecker?limit=10')
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setWoodpeckerStatus(data)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load Woodpecker data:', err)
|
||||
setWoodpeckerStatus({
|
||||
status: 'offline',
|
||||
pipelines: [],
|
||||
lastUpdate: new Date().toISOString(),
|
||||
error: 'Verbindung fehlgeschlagen'
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
const loadAllData = useCallback(async () => {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
await Promise.all([loadPipelineData(), loadContainerData(), loadWoodpeckerData()])
|
||||
setLoading(false)
|
||||
}, [loadPipelineData, loadContainerData, loadWoodpeckerData])
|
||||
|
||||
useEffect(() => {
|
||||
loadAllData()
|
||||
}, [loadAllData])
|
||||
|
||||
// Auto-refresh every 30 seconds
|
||||
useEffect(() => {
|
||||
const interval = setInterval(loadAllData, 30000)
|
||||
return () => clearInterval(interval)
|
||||
}, [loadAllData])
|
||||
|
||||
// ============================================================================
|
||||
// Actions
|
||||
// ============================================================================
|
||||
|
||||
const triggerPipeline = async () => {
|
||||
setTriggeringPipeline(true)
|
||||
try {
|
||||
const response = await fetch(`${BACKEND_URL}/api/v1/security/sbom/pipeline/trigger`, {
|
||||
method: 'POST',
|
||||
})
|
||||
if (response.ok) {
|
||||
setMessage('Pipeline gestartet!')
|
||||
setTimeout(loadPipelineData, 2000)
|
||||
setTimeout(loadPipelineData, 5000)
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Pipeline-Trigger fehlgeschlagen')
|
||||
} finally {
|
||||
setTriggeringPipeline(false)
|
||||
}
|
||||
}
|
||||
|
||||
const triggerWoodpeckerPipeline = async () => {
|
||||
setTriggeringWoodpecker(true)
|
||||
setMessage(null)
|
||||
try {
|
||||
const response = await fetch('/api/admin/infrastructure/woodpecker', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ branch: 'main' })
|
||||
})
|
||||
if (response.ok) {
|
||||
const result = await response.json()
|
||||
setMessage(`Woodpecker Pipeline #${result.pipeline?.number || '?'} gestartet!`)
|
||||
setTimeout(loadWoodpeckerData, 2000)
|
||||
setTimeout(loadWoodpeckerData, 5000)
|
||||
} else {
|
||||
setError('Pipeline-Start fehlgeschlagen')
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Pipeline konnte nicht gestartet werden')
|
||||
} finally {
|
||||
setTriggeringWoodpecker(false)
|
||||
}
|
||||
}
|
||||
|
||||
const containerAction = async (containerId: string, action: 'start' | 'stop' | 'restart') => {
|
||||
setActionLoading(`${containerId}-${action}`)
|
||||
setMessage(null)
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/admin/infrastructure/mac-mini', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ container_id: containerId, action }),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Aktion fehlgeschlagen')
|
||||
}
|
||||
|
||||
setMessage(`Container ${action} erfolgreich`)
|
||||
setTimeout(loadContainerData, 1000)
|
||||
setTimeout(loadContainerData, 3000)
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Fehler')
|
||||
} finally {
|
||||
setActionLoading(null)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Derived
|
||||
// ============================================================================
|
||||
|
||||
const filteredContainers = dockerStats?.containers.filter(c => {
|
||||
if (containerFilter === 'all') return true
|
||||
if (containerFilter === 'running') return c.state === 'running'
|
||||
if (containerFilter === 'stopped') return c.state !== 'running'
|
||||
return true
|
||||
}) || []
|
||||
|
||||
return {
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
pipelineStatus,
|
||||
pipelineHistory,
|
||||
triggeringPipeline,
|
||||
triggerPipeline,
|
||||
systemStats,
|
||||
dockerStats,
|
||||
containerFilter,
|
||||
setContainerFilter,
|
||||
filteredContainers,
|
||||
actionLoading,
|
||||
containerAction,
|
||||
loadContainerData,
|
||||
woodpeckerStatus,
|
||||
triggeringWoodpecker,
|
||||
triggerWoodpeckerPipeline,
|
||||
loading,
|
||||
error,
|
||||
message,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user