fix(admin-v2): Restore complete admin-v2 application
The admin-v2 application was incomplete in the repository. This commit restores all missing components: - Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education, infrastructure, communication, development, onboarding, rbac - SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen, vendor-compliance, tom-generator, dsr, and more - Developer portal (25 pages): API docs, SDK guides, frameworks - All components, lib files, hooks, and types - Updated package.json with all dependencies The issue was caused by incomplete initial repository state - the full admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2 but was never fully synced to the main admin-v2 directory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import {
|
||||
Finding,
|
||||
CONTRACT_REVIEW_SYSTEM_PROMPT,
|
||||
} from '@/lib/sdk/vendor-compliance'
|
||||
|
||||
/**
|
||||
* POST /api/sdk/v1/vendor-compliance/contracts/[id]/review
|
||||
*
|
||||
* Starts the LLM-based contract review process
|
||||
*/
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id: contractId } = await params
|
||||
|
||||
// In production:
|
||||
// 1. Fetch contract from database
|
||||
// 2. Extract text from PDF/DOCX using embedding-service
|
||||
// 3. Send to LLM for analysis
|
||||
// 4. Store findings in database
|
||||
// 5. Update contract with compliance score
|
||||
|
||||
// For demo, return mock analysis results
|
||||
const mockFindings: Finding[] = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
tenantId: 'default',
|
||||
contractId,
|
||||
vendorId: 'mock-vendor',
|
||||
type: 'OK',
|
||||
category: 'AVV_CONTENT',
|
||||
severity: 'LOW',
|
||||
title: {
|
||||
de: 'Weisungsgebundenheit vorhanden',
|
||||
en: 'Instruction binding present',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag enthält eine angemessene Regelung zur Weisungsgebundenheit des Auftragsverarbeiters.',
|
||||
en: 'The contract contains an appropriate provision for processor instruction binding.',
|
||||
},
|
||||
citations: [
|
||||
{
|
||||
documentId: contractId,
|
||||
page: 2,
|
||||
startChar: 150,
|
||||
endChar: 350,
|
||||
quotedText: 'Der Auftragnehmer verarbeitet personenbezogene Daten ausschließlich auf dokumentierte Weisung des Auftraggebers.',
|
||||
quoteHash: 'abc123',
|
||||
},
|
||||
],
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. a DSGVO',
|
||||
triggeredControls: ['VND-CON-01'],
|
||||
status: 'OPEN',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
tenantId: 'default',
|
||||
contractId,
|
||||
vendorId: 'mock-vendor',
|
||||
type: 'GAP',
|
||||
category: 'INCIDENT',
|
||||
severity: 'HIGH',
|
||||
title: {
|
||||
de: 'Meldefrist für Datenpannen zu lang',
|
||||
en: 'Data breach notification deadline too long',
|
||||
},
|
||||
description: {
|
||||
de: 'Die vereinbarte Meldefrist von 72 Stunden ist zu lang, um die eigene Meldepflicht gegenüber der Aufsichtsbehörde fristgerecht erfüllen zu können.',
|
||||
en: 'The agreed notification deadline of 72 hours is too long to meet own notification obligations to the supervisory authority in time.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Verhandeln Sie eine kürzere Meldefrist von maximal 24-48 Stunden.',
|
||||
en: 'Negotiate a shorter notification deadline of maximum 24-48 hours.',
|
||||
},
|
||||
citations: [
|
||||
{
|
||||
documentId: contractId,
|
||||
page: 5,
|
||||
startChar: 820,
|
||||
endChar: 950,
|
||||
quotedText: 'Der Auftragnehmer wird den Auftraggeber innerhalb von 72 Stunden über eine Verletzung des Schutzes personenbezogener Daten informieren.',
|
||||
quoteHash: 'def456',
|
||||
},
|
||||
],
|
||||
affectedRequirement: 'Art. 33 Abs. 2 DSGVO',
|
||||
triggeredControls: ['VND-INC-01'],
|
||||
status: 'OPEN',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
tenantId: 'default',
|
||||
contractId,
|
||||
vendorId: 'mock-vendor',
|
||||
type: 'RISK',
|
||||
category: 'TRANSFER',
|
||||
severity: 'MEDIUM',
|
||||
title: {
|
||||
de: 'Drittlandtransfer USA ohne TIA',
|
||||
en: 'Third country transfer to USA without TIA',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag erlaubt Datenverarbeitung in den USA. Es liegt jedoch kein Transfer Impact Assessment (TIA) vor.',
|
||||
en: 'The contract allows data processing in the USA. However, no Transfer Impact Assessment (TIA) is available.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Führen Sie ein TIA durch und dokumentieren Sie zusätzliche Schutzmaßnahmen.',
|
||||
en: 'Conduct a TIA and document supplementary measures.',
|
||||
},
|
||||
citations: [
|
||||
{
|
||||
documentId: contractId,
|
||||
page: 8,
|
||||
startChar: 1200,
|
||||
endChar: 1350,
|
||||
quotedText: 'Die Verarbeitung kann auch in Rechenzentren in den Vereinigten Staaten von Amerika erfolgen.',
|
||||
quoteHash: 'ghi789',
|
||||
},
|
||||
],
|
||||
affectedRequirement: 'Art. 44-49 DSGVO, Schrems II',
|
||||
triggeredControls: ['VND-TRF-01', 'VND-TRF-03'],
|
||||
status: 'OPEN',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
]
|
||||
|
||||
// Calculate compliance score based on findings
|
||||
const okFindings = mockFindings.filter((f) => f.type === 'OK').length
|
||||
const totalChecks = mockFindings.length + 5 // Assume 5 additional checks passed
|
||||
const complianceScore = Math.round((okFindings / totalChecks) * 100 + 60) // Base score + passed checks
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
contractId,
|
||||
findings: mockFindings,
|
||||
complianceScore: Math.min(100, complianceScore),
|
||||
reviewCompletedAt: new Date().toISOString(),
|
||||
topRisks: [
|
||||
{ de: 'Meldefrist für Datenpannen zu lang', en: 'Data breach notification deadline too long' },
|
||||
{ de: 'Fehlende TIA für USA-Transfer', en: 'Missing TIA for USA transfer' },
|
||||
],
|
||||
requiredActions: [
|
||||
{ de: 'Meldefrist auf 24-48h verkürzen', en: 'Reduce notification deadline to 24-48h' },
|
||||
{ de: 'TIA für USA-Transfer durchführen', en: 'Conduct TIA for USA transfer' },
|
||||
],
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error reviewing contract:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to review contract' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/sdk/v1/vendor-compliance/contracts/[id]/review
|
||||
*
|
||||
* Get existing review results
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id: contractId } = await params
|
||||
|
||||
// In production, fetch from database
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
contractId,
|
||||
findings: [],
|
||||
complianceScore: null,
|
||||
reviewStatus: 'PENDING',
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching review:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to fetch review' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
88
admin-v2/app/api/sdk/v1/vendor-compliance/contracts/route.ts
Normal file
88
admin-v2/app/api/sdk/v1/vendor-compliance/contracts/route.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { ContractDocument } from '@/lib/sdk/vendor-compliance'
|
||||
|
||||
// In-memory storage for demo purposes
|
||||
const contracts: Map<string, ContractDocument> = new Map()
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const contractList = Array.from(contracts.values())
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: contractList,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching contracts:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to fetch contracts' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// Handle multipart form data for file upload
|
||||
const formData = await request.formData()
|
||||
const file = formData.get('file') as File | null
|
||||
const vendorId = formData.get('vendorId') as string
|
||||
const metadataStr = formData.get('metadata') as string
|
||||
|
||||
if (!file || !vendorId) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'File and vendorId are required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const metadata = metadataStr ? JSON.parse(metadataStr) : {}
|
||||
const id = uuidv4()
|
||||
|
||||
// In production, upload file to storage (MinIO, S3, etc.)
|
||||
const storagePath = `contracts/${id}/${file.name}`
|
||||
|
||||
const contract: ContractDocument = {
|
||||
id,
|
||||
tenantId: 'default',
|
||||
vendorId,
|
||||
fileName: `${id}-${file.name}`,
|
||||
originalName: file.name,
|
||||
mimeType: file.type,
|
||||
fileSize: file.size,
|
||||
storagePath,
|
||||
documentType: metadata.documentType || 'OTHER',
|
||||
version: metadata.version || '1.0',
|
||||
previousVersionId: metadata.previousVersionId,
|
||||
parties: metadata.parties,
|
||||
effectiveDate: metadata.effectiveDate ? new Date(metadata.effectiveDate) : undefined,
|
||||
expirationDate: metadata.expirationDate ? new Date(metadata.expirationDate) : undefined,
|
||||
autoRenewal: metadata.autoRenewal,
|
||||
renewalNoticePeriod: metadata.renewalNoticePeriod,
|
||||
terminationNoticePeriod: metadata.terminationNoticePeriod,
|
||||
reviewStatus: 'PENDING',
|
||||
status: 'DRAFT',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
|
||||
contracts.set(id, contract)
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
data: contract,
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
{ status: 201 }
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Error uploading contract:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to upload contract' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
28
admin-v2/app/api/sdk/v1/vendor-compliance/controls/route.ts
Normal file
28
admin-v2/app/api/sdk/v1/vendor-compliance/controls/route.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { CONTROLS_LIBRARY } from '@/lib/sdk/vendor-compliance'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams
|
||||
const domain = searchParams.get('domain')
|
||||
|
||||
let controls = [...CONTROLS_LIBRARY]
|
||||
|
||||
// Filter by domain if provided
|
||||
if (domain) {
|
||||
controls = controls.filter((c) => c.domain === domain)
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: controls,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching controls:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to fetch controls' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
/**
|
||||
* GET /api/sdk/v1/vendor-compliance/export/[reportId]/download
|
||||
*
|
||||
* Download a generated report file.
|
||||
* In production, this would redirect to a signed MinIO/S3 URL or stream the file.
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ reportId: string }> }
|
||||
) {
|
||||
const { reportId } = await params
|
||||
|
||||
// TODO: Implement actual file download
|
||||
// This would typically:
|
||||
// 1. Verify report exists and user has access
|
||||
// 2. Generate signed URL for MinIO/S3
|
||||
// 3. Redirect to signed URL or stream file
|
||||
|
||||
// For now, return a placeholder PDF
|
||||
const placeholderContent = `
|
||||
%PDF-1.4
|
||||
1 0 obj
|
||||
<< /Type /Catalog /Pages 2 0 R >>
|
||||
endobj
|
||||
2 0 obj
|
||||
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
|
||||
endobj
|
||||
3 0 obj
|
||||
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>
|
||||
endobj
|
||||
4 0 obj
|
||||
<< /Length 200 >>
|
||||
stream
|
||||
BT
|
||||
/F1 24 Tf
|
||||
100 700 Td
|
||||
(Vendor Compliance Report) Tj
|
||||
/F1 12 Tf
|
||||
100 650 Td
|
||||
(Report ID: ${reportId}) Tj
|
||||
100 620 Td
|
||||
(Generated: ${new Date().toISOString()}) Tj
|
||||
100 580 Td
|
||||
(This is a placeholder. Implement actual report generation.) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
5 0 obj
|
||||
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
|
||||
endobj
|
||||
xref
|
||||
0 6
|
||||
0000000000 65535 f
|
||||
0000000009 00000 n
|
||||
0000000058 00000 n
|
||||
0000000115 00000 n
|
||||
0000000266 00000 n
|
||||
0000000519 00000 n
|
||||
trailer
|
||||
<< /Size 6 /Root 1 0 R >>
|
||||
startxref
|
||||
598
|
||||
%%EOF
|
||||
`.trim()
|
||||
|
||||
// Return as PDF
|
||||
return new NextResponse(placeholderContent, {
|
||||
headers: {
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Disposition': `attachment; filename="Report_${reportId.slice(0, 8)}.pdf"`,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
/**
|
||||
* GET /api/sdk/v1/vendor-compliance/export/[reportId]
|
||||
*
|
||||
* Get report metadata by ID.
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ reportId: string }> }
|
||||
) {
|
||||
const { reportId } = await params
|
||||
|
||||
// TODO: Fetch report metadata from database
|
||||
// For now, return mock data
|
||||
|
||||
return NextResponse.json({
|
||||
id: reportId,
|
||||
status: 'completed',
|
||||
filename: `Report_${reportId.slice(0, 8)}.pdf`,
|
||||
generatedAt: new Date().toISOString(),
|
||||
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 24h
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/sdk/v1/vendor-compliance/export/[reportId]
|
||||
*
|
||||
* Delete a generated report.
|
||||
*/
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ reportId: string }> }
|
||||
) {
|
||||
const { reportId } = await params
|
||||
|
||||
// TODO: Delete report from storage and database
|
||||
console.log('Deleting report:', reportId)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
deletedId: reportId,
|
||||
})
|
||||
}
|
||||
118
admin-v2/app/api/sdk/v1/vendor-compliance/export/route.ts
Normal file
118
admin-v2/app/api/sdk/v1/vendor-compliance/export/route.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
/**
|
||||
* POST /api/sdk/v1/vendor-compliance/export
|
||||
*
|
||||
* Generate and export reports in various formats.
|
||||
* Currently returns mock data - integrate with actual report generation service.
|
||||
*/
|
||||
|
||||
interface ExportConfig {
|
||||
reportType: 'VVT_EXPORT' | 'VENDOR_AUDIT' | 'ROPA' | 'MANAGEMENT_SUMMARY' | 'DPIA_INPUT'
|
||||
format: 'PDF' | 'DOCX' | 'XLSX' | 'JSON'
|
||||
scope: {
|
||||
vendorIds: string[]
|
||||
processingActivityIds: string[]
|
||||
includeFindings: boolean
|
||||
includeControls: boolean
|
||||
includeRiskAssessment: boolean
|
||||
dateRange?: {
|
||||
from: string
|
||||
to: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const REPORT_TYPE_NAMES: Record<ExportConfig['reportType'], string> = {
|
||||
VVT_EXPORT: 'Verarbeitungsverzeichnis',
|
||||
VENDOR_AUDIT: 'Vendor-Audit-Pack',
|
||||
ROPA: 'RoPA',
|
||||
MANAGEMENT_SUMMARY: 'Management-Summary',
|
||||
DPIA_INPUT: 'DSFA-Input',
|
||||
}
|
||||
|
||||
const FORMAT_EXTENSIONS: Record<ExportConfig['format'], string> = {
|
||||
PDF: 'pdf',
|
||||
DOCX: 'docx',
|
||||
XLSX: 'xlsx',
|
||||
JSON: 'json',
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const config = (await request.json()) as ExportConfig
|
||||
|
||||
// Validate request
|
||||
if (!config.reportType || !config.format) {
|
||||
return NextResponse.json(
|
||||
{ error: 'reportType and format are required' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Generate report ID and filename
|
||||
const reportId = uuidv4()
|
||||
const timestamp = new Date().toISOString().slice(0, 10).replace(/-/g, '')
|
||||
const filename = `${REPORT_TYPE_NAMES[config.reportType]}_${timestamp}.${FORMAT_EXTENSIONS[config.format]}`
|
||||
|
||||
// TODO: Implement actual report generation
|
||||
// This would typically:
|
||||
// 1. Fetch data from database based on scope
|
||||
// 2. Generate report using template engine (e.g., docx-templates, pdfkit)
|
||||
// 3. Store in MinIO/S3
|
||||
// 4. Return download URL
|
||||
|
||||
// Mock implementation - simulate processing time
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
|
||||
// In production, this would be a signed URL to MinIO/S3
|
||||
const downloadUrl = `/api/sdk/v1/vendor-compliance/export/${reportId}/download`
|
||||
|
||||
// Log export for audit trail
|
||||
console.log('Export generated:', {
|
||||
reportId,
|
||||
reportType: config.reportType,
|
||||
format: config.format,
|
||||
scope: config.scope,
|
||||
filename,
|
||||
generatedAt: new Date().toISOString(),
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
id: reportId,
|
||||
reportType: config.reportType,
|
||||
format: config.format,
|
||||
filename,
|
||||
downloadUrl,
|
||||
generatedAt: new Date().toISOString(),
|
||||
scope: {
|
||||
vendorCount: config.scope.vendorIds?.length || 0,
|
||||
activityCount: config.scope.processingActivityIds?.length || 0,
|
||||
includesFindings: config.scope.includeFindings,
|
||||
includesControls: config.scope.includeControls,
|
||||
includesRiskAssessment: config.scope.includeRiskAssessment,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Export error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to generate export' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/sdk/v1/vendor-compliance/export
|
||||
*
|
||||
* List recent exports for the current tenant.
|
||||
*/
|
||||
export async function GET() {
|
||||
// TODO: Implement fetching recent exports from database
|
||||
// For now, return empty list
|
||||
return NextResponse.json({
|
||||
exports: [],
|
||||
totalCount: 0,
|
||||
})
|
||||
}
|
||||
43
admin-v2/app/api/sdk/v1/vendor-compliance/findings/route.ts
Normal file
43
admin-v2/app/api/sdk/v1/vendor-compliance/findings/route.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { Finding } from '@/lib/sdk/vendor-compliance'
|
||||
|
||||
// In-memory storage for demo purposes
|
||||
const findings: Map<string, Finding> = new Map()
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams
|
||||
const vendorId = searchParams.get('vendorId')
|
||||
const contractId = searchParams.get('contractId')
|
||||
const status = searchParams.get('status')
|
||||
|
||||
let findingsList = Array.from(findings.values())
|
||||
|
||||
// Filter by vendor
|
||||
if (vendorId) {
|
||||
findingsList = findingsList.filter((f) => f.vendorId === vendorId)
|
||||
}
|
||||
|
||||
// Filter by contract
|
||||
if (contractId) {
|
||||
findingsList = findingsList.filter((f) => f.contractId === contractId)
|
||||
}
|
||||
|
||||
// Filter by status
|
||||
if (status) {
|
||||
findingsList = findingsList.filter((f) => f.status === status)
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: findingsList,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching findings:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to fetch findings' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
// This would reference the same storage as the main route
|
||||
// In production, this would be database calls
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
|
||||
// In production, fetch from database
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: null, // Would return the activity
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching processing activity:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to fetch processing activity' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
const body = await request.json()
|
||||
|
||||
// In production, update in database
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: { id, ...body, updatedAt: new Date() },
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error updating processing activity:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to update processing activity' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
|
||||
// In production, delete from database
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error deleting processing activity:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to delete processing activity' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { ProcessingActivity, generateVVTId } from '@/lib/sdk/vendor-compliance'
|
||||
|
||||
// In-memory storage for demo purposes
|
||||
// In production, this would be replaced with database calls
|
||||
const processingActivities: Map<string, ProcessingActivity> = new Map()
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const activities = Array.from(processingActivities.values())
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: activities,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching processing activities:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to fetch processing activities' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
|
||||
// Generate IDs
|
||||
const id = uuidv4()
|
||||
const existingIds = Array.from(processingActivities.values()).map((a) => a.vvtId)
|
||||
const vvtId = body.vvtId || generateVVTId(existingIds)
|
||||
|
||||
const activity: ProcessingActivity = {
|
||||
id,
|
||||
tenantId: 'default', // Would come from auth context
|
||||
vvtId,
|
||||
name: body.name,
|
||||
responsible: body.responsible,
|
||||
dpoContact: body.dpoContact,
|
||||
purposes: body.purposes || [],
|
||||
dataSubjectCategories: body.dataSubjectCategories || [],
|
||||
personalDataCategories: body.personalDataCategories || [],
|
||||
recipientCategories: body.recipientCategories || [],
|
||||
thirdCountryTransfers: body.thirdCountryTransfers || [],
|
||||
retentionPeriod: body.retentionPeriod || { description: { de: '', en: '' } },
|
||||
technicalMeasures: body.technicalMeasures || [],
|
||||
legalBasis: body.legalBasis || [],
|
||||
dataSources: body.dataSources || [],
|
||||
systems: body.systems || [],
|
||||
dataFlows: body.dataFlows || [],
|
||||
protectionLevel: body.protectionLevel || 'MEDIUM',
|
||||
dpiaRequired: body.dpiaRequired || false,
|
||||
dpiaJustification: body.dpiaJustification,
|
||||
subProcessors: body.subProcessors || [],
|
||||
legalRetentionBasis: body.legalRetentionBasis,
|
||||
status: body.status || 'DRAFT',
|
||||
owner: body.owner || '',
|
||||
lastReviewDate: body.lastReviewDate,
|
||||
nextReviewDate: body.nextReviewDate,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
|
||||
processingActivities.set(id, activity)
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
data: activity,
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
{ status: 201 }
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Error creating processing activity:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to create processing activity' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
82
admin-v2/app/api/sdk/v1/vendor-compliance/vendors/route.ts
vendored
Normal file
82
admin-v2/app/api/sdk/v1/vendor-compliance/vendors/route.ts
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { Vendor } from '@/lib/sdk/vendor-compliance'
|
||||
|
||||
// In-memory storage for demo purposes
|
||||
const vendors: Map<string, Vendor> = new Map()
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const vendorList = Array.from(vendors.values())
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: vendorList,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching vendors:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to fetch vendors' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const id = uuidv4()
|
||||
|
||||
const vendor: Vendor = {
|
||||
id,
|
||||
tenantId: 'default',
|
||||
name: body.name,
|
||||
legalForm: body.legalForm,
|
||||
country: body.country,
|
||||
address: body.address,
|
||||
website: body.website,
|
||||
role: body.role,
|
||||
serviceDescription: body.serviceDescription,
|
||||
serviceCategory: body.serviceCategory,
|
||||
dataAccessLevel: body.dataAccessLevel || 'NONE',
|
||||
processingLocations: body.processingLocations || [],
|
||||
transferMechanisms: body.transferMechanisms || [],
|
||||
certifications: body.certifications || [],
|
||||
primaryContact: body.primaryContact,
|
||||
dpoContact: body.dpoContact,
|
||||
securityContact: body.securityContact,
|
||||
contractTypes: body.contractTypes || [],
|
||||
contracts: body.contracts || [],
|
||||
inherentRiskScore: body.inherentRiskScore || 50,
|
||||
residualRiskScore: body.residualRiskScore || 50,
|
||||
manualRiskAdjustment: body.manualRiskAdjustment,
|
||||
riskJustification: body.riskJustification,
|
||||
reviewFrequency: body.reviewFrequency || 'ANNUAL',
|
||||
lastReviewDate: body.lastReviewDate,
|
||||
nextReviewDate: body.nextReviewDate,
|
||||
status: body.status || 'ACTIVE',
|
||||
processingActivityIds: body.processingActivityIds || [],
|
||||
notes: body.notes,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
|
||||
vendors.set(id, vendor)
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
data: vendor,
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
{ status: 201 }
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Error creating vendor:', error)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Failed to create vendor' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user