├── .gitignore ├── README.md ├── index.js ├── npm-debug.log └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nodebb-backup 2 | A simple backup script based on the steps described in https://docs.nodebb.org/vi/latest/upgrading/ 3 | 4 | # Install 5 | 6 | npm install -g nodebb-backup 7 | 8 | # Run (inside your nodebb folder) 9 | 10 | nodebb-backup 11 | 12 | # Features 13 | 14 | - Backs up the mongo database specified in NodeBB's config.json file 15 | - Backs up the nodebb/uploads directory which contains uploaded images 16 | - Compresses dumped db and files from /uploads into a tar with a format like this: nodebb-backup-2015-09-01_1509-v0.7.3.tar 17 | - Resulting tar file is saved to the directory above nodebb 18 | 19 | # Limitations 20 | 21 | - Many, it's pretty raw! 22 | - Mongo only 23 | - No command line options yet 24 | 25 | # Needed Features (please send me feedback) 26 | 27 | - Be able to provide a path to place the resulting back up file 28 | - Redis support 29 | - Maybe be able to restore from a provided tar file? 30 | 31 | # Unpacking tar to new directory, 'unpackeddir' 32 | 33 | mkdir -p unpackeddir; tar -xvf nodebb-backup-2015-11-10_1532-v0.8.2.tar -C $_ 34 | 35 | # Restoring db (from inside unpacked directory created above) 36 | 37 | mongorestore -d nodebb -u username -p password --dir=unpackeddir/nodebb --host=127.0.0.1:27017 -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | var nodeCLI = require('shelljs-nodecli'); 3 | var fs = require('fs-extra'); 4 | var tar = require('tar-fs'); 5 | var path = require('path'); 6 | var moment = require('moment'); 7 | 8 | console.log('Preparing to backup NodeBB in this directory'); 9 | var pwd = nodeCLI.exec('pwd', '', {silent:true}).output.trim(); 10 | console.log("Present Working Directory: " + pwd); 11 | 12 | 13 | //--------------------------------------------- 14 | //Find and load config.json settings for NodeBB, give warning and exit if not Mongo 15 | //--------------------------------------------- 16 | 17 | 18 | var configPath = path.join(pwd, "/config.json"); 19 | console.log("Loading: " + configPath); 20 | 21 | var configContents; 22 | try { 23 | configContents = fs.readFileSync(configPath, 'utf8'); 24 | } catch (err) { 25 | console.log("Unable to load necessary nodebb config.json file: " + configPath); 26 | throw err; 27 | } 28 | 29 | var config = JSON.parse(configContents); 30 | if (config.database !== "mongo") { 31 | throw new Error("Currently this script only works with backing up mongo. Want to improve it? https://github.com/jongarrison/nodebb-backup") 32 | } 33 | 34 | console.log("Found db config info for: " + config.mongo.database); 35 | 36 | 37 | //--------------------------------------------- 38 | //Find and load current NodeBB version name from package.json (like: 0.7.3) 39 | //--------------------------------------------- 40 | 41 | 42 | var packagePath = path.join(pwd, "/package.json"); 43 | console.log("Loading: " + packagePath); 44 | 45 | var packageContents; 46 | try { 47 | packageContents = fs.readFileSync(packagePath, 'utf8'); 48 | } catch (err) { 49 | console.log("Unable to load necessary nodebb package.json file: " + packagePath); 50 | throw err; 51 | } 52 | 53 | var package = JSON.parse(packageContents); 54 | 55 | console.log("Found NodeBB version: " + package.version); 56 | 57 | 58 | //--------------------------------------------- 59 | //Create temp-backup directory to load back up files into 60 | //--------------------------------------------- 61 | 62 | 63 | var tempBackupDir = path.join(pwd, "/temp-backup"); 64 | console.log("creating temp backup directory at: " + tempBackupDir); 65 | try { 66 | if (fs.existsSync(tempBackupDir)) { 67 | //try to clean it up 68 | fs.removeSync(tempBackupDir); 69 | if (fs.existsSync(tempBackupDir)) { throw new Error("Unable to remove dir"); } 70 | } 71 | } catch (err) { 72 | console.log("Unable to continue. Dirty backup dir already exists at: " + tempBackupDir + "\n" + err); 73 | process.exit(1); 74 | } 75 | 76 | fs.mkdirSync(tempBackupDir); 77 | 78 | 79 | //--------------------------------------------- 80 | //MongoDump into temp-backup directory 81 | //--------------------------------------------- 82 | 83 | 84 | console.log("About to backup db: " + config.mongo.database); 85 | 86 | //mongodump -d 0 -h 127.0.0.1:27017 87 | 88 | if (config.mongo.username && config.mongo.password) { 89 | 90 | console.log("Running back up using mongo db username and password"); 91 | nodeCLI.exec( 92 | 'mongodump', 93 | '-v', 94 | '-d', config.mongo.database, 95 | '-u', config.mongo.username, 96 | '-p', config.mongo.password, 97 | '-o', tempBackupDir, 98 | '-h', config.mongo.host + ":" + config.mongo.port 99 | ); 100 | 101 | } else { 102 | 103 | console.log("Running back up WITHOUT using mongo db username and password"); 104 | nodeCLI.exec( 105 | 'mongodump', 106 | '-v', 107 | '-d', config.mongo.database, 108 | '-o', tempBackupDir, 109 | '-h', config.mongo.host + ":" + config.mongo.port 110 | ); 111 | 112 | } 113 | 114 | //--------------------------------------------- 115 | //Get the uploaded files, including avatars 116 | //--------------------------------------------- 117 | 118 | 119 | var uploadPath = path.join(pwd, "/public/uploads"); 120 | var backUploadPath = path.join(tempBackupDir, "/uploads"); 121 | 122 | console.log("Copying uploaded images in: " + uploadPath); 123 | fs.copySync(uploadPath, backUploadPath); 124 | 125 | 126 | //--------------------------------------------- 127 | //Now tar them up, give them a reasonable name and move the whole thing up one directory to get out of this git repo 128 | //--------------------------------------------- 129 | 130 | 131 | var timeString = moment().format("YYYY-MM-DD_HHmm"); 132 | 133 | var backupFileName = "nodebb-backup-" + timeString + "-v" + package.version + ".tar"; 134 | var outputFilePath = path.join(pwd, "..", backupFileName); 135 | 136 | console.log("Creating compressed backup file: " + outputFilePath); 137 | 138 | tar.pack(tempBackupDir) 139 | .on('error', 140 | function(err) { 141 | console.log("Error creating compressed backup file: " + err); 142 | } 143 | ).pipe(fs.createWriteStream(outputFilePath)) 144 | .on('finish', 145 | function(data) { 146 | //cleaning up 147 | fs.remove(tempBackupDir); 148 | console.log("Done and cleaned up"); 149 | } 150 | ); 151 | 152 | -------------------------------------------------------------------------------- /npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/Users/jon/.nvm/versions/node/v6.9.1/bin/node', 3 | 1 verbose cli '/Users/jon/.nvm/versions/node/v6.9.1/bin/npm', 4 | 1 verbose cli 'publish' ] 5 | 2 info using npm@3.10.8 6 | 3 info using node@v6.9.1 7 | 4 verbose publish [ '.' ] 8 | 5 silly cache add args [ '.', null ] 9 | 6 verbose cache add spec . 10 | 7 silly cache add parsed spec Result { 11 | 7 silly cache add raw: '.', 12 | 7 silly cache add scope: null, 13 | 7 silly cache add escapedName: null, 14 | 7 silly cache add name: null, 15 | 7 silly cache add rawSpec: '.', 16 | 7 silly cache add spec: '/Users/jon/workspace_js/nodebb-backup', 17 | 7 silly cache add type: 'directory' } 18 | 8 verbose addLocalDirectory /Users/jon/.npm/nodebb-backup/0.1.1/package.tgz not in flight; packing 19 | 9 verbose correctMkdir /Users/jon/.npm correctMkdir not in flight; initializing 20 | 10 info lifecycle nodebb-backup@0.1.1~prepublish: nodebb-backup@0.1.1 21 | 11 silly lifecycle nodebb-backup@0.1.1~prepublish: no script for prepublish, continuing 22 | 12 verbose tar pack [ '/Users/jon/.npm/nodebb-backup/0.1.1/package.tgz', 23 | 12 verbose tar pack '/Users/jon/workspace_js/nodebb-backup' ] 24 | 13 verbose tarball /Users/jon/.npm/nodebb-backup/0.1.1/package.tgz 25 | 14 verbose folder /Users/jon/workspace_js/nodebb-backup 26 | 15 verbose addLocalTarball adding from inside cache /Users/jon/.npm/nodebb-backup/0.1.1/package.tgz 27 | 16 verbose correctMkdir /Users/jon/.npm correctMkdir not in flight; initializing 28 | 17 silly cache afterAdd nodebb-backup@0.1.1 29 | 18 verbose afterAdd /Users/jon/.npm/nodebb-backup/0.1.1/package/package.json not in flight; writing 30 | 19 verbose correctMkdir /Users/jon/.npm correctMkdir not in flight; initializing 31 | 20 verbose afterAdd /Users/jon/.npm/nodebb-backup/0.1.1/package/package.json written 32 | 21 silly publish { name: 'nodebb-backup', 33 | 21 silly publish version: '0.1.1', 34 | 21 silly publish description: 'A simple backup script based on https://docs.nodebb.org/vi/latest/upgrading/', 35 | 21 silly publish main: 'index.js', 36 | 21 silly publish scripts: { test: 'echo "Error: no test specified" && exit 1' }, 37 | 21 silly publish repository: 38 | 21 silly publish { type: 'git', 39 | 21 silly publish url: 'git+https://github.com/jongarrison/nodebb-backup.git' }, 40 | 21 silly publish keywords: [ 'nodebb', 'backup', 'mongo' ], 41 | 21 silly publish preferGlobal: true, 42 | 21 silly publish author: { name: 'Jon Garrison' }, 43 | 21 silly publish license: 'ISC', 44 | 21 silly publish bugs: { url: 'https://github.com/jongarrison/nodebb-backup/issues' }, 45 | 21 silly publish homepage: 'https://github.com/jongarrison/nodebb-backup#readme', 46 | 21 silly publish dependencies: 47 | 21 silly publish { async: '*', 48 | 21 silly publish 'shelljs-nodecli': '*', 49 | 21 silly publish 'tar-fs': '*', 50 | 21 silly publish 'fs-extra': '*', 51 | 21 silly publish moment: '*' }, 52 | 21 silly publish bin: { 'nodebb-backup': 'index.js' }, 53 | 21 silly publish readme: '# nodebb-backup\nA simple backup script based on the steps described in https://docs.nodebb.org/vi/latest/upgrading/\n\n# Install\n\nnpm install -g nodebb-backup\n\n# Run (inside your nodebb folder)\n\nnodebb-backup\n\n# Features\n\n- Backs up the mongo database specified in NodeBB\'s config.json file\n- Backs up the nodebb/uploads directory which contains uploaded images\n- Compresses dumped db and files from /uploads into a tar with a format like this: nodebb-backup-2015-09-01_1509-v0.7.3.tar\n- Resulting tar file is saved to the directory above nodebb \n\n# Limitations\n\n- Many, it\'s pretty raw!\n- Mongo only\n- No command line options yet\n\n# Needed Features (please send me feedback)\n\n- Be able to provide a path to place the resulting back up file\n- Redis support\n- Maybe be able to restore from a provided tar file?\n\n# Unpacking tar to new directory, \'unpackeddir\'\n\nmkdir -p unpackeddir; tar -xvf nodebb-backup-2015-11-10_1532-v0.8.2.tar -C $_\n\n# Restoring db (from inside unpacked directory created above)\n\nmongorestore -d nodebb -u username -p password --dir=unpackeddir/nodebb --host=127.0.0.1:27017', 54 | 21 silly publish readmeFilename: 'README.md', 55 | 21 silly publish gitHead: 'b51220128b94578e83cbdf52edfef38498c660f9', 56 | 21 silly publish _id: 'nodebb-backup@0.1.1', 57 | 21 silly publish _shasum: 'e91cd0d2a41d4e78a2680165b422a0190f398425', 58 | 21 silly publish _from: '.' } 59 | 22 verbose getPublishConfig undefined 60 | 23 silly mapToRegistry name nodebb-backup 61 | 24 silly mapToRegistry using default registry 62 | 25 silly mapToRegistry registry https://registry.npmjs.org/ 63 | 26 silly mapToRegistry data Result { 64 | 26 silly mapToRegistry raw: 'nodebb-backup', 65 | 26 silly mapToRegistry scope: null, 66 | 26 silly mapToRegistry escapedName: 'nodebb-backup', 67 | 26 silly mapToRegistry name: 'nodebb-backup', 68 | 26 silly mapToRegistry rawSpec: '', 69 | 26 silly mapToRegistry spec: 'latest', 70 | 26 silly mapToRegistry type: 'tag' } 71 | 27 silly mapToRegistry uri https://registry.npmjs.org/nodebb-backup 72 | 28 verbose publish registryBase https://registry.npmjs.org/ 73 | 29 silly publish uploading /Users/jon/.npm/nodebb-backup/0.1.1/package.tgz 74 | 30 verbose request uri https://registry.npmjs.org/nodebb-backup 75 | 31 verbose request sending authorization for write operation 76 | 32 info attempt registry request try #1 at 6:47:24 PM 77 | 33 verbose request using bearer token for auth 78 | 34 verbose request id 6404740f756ea0b3 79 | 35 http request PUT https://registry.npmjs.org/nodebb-backup 80 | 36 http 403 https://registry.npmjs.org/nodebb-backup 81 | 37 verbose headers { 'content-type': 'application/json', 82 | 37 verbose headers 'cache-control': 'max-age=300', 83 | 37 verbose headers 'content-length': '95', 84 | 37 verbose headers 'accept-ranges': 'bytes', 85 | 37 verbose headers date: 'Thu, 13 Apr 2017 01:47:25 GMT', 86 | 37 verbose headers via: '1.1 varnish', 87 | 37 verbose headers connection: 'keep-alive', 88 | 37 verbose headers 'x-served-by': 'cache-sea1040-SEA', 89 | 37 verbose headers 'x-cache': 'MISS', 90 | 37 verbose headers 'x-cache-hits': '0', 91 | 37 verbose headers 'x-timer': 'S1492048045.108171,VS0,VE760', 92 | 37 verbose headers vary: 'Accept-Encoding' } 93 | 38 verbose request invalidating /Users/jon/.npm/registry.npmjs.org/nodebb-backup on PUT 94 | 39 error publish Failed PUT 403 95 | 40 verbose stack Error: "You cannot publish over the previously published version 0.1.1." : nodebb-backup 96 | 40 verbose stack at makeError (/Users/jon/.nvm/versions/node/v6.9.1/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:302:12) 97 | 40 verbose stack at CachingRegistryClient. (/Users/jon/.nvm/versions/node/v6.9.1/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:290:14) 98 | 40 verbose stack at Request._callback (/Users/jon/.nvm/versions/node/v6.9.1/lib/node_modules/npm/node_modules/npm-registry-client/lib/request.js:210:14) 99 | 40 verbose stack at Request.self.callback (/Users/jon/.nvm/versions/node/v6.9.1/lib/node_modules/npm/node_modules/request/request.js:187:22) 100 | 40 verbose stack at emitTwo (events.js:106:13) 101 | 40 verbose stack at Request.emit (events.js:191:7) 102 | 40 verbose stack at Request. (/Users/jon/.nvm/versions/node/v6.9.1/lib/node_modules/npm/node_modules/request/request.js:1044:10) 103 | 40 verbose stack at emitOne (events.js:96:13) 104 | 40 verbose stack at Request.emit (events.js:188:7) 105 | 40 verbose stack at IncomingMessage. (/Users/jon/.nvm/versions/node/v6.9.1/lib/node_modules/npm/node_modules/request/request.js:965:12) 106 | 41 verbose statusCode 403 107 | 42 verbose pkgid nodebb-backup 108 | 43 verbose cwd /Users/jon/workspace_js/nodebb-backup 109 | 44 error Darwin 15.6.0 110 | 45 error argv "/Users/jon/.nvm/versions/node/v6.9.1/bin/node" "/Users/jon/.nvm/versions/node/v6.9.1/bin/npm" "publish" 111 | 46 error node v6.9.1 112 | 47 error npm v3.10.8 113 | 48 error code E403 114 | 49 error "You cannot publish over the previously published version 0.1.1." : nodebb-backup 115 | 50 error If you need help, you may report this error at: 116 | 50 error 117 | 51 verbose exit [ 1, true ] 118 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodebb-backup", 3 | "version": "0.1.2", 4 | "description": "A simple backup script based on https://docs.nodebb.org/vi/latest/upgrading/", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/jongarrison/nodebb-backup.git" 12 | }, 13 | "keywords": [ 14 | "nodebb", 15 | "backup", 16 | "mongo" 17 | ], 18 | "preferGlobal" : true, 19 | "author": "Jon Garrison", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/jongarrison/nodebb-backup/issues" 23 | }, 24 | "homepage": "https://github.com/jongarrison/nodebb-backup#readme", 25 | "dependencies": { 26 | "async": "*", 27 | "shelljs-nodecli": "*", 28 | "tar-fs": "*", 29 | "fs-extra": "*", 30 | "moment": "*" 31 | }, 32 | "bin": { 33 | "nodebb-backup": "index.js" 34 | } 35 | } 36 | --------------------------------------------------------------------------------