@idoconfig/provider-folder
Version:
Provider for idoconfig that reads values from files within a folder Useful for Docker Secrets.
127 lines • 4.99 kB
JavaScript
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const base_1 = require("@idoconfig/base");
const isbinaryfile_1 = require("isbinaryfile");
const path_1 = require("path");
const folder_provider_options_1 = require("./folder-provider-options");
/**
* The size of one MegaByte (1024 * 1024 or 2^20)
*/
const SIZE_ONE_MB = 1048576;
/**
* Limits the amount of files read from given folder
*/
const MAX_FILES = 100;
/**
* Read files (content) from a folder. Filenames are mangled through filters which (may)
* transform the filename into ENV_VAR_SYNTAX (or something entirely different).
*
* Default filename filters remove the file extension and enforce ALL_CAPS_STYLE, so
* "my-secret-file.txt" becomes "MY_SECRET_FILE". Additional filename filters may be
* inserted via dependency injection.
*/
class FolderConfigurationValueProvider extends base_1.ConfigurationProviderAbstract {
constructor(options, additionalFilters = []) {
super();
this.filenameFilters = [];
const defaultOptions = new folder_provider_options_1.FolderProviderOptions();
const opts = Object.assign({}, defaultOptions, options);
if (opts.stripFileExtension) {
this.filenameFilters.push(this.removeExtension);
}
this.options = opts;
// Push additional filename filters onto list
for (const filter of additionalFilters) {
this.filenameFilters.push(filter);
}
// Read files from folder
try {
const normalizedPath = this.normalizePath(this.options.path);
const items = fs.readdirSync(normalizedPath);
if (items.length <= MAX_FILES) {
for (const item of items) {
if (this.options.blacklist.indexOf(item) !== -1) {
continue;
}
const file = path_1.join(normalizedPath, item);
const stat = fs.statSync(file);
// Only read files that are non-binary and smaller than 1MB
if (stat.isFile() && stat.size < SIZE_ONE_MB && !isbinaryfile_1.isBinaryFileSync(file, stat.size)) {
const filename = this.applyFiltersToFilename(item);
const key = this.sanitizeKey(filename);
const val = fs.readFileSync(file, "utf8").trim();
// Regular version
this.values[key] = val;
// All underscore version
this.values[key.replace(/[^0-9a-z]+/ig, "_")] = val;
// All dash version
this.values[key.replace(/[^0-9a-z]+/ig, "-")] = val;
}
}
}
}
catch (e) {
// @TODO: Implement log emitter
// console.warn(`Could not read secrets from ${this.options.path}. ${e.message}`);
}
}
/**
* Try to get cached value first. Upon cache miss read file directly
* @param key
*/
getValue(key) {
key = this.sanitizeKey(key);
if (this.values[key]) {
return this.values[key];
}
return this.getValueFromFile(key);
}
/**
* When given path starts with PATH_SEP it is absolute. Don't perform
* any other action. If it is relative prepend the current path.
*
* @param unsafePath
*/
normalizePath(unsafePath) {
const parts = unsafePath.split("/");
return path_1.normalize(parts.join(path_1.sep));
}
/**
* Return trimmed value of requested file
* @param file
*/
getValueFromFile(file) {
const filepath = `${this.options.path}${file}`;
if (file.indexOf(path_1.sep) !== -1) {
throw new Error("File name cannot contain path separator");
}
if (!fs.existsSync(filepath)) {
return;
}
return fs.readFileSync(filepath, "utf8").trim();
}
applyFiltersToFilename(val) {
for (const filter of this.filenameFilters) {
val = filter.call(this, val);
}
return val;
}
removeExtension(val) {
const parts = val.split(".");
if (parts.length > 1) {
parts.pop();
}
return parts.join(".");
}
}
exports.FolderConfigurationValueProvider = FolderConfigurationValueProvider;
exports.default = FolderConfigurationValueProvider;
//# sourceMappingURL=folder-provider.js.map