fixed missing endpoint GET /routes/:id/pois/:id and added json style fileupload

This commit is contained in:
Florian Zumpe
2026-06-17 01:39:50 +02:00
parent 5c85a0194b
commit 09ce7c0605
10 changed files with 380 additions and 50 deletions
+85
View File
@@ -1,3 +1,4 @@
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import multer from 'multer';
@@ -28,3 +29,87 @@ export const upload = multer({
limits: { fileSize: config.maxUploadBytes, files: 25 },
fileFilter
});
function decodeBase64(value, fieldName) {
if (typeof value !== 'string' || !value.trim()) {
throw new HttpError(400, `${fieldName}.base64 muss eine nichtleere Base64-Zeichenkette sein.`);
}
const compact = value.replace(/\s+/g, '');
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(compact) || compact.length % 4 === 1) {
throw new HttpError(400, `${fieldName}.base64 enthält keine gültigen Base64-Daten.`);
}
const buffer = Buffer.from(compact, 'base64');
const canonicalInput = compact.replace(/=+$/, '');
const canonicalOutput = buffer.toString('base64').replace(/=+$/, '');
if (canonicalInput !== canonicalOutput) {
throw new HttpError(400, `${fieldName}.base64 enthält keine gültigen Base64-Daten.`);
}
if (buffer.length > config.maxUploadBytes) {
throw new HttpError(413, `Die Datei überschreitet das Limit von ${Math.floor(config.maxUploadBytes / 1024 / 1024)} MB.`);
}
return buffer;
}
function jsonFileDescriptor(value, fieldName) {
if (value == null) return null;
if (typeof value !== 'object' || Array.isArray(value)) {
throw new HttpError(400, `${fieldName} muss ein JSON-Objekt sein.`);
}
const originalname = String(value.filename ?? '').trim();
if (!originalname) throw new HttpError(400, `${fieldName}.filename ist erforderlich.`);
let mimetype = String(value.contentType ?? value.mimeType ?? '').trim().toLowerCase();
let base64 = value.base64;
if (value.dataUrl != null) {
if (typeof value.dataUrl !== 'string') {
throw new HttpError(400, `${fieldName}.dataUrl muss eine Zeichenkette sein.`);
}
const match = /^data:([^;,]+);base64,(.*)$/s.exec(value.dataUrl.trim());
if (!match) {
throw new HttpError(400, `${fieldName}.dataUrl muss eine Base64-Data-URL sein.`);
}
if (!mimetype) mimetype = match[1].trim().toLowerCase();
base64 = match[2];
}
if (!mimetype) throw new HttpError(400, `${fieldName}.contentType ist erforderlich.`);
return { originalname, mimetype, buffer: decodeBase64(base64, fieldName) };
}
export function jsonUpload(fieldName) {
return function parseJsonUpload(req, res, next) {
try {
if (req.file || req.body?.[fieldName] == null) return next();
const descriptor = jsonFileDescriptor(req.body[fieldName], fieldName);
if (!descriptor) return next();
const destination = path.join(config.storageDir, 'uploads');
fs.mkdirSync(destination, { recursive: true });
const safe = descriptor.originalname.replace(/[^a-zA-Z0-9._-]/g, '_');
const filename = `${Date.now()}-${crypto.randomUUID()}-${safe}`;
const filePath = path.join(destination, filename);
fs.writeFileSync(filePath, descriptor.buffer, { flag: 'wx' });
req.file = {
fieldname: fieldName,
originalname: descriptor.originalname,
encoding: '7bit',
mimetype: descriptor.mimetype,
destination,
filename,
path: filePath,
size: descriptor.buffer.length
};
delete req.body[fieldName];
next();
} catch (error) {
next(error);
}
};
}