Source: models/backupmanager.js

const EventEmitter = require('events');
const glob = require('glob');
const fs = require('fs');
const fs_extra = require('fs-extra');
const path = require('path');
const archiver = require('archiver');
const Zip = require("adm-zip");
const moment = require('moment');

/**
 * Class offers functionality to manage backups
 * @class
 */
class BackupManager extends EventEmitter {

    /**
     * @constructor
     */
    constructor() {
        super();

        //defne backuplocation
        this.backupfolder = path.join(global.appRoot, `backup`);
        // check if backup directory exists. If not -> create it
        if(!fs.existsSync(this.backupfolder)){
            fs.mkdirSync(this.backupfolder);
        }

        //define backup-content
        this.content_folders = [
            path.join(global.appRoot, 'store'),
            path.join(global.appRoot, 'public', 'assets', 'img')
        ];
    }

    /**
     * creates a new Backup
     * @param {function} callback 
     */
    Create(callback) {
        var now = moment();
        var zip_name = `alarmiatorserver_backup_` + now.format("YYYYMMDDHHmmss") + `.zip`;    
        var zip_file = path.join(this.backupfolder, zip_name);
        var output = fs.createWriteStream(zip_file);
        
        var archive = archiver('zip');

        output.on('close', function () {
            console.log('CORE | MODELS | BACKUP | ' + archive.pointer() + ' total bytes');
            console.log('CORE | MODELS | BACKUP | archiver has been finalized and the output file descriptor has closed.');
            return callback('reloadPage')
            
        });

        archive.on('error', function(err){
            callback(err);
        });

        console.log('CORE | MODELS | BACKUP | start create backup: ' + zip_file);
        archive.pipe(output);

        this.content_folders.forEach((folder) => {
            var foldername = path.basename(folder);
            archive.directory(folder, foldername);
        });
        
        archive.finalize();

        return callback(null);
    }

     /**
     * Delete a Backup
     * @param {function} callback 
     */
    Delete(filename, callback) {

        var backupfile = path.join(this.backupfolder, filename)

        if(fs.existsSync(backupfile)){
            fs.unlink(backupfile, (err) => {
                if(err){
                    console.log('CORE | MODELS | BACKUP | error during delete ' + filename + '!');
                    return callback(err);
                }
                else{
                    console.log('CORE | MODELS | BACKUP | ' + filename + ' deleted.');
                    return callback(null);
                }
            });
        }
        else
        {
            return callback(new Error(filename + ' not found!'));
        }
    }

    /**
     * restore a backupfile
     * @param {string} filename 
     * @param {*} callback 
     */
    Restore(filename, callback) {

        var backupfile = path.join(this.backupfolder, filename)

        if(fs.existsSync(backupfile)){
            
            // implement restore here
            // 1. unzip
            var zip = new Zip(backupfile);
            
            var restorefolder = path.join(this.backupfolder, 'restore');
            if(!fs.existsSync(restorefolder))
            {
                fs.mkdirSync(restorefolder);
            }

            zip.extractAllTo(restorefolder, true);

            // 2 copy unzipped folders to location under global.appRoot
            this.content_folders.forEach((dst_folder) => {
                var foldername = path.basename(dst_folder)

                var src_folder = path.join(restorefolder, foldername);

                if(foldername === 'store')
                {
                    dst_folder = path.join(dst_folder, 'restore');
                }

                fs_extra.copySync(src_folder, dst_folder);
            });

            fs.rmdirSync(restorefolder, { recursive: true });

            return callback('finished');

        }
        else
        {
            return callback(new Error(filename + ' not found!'));
        }
    }

    /**
     * returns the complete path of a backupfile
     * @param {string} filename 
     * @param {function} callback null or path to backupfile
     */
    GetBackupfilePath(filename, callback) {
        
        var backupfile = path.join(this.backupfolder, filename)
            
        if (fs.existsSync(backupfile)) {
            return callback(backupfile)
        } else {
            console.log('CORE | MODELS | BACKUP | ' + backupfile + ' not found!');
            return callback(null);
        }
        
    }

    /**
     * returns list of backupfiles in a given folder
     * @param {string} folder folder to look for backupfiles
     * @param {function} callback list of files in folder as array
     */
    getListOfBackupfilesInFolder(callback) {

        var fullbackuppath = path.join(this.backupfolder, '*.zip');

        glob(fullbackuppath, null, (er, files) => {
            if (er) {
                console.log('CORE | MODELS | BACKUP | Error getting list of backupfiles from filesystem: ' + er.message);
                return callback(null);
            } else {
                return callback(files);
            }
        });
    }
}

module.exports = BackupManager;