113 lines
3.2 KiB
JavaScript
113 lines
3.2 KiB
JavaScript
const functions = require("firebase-functions");
|
|
const admin = require("firebase-admin");
|
|
const archiver = require("archiver");
|
|
const fs = require("fs");
|
|
const os = require("os");
|
|
const path = require("path");
|
|
|
|
admin.initializeApp();
|
|
|
|
const bucket = admin.storage().bucket();
|
|
|
|
exports.generateGroupZip = functions.https.onRequest(async (req, res) => {
|
|
try {
|
|
|
|
const groupId = req.body.unique_url; // required
|
|
const folder_prefix = req.body.prefix;// required
|
|
const zip_prefix = req.body.zip_file_prefix;// required
|
|
|
|
if (!groupId) {
|
|
return res.status(400).json({ error: "unique_url required" });
|
|
}
|
|
if (!folder_prefix) {
|
|
return res.status(400).json({ error: "prefix required" });
|
|
}
|
|
if (!zip_prefix) {
|
|
return res.status(400).json({ error: "zip_file_prefix required" });
|
|
}
|
|
|
|
const uniqueId = Date.now() + "_" + Math.floor(Math.random() * 10000);
|
|
|
|
// ✅ SAFE: prefix controlled only by server
|
|
const prefix = `${folder_prefix}${groupId}/`;
|
|
|
|
// STEP 1: LIST ALL FILES IN FOLDER
|
|
const [files] = await bucket.getFiles({
|
|
prefix: prefix
|
|
});
|
|
|
|
if (!files.length) {
|
|
return res.status(404).json({ error: "No files found" });
|
|
}
|
|
|
|
// STEP 2: CREATE ZIP
|
|
const zipFileName = `${zip_prefix}${groupId}_${uniqueId}.zip`;
|
|
const tempZipPath = path.join(os.tmpdir(), `${uniqueId}.zip`);
|
|
|
|
const output = fs.createWriteStream(tempZipPath);
|
|
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
|
|
archive.pipe(output);
|
|
|
|
// collect temp files for cleanup
|
|
const tempFiles = [];
|
|
|
|
for (const file of files) {
|
|
|
|
if (!file.name || file.name.endsWith("/")) continue;
|
|
|
|
const tempFilePath = path.join(
|
|
os.tmpdir(),
|
|
`${Date.now()}_${path.basename(file.name)}`
|
|
);
|
|
|
|
await file.download({
|
|
destination: tempFilePath
|
|
});
|
|
|
|
tempFiles.push(tempFilePath);
|
|
|
|
archive.file(tempFilePath, {
|
|
name: file.name.replace(`${folder_prefix}${groupId}/`, "")
|
|
});
|
|
}
|
|
|
|
await archive.finalize();
|
|
|
|
await new Promise(resolve => output.on("close", resolve));
|
|
|
|
// STEP 3: UPLOAD ZIP
|
|
await bucket.upload(tempZipPath, {
|
|
destination: zipFileName,
|
|
metadata: {
|
|
contentType: "application/zip"
|
|
}
|
|
});
|
|
|
|
// STEP 4: GENERATE SIGNED URL
|
|
const file = bucket.file(zipFileName);
|
|
|
|
// STEP 5: CLEAN TEMP FILES (IMPORTANT)
|
|
try {
|
|
fs.unlinkSync(tempZipPath);
|
|
tempFiles.forEach(f => {
|
|
if (fs.existsSync(f)) fs.unlinkSync(f);
|
|
});
|
|
} catch (e) {
|
|
console.log("Cleanup error:", e.message);
|
|
}
|
|
|
|
return res.json({
|
|
success: true,
|
|
unique_id: uniqueId,
|
|
zip_path: zipFileName
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error(err);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: err.message
|
|
});
|
|
}
|
|
}); |