|
| 1 | +import * as dotenv from 'dotenv'; |
| 2 | +import validator from 'validator'; |
| 3 | + |
| 4 | +dotenv.config(); |
| 5 | + |
| 6 | +// -------------------------------------------------------------------------------- |
| 7 | + |
| 8 | +// -------------------------------------------------------------------------------- |
| 9 | + |
| 10 | +class EnvValidationError extends Error { |
| 11 | + constructor(message) { |
| 12 | + super(`\n${message}`); |
| 13 | + this.name = 'EnvValidationError'; |
| 14 | + } |
| 15 | +} |
| 16 | + |
| 17 | +// function validateEnvVariablesResults(errors: string[]): void { |
| 18 | +// if (0 < errors.length) { |
| 19 | +// throw new EnvValidationErrorr(errors.join('\n')); |
| 20 | +// } |
| 21 | +// } |
| 22 | + |
| 23 | +function envErrorMessage(envName: string, envValue: string, message: string): string { |
| 24 | + return `[Env_Error] ${envName}: '${envValue}' ${message}`; |
| 25 | +} |
| 26 | + |
| 27 | +// -------------------------------------------------------------------------------- |
| 28 | + |
| 29 | +/** |
| 30 | + * Validation functions for environment variables |
| 31 | + */ |
| 32 | + |
| 33 | +export function isValidProtocol(protocol: string): boolean { |
| 34 | + return ['http', 'https'].includes(protocol); |
| 35 | +} |
| 36 | + |
| 37 | +export function isValidHost(host: string): boolean { |
| 38 | + const ipv4Regex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/g; |
| 39 | + const ipv6Regex = |
| 40 | + /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))g/; |
| 41 | + return ipv4Regex.test(host) || 'localhost' === host || ipv6Regex.test(host); |
| 42 | +} |
| 43 | + |
| 44 | +export function isValidPort(port: string): boolean { |
| 45 | + const portNumber = Number(port); |
| 46 | + return !isNaN(portNumber) && 1024 < portNumber && 65536 > portNumber; |
| 47 | +} |
| 48 | + |
| 49 | +export function isValidEndpoint(endpoint: string): { errorMessage: string; isValid: boolean } { |
| 50 | + const [host, port] = endpoint.split(':'); |
| 51 | + return { |
| 52 | + errorMessage: envErrorMessage('VARIABLE', endpoint, 'is not a valid endpoint.'), |
| 53 | + isValid: isValidHost(host) && isValidPort(port) |
| 54 | + }; |
| 55 | +} |
| 56 | + |
| 57 | +export function isValidURL(url: string): { errorMessage: string; isValid: boolean } { |
| 58 | + return { |
| 59 | + errorMessage: envErrorMessage('VARIABLE', url, 'is not a valid URL.'), |
| 60 | + isValid: validator.isURL(url) || url.startsWith('http://localhost') // REVISAR |
| 61 | + }; |
| 62 | +} |
| 63 | + |
| 64 | +// export function isNotEmptyString(value: string): boolean { |
| 65 | +// return '' !== value.trim(); |
| 66 | +// } |
| 67 | + |
| 68 | +// -------------------------------------------------------------------------------- |
| 69 | + |
| 70 | +// class EnvValidationError extends Error { |
| 71 | +// constructor(envName: string, envValue: string, message: string) { |
| 72 | +// super(`[Env_Error] ${envName}: '${envValue}' ${message}`); |
| 73 | +// this.name = 'EnvValidationError'; |
| 74 | +// } |
| 75 | +// } |
| 76 | + |
| 77 | +type ValidatorFn = (envVariable: string) => { errorMessage: string; isValid: boolean }; |
| 78 | + |
| 79 | +type EnvValidationResult = { |
| 80 | + error: string; |
| 81 | + success: boolean; |
| 82 | + // data: { [K in keyof T]: string }; |
| 83 | +}; |
| 84 | + |
| 85 | +class EnvSchema { |
| 86 | + private schema: Record<string, ValidatorFn>; |
| 87 | + private errors: string[]; |
| 88 | + // private validatedVariables = {}; |
| 89 | + |
| 90 | + constructor(schema: Record<string, ValidatorFn>) { |
| 91 | + this.schema = schema; |
| 92 | + this.errors = []; |
| 93 | + } |
| 94 | + |
| 95 | + public validate(env: Record<string, string>): EnvValidationResult { |
| 96 | + for (const [envVariable, validatorFn] of Object.entries(this.schema)) { |
| 97 | + const { errorMessage, isValid } = validatorFn(env[envVariable]); |
| 98 | + |
| 99 | + if (!isValid) { |
| 100 | + this.errors.push(errorMessage); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + const hasErrors = 0 < this.errors.length; |
| 105 | + if (hasErrors) { |
| 106 | + return { |
| 107 | + error: this.errors.join('\n'), |
| 108 | + success: false |
| 109 | + }; |
| 110 | + } |
| 111 | + return { error: null, success: true }; |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +const envSchema = new EnvSchema({ |
| 116 | + FRONT_END_URL: isValidURL, |
| 117 | + API_ENDPOINT: isValidEndpoint |
| 118 | +}); |
| 119 | + |
| 120 | +const { error, success } = envSchema.validate(process.env); |
| 121 | + |
| 122 | +if (!success) { |
| 123 | + throw new EnvValidationError(error); |
| 124 | +} |
| 125 | + |
| 126 | +// const { |
| 127 | +// // API |
| 128 | +// // API_GATEWAY_PROTOCOL, |
| 129 | +// // API_GATEWAY_HOST, |
| 130 | +// // API_GATEWAY_PORT, |
| 131 | +// // API_GATEWAY_PROTOCOL_SECURE, |
| 132 | +// // API_ENDPOINT, |
| 133 | + |
| 134 | +// // FRONT_END |
| 135 | +// // FRONT_END_URL |
| 136 | + |
| 137 | +// // MOBILE |
| 138 | +// // MOBILE_APP, |
| 139 | +// // MOBILE_APP_NAME, |
| 140 | +// // MOBILE_APP_DOWNLOAD_URL, |
| 141 | +// // PLAY_STORE_DOWNLOAD_LINK |
| 142 | +// // IOS_DOWNLOAD_LINK |
| 143 | +// } = process.env; |
| 144 | + |
| 145 | +// const { error , isSuccess, data } = validar(process.env); |
| 146 | + |
| 147 | +// if (!isSuccess) { |
| 148 | +// throw [new Error("pepe")]; |
| 149 | +// } |
| 150 | + |
| 151 | +// if (0 < errors.length) { |
| 152 | +// throw errors; |
| 153 | +// } |
| 154 | + |
| 155 | +// -------------------------------------------------------------------------------- |
| 156 | + |
| 157 | +// if (!isValidURL(FRONT_END_URL)) { |
| 158 | +// // throw new EnvValidationError('FRONT_END_URL', FRONT_END_URL, 'is not a valid URL.'); |
| 159 | +// errors.push(new EnvValidationError('FRONT_END_URL', FRONT_END_URL, 'is not a valid URL.')); |
| 160 | +// // errors.push(); |
| 161 | +// } |
| 162 | + |
| 163 | +// export { FRONT_END_URL }; |
| 164 | + |
| 165 | +// -------------------------------------------------------------------------------- |
| 166 | + |
| 167 | +// if (!isNotEmptyString(MOBILE_APP)) { |
| 168 | +// throw new EnvValidationError('MOBILE_APP', MOBILE_APP, 'must not be an empty string.'); |
| 169 | + |
| 170 | +// } |
| 171 | + |
| 172 | +// if (!isNotEmptyString(MOBILE_APP_NAME)) { |
| 173 | +// throw new EnvValidationError('MOBILE_APP_NAME', MOBILE_APP_NAME, 'is not a valid URL.'); |
| 174 | +// } |
| 175 | + |
| 176 | +// if (!isValidURL(MOBILE_APP_DOWNLOAD_URL)) { |
| 177 | +// throw new EnvValidationError('MOBILE_APP_DOWNLOAD_URL', MOBILE_APP_DOWNLOAD_URL, 'is not a valid URL.'); |
| 178 | +// } |
| 179 | + |
| 180 | +// if (!isValidURL(PLAY_STORE_DOWNLOAD_LINK)) { |
| 181 | +// throw new EnvValidationError('PLAY_STORE_DOWNLOAD_LINK', PLAY_STORE_DOWNLOAD_LINK, 'is not a valid URL.'); |
| 182 | +// } |
| 183 | + |
| 184 | +// -------------------------------------------------------------------------------- |
| 185 | + |
| 186 | +// if (!isValidProtocol(API_GATEWAY_PROTOCOL)) { |
| 187 | +// // throw new EnvValidationError('API_GATEWAY_PROTOCOL', API_GATEWAY_PROTOCOL, 'is not a valid protocol.'); |
| 188 | +// errors.push(new EnvValidationError('API_GATEWAY_PROTOCOL', API_GATEWAY_PROTOCOL, 'is not a valid protocol.')); |
| 189 | +// } |
| 190 | + |
| 191 | +// if (!isValidHost(API_GATEWAY_HOST)) { |
| 192 | +// // throw new EnvValidationError('API_GATEWAY_HOST', API_GATEWAY_HOST, 'is not a valid host.'); |
| 193 | +// errors.push(new EnvValidationError('API_GATEWAY_HOST', API_GATEWAY_HOST, 'is not a valid host.')); |
| 194 | +// } |
| 195 | + |
| 196 | +// const success = true; |
| 197 | + |
| 198 | +// if (success) { |
| 199 | +// throw errors; |
| 200 | +// } |
| 201 | + |
| 202 | +// if (!isValidPort(API_GATEWAY_PORT)) { |
| 203 | +// throw new EnvValidationError('API_GATEWAY_PORT', API_GATEWAY_PORT, 'is not a valid port.'); |
| 204 | +// } |
| 205 | + |
| 206 | +// if (!isValidProtocol(API_GATEWAY_PROTOCOL_SECURE)) { |
| 207 | +// throw new EnvValidationError('API_GATEWAY_PROTOCOL_SECURE', API_GATEWAY_PROTOCOL_SECURE, 'is not a valid protocol.'); |
| 208 | +// } |
| 209 | + |
| 210 | +// if (!isValidEndpoint(API_ENDPOINT)) { |
| 211 | +// throw new EnvValidationError('API_ENDPOINT', API_ENDPOINT, 'is not a valid endpoint.'); |
| 212 | +// } |
| 213 | + |
| 214 | +// export const API = { |
| 215 | +// GATEWAY_PROTOCOL: API_GATEWAY_PROTOCOL, |
| 216 | +// GATEWAY_HOST: API_GATEWAY_HOST, |
| 217 | +// GATEWAY_PORT: API_GATEWAY_PORT, |
| 218 | +// GATEWAY_PROTOCOL_SECURE: API_GATEWAY_PROTOCOL_SECURE, |
| 219 | +// ENDPOINT: API_ENDPOINT |
| 220 | +// }; |
| 221 | + |
| 222 | +// -------------------------------------------------------------------------------- |
| 223 | + |
| 224 | +// -------------------------------------------------------------------------------- |
0 commit comments