DRRR
4
This is the script I created and now try to refine:
function(properties, context) {
const JSZip = require("jszip");
const axios = require("axios");
const FormData = require("form-data");
// 1) Create a new zip instance.
const zip = new JSZip();
// 2) Read and validate input properties.
const folderStructure = properties.folder_structure || "{}";
const outputFileName = properties.output_file_name || "output.zip";
let fileUploadBaseURL = properties.bubble_website_url ? String(properties.bubble_website_url) : "";
// Validate that the upload base URL starts with "https://"
if (!fileUploadBaseURL.startsWith("https://")) {
throw new Error(
"Invalid file_upload_url (must start with 'https://'). Provided: " +
fileUploadBaseURL
);
}
// Remove any trailing slash.
fileUploadBaseURL = fileUploadBaseURL.replace(/\/$/, "");
// 3) Parse the folder structure JSON.
let folderData;
try {
folderData = JSON.parse(folderStructure);
} catch (err) {
throw new Error("Invalid JSON for 'folder_structure': " + err.message);
}
// 4) Recursive function to add files (or folders) to the zip.
function addToZip(currentFolder, data) {
let promises = [];
for (let key in data) {
const value = data[key];
// If the value is a string…
if (typeof value === "string") {
// …and if it starts with "https://", download it.
if (value.startsWith("https://")) {
const p = axios
.get(value, { responseType: "arraybuffer" })
.then(resp => {
// Log download info.
console.log(
Downloaded "${key}" from ${value} with ${resp.data.byteLength} bytes
);
// Add the downloaded file to the current folder in the zip.
currentFolder.file(key, resp.data, { binary: true });
})
.catch(err => {
console.warn(⚠️ Failed to download "${value}": ${err.message});
});
promises.push(p);
} else {
// Otherwise, treat the string as plain text content.
currentFolder.file(key, value);
}
} else if (value && typeof value === "object") {
// If the value is an object, treat it as a nested folder.
const subFolder = currentFolder.folder(key);
// Recursively add the nested folder's contents.
promises = promises.concat(addToZip(subFolder, value));
}
}
return promises;
}
// 5) Determine the attachment parameters.
let thingToConnect = properties.thing_to_connect;
let attachType = null;
let attachID = null;
if (thingToConnect && typeof thingToConnect === "object") {
if (
thingToConnect._pointer &&
thingToConnect._pointer._id &&
thingToConnect._pointer._type
) {
attachType = thingToConnect._pointer._type;
attachID = thingToConnect._pointer._id;
} else if (thingToConnect.datatype && thingToConnect.unique_id) {
attachType = thingToConnect.datatype;
attachID = thingToConnect.unique_id;
}
} else if (typeof thingToConnect === "string" && thingToConnect.length > 5) {
console.warn(
"⚠️ Received a plain string for thing_to_connect, but no type info. Not attaching."
);
}
// 6) Build the file upload URL.
let fileUploadURL = ${fileUploadBaseURL}/fileupload;
// 7) Download all files, add them to the zip, and then generate the ZIP buffer.
const promises = addToZip(zip, folderData);
return Promise.all(promises)
.then(() => zip.generateAsync({ type: "nodebuffer" }))
.then(buffer => {
// Create a FormData instance and append the zip file.
const form = new FormData();
form.append("file", buffer, {
filename: outputFileName,
contentType: "application/zip"
});
// Attach the file to a Thing if a valid attachment was provided.
if (attachID) {
form.append("attach_to", attachID);
form.append("private", "true");
console.log(🔗 Attaching to: ${attachType} (ID: ${attachID}));
} else {
console.warn("⚠️ No valid Thing detected. File will not be attached.");
}
// Upload the ZIP file.
return axios.post(fileUploadURL, form, {
headers: form.getHeaders()
});
})
.then(resp => {
// 8) Process Bubble's response.
const data = resp.data;
let fileUrl;
if (typeof data === "string") {
fileUrl = data.trim();
} else if (data && data.url) {
fileUrl = data.url;
} else {
throw new Error("File upload failed. Unexpected response: " + JSON.stringify(data));
}
// Fix protocol-relative URLs.
if (fileUrl.startsWith("//")) {
fileUrl = "https:" + fileUrl;
}
// Return the file URL in a way that Bubble expects.
return {
zip_file_url: fileUrl,
zip_file: fileUrl
};
})
.catch(err => {
throw new Error("Error generating ZIP file: " + err.message);
});
}