diff --git a/backend/assets/static/360-badge.png b/backend/assets/static/360-badge.png
new file mode 100644
index 0000000..4729284
Binary files /dev/null and b/backend/assets/static/360-badge.png differ
diff --git a/backend/routes/apiRoutes.js b/backend/routes/apiRoutes.js
index c4a16e1..fdb9e85 100644
--- a/backend/routes/apiRoutes.js
+++ b/backend/routes/apiRoutes.js
@@ -3,6 +3,7 @@ const multer = require('multer');
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
+const sharp = require('sharp');
const User = require('../models/User');
const Asset = require('../models/Asset');
@@ -96,20 +97,13 @@ router.post('/scenes', protect, uploadSinglePanorama, async (req, res) => {
// Lấy tọa độ GPS gốc từ ảnh vừa upload trước khi nén/xử lý
const originalGPS = await getGPSCoordinates(tempFilePath);
- // BACKGROUND PROCESSING: Thực hiện song song không chặn response
- setImmediate(async () => {
- try {
- // 1. Resize to 8K
- await resizeTo8K(tempFilePath, processedFilePath);
- // 2. Inject GPS
- await injectGPSCoordinates(processedFilePath, latitude, longitude);
- // 3. Cleanup temp file
- if (fs.existsSync(tempFilePath)) fs.unlinkSync(tempFilePath);
- console.log(`Background processing finished for: ${processedFileName}`);
- } catch (err) {
- console.error(`Background Image processing failed: ${err.message}`);
- }
- });
+ // Thực hiện xử lý ảnh tuần tự để đảm bảo file được tạo thành công trước khi lưu DB
+ // 1. Resize to 8K (Sẽ convert từ DNG sang JPG nếu sharp hỗ trợ libraw, nếu không sẽ báo lỗi)
+ await resizeTo8K(tempFilePath, processedFilePath);
+ // 2. Inject GPS vào file JPG vừa tạo
+ await injectGPSCoordinates(processedFilePath, latitude, longitude);
+ // 3. Cleanup temp file (file gốc dng)
+ if (fs.existsSync(tempFilePath)) fs.unlinkSync(tempFilePath);
// 5. Save Asset to DB
const asset = new Asset({
@@ -151,17 +145,17 @@ router.post('/scenes', protect, uploadSinglePanorama, async (req, res) => {
});
await scene.save();
- res.status(202).json({
+ res.status(201).json({
message: 'Scene created successfully',
scene
});
} catch (error) {
- // Cleanup file if error occurs
+ // Dọn dẹp các file rác nếu quá trình xử lý thất bại
if (req.file && fs.existsSync(req.file.path)) {
try { fs.unlinkSync(req.file.path); } catch (e) {}
}
- res.status(500).json({ message: error.message });
+ res.status(500).json({ message: "Xử lý ảnh 360 thất bại. Vui lòng đảm bảo bạn upload ảnh Panorama đã được stitch (JPEG). Chi tiết: " + error.message });
}
});
@@ -267,8 +261,9 @@ router.get('/share/:sceneId', optionalAuth, async (req, res) => {
+
-
+
@@ -496,6 +491,41 @@ router.get('/assets/view/:assetId', verifyReferer, optionalAuth, async (req, res
const resolvedPath = path.resolve(asset.filePath);
+ // Kiểm tra xem có cần chèn watermark 360° hay không (dành cho Social Bot hoặc yêu cầu thủ công)
+ const userAgent = req.headers['user-agent'] || '';
+ const isSocialBot = /facebookexternalhit|Facebot|ZaloBot|Twitterbot|Slackbot|LinkedInBot|Embedly/i.test(userAgent);
+ const wantWatermark = isSocialBot || req.query.watermark === 'true';
+
+ if (wantWatermark) {
+ const iconPath = path.join(__dirname, '../assets/static/360-badge.png');
+ if (fs.existsSync(iconPath)) {
+ try {
+ // Resize ảnh về kích thước vuông (1200x1200) trước khi chèn icon
+ // Việc này giúp icon 360 hiển thị rõ ràng và tỷ lệ hợp lý hơn
+ const imageBuffer = await sharp(resolvedPath)
+ .resize(1200, 1200, {
+ fit: 'cover'
+ })
+ .composite([{
+ input: iconPath,
+ gravity: 'center'
+ }])
+ .jpeg({ quality: 90 })
+ .toBuffer();
+
+ res.set({
+ 'Content-Type': 'image/jpeg',
+ 'Cache-Control': 'public, max-age=2592000',
+ 'Content-Disposition': 'inline; filename="preview_360.jpg"'
+ });
+ return res.send(imageBuffer);
+ } catch (sharpError) {
+ console.error("[Sharp Error]:", sharpError.message);
+ // Nếu lỗi sharp, fallback xuống sendFile bình thường ở dưới
+ }
+ }
+ }
+
// Sử dụng res.sendFile để tối ưu hóa việc truyền tải file lớn và hỗ trợ Caching (ETag)
res.sendFile(resolvedPath, {
maxAge: 2592000000, // 30 ngày (tính bằng ms)
diff --git a/backend/uploads/processed_1780970998028_047bd312.JPG.jpg b/backend/uploads/processed_1780970998028_047bd312.JPG.jpg
new file mode 100644
index 0000000..646787a
Binary files /dev/null and b/backend/uploads/processed_1780970998028_047bd312.JPG.jpg differ
diff --git a/backend/uploads/temp/1780971557798_da18c0af.dng b/backend/uploads/temp/1780971557798_da18c0af.dng
new file mode 100644
index 0000000..50f3be0
Binary files /dev/null and b/backend/uploads/temp/1780971557798_da18c0af.dng differ