import express from 'express'; import path from 'path'; import fs from 'fs/promises'; import { fileURLToPath } from 'url'; import process from 'process'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); app.use(express.json({ limit: '20mb' })); const PORT = parseInt(process.env.PORT || process.env.HTTPS_PORT || '2093', 10); const publicDir = path.join(__dirname, '..', 'public'); const snapshotsDir = path.join(publicDir, 'snapshots'); const WEBCAM_URL = process.env.WEBCAM_URL || ''; const BODYTRACKER_URL = process.env.BODYTRACKER_URL || ''; app.use(express.static(publicDir)); app.get('/api/health', (req, res) => { res.json({ ok: true, mode: 'backend-proxy', webcamUrl: WEBCAM_URL || null, bodyTrackerUrl: BODYTRACKER_URL || null }); }); async function findLatestSnapshotFile() { const files = await fs.readdir(snapshotsDir); const entries = await Promise.all( files .filter((name) => name.endsWith('.csv')) .map(async (name) => ({ name, mtime: (await fs.stat(path.join(snapshotsDir, name))).mtime.valueOf() })) ); if (entries.length === 0) return null; entries.sort((a, b) => b.mtime - a.mtime); return entries[0].name; } app.get('/api/latest-snapshot', async (req, res) => { try { if (WEBCAM_URL) { const url = new URL('/api/latest-snapshot', WEBCAM_URL).toString(); const fetchRes = await fetch(url); const contentType = fetchRes.headers.get('content-type') || ''; if (!fetchRes.ok) { const text = await fetchRes.text(); return res.status(fetchRes.status).type('text/plain').send(text); } if (contentType.includes('application/json')) { const body = await fetchRes.json(); return res.json(body); } const text = await fetchRes.text(); return res.json({ filename: 'latest.csv', mtime: new Date().toISOString(), content: text }); } const latestFile = await findLatestSnapshotFile(); if (!latestFile) { return res.status(404).json({ error: 'Keine Snapshot-CSV-Datei gefunden' }); } const baseName = path.basename(latestFile, path.extname(latestFile)); const csvPath = path.join(snapshotsDir, latestFile); const jsonPath = path.join(snapshotsDir, `${baseName}.json`); const imagePath = path.join(snapshotsDir, `${baseName}_annotated.jpg`); const imagePath2 = path.join(snapshotsDir, `${baseName}_annotated2.jpg`); const content = await fs.readFile(csvPath, 'utf8'); const result = { filename: latestFile, mtime: (await fs.stat(csvPath)).mtime.toISOString(), content }; try { result.jsonFile = { filename: `${baseName}.json`, content: await fs.readFile(jsonPath, 'utf8') }; } catch {} try { const jpg = await fs.readFile(imagePath); result.imageFile = { filename: path.basename(imagePath), mimeType: 'image/jpeg', contentBase64: jpg.toString('base64') }; } catch {} try { const jpg2 = await fs.readFile(imagePath2); result.image2 = { filename: path.basename(imagePath2), mimeType: 'image/jpeg', contentBase64: jpg2.toString('base64') }; } catch {} return res.json(result); } catch (err) { console.error('latest-snapshot error:', err); return res.status(500).json({ error: 'Fehler beim Laden des Snapshots', details: String(err) }); } }); app.post('/api/estimate', async (req, res) => { if (!BODYTRACKER_URL) { return res.status(501).json({ error: 'BODYTRACKER_URL ist nicht konfiguriert' }); } try { const { imageFile, image2, robotIntrinsics } = req.body; const formData = new FormData(); if (imageFile?.contentBase64) { const buffer = Buffer.from(imageFile.contentBase64, 'base64'); formData.append('images', new Blob([buffer], { type: imageFile.mimeType || 'image/jpeg' }), imageFile.filename || 'snapshot.jpg'); } if (image2?.contentBase64) { const buffer2 = Buffer.from(image2.contentBase64, 'base64'); formData.append('images', new Blob([buffer2], { type: image2.mimeType || 'image/jpeg' }), image2.filename || 'snapshot2.jpg'); } if (robotIntrinsics) { formData.append('intrinsics', new Blob([JSON.stringify(robotIntrinsics)], { type: 'application/json' }), 'intrinsics.json'); } const estimateUrl = new URL('/v1/estimate', BODYTRACKER_URL).toString(); const fetchRes = await fetch(estimateUrl, { method: 'POST', body: formData }); if (!fetchRes.ok) { const message = await fetchRes.text(); return res.status(fetchRes.status).json({ error: 'BodyTracker-Fehler', details: message }); } const body = await fetchRes.json(); return res.json(body); } catch (err) { console.error('estimate error:', err); return res.status(500).json({ error: 'Fehler beim Aufruf des BodyTracker', details: String(err) }); } }); app.listen(PORT, () => { console.log(`appRobotHoming backend listening on port ${PORT}`); console.log(`WEBCAM_URL=${WEBCAM_URL || ''}`); console.log(`BODYTRACKER_URL=${BODYTRACKER_URL || ''}`); });