Skip to content

Instantly share code, notes, and snippets.

@lansolo99
Last active July 28, 2022 19:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lansolo99/fb2983c7f17aac23f5261b3fed0da3b6 to your computer and use it in GitHub Desktop.
Save lansolo99/fb2983c7f17aac23f5261b3fed0da3b6 to your computer and use it in GitHub Desktop.
Backup and export MongoDB on Heroku and push it to S3 bucket
const cron = require('node-cron');
const fs = require('fs');
const aws = require('aws-sdk');
const path = require('path');
const shell = require('./shellHelper');
const mongodumbBackup = async () => {
// Backup everyday @1AM
cron.schedule('0 1 * * *', () => {
// Backup mongo dump + mongo export to a Heroku static folder
shell.series([
'mongodump --uri <COMPLETE URI MONGODB STRING> --collection <COLLECTIONNAME>',
'mongoexport --uri <COMPLETE URI MONGODB STRING> --collection <COLLECTIONNAME> --type cs --out <PATH/TO/CSV>.csv',
], (err) => {
console.log('Mongo Backup + Exports created');
// S3 upload
// Config -> we need to define the region so that it's the same as heroku app, to low latency
aws.config.update({ region: 'us-east-1' });
// Secrets -> store this in your .env file
const s3 = new aws.S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});
const uploadFile = async (filePath, fileName) => {
console.log('uploading to s3...');
// File
const fileStream = fs.createReadStream(`${filePath}${fileName}`);
fileStream.on('error', (fsErr) => {
console.log('File Error', fsErr);
});
// Payload
const uploadParams = {
Bucket: process.env.S3_BUCKET_NAME,
Key: fileName,
Body: fileStream,
};
// Upload
await s3.upload(uploadParams, (s3err, data) => {
if (s3err) {
console.log('Error', s3err);
} if (data) {
console.log(`Uploaded ${fileName} with success`, data.Location);
}
});
};
// Trigger uploads to S3 consecutively
uploadFile('./dump/<databaseName>/', '<collectionName>.bson');
uploadFile('./exports/csv/', 'export.csv');
});
});
};
module.exports = mongodumbBackup;
const express = require('express');
const path = require('path');
const dotenv = require('dotenv');
const mongodumbBackup = require('./utils/mongodumbBackup');
// Env
dotenv.config({ path: './config/config.env' });
// Express
const app = express();
// Body parser
app.use(express.json());
// Cors
app.use(cors());
// Backups
app.use(express.static(path.join(__dirname, './dump/<databasename>')));
// Start server
const port = process.env.PORT || 5000;
const mode = process.env.NODE_ENV;
const server = app.listen(port, () => {
console.log(`Server Started in ${mode} mode on port ${port}`.green);
if (process.env.NODE_ENV === 'production') {
// Startup backup + upload tasks
mongodumbBackup();
}
});
const childProcess = require('child_process');
// Execute a single shell command where "cmd" is a string
exports.exec = function (cmd, cb) {
const parts = cmd.split(/\s+/g);
const p = childProcess.spawn(parts[0], parts.slice(1), { stdio: 'inherit' });
p.on('exit', (code) => {
let err = null;
if (code) {
err = new Error(`command "${cmd}" exited with wrong status code "${code}"`);
err.code = code;
err.cmd = cmd;
}
if (cb) cb(err);
});
};
// Execute multiple commands in series
exports.series = function (cmds, cb) {
const execNext = function () {
exports.exec(cmds.shift(), (err) => {
if (err) {
cb(err);
} else if (cmds.length) execNext();
else cb(null);
});
};
execNext();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment