| Server IP : 217.154.3.148 / Your IP : 216.73.216.90 Web Server : Apache System : Linux blissful-wright.217-154-3-148.plesk.page 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64 User : gracious-boyd_hfhh1h8vo9n ( 10000) PHP Version : 8.3.31 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs/pages/api/ |
Upload File : |
import type { NextApiRequest, NextApiResponse } from 'next'
import { estimateLightShadow } from '../../lib/illumination'
type ParsedUpload = { file: Buffer; fields: Record<string, string> }
type AnalyzeMixResponse = { fg_mask?: string | null }
export const config = { api: { bodyParser: false } }
// Hilfs-Aufruf: deine Analyze-Mix-Route holen, um fg_mask zu bekommen (falls nicht mitgesendet)
const LOCAL_ANALYZE = process.env.LOCAL_ANALYZE_URL || 'http://127.0.0.1:3001/nextjs/api/analyze-mix?ransac=auto'
async function callAnalyze(buf: Buffer): Promise<AnalyzeMixResponse> {
const fd = new FormData()
const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer
const blob = new Blob([arrayBuffer], { type: 'application/octet-stream' })
fd.append('file', blob, 'img.jpg')
const r = await fetch(LOCAL_ANALYZE, { method: 'POST', body: fd })
if (!r.ok) throw new Error('analyze failed ' + r.status)
return r.json() as Promise<AnalyzeMixResponse>
}
async function streamToBuffer(req: NextApiRequest): Promise<Buffer> {
const chunks: Buffer[] = []
const iterable = req as unknown as AsyncIterable<Buffer | Uint8Array | string>
for await (const chunk of iterable) {
if (typeof chunk === 'string') {
chunks.push(Buffer.from(chunk))
} else {
chunks.push(Buffer.from(chunk))
}
}
return Buffer.concat(chunks)
}
function parseMultipart(body: Buffer, boundary: string): ParsedUpload {
const boundaryBuffer = Buffer.from(`--${boundary}`)
let cursor = body.indexOf(boundaryBuffer)
const result: ParsedUpload = { file: Buffer.alloc(0), fields: {} }
let fileFound = false
while (cursor !== -1) {
cursor += boundaryBuffer.length
if (body[cursor] === 45 && body[cursor + 1] === 45) break
if (body[cursor] === 13 && body[cursor + 1] === 10) {
cursor += 2
}
const headerEnd = body.indexOf(Buffer.from('\r\n\r\n'), cursor)
if (headerEnd === -1) break
const headersBuf = body.slice(cursor, headerEnd)
const headers = headersBuf.toString('utf8')
cursor = headerEnd + 4
const nextBoundary = body.indexOf(boundaryBuffer, cursor)
if (nextBoundary === -1) break
let contentEnd = nextBoundary
if (body[contentEnd - 2] === 13 && body[contentEnd - 1] === 10) {
contentEnd -= 2
}
const content = body.slice(cursor, contentEnd)
cursor = nextBoundary
const disposition = headers
.split(/\r?\n/)
.find((line) => /content-disposition/i.test(line)) || ''
const nameMatch = disposition.match(/name="([^"]+)"/i)
const fileMatch = disposition.match(/filename="([^"]*)"/i)
const fieldName = nameMatch?.[1]
if (fileMatch && fieldName) {
result.file = content
fileFound = true
} else if (fieldName) {
result.fields[fieldName] = content.toString('utf8')
}
}
if (!fileFound) {
throw new Error('no file part in multipart payload')
}
return result
}
async function parseUpload(req: NextApiRequest): Promise<ParsedUpload> {
const contentType = req.headers['content-type'] || ''
const body = await streamToBuffer(req)
if (/multipart\/form-data/i.test(contentType)) {
const boundaryMatch = contentType.match(/boundary=([^;]+)/i)
if (!boundaryMatch) throw new Error('multipart boundary missing')
return parseMultipart(body, boundaryMatch[1])
}
return { file: body, fields: {} }
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { file, fields } = await parseUpload(req)
if (!file || file.length === 0) {
throw new Error('empty image payload')
}
// optional: der Client kann fg_mask als dataURL mitliefern (FormData mit text-part 'fg_mask')
// hier vereinfachen wir: wenn kein fg_mask-Text vorliegt, fragen wir analyze-mix ab
let fgMask: string | undefined = fields['fg_mask']
try {
if (!fgMask) {
const analyzeResp = await callAnalyze(file)
fgMask = typeof analyzeResp.fg_mask === 'string' ? analyzeResp.fg_mask : undefined
}
} catch {
// analyze fallback failed -> continue without fg mask
}
const out = await estimateLightShadow(file, { fgMask })
res.status(200).json({ ok: true, ...out, source: 'illumination@lowfreq' })
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error)
res.status(500).json({ ok: false, error: message })
}
}