initial commit
This commit is contained in:
113
firebase_functions/generate_zip/functions/index.js
Normal file
113
firebase_functions/generate_zip/functions/index.js
Normal file
@@ -0,0 +1,113 @@
|
||||
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
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user