Skip to content

Commit 2bfb216

Browse files
committed
Refactor error validation
Signed-off-by: rockito10 <[email protected]>
1 parent f4060d5 commit 2bfb216

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed

config/config-old.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import * as dotenv from 'dotenv';
2+
import validator from 'validator';
3+
4+
dotenv.config();
5+
6+
const {
7+
// MODE,
8+
// SUPABASE_URL,
9+
// SUPABASE_KEY,
10+
// SUPABASE_JWT_SECRET,
11+
API_GATEWAY_PROTOCOL,
12+
API_GATEWAY_HOST,
13+
API_GATEWAY_PORT,
14+
API_GATEWAY_PROTOCOL_SECURE
15+
} = process.env;
16+
17+
// console.log({ SUPABASE_KEY, SUPABASE_JWT_SECRET });
18+
19+
// VALIDATION START ---------------------------------------------
20+
21+
export function isValidProtocol(protocol: string): boolean {
22+
return ['http', 'https'].includes(protocol);
23+
}
24+
25+
export function isValidHost(host: string): boolean {
26+
const ipv4Regex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/g;
27+
28+
return ipv4Regex.test(host) || 'localhost' === host;
29+
}
30+
31+
// VALIDATION END---------------------------------------------
32+
33+
// if ('PROD' !== MODE && 'DEV' !== MODE) {
34+
// throw new Error('env.MODE must be "PROD" or "DEV".');
35+
// }
36+
37+
// export { MODE };
38+
39+
// if (
40+
// !validator.isURL(SUPABASE_URL, {
41+
// protocols: ['http', 'https']
42+
// })
43+
// ) {
44+
// throw new Error('env SUPABASE_URL is not an URL');
45+
// }
46+
47+
// if (!SUPABASE_KEY) {
48+
// throw new Error('env SUPABASE_KEY value not set".');
49+
// }
50+
51+
// if (!SUPABASE_JWT_SECRET) {
52+
// throw new Error('env SUPABASE_JWT_SECRET value not set".');
53+
// }
54+
55+
// export const SUPABASE = {
56+
// URL: SUPABASE_URL,
57+
// KEY: SUPABASE_KEY,
58+
// JWT_SECRET: SUPABASE_JWT_SECRET
59+
// };
60+
61+
if (!isValidProtocol(API_GATEWAY_PROTOCOL)) {
62+
throw new Error('env API_GATEWAY_PROTOCOL is not a valid protocol.');
63+
}
64+
65+
if (!isValidHost(API_GATEWAY_HOST)) {
66+
throw new Error('env API_GATEWAY_HOST is not a valid host.');
67+
}
68+
69+
if (!validator.isNumeric(API_GATEWAY_PORT)) {
70+
throw new Error('env API_GATEWAY_PORT is not a valid port.');
71+
}
72+
73+
if (!isValidProtocol(API_GATEWAY_PROTOCOL_SECURE)) {
74+
throw new Error('env API_GATEWAY_PROTOCOL_SECURE is not a valid protocol.');
75+
}
76+
77+
export const API_GATEWAY = {
78+
PROTOCOL: API_GATEWAY_PROTOCOL,
79+
HOST: API_GATEWAY_HOST,
80+
PORT: API_GATEWAY_PORT,
81+
PROTOCOL_SECURE: API_GATEWAY_PROTOCOL_SECURE
82+
};

config/config.ts

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
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

Comments
 (0)