perf(booth): optimize assets and result flow

WebP reduces UI payload; JPEG keeps portrait saves compatible.\nFinish ends the visit without prompting another portrait.
This commit is contained in:
Ajasra committed 2026-10-30 00:00:16 +08:00
1 parent 56da9c86d2
commit 6772e0cf37
41 files changed
+225 -97

No files matched your search

+23 -26
View File
@@ -1,5 +1,6 @@
import fs from "fs";
import path from "path";
import sharp from "sharp";
import { validateAndSanitizeImage } from "@/lib/security";
export interface GenerationRecord {
@@ -202,26 +203,34 @@ export async function persistImageFile(
return { imageUrl: imageInput, filename: "" };
}
// If it's a data URL or raw base64
if (imageInput.startsWith("data:image/") || imageInput.startsWith("data:application/")) {
const validation = validateAndSanitizeImage(imageInput, 15 * 1024 * 1024);
if (validation.valid && validation.extension) {
const ext = validation.extension; // Strictly "jpg" | "png" | "webp"
const filename = `${safeId}.${ext}`;
const filePath = path.join(IMAGES_DIR, filename);
const persistJpeg = async (inputBuffer: Buffer) => {
const sourcePath = path.join(IMAGES_DIR, `${safeId}.source`);
const filename = `${safeId}.jpg`;
const jpegPath = path.join(IMAGES_DIR, filename);
fs.writeFileSync(sourcePath, inputBuffer);
const base64Data = validation.sanitizedBase64!.split(",")[1];
fs.writeFileSync(filePath, Buffer.from(base64Data, "base64"));
// Automatically apply school branding: logo (top-left), WeChat QR (bottom-right), and footer text
try {
// Apply the existing branding before encoding the final downloadable image.
try {
const { applyWatermarkToFile } = await import("@/lib/watermark");
await applyWatermarkToFile(filePath);
await applyWatermarkToFile(sourcePath);
} catch (wmErr) {
console.warn("[Storage] Watermark application skipped:", wmErr);
}
await sharp(sourcePath).jpeg({ quality: 92, mozjpeg: true }).toFile(jpegPath);
return { imageUrl: `/api/images/${filename}`, filename };
} finally {
if (fs.existsSync(sourcePath)) fs.unlinkSync(sourcePath);
}
};
// If it's a data URL or raw base64
if (imageInput.startsWith("data:image/") || imageInput.startsWith("data:application/")) {
const validation = validateAndSanitizeImage(imageInput, 15 * 1024 * 1024);
if (validation.valid) {
const base64Data = validation.sanitizedBase64!.split(",")[1];
return persistJpeg(Buffer.from(base64Data, "base64"));
} else {
console.warn(`[Storage Security Alert] Refused to persist invalid image: ${validation.error}`);
return { imageUrl: imageInput, filename: "" };
@@ -258,20 +267,8 @@ export async function persistImageFile(
15 * 1024 * 1024
);
if (validation.valid && validation.extension) {
const filename = `${safeId}.${validation.extension}`;
const filePath = path.join(IMAGES_DIR, filename);
fs.writeFileSync(filePath, buffer);
// Automatically apply school branding: logo (top-left), WeChat QR (bottom-right), and footer text
try {
const { applyWatermarkToFile } = await import("@/lib/watermark");
await applyWatermarkToFile(filePath);
} catch (wmErr) {
console.warn("[Storage] Watermark application skipped:", wmErr);
}
return { imageUrl: `/api/images/${filename}`, filename };
if (validation.valid) {
return persistJpeg(buffer);
} else {
console.warn(`[Storage Security Alert] Remote file was not a valid image: ${validation.error}`);
}