This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

288 lines
7.5 KiB
TypeScript

/**
* API Proxy for PCA Platform (Heuristic Service)
*
* Provides secure server-side access to the PCA Heuristic Service
* for bot detection, session monitoring, and configuration management
*
* Heuristic Service runs on port 8085
*/
import { NextRequest, NextResponse } from 'next/server'
const PCA_SERVICE_URL = process.env.PCA_SERVICE_URL || 'http://localhost:8085'
// Helper to make fetch with timeout and error handling
async function safeFetch(url: string, options?: RequestInit): Promise<Response | null> {
try {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 5000)
const response = await fetch(url, {
...options,
signal: controller.signal,
})
clearTimeout(timeoutId)
return response
} catch {
return null
}
}
// GET: Fetch health, sessions, config, or stats
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const action = searchParams.get('action')
// Health check
if (action === 'health') {
const response = await safeFetch(`${PCA_SERVICE_URL}/health`, {
headers: { 'Content-Type': 'application/json' },
})
if (!response || !response.ok) {
return NextResponse.json({
status: 'offline',
message: 'PCA Heuristic Service not available',
})
}
try {
const data = await response.json()
return NextResponse.json({
status: 'healthy',
...data,
})
} catch {
return NextResponse.json({ status: 'healthy' })
}
}
// Get client config
if (action === 'config') {
const response = await safeFetch(`${PCA_SERVICE_URL}/pca/v1/config`, {
headers: { 'Content-Type': 'application/json' },
})
if (response && response.ok) {
try {
const data = await response.json()
return NextResponse.json(data)
} catch {
// Fall through to default
}
}
// Return default config if service unavailable or error
return NextResponse.json({
tick: { endpoint: '/pca/v1/tick', interval_ms: 5000 },
thresholds: { score_pass: 0.7, score_challenge: 0.4 },
weights: {
dwell_ratio: 0.30,
scroll_score: 0.25,
pointer_variance: 0.20,
click_rate: 0.25,
},
step_up: { methods: ['webauthn', 'pow'], primary: 'webauthn' },
service_status: 'offline',
})
}
// Get admin stats
if (action === 'stats') {
const response = await safeFetch(`${PCA_SERVICE_URL}/pca/v1/admin/stats`, {
headers: { 'Content-Type': 'application/json' },
})
if (response && response.ok) {
try {
const data = await response.json()
return NextResponse.json(data)
} catch {
// Fall through to default
}
}
// Return mock stats if service unavailable
return NextResponse.json({
active_sessions: 0,
total_ticks: 0,
challenges_issued: 0,
challenges_passed: 0,
avg_score: 0,
humans_detected: 0,
bots_detected: 0,
service_status: 'offline',
})
}
// Get active sessions
if (action === 'sessions') {
const response = await safeFetch(`${PCA_SERVICE_URL}/pca/v1/admin/sessions`, {
headers: { 'Content-Type': 'application/json' },
})
if (response && response.ok) {
try {
const data = await response.json()
return NextResponse.json(data)
} catch {
// Fall through to default
}
}
// Return empty sessions if service unavailable
return NextResponse.json({
sessions: [],
total: 0,
service_status: 'offline',
})
}
// Get ai-access.json configuration
if (action === 'ai-access') {
const response = await safeFetch(`${PCA_SERVICE_URL}/pca/v1/admin/ai-access`, {
headers: { 'Content-Type': 'application/json' },
})
if (response && response.ok) {
try {
const data = await response.json()
return NextResponse.json(data)
} catch {
// Fall through to default
}
}
// Return default ai-access config
return NextResponse.json({
version: '1.0',
thresholds: { score_pass: 0.7, score_challenge: 0.4 },
weights: {
dwell_ratio: 0.30,
scroll_score: 0.25,
pointer_variance: 0.20,
click_rate: 0.25,
},
step_up: { methods: ['webauthn', 'pow'], primary: 'webauthn' },
pca_roles: {
Person: { access: 'allow', price: null },
Corporate: { access: 'allow', price: '0.01 EUR' },
Agent: { access: 'charge', price: '0.001 EUR' },
},
payment: { enabled: false },
service_status: 'offline',
})
}
// Default: health check
const response = await safeFetch(`${PCA_SERVICE_URL}/health`, {
headers: { 'Content-Type': 'application/json' },
})
if (!response || !response.ok) {
return NextResponse.json({
status: 'offline',
service: 'pca-heuristic-service',
})
}
try {
const data = await response.json()
return NextResponse.json(data)
} catch {
return NextResponse.json({
status: 'offline',
service: 'pca-heuristic-service',
})
}
}
// POST: Evaluate session or update config
export async function POST(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const action = searchParams.get('action')
const body = await request.json().catch(() => ({}))
// Evaluate a specific session
if (action === 'evaluate') {
const sessionId = searchParams.get('sessionId')
if (!sessionId) {
return NextResponse.json({ error: 'Session ID required' }, { status: 400 })
}
const response = await safeFetch(
`${PCA_SERVICE_URL}/pca/v1/evaluate?session_id=${sessionId}`,
{ headers: { 'Content-Type': 'application/json' } }
)
if (!response || !response.ok) {
return NextResponse.json(
{ error: 'Failed to evaluate session - service offline' },
{ status: 503 }
)
}
try {
const data = await response.json()
return NextResponse.json(data)
} catch {
return NextResponse.json(
{ error: 'Invalid response from service' },
{ status: 500 }
)
}
}
// Update configuration (admin only)
if (action === 'update-config') {
const response = await safeFetch(`${PCA_SERVICE_URL}/pca/v1/admin/config`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
if (!response || !response.ok) {
return NextResponse.json(
{ error: 'Failed to update configuration - service offline' },
{ status: 503 }
)
}
try {
const data = await response.json()
return NextResponse.json(data)
} catch {
return NextResponse.json({ success: true })
}
}
// Clear session (admin only)
if (action === 'clear-session') {
const sessionId = searchParams.get('sessionId')
if (!sessionId) {
return NextResponse.json({ error: 'Session ID required' }, { status: 400 })
}
const response = await safeFetch(
`${PCA_SERVICE_URL}/pca/v1/admin/sessions/${sessionId}`,
{
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
}
)
if (!response || !response.ok) {
return NextResponse.json(
{ error: 'Failed to clear session - service offline' },
{ status: 503 }
)
}
return NextResponse.json({ deleted: true, session_id: sessionId })
}
return NextResponse.json({ error: 'Invalid action' }, { status: 400 })
}