@j2blasco/ts-env
Version:
A TypeScript utility for securely managing environment variables through file encryption and runtime loading
113 lines (109 loc) • 4.03 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key2 of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key2) && key2 !== except)
__defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// src/env/encrypt-file.ts
var fs = __toESM(require("fs"), 1);
var crypto = __toESM(require("crypto"), 1);
var ALGORITHM = "aes-128-cbc";
var KEY_LENGTH = 16;
var IV_LENGTH = 16;
var MARKER = Buffer.from("ENCRYPTED");
function encryptFile(inputPath, outputPath, key2) {
const input = fs.readFileSync(inputPath);
const marker = input.subarray(0, MARKER.length);
if (marker.equals(MARKER)) {
return;
}
const keyBuffer = Buffer.from(key2, "utf-8").subarray(0, KEY_LENGTH);
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(ALGORITHM, keyBuffer, iv);
const encrypted = Buffer.concat([
MARKER,
iv,
cipher.update(input),
cipher.final()
]);
fs.writeFileSync(outputPath, encrypted);
}
function decryptFile(inputPath, outputPath, key2) {
try {
const keyBuffer = Buffer.from(key2, "utf-8").subarray(0, KEY_LENGTH);
const input = fs.readFileSync(inputPath);
const marker = input.subarray(0, MARKER.length);
if (!marker.equals(MARKER)) {
fs.writeFileSync(outputPath, input);
return;
}
const iv = input.subarray(MARKER.length, MARKER.length + IV_LENGTH);
const encryptedText = input.subarray(MARKER.length + IV_LENGTH);
const decipher = crypto.createDecipheriv(ALGORITHM, keyBuffer, iv);
const decrypted = Buffer.concat([
decipher.update(encryptedText),
decipher.final()
]);
fs.writeFileSync(outputPath, decrypted);
} catch (error) {
console.error("Decryption failed:", error);
}
}
// src/env/set-env-vars.ts
function getEnvKeyEnvironmentVariable(envType2) {
return `ENV_${envType2.toUpperCase().replace(/-/g, "_")}_KEY`;
}
// src/bin/cli.ts
var import_path = require("path");
var args = process.argv.slice(2);
if (args.length < 2) {
console.log("Usage: ts-env <encrypt|decrypt> <envFilePath>\n");
process.exit(1);
}
var [action, envFilePath] = args;
var fileName = (0, import_path.basename)(envFilePath);
var envFilePattern = /^env\.(.+)\.json$/;
var match = fileName.match(envFilePattern);
if (!match) {
console.error("Invalid file name. Expected format: env.{env_type}.json");
process.exit(1);
}
var envType = match[1];
var envPath = envFilePath;
var key = process.env[getEnvKeyEnvironmentVariable(envType)];
if (!key) {
console.error(`ENV_${envType.toUpperCase()}_KEY is not set.`);
process.exit(1);
}
var keyCharLength = KEY_LENGTH * 2;
if (key.length !== keyCharLength) {
console.error(
`The key must be a 128-bit (${keyCharLength}-character hexadecimal) string. Provided key length: ${key.length}`
);
throw new Error("Invalid key length");
}
if (action === "encrypt") {
encryptFile(envPath, envPath, key);
} else if (action === "decrypt") {
decryptFile(envPath, envPath, key);
} else {
console.error('Invalid action. Use "encrypt" or "decrypt".');
process.exit(1);
}