fix(qenv): replace smartfile-based config loading with native fs and yaml parsing for env files
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/qenv',
|
||||
version: '6.1.3',
|
||||
version: '6.1.4',
|
||||
description: 'A module for easily handling environment variables in Node.js projects with support for .yml and .json configuration.'
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import * as plugins from './qenv.plugins.js';
|
||||
|
||||
export class CloudlyAdapter {
|
||||
public configVaultUrl: string;
|
||||
public configVaultUrl?: string;
|
||||
|
||||
constructor(configVaultUrl?: string) {
|
||||
this.configVaultUrl = configVaultUrl;
|
||||
}
|
||||
|
||||
public async getConfigBundle(): Promise<plugins.configvaultInterfaces.data.IConfigBundle> {
|
||||
public async getConfigBundle(): Promise<plugins.configvaultInterfaces.data.IEnvBundle | null> {
|
||||
if (this.configVaultUrl) {
|
||||
console.log(`ConfigVault specified through constructor`)
|
||||
} else if (process.env['CONFIGVAULT_URL']) {
|
||||
@@ -26,5 +26,6 @@ export class CloudlyAdapter {
|
||||
const response = await tr.fire({
|
||||
authorization: parsedUrl.pathname.replace('/', ''),
|
||||
})
|
||||
return response.envBundle;
|
||||
}
|
||||
}
|
||||
|
||||
+47
-20
@@ -2,18 +2,19 @@ import { CloudlyAdapter } from './qenv.classes.configvaultadapter.js';
|
||||
import * as plugins from './qenv.plugins.js';
|
||||
|
||||
export type TEnvVarRef = string | (() => Promise<string>);
|
||||
type TKeyValueObject = Record<string, any>;
|
||||
|
||||
export class Qenv {
|
||||
public requiredEnvVars: string[] = [];
|
||||
public availableEnvVars: string[] = [];
|
||||
public missingEnvVars: string[] = [];
|
||||
public keyValueObject: { [key: string]: any } = {};
|
||||
public keyValueObject: TKeyValueObject = {};
|
||||
public logger = new plugins.smartlog.ConsoleLog();
|
||||
|
||||
public cloudlyAdapter: CloudlyAdapter;
|
||||
|
||||
public qenvFilePathAbsolute: string;
|
||||
public envFilePathAbsolute: string;
|
||||
public qenvFilePathAbsolute = '';
|
||||
public envFilePathAbsolute?: string;
|
||||
|
||||
constructor(
|
||||
qenvFileBasePathArg: string = process.cwd(),
|
||||
@@ -27,7 +28,7 @@ export class Qenv {
|
||||
this.checkForMissingEnvVars(failOnMissing);
|
||||
}
|
||||
|
||||
private initializeFilePaths(qenvFileBasePathArg: string, envFileBasePathArg: string) {
|
||||
private initializeFilePaths(qenvFileBasePathArg: string, envFileBasePathArg?: string) {
|
||||
this.qenvFilePathAbsolute = plugins.path.join(
|
||||
plugins.path.resolve(qenvFileBasePathArg),
|
||||
'qenv.yml'
|
||||
@@ -40,9 +41,9 @@ export class Qenv {
|
||||
const envFileYmlPath = plugins.path.join(envFileBasePath, 'env.yml');
|
||||
const envFileYamlPath = plugins.path.join(envFileBasePath, 'env.yaml');
|
||||
|
||||
const envFileJsonExists = plugins.smartfile.fs.fileExistsSync(envFileJsonPath);
|
||||
const envFileYmlExists = plugins.smartfile.fs.fileExistsSync(envFileYmlPath);
|
||||
const envFileYamlExists = plugins.smartfile.fs.fileExistsSync(envFileYamlPath);
|
||||
const envFileJsonExists = this.fileExists(envFileJsonPath);
|
||||
const envFileYmlExists = this.fileExists(envFileYmlPath);
|
||||
const envFileYamlExists = this.fileExists(envFileYamlPath);
|
||||
|
||||
if (envFileJsonExists && (envFileYmlExists || envFileYamlExists)) {
|
||||
this.logger.log('warn', 'Both env.json and env.yml files exist! Using env.json');
|
||||
@@ -58,10 +59,13 @@ export class Qenv {
|
||||
}
|
||||
|
||||
private loadRequiredEnvVars() {
|
||||
if (plugins.smartfile.fs.fileExistsSync(this.qenvFilePathAbsolute)) {
|
||||
const qenvFile = plugins.smartfile.fs.toObjectSync(this.qenvFilePathAbsolute);
|
||||
if (qenvFile?.required && Array.isArray(qenvFile.required)) {
|
||||
this.requiredEnvVars.push(...qenvFile.required);
|
||||
if (this.fileExists(this.qenvFilePathAbsolute)) {
|
||||
const qenvFile = this.readObjectFromFile(this.qenvFilePathAbsolute);
|
||||
const requiredEnvVars = qenvFile.required;
|
||||
if (Array.isArray(requiredEnvVars)) {
|
||||
this.requiredEnvVars.push(
|
||||
...requiredEnvVars.filter((envVar): envVar is string => typeof envVar === 'string')
|
||||
);
|
||||
} else {
|
||||
this.logger.log('warn', 'qenv.yml does not contain a "required" Array!');
|
||||
}
|
||||
@@ -194,16 +198,16 @@ export class Qenv {
|
||||
}
|
||||
|
||||
private getFromEnvYamlOrJsonFile(envVarName: string): string | undefined {
|
||||
if (!plugins.smartfile.fs.fileExistsSync(this.envFilePathAbsolute)) {
|
||||
if (!this.fileExists(this.envFilePathAbsolute)) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const envJson = plugins.smartfile.fs.toObjectSync(this.envFilePathAbsolute);
|
||||
const envJson = this.readObjectFromFile(this.envFilePathAbsolute);
|
||||
const value = envJson[envVarName];
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
return 'base64Object:' + this.encodeBase64(value);
|
||||
}
|
||||
return String(value);
|
||||
@@ -214,23 +218,23 @@ export class Qenv {
|
||||
|
||||
private getFromDockerSecret(envVarName: string): string | undefined {
|
||||
const secretPath = `/run/secrets/${envVarName}`;
|
||||
if (plugins.smartfile.fs.fileExistsSync(secretPath)) {
|
||||
return plugins.smartfile.fs.toStringSync(secretPath);
|
||||
if (this.fileExists(secretPath)) {
|
||||
return plugins.fs.readFileSync(secretPath, 'utf8');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getFromDockerSecretJson(envVarName: string): string | undefined {
|
||||
if (plugins.smartfile.fs.isDirectory('/run/secrets')) {
|
||||
const availableSecrets = plugins.smartfile.fs.listAllItemsSync('/run/secrets');
|
||||
if (this.directoryExists('/run/secrets')) {
|
||||
const availableSecrets = plugins.fs.readdirSync('/run/secrets');
|
||||
for (const secret of availableSecrets) {
|
||||
if (secret.includes('secret.json')) {
|
||||
const secretObject = plugins.smartfile.fs.toObjectSync(`/run/secrets/${secret}`);
|
||||
const secretObject = this.readObjectFromFile(`/run/secrets/${secret}`);
|
||||
const value = secretObject[envVarName];
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
return 'base64Object:' + this.encodeBase64(value);
|
||||
}
|
||||
return String(value);
|
||||
@@ -249,4 +253,27 @@ export class Qenv {
|
||||
const decodedString = Buffer.from(encodedString, 'base64').toString('utf-8');
|
||||
return JSON.parse(decodedString);
|
||||
}
|
||||
|
||||
private fileExists(filePath: string | undefined): filePath is string {
|
||||
if (!filePath) {
|
||||
return false;
|
||||
}
|
||||
return plugins.fs.existsSync(filePath);
|
||||
}
|
||||
|
||||
private directoryExists(directoryPath: string): boolean {
|
||||
try {
|
||||
return plugins.fs.statSync(directoryPath).isDirectory();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private readObjectFromFile(filePath: string): TKeyValueObject {
|
||||
const fileString = plugins.fs.readFileSync(filePath, 'utf8');
|
||||
const parsedObject = filePath.endsWith('.json')
|
||||
? JSON.parse(fileString)
|
||||
: plugins.yaml.parse(fileString);
|
||||
return typeof parsedObject === 'object' && parsedObject !== null ? parsedObject : {};
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -1,7 +1,8 @@
|
||||
// native
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'path';
|
||||
|
||||
export { path };
|
||||
export { fs, path };
|
||||
|
||||
// @api.global scope
|
||||
import * as typedrequest from '@api.global/typedrequest';
|
||||
@@ -11,12 +12,16 @@ export {
|
||||
}
|
||||
|
||||
// @pushrocks scope
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as smartlog from '@push.rocks/smartlog';
|
||||
|
||||
export { smartfile, smartlog };
|
||||
export { smartlog };
|
||||
|
||||
// @configvault.io scope
|
||||
import * as configvaultInterfaces from '@configvault.io/interfaces';
|
||||
|
||||
export { configvaultInterfaces };
|
||||
|
||||
// third party scope
|
||||
import * as yaml from 'yaml';
|
||||
|
||||
export { yaml };
|
||||
|
||||
Reference in New Issue
Block a user