UNPKG

3.1 kBJavaScriptView Raw
1"use strict";
2
3const path = require('path');
4const Promise = require('bluebird');
5const debounce = require('lodash/debounce');
6const fs = Promise.promisifyAll(require('fs'));
7const { getCliLocation } = require('../utils');
8
9const cliLocation = getCliLocation();
10const CACHE_LOCATION = path.resolve(cliLocation, 'testim-cache', 'testim.cache')
11
12const logger = require('./logger').getLogger('local cache');
13const crypto = require('crypto');
14
15let encryptKeyResolve;
16let _encryptKeyPromise = new Promise(resolve => encryptKeyResolve = resolve);
17
18const THREE_HOURS = 1000 * 60 * 60 * 3;
19
20const localRunnerCache = fs.readFileAsync(CACHE_LOCATION).then(async buffer => {
21 const key = await _encryptKeyPromise;
22 return decrypt(key, buffer);
23}).timeout(30000).catch(() => ({}));
24
25const encryptAndSave = debounce(async function encryptAndSave(object) {
26 const key = await _encryptKeyPromise;
27 const iv = crypto.randomBytes(16);
28 const objStr = JSON.stringify(object);
29 const keyBuffer = Buffer.from(key);
30 let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.concat([keyBuffer, Buffer.alloc(32)], 32), iv);
31 let result = Buffer.concat([iv, cipher.update(objStr), cipher.final()]);
32 fs.writeFileAsync(CACHE_LOCATION, result).catch((err) => {
33 logger.error('failed saving cache', {err});
34 });
35}, 200);
36
37function decrypt(key, buffer) {
38 const iv = buffer.slice(0, 16);
39 const encryptedText = buffer.slice(16);
40 const keyBuffer = Buffer.from(key);
41 let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.concat([keyBuffer, Buffer.alloc(32)], 32), iv);
42 let decrypted = decipher.update(encryptedText);
43 return JSON.parse(Buffer.concat([decrypted, decipher.final()]));
44}
45// argument less memoize for functions with a global cache name
46function memoize(fn, fnName, duration = THREE_HOURS, parameters = undefined) {
47 return Promise.method(async function memoized() {
48 if (parameters) {
49 fnName = fnName + JSON.stringify(parameters);
50 }
51 const cached = await get(fnName);
52 if (cached) {
53 logger.debug("cache hit:", { fnName });
54 return cached;
55 }
56 logger.debug("cache miss:", { fnName });
57 const value = await fn();
58 if (value) {
59 await set(fnName, value, duration);
60 }
61 return value;
62 });
63}
64async function get(key) {
65 const obj = await localRunnerCache;
66 const valueExpiry = obj[key];
67 if (!valueExpiry) {
68 return undefined; // not in cache
69 }
70 const { value, expiry } = valueExpiry;
71 if (expiry < Date.now()) {
72 return undefined;
73 }
74 if (!value) {
75 return undefined;
76 }
77 return value;
78};
79
80async function set(key, value, ttl) {
81 try {
82 const obj = await localRunnerCache;
83 obj[key] = { value, expiry: Date.now() + ttl };
84 encryptAndSave(obj);
85 } catch (e) {
86 logger.error('failed updating cache');
87 }
88};
89
90module.exports.setEncryptKey = encryptKeyResolve;
91module.exports.memoize = memoize;
92module.exports.get = get;
93
94module.exports.set = set;