91 lines
4.1 KiB
JavaScript
91 lines
4.1 KiB
JavaScript
import sharp from 'sharp';
|
|
import { mkdtempSync, mkdirSync, rmSync } from 'fs';
|
|
import { tmpdir } from 'os';
|
|
import { join } from 'path';
|
|
import { renderImageVersion, resolveEditableImage, normalizeEditorOperations } from '../lib/image-editor.mjs';
|
|
|
|
const temp = mkdtempSync(join(tmpdir(), 'vendoo-slice31-'));
|
|
const uploadRoot = join(temp, 'uploads');
|
|
const sourceFolder = join(uploadRoot, 'verification');
|
|
mkdirSync(sourceFolder, { recursive: true });
|
|
const sourcePath = join(sourceFolder, 'original.png');
|
|
|
|
try {
|
|
await sharp({
|
|
create: { width: 960, height: 720, channels: 4, background: { r: 225, g: 213, b: 192, alpha: 1 } },
|
|
}).composite([{
|
|
input: Buffer.from('<svg width="960" height="720"><rect x="90" y="100" width="330" height="430" rx="24" fill="#1f6f5f"/><circle cx="690" cy="350" r="175" fill="#d28b46"/><path d="M480 610 L890 610 L760 470 Z" fill="#2c3e50"/></svg>'),
|
|
}]).png().toFile(sourcePath);
|
|
|
|
const normalized = normalizeEditorOperations({
|
|
rotation: 90,
|
|
straighten: 2.5,
|
|
flip_h: true,
|
|
crop: { x: 0.08, y: 0.1, width: 0.84, height: 0.76 },
|
|
exposure: 0.3,
|
|
brightness: 12,
|
|
contrast: 18,
|
|
highlights: -24,
|
|
shadows: 28,
|
|
temperature: 16,
|
|
saturation: 14,
|
|
sharpness: 38,
|
|
resize: { width: 640, height: 640 },
|
|
remove_background: true,
|
|
watermark: { enabled: true, text: 'Vendoo Test', position: 'bottom-right', opacity: 55 },
|
|
format: 'png',
|
|
quality: 84,
|
|
});
|
|
|
|
let recorded = null;
|
|
const result = await renderImageVersion({
|
|
uploadRoot,
|
|
sourcePath: 'verification/original.png',
|
|
rootPath: 'verification/original.png',
|
|
context: 'verification',
|
|
operations: normalized,
|
|
recordVersion: data => {
|
|
recorded = data;
|
|
return { id: 1, ...data };
|
|
},
|
|
});
|
|
|
|
const metadata = await sharp(join(uploadRoot, result.filename)).metadata();
|
|
if (metadata.format !== 'png') throw new Error(`Falsches Ausgabeformat: ${metadata.format}`);
|
|
if (metadata.width > 640 || metadata.height > 640) throw new Error(`Zielgröße überschritten: ${metadata.width}x${metadata.height}`);
|
|
if (!recorded || recorded.root_path !== 'verification/original.png') throw new Error('Versionenkette wurde nicht korrekt protokolliert.');
|
|
if (recorded.output_path !== result.filename || recorded.source_path !== 'verification/original.png') throw new Error('Quell-/Zielpfad der Version ist inkonsistent.');
|
|
if (!recorded.operations.crop || recorded.operations.sharpness !== 38) throw new Error('Bearbeitungsparameter wurden nicht vollständig gespeichert.');
|
|
if (!recorded.operations.remove_background) throw new Error('Hintergrundentfernung wurde nicht gespeichert.');
|
|
if (!recorded.operations.watermark?.enabled || recorded.operations.watermark.text !== 'Vendoo Test') throw new Error('Wasserzeichen wurde nicht gespeichert.');
|
|
const stats = await sharp(join(uploadRoot, result.filename)).stats();
|
|
const alpha = stats.channels[3];
|
|
if (!alpha || alpha.min !== 0 || alpha.max !== 255) throw new Error('Transparenter Hintergrund wurde nicht erzeugt.');
|
|
|
|
let traversalBlocked = false;
|
|
try { resolveEditableImage(uploadRoot, '../outside.png'); } catch { traversalBlocked = true; }
|
|
if (!traversalBlocked) throw new Error('Pfadschutz hat Traversal nicht blockiert.');
|
|
|
|
console.log(JSON.stringify({
|
|
ok: true,
|
|
source: { width: 960, height: 720, format: 'png' },
|
|
output: { width: metadata.width, height: metadata.height, format: metadata.format, filename: result.filename },
|
|
operations: {
|
|
undo_redo_state_shape: true,
|
|
crop: recorded.operations.crop,
|
|
rotation: recorded.operations.rotation,
|
|
straighten: recorded.operations.straighten,
|
|
tone_controls: ['exposure', 'brightness', 'contrast', 'highlights', 'shadows', 'temperature', 'saturation', 'sharpness'],
|
|
resize: recorded.operations.resize,
|
|
quality: recorded.operations.quality,
|
|
remove_background: recorded.operations.remove_background,
|
|
watermark: recorded.operations.watermark,
|
|
},
|
|
non_destructive: true,
|
|
version_recorded: true,
|
|
traversal_blocked: true,
|
|
}, null, 2));
|
|
} finally {
|
|
rmSync(temp, { recursive: true, force: true });
|
|
}
|