UNPKG

@idoconfig/provider-folder

Version:

Provider for idoconfig that reads values from files within a folder Useful for Docker Secrets.

127 lines 4.99 kB
"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