Files
ExpenseCount/firebase_functions/generate_zip/functions/index.js
2026-06-25 13:03:45 +06:00

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
});
}
});