diff --git a/ReadMe.md b/ReadMe.md
index 23c1afd..475797e 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -214,7 +214,7 @@ To start up:
b. `git pull`
c. `npm run package`
4. `cd your-dev-folder/nc-mutiplex`
-5. `/.start-nc-multiplex.sh`
+5. `./start-nc-multiplex.sh`
---
diff --git a/nc-multiplex.js b/nc-multiplex.js
index bfedf5d..85e466b 100644
--- a/nc-multiplex.js
+++ b/nc-multiplex.js
@@ -460,13 +460,12 @@ function RenderDownloadLogs() {
return `
Download Data
-
Download data for all graphs for ${SERVER_IP} as a zip file.
+
Download data for all graphs for ${SERVER_IP} as a zip file.
+ This includes all data you need to completely restore a droplet
+ (active graphs, loki, templates, logs)
+
`;
}
@@ -1140,71 +1139,164 @@ function m_Archive(folderPath, fileExtensions, zipFilename, req, res) {
archive.finalize(); // Finish zipping
});
}
+
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-/// HANDLE "/download_all_networks" -- DOWNLOAD LOKI and TEMPLATES
-app.get('/download_all_networks', (req, res) => {
- console.log(PRE, $T(), `GET /download_all_networks (client ${req})`);
- const folderPath = path.join(NC_RUNTIME_PATH);
- const zipFilename = `netcreate_networks_${SERVER_IP}_${$T()}.zip`;
- return m_Archive(
- folderPath,
- ['.loki', '.template.toml'],
- zipFilename,
- req,
- res
- );
-});
-/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-/// HANDLE "/download_logs" -- DOWNLOAD RESEARCH LOGS
-app.get('/download_logs', (req, res) => {
- console.log(PRE, $T(), `GET /download_logs (client ${req})`);
- const folderPath = path.join(NC_LOGS_PATH);
- const zipFilename = `netcreate_logs_${SERVER_IP}_${$T()}.zip`;
- return m_Archive(
- folderPath,
- '.txt',
- zipFilename,
- req,
- res
- );
-});
-/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-/// HANDLE "/download_lokis" -- DOWNLOAD RESEARCH LOGS
-app.get('/download_lokis', (req, res) => {
- console.log(PRE, $T(), `GET /download_lokis (client ${req.ip})`);
- const folderPath = path.join(NC_RUNTIME_PATH);
- const zipFilename = `netcreate_lokis_${SERVER_IP}_${$T()}.zip`;
- return m_Archive(
- folderPath,
- '.loki',
- zipFilename,
- req,
- res
- );
-});
-/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-/// HANDLE "/download_backups" -- DOWNLOAD RESEARCH LOGS
-app.get('/download_backups', (req, res) => {
- console.log(PRE, $T(), `GET /download_backups (client ${req.ip})`);
- const folderPath = path.join(NC_BACKUPS_PATH);
- const zipFilename = `netcreate_backups_${SERVER_IP}_${$T()}.zip`;
- return m_Archive(
- folderPath,
- '.loki',
- zipFilename,
- req,
- res
- );
-});
+/// ARCHIVE MULTIPLE DISCRETE FILES AND FOLDERS
+function m_ArchiveMultiple(items, zipFilename, req, res) {
+ if (!CookieIsValid(req)) {
+ console.log(PRE, 'DEBUG: Cookie validation failed');
+ res.redirect(`/error_not_authorized`);
+ return;
+ }
+
+ // Create a zip archive
+ const archive = archiver('zip', {
+ zlib: { level: 9 } // Compression level
+ });
+
+ archive.on('error', err => {
+ const errmsg = `Error creating zip: ${err.message}`;
+ console.log(PRE, errmsg);
+ m_SendErrorResponse(res, errmsg);
+ return;
+ });
+
+ archive.pipe(res);
+
+ // Set response headers
+ res.setHeader('Content-Disposition', `attachment; filename=${zipFilename}`);
+ res.setHeader('Content-Type', 'application/zip');
+
+ // Process each item and add to archive
+ const validItems = items.filter(item => {
+ if (!fs.existsSync(item.path)) {
+ console.log(PRE, `Warning: ${item.path} does not exist, skipping`);
+ return false;
+ }
+ return true;
+ });
+
+ if (validItems.length === 0) {
+ console.log(PRE, 'No valid items to archive');
+ archive.finalize();
+ return;
+ }
+
+ // Add all valid items to the archive
+ validItems.forEach((item) => {
+ const itemPath = item.path;
+ const archiveName = item.name || path.basename(itemPath);
+ const stats = fs.statSync(itemPath);
+
+ if (stats.isFile()) {
+ archive.file(itemPath, { name: archiveName });
+ } else if (stats.isDirectory()) {
+ archive.directory(itemPath, archiveName);
+ } else {
+ console.log(PRE, `Warning: ${itemPath} is neither file nor directory, skipping`);
+ }
+ });
+
+ // Use setImmediate to ensure all archive operations are queued before finalizing
+ setImmediate(() => {
+ console.log(PRE, `Downloading archived data..."${zipFilename}`);
+ archive.finalize();
+ });
+}
+
+// DEPRECATED: Use single file archive instead
+//
+// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// /// HANDLE "/download_all_networks" -- DOWNLOAD LOKI and TEMPLATES
+// app.get('/download_all_networks', (req, res) => {
+// console.log(PRE, $T(), `GET /download_all_networks (client ${req})`);
+// const folderPath = path.join(NC_RUNTIME_PATH);
+// const zipFilename = `netcreate_networks_${SERVER_IP}_${$T()}.zip`;
+// return m_Archive(
+// folderPath,
+// ['.loki', '.template.toml'],
+// zipFilename,
+// req,
+// res
+// );
+// });
+// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// /// HANDLE "/download_logs" -- DOWNLOAD RESEARCH LOGS
+// app.get('/download_logs', (req, res) => {
+// console.log(PRE, $T(), `GET /download_logs (client ${req})`);
+// const folderPath = path.join(NC_LOGS_PATH);
+// const zipFilename = `netcreate_logs_${SERVER_IP}_${$T()}.zip`;
+// return m_Archive(
+// folderPath,
+// '.txt',
+// zipFilename,
+// req,
+// res
+// );
+// });
+// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// /// HANDLE "/download_lokis" -- DOWNLOAD RESEARCH LOGS
+// app.get('/download_lokis', (req, res) => {
+// console.log(PRE, $T(), `GET /download_lokis (client ${req.ip})`);
+// const folderPath = path.join(NC_RUNTIME_PATH);
+// const zipFilename = `netcreate_lokis_${SERVER_IP}_${$T()}.zip`;
+// return m_Archive(
+// folderPath,
+// '.loki',
+// zipFilename,
+// req,
+// res
+// );
+// });
+// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// /// HANDLE "/download_backups" -- DOWNLOAD RESEARCH LOGS
+// app.get('/download_backups', (req, res) => {
+// console.log(PRE, $T(), `GET /download_backups (client ${req.ip})`);
+// const folderPath = path.join(NC_BACKUPS_PATH);
+// const zipFilename = `netcreate_backups_${SERVER_IP}_${$T()}.zip`;
+// return m_Archive(
+// folderPath,
+// '.loki',
+// zipFilename,
+// req,
+// res
+// );
+// });
+// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// /// HANDLE "/download_templates" -- DOWNLOAD RESEARCH LOGS
+// app.get('/download_templates', (req, res) => {
+// console.log(PRE, $T(), `GET /download_templates (client ${req.ip})`);
+// const folderPath = path.join(NC_RUNTIME_PATH);
+// const zipFilename = `netcreate_templates_${SERVER_IP}_${$T()}.zip`;
+// return m_Archive(
+// folderPath,
+// '.template.toml',
+// zipFilename,
+// req,
+// res
+// );
+// });
+
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-/// HANDLE "/download_templates" -- DOWNLOAD RESEARCH LOGS
-app.get('/download_templates', (req, res) => {
- console.log(PRE, $T(), `GET /download_templates (client ${req.ip})`);
- const folderPath = path.join(NC_RUNTIME_PATH);
- const zipFilename = `netcreate_templates_${SERVER_IP}_${$T()}.zip`;
- return m_Archive(
- folderPath,
- '.template.toml',
+/// HANDLE "/download_data" -- DOWNLOAD CUSTOM DATA ARCHIVE
+app.get('/download_data', (req, res) => {
+ console.log(PRE, $T(), `GET /download_data (client ${req.ip})`);
+
+ // Define the items to archive - customize this array as needed
+ const itemsToArchive = [
+ // nc-process-state.json -- active graphs
+ { path: '.nc-process-state.json', name: '.nc-process-state.json' },
+ // nc-server-start.txt -- log start time
+ { path: '.nc-server-start.txt', name: '.nc-server-start.txt' },
+ // nc-multiplex log file
+ { path: 'log.txt', name: 'log.txt' },
+ // netcreate-itest/runtime folder - will archive entire folder
+ // including loki, templates, template backups, loki, backup lokis, logs, etc.
+ { path: NC_RUNTIME_PATH, name: 'runtime' }
+ ];
+ const zipFilename = `netcreate_data_${SERVER_IP}_${$T()}.zip`;
+ return m_ArchiveMultiple(
+ itemsToArchive,
zipFilename,
req,
res