shelveit
Version:
state persistence in json, yaml, toml, or ini files, optionally encrypted
153 lines (137 loc) • 3.51 kB
JavaScript
// @flow
const {basename, extname, resolve} = require('path');
const {
createHash,
createCipher,
createDecipher,
randomBytes
} = require('crypto');
const {promisify} = require('util');
const {readFile, stat, writeFile} = require('fs');
const readFileP = promisify(readFile),
statP = promisify(stat),
writeFileP = promisify(writeFile);
const ini = require('ini');
const toml = require('toml-j0.4');
const tomlify = require('tomlify-j0.4');
const yaml = require('js-yaml');
const globby = require('globby');
async function store(
data: any,
options?: {
encrypt?: {
password: string | Buffer,
algorithm?: string
},
extensions?: string | Array<string>,
filename?: string
} = {}
): Promise<any> {
// let ext = extensions[0];
const extensions = [].concat(options.extensions || 'json');
const filename = options.filename || '.shelveit';
const algorithm = (options.encrypt && options.encrypt.algorithm) || 'aes256';
await Promise.all(
extensions.map(async ext => {
let state;
switch (ext) {
case 'yml':
case 'yaml':
state = yaml.dump(data);
break;
case 'tml':
case 'toml':
state = tomlify.toToml(data);
break;
case 'ini':
state = ini.stringify(data);
break;
case 'json':
default:
state = JSON.stringify(data);
ext = 'json';
}
if (options.encrypt != null) {
state = encrypt(state, options.encrypt.password, algorithm);
ext += '.aes';
}
try {
await writeFileP(resolve([filename, ext].join('.')), state, {
encoding: 'utf8'
});
}
catch (error) {
console.error(error);
}
})
);
}
async function retrieve(
options?: {
encrypt?: {password: string | Buffer, algorithm: string},
path?: string
} = {}
) {
const algorithm = (options.encrypt && options.encrypt.algorithm) || 'aes256';
let file = options.path;
if (file == null) {
file = (await globby('.shelveit*'))[0];
if (!file) throw Error('Cannot find stored file!');
}
const ext = basename(file).split('.')[2];
try {
let data = await readFileP(file, {encoding: 'utf8'});
if (options.encrypt != null) {
data = decrypt(data, options.encrypt.password, algorithm);
}
switch (ext) {
case 'yml':
case 'yaml':
data = yaml.safeLoad(data);
break;
case 'tml':
case 'toml':
data = toml.parse(data);
break;
case 'ini':
data = ini.parse(data);
break;
case 'json':
default:
data = JSON.parse(data);
}
return data;
}
catch (error) {
throw error;
}
}
function encrypt(
data: string | Buffer,
password: string | Buffer,
algorithm
): string {
/*
if upgrading to createCipheriv
*/
// const iv = randomBytes(16);
// const key = createHash('sha1')
// .update(password)
// .digest()
// .toString('hex')
// .slice(0, 32);
const cipher = createCipher(algorithm, password);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
function decrypt(data: string, password: string | Buffer, algorithm: string) {
const decipher = createDecipher('aes256', password);
let decrypted = decipher.update(data, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
module.exports = {
store,
retrieve
};