Files
ember-market-frontend/backend/routes/storefront.routes.js
2025-03-10 17:39:37 +00:00

265 lines
8.1 KiB
JavaScript

import express from "express";
import { protectVendor } from "../middleware/vendorAuthMiddleware.js";
import Store from "../models/Store.model.js";
import TelegramUser from "../models/TelegramUser.model.js";
import { sendBulkTelegramMessages } from "../utils/telegramUtils.js";
import multer from "multer";
import sharp from "sharp";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
const router = express.Router();
// Get the current directory and set up a relative path for uploads
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const uploadsBaseDir = path.join(process.cwd(), 'uploads');
const broadcastsDir = path.join(uploadsBaseDir, 'broadcasts');
console.log('Upload directory:', {
path: broadcastsDir,
exists: fs.existsSync(broadcastsDir),
absolutePath: path.resolve(broadcastsDir)
});
// Create both the base uploads directory and the broadcasts subdirectory
if (!fs.existsSync(uploadsBaseDir)) {
fs.mkdirSync(uploadsBaseDir, { recursive: true });
}
if (!fs.existsSync(broadcastsDir)) {
fs.mkdirSync(broadcastsDir, { recursive: true });
}
const upload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit for Telegram
fileFilter: (req, file, cb) => {
if (file.mimetype.startsWith("image/")) {
cb(null, true);
} else {
cb(new Error("Only image files are allowed!"), false);
}
},
});
const handleMulterError = (err, req, res, next) => {
if (err instanceof multer.MulterError) {
if (err.code === "LIMIT_FILE_SIZE") {
return res.status(400).json({ error: "File size too large. Maximum allowed size is 10MB." });
}
return res.status(400).json({ error: "Upload error occurred.", details: err.message });
}
next(err);
};
/**
* Get Storefront Details
* @route GET /api/storefront
* @access Private (Vendors only)
*/
router.get("/", protectVendor, async (req, res) => {
try {
const storeId = req.user.storeId;
const store = await Store.findById(storeId);
if (!store) {
return res.status(404).json({ error: "Storefront not found" });
}
return res.json(store);
} catch (error) {
console.error("Error fetching storefront:", error);
return res.status(500).json({ error: "Failed to retrieve storefront" });
}
});
/**
* Update Storefront Settings
* @route PUT /api/storefront
* @access Private (Vendors only)
*/
router.put("/", protectVendor, async (req, res) => {
try {
const storeId = req.user.storeId;
const { pgpKey, welcomeMessage, telegramToken, shipsFrom, shipsTo, wallets } = req.body;
const updatedStore = await Store.findByIdAndUpdate(
storeId,
{
...(pgpKey !== undefined && { pgpKey }),
...(welcomeMessage !== undefined && { welcomeMessage }),
...(telegramToken !== undefined && { telegramToken }),
...(shipsFrom !== undefined && { shipsFrom }),
...(shipsTo !== undefined && { shipsTo }),
...(wallets !== undefined && { wallets })
},
{ new: true }
);
if (!updatedStore) {
return res.status(404).json({ error: "Storefront not found" });
}
return res.json({
message: "Storefront updated successfully",
store: updatedStore,
});
} catch (error) {
console.error("Error updating storefront:", error);
return res.status(500).json({ error: "Failed to update storefront" });
}
});
/**
* Broadcast a Message
* @route POST /api/storefront/broadcast
* @access Private (Vendors only)
*/
router.post("/broadcast", protectVendor, upload.single("file"), handleMulterError, async (req, res) => {
let photoPath = null;
try {
const storeId = req.user.storeId;
const { message } = req.body;
// Handle image upload if present
if (req.file) {
const outputFileName = `${Date.now()}-${req.file.originalname.split('.').slice(0, -1).join('.')}.jpg`;
const outputFilePath = path.join(broadcastsDir, outputFileName);
// Ensure upload directory exists
if (!fs.existsSync(broadcastsDir)) {
fs.mkdirSync(broadcastsDir, { recursive: true });
}
console.log('Processing upload:', {
fileName: outputFileName,
filePath: outputFilePath,
absolutePath: path.resolve(outputFilePath),
uploadDirExists: fs.existsSync(broadcastsDir),
uploadDirContents: fs.readdirSync(broadcastsDir)
});
try {
// Process and save the image
await sharp(req.file.buffer)
.resize(1280, 1280, { fit: 'inside', withoutEnlargement: true })
.jpeg({ quality: 80 })
.toFile(outputFilePath);
// Verify file was saved and get stats
await new Promise((resolve, reject) => {
fs.stat(outputFilePath, (err, stats) => {
if (err) {
console.error('Error verifying file:', err);
reject(err);
} else {
console.log('File processed and verified:', {
path: outputFilePath,
size: stats.size,
exists: fs.existsSync(outputFilePath),
stats: stats
});
resolve(stats);
}
});
});
// Additional verification
if (!fs.existsSync(outputFilePath)) {
throw new Error('File was not saved successfully');
}
const stats = fs.statSync(outputFilePath);
if (stats.size > 10 * 1024 * 1024) {
fs.unlinkSync(outputFilePath);
return res.status(400).json({ error: "File size too large after processing." });
}
// Set the photo path for sending
photoPath = outputFilePath;
} catch (err) {
console.error("Error processing image:", {
error: err,
outputFilePath,
exists: fs.existsSync(outputFilePath)
});
// Cleanup if file exists
if (outputFilePath && fs.existsSync(outputFilePath)) {
fs.unlinkSync(outputFilePath);
}
return res.status(500).json({ error: "Failed to process image file." });
}
} else if (!message || !message.trim()) {
return res.status(400).json({ error: "Either message or image must be provided" });
}
const users = await TelegramUser.find({ "stores.store": storeId });
if (!users.length) {
return res.status(404).json({ error: "No users found to broadcast to." });
}
const store = await Store.findById(storeId);
if (!store || !store.telegramToken) {
return res.status(400).json({ error: "Store has no Telegram bot token configured." });
}
// Flatten out each store entry to gather all chat IDs belonging to this store
const chatIds = users.flatMap((user) =>
user.stores
.filter((s) => s.store.toString() === storeId.toString())
.map((s) => s.chatId)
);
console.log("Broadcasting to chat IDs:", chatIds);
try {
if (photoPath) {
console.log('Sending photo broadcast:', {
photoPath,
exists: fs.existsSync(photoPath),
stats: fs.existsSync(photoPath) ? fs.statSync(photoPath) : null
});
await sendBulkTelegramMessages(store.telegramToken, chatIds, message, photoPath);
} else {
await sendBulkTelegramMessages(store.telegramToken, chatIds, message);
}
return res.json({
message: "Broadcast sent successfully",
totalUsers: chatIds.length,
});
} catch (error) {
console.error("Error sending broadcast:", {
error: error.message,
photoPath,
messageLength: message?.length,
fileExists: photoPath ? fs.existsSync(photoPath) : null
});
throw error;
}
} catch (error) {
console.error("Error broadcasting message:", error);
return res.status(500).json({
error: "Failed to broadcast message",
details: error.message,
});
} finally {
// Clean up the file after everything is done
if (photoPath && fs.existsSync(photoPath)) {
try {
fs.unlinkSync(photoPath);
} catch (err) {
console.error("Error cleaning up file:", err);
}
}
}
});
export default router;