const Redis = require('ioredis') const commander = require('commander') const fs = require('fs') const yaml = require('yaml') function isNumeric (v) { return isFinite(v) && v !== null && v !== false && v !== true } /** * @param {any} obj * @param {string[]} keys * @param {any} value */ function setKey (obj, keys, value) { const key = isNumeric(keys[0]) ? parseInt(keys[0], 10) : keys[0] if (keys.length === 1) { obj[key] = value return } if (!(key in obj)) { if (isNumeric(keys[1])) { obj[key] = [] } else { obj[key] = {} } } setKey(obj[key], keys.slice(1), value) } /** * @param {string} file * @return {boolean} */ async function checkFileExists (file) { if (!file) return false try { await fs.promises.access(file, fs.constants.W_OK || fs.constants.R_OK) } catch (err) { console.log(err) return false } return true } function getRedis () { const { port, host } = commander const redis = new Redis(port || 6379, host || 'localhost') return redis } function generateFromStruct (obj, prefix = '') { const kvs = [] if (Object.keys(obj).length === 0) return kvs for (const k in obj) { const key = !prefix ? k : [prefix, k].join('/') if (typeof obj[k] === 'object') { kvs.push(...generateFromStruct(obj[k], key)) } else { kvs.push({ key, value: `${obj[k]}` }) } } return kvs } async function importData () { const { file } = commander if (!await checkFileExists(file)) throw new Error(`check file (${file}) fail`) const redis = getRedis() const str = await fs.promises.readFile(file, 'utf8') const obj = yaml.parse(str) const kvs = generateFromStruct(obj) try { // clean redis await redis.flushdb() for (const o of kvs) { console.log(`set: ${o.key} => ${o.value}`) await redis.set(o.key, o.value) } } finally { await redis.disconnect() } } async function exportData () { const { file } = commander const redis = getRedis() const obj = {} try { let cursor = '' while (cursor !== '0') { const result = await redis.scan(cursor || '0') console.log(result) cursor = result[0] for (const k of result[1]) { const str = await redis.get(k) if (!str) continue setKey(obj, k.split('/'), str) } } await fs.promises.writeFile(file, yaml.stringify(obj)) } finally { await redis.disconnect() } } commander.option('-r, --run ', 'run tool module [import, export]') .option('-p, --port [port]', 'redis server port default: 6379') .option('-h, --host [host]', 'redis host addr default: localhost') .option('-f, --file ', 'yaml config path') .parse(process.argv) ; (async () => { const { run, port, host, file } = commander console.log(run, port, host, file) switch (run) { case 'import': await importData() break case 'export': await exportData() break default: throw new Error('run module not support') } // for (const key in json) { // const strs = key.split('/') // setKey(obj, strs, json[key]) // } // // console.log(yaml.stringify(obj)) // await fs.promises.writeFile('store.yml', yaml.stringify(obj)) })().then(console.log).catch(console.log)