-
Notifications
You must be signed in to change notification settings - Fork 55
refactor: env process into config file #1189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
e55d113
5e3b4bb
3b91a03
1e4943f
971a8d5
967291c
b92e2fc
c001f78
81eccb7
f4060d5
2bfb216
dce1c43
10e4422
7b84c79
c2163f9
dd2dc0e
6ea842c
6d23060
8d1893d
1c3e83b
4d2e92d
1715b29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ import { | |
IsString, | ||
IsUUID, | ||
IsUrl, | ||
IsUUID, | ||
MaxLength, | ||
ValidateNested | ||
} from 'class-validator'; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import { v } from './core'; | ||
|
||
const envDemoSchema = v.schema({ | ||
// API_GATEWAY | ||
API_GATEWAY_PROTOCOL: v.str().protocol(), | ||
API_GATEWAY_HOST: v.str().host(), | ||
API_GATEWAY_PORT: v.str().port(), | ||
API_GATEWAY_PROTOCOL_SECURE: v.str().protocol(), | ||
API_ENDPOINT: v.str().endpoint(), | ||
|
||
// FRONT_END_URL | ||
FRONT_END_URL: v.str().url(), | ||
|
||
// MOBILE_APP | ||
MOBILE_APP: v.str().notEmpty(), | ||
MOBILE_APP_NAME: v.str().notEmpty(), | ||
MOBILE_APP_DOWNLOAD_URL: v.str().url(), | ||
PLAY_STORE_DOWNLOAD_LINK: v.str().url(), | ||
IOS_DOWNLOAD_LINK: v.str().url(), | ||
|
||
// PLATFORM | ||
PLATFORM_NAME: v.str().notEmpty(), | ||
POWERED_BY: v.str().notEmpty(), | ||
PLATFORM_WEB_URL: v.str().url(), | ||
POWERED_BY_URL: v.str().url(), | ||
UPLOAD_LOGO_HOST: v.str().host(), | ||
BRAND_LOGO: v.str().url(), | ||
PLATFORM_ADMIN_EMAIL: v.str().email(), | ||
|
||
// SOCKET | ||
SOCKET_HOST: v.str().url(), | ||
|
||
// NATS | ||
NATS_HOST: v.str().host(), | ||
NATS_PORT: v.str().port(), | ||
NATS_URL: v.str().url(), | ||
|
||
// REDIS | ||
REDIS_HOST: v.str().host(), | ||
REDIS_PORT: v.str().port(), | ||
|
||
// SENDGRID | ||
SENDGRID_API_KEY: v.str().optional(), | ||
|
||
// WALLET_STORAGE | ||
WALLET_STORAGE_HOST: v.str().host(), | ||
WALLET_STORAGE_PORT: v.str().port(), | ||
WALLET_STORAGE_USER: v.str().notEmpty(), | ||
WALLET_STORAGE_PASSWORD: v.str().notEmpty(), | ||
|
||
// CRYPTO | ||
CRYPTO_PRIVATE_KEY: v.str().notEmpty(), | ||
PLATFORM_URL: v.str().url(), | ||
PLATFORM_PROFILE_MODE: v.str().notEmpty(), | ||
|
||
// PUBLIC_URL | ||
PUBLIC_LOCALHOST_URL: v.str().localhost(), | ||
PUBLIC_DEV_API_URL: v.str().url(), | ||
PUBLIC_QA_API_URL: v.str().url(), | ||
PUBLIC_PRODUCTION_API_URL: v.str().url(), | ||
PUBLIC_SANDBOX_API_URL: v.str().url(), | ||
PUBLIC_PLATFORM_SUPPORT_EMAIL: v.str().email(), | ||
|
||
// AFJ | ||
AFJ_VERSION: v.str().notEmpty(), | ||
|
||
// PLATFORM_WALLET | ||
PLATFORM_WALLET_NAME: v.str().notEmpty(), | ||
PLATFORM_WALLET_PASSWORD: v.str().notEmpty(), | ||
PLATFORM_SEED: v.str().notEmpty(), | ||
PLATFORM_ID: v.str().number(), | ||
|
||
// DATABASE | ||
POOL_DATABASE_URL: v.str().postgresUrl(), | ||
DATABASE_URL: v.str().postgresUrl(), | ||
|
||
// AWS (optional) | ||
AWS_ACCESS_KEY: v.str().optional(), | ||
AWS_SECRET_KEY: v.str().optional(), | ||
AWS_REGION: v.str().optional(), | ||
AWS_BUCKET: v.str().optional(), | ||
|
||
// AWS_PUBLIC (optional) | ||
AWS_PUBLIC_ACCESS_KEY: v.str().optional(), | ||
AWS_PUBLIC_SECRET_KEY: v.str().optional(), | ||
AWS_PUBLIC_REGION: v.str().optional(), | ||
AWS_ORG_LOGO_BUCKET_NAME: v.str().optional(), | ||
|
||
// AWS_S3_STOREOBJECT (optional) | ||
AWS_S3_STOREOBJECT_ACCESS_KEY: v.str().optional(), | ||
AWS_S3_STOREOBJECT_SECRET_KEY: v.str().optional(), | ||
AWS_S3_STOREOBJECT_REGION: v.str().optional(), | ||
AWS_S3_STOREOBJECT_BUCKET: v.str().optional(), | ||
|
||
SHORTENED_URL_DOMAIN: v.str().url(), | ||
DEEPLINK_DOMAIN: v.str().url(), | ||
|
||
ENABLE_CORS_IP_LIST: v.str().multipleUrl(), | ||
|
||
// SEEDS | ||
USER_NKEY_SEED: v.str().notEmpty(), | ||
API_GATEWAY_NKEY_SEED: v.str().notEmpty(), | ||
ORGANIZATION_NKEY_SEED: v.str().notEmpty(), | ||
AGENT_PROVISIONING_NKEY_SEED: v.str().notEmpty(), | ||
AGENT_SERVICE_NKEY_SEED: v.str().notEmpty(), | ||
VERIFICATION_NKEY_SEED: v.str().notEmpty(), | ||
LEDGER_NKEY_SEED: v.str().notEmpty(), | ||
ISSUANCE_NKEY_SEED: v.str().notEmpty(), | ||
CONNECTION_NKEY_SEED: v.str().notEmpty(), | ||
ECOSYSTEM_NKEY_SEED: v.str().notEmpty(), | ||
CREDENTAILDEFINITION_NKEY_SEED: v.str().notEmpty(), | ||
SCHEMA_NKEY_SEED: v.str().notEmpty(), | ||
UTILITIES_NKEY_SEED: v.str().notEmpty(), | ||
CLOUD_WALLET_NKEY_SEED: v.str().notEmpty(), | ||
GEOLOCATION_NKEY_SEED: v.str().notEmpty(), | ||
NOTIFICATION_NKEY_SEED: v.str().notEmpty(), | ||
|
||
// KEYCLOAK | ||
KEYCLOAK_DOMAIN: v.str().url(), | ||
KEYCLOAK_ADMIN_URL: v.str().url(), | ||
KEYCLOAK_MASTER_REALM: v.str().notEmpty(), | ||
KEYCLOAK_MANAGEMENT_CLIENT_ID: v.str().notEmpty(), | ||
KEYCLOAK_MANAGEMENT_CLIENT_SECRET: v.str().optional(), | ||
KEYCLOAK_REALM: v.str().notEmpty(), | ||
|
||
// SCHEMA_FILE_SERVER | ||
SCHEMA_FILE_SERVER_URL: v.str().url(), | ||
SCHEMA_FILE_SERVER_TOKEN: v.str().optional(), | ||
|
||
// SCRIPTS | ||
GEO_LOCATION_MASTER_DATA_IMPORT_SCRIPT: v.str().notEmpty(), | ||
UPDATE_CLIENT_CREDENTIAL_SCRIPT: v.str().notEmpty(), | ||
|
||
// AFJ_AGENT IN CASE OF DOCKER | ||
AFJ_AGENT_TOKEN_PATH: v.str().optional(), | ||
AFJ_AGENT_SPIN_UP: v.str().optional(), | ||
AFJ_AGENT_ENDPOINT_PATH: v.str().optional(), | ||
|
||
// AGENT_PROTOCOL | ||
AGENT_PROTOCOL: v.str().protocol(), | ||
OOB_BATCH_SIZE: v.str().number(), | ||
PROOF_REQ_CONN_LIMIT: v.str().number(), | ||
MAX_ORG_LIMIT: v.str().number(), | ||
FIDO_API_ENDPOINT: v.str().url(), | ||
|
||
// LOGGING | ||
IS_ECOSYSTEM_ENABLE: v.str().boolean(), | ||
CONSOLE_LOG_FLAG: v.str().boolean(), | ||
ELK_LOG: v.str().boolean(), | ||
LOG_LEVEL: v.str().notEmpty(), | ||
ELK_LOG_PATH: v.str().url(), | ||
ELK_USERNAME: v.str().notEmpty(), | ||
ELK_PASSWORD: v.str().notEmpty(), | ||
|
||
// ORGANIZATION | ||
ORGANIZATION: v.str().notEmpty(), | ||
CONTEXT: v.str().notEmpty(), | ||
APP: v.str().notEmpty() | ||
}); | ||
|
||
const parsedEnv = envDemoSchema.safeParse(process.env); | ||
|
||
if (!parsedEnv.success) { | ||
throw parsedEnv.error.flatten(); | ||
} | ||
|
||
const { data: env } = parsedEnv; | ||
|
||
export { env }; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
type Primitive = string | number | null | undefined; | ||
|
||
export type Issue = { | ||
path?: string; | ||
expected?: Primitive; | ||
received?: Primitive; | ||
message: string; | ||
}; | ||
|
||
type FlattenedIssue = { | ||
[key: string]: { expected?: Primitive; received?: Primitive; message: string }[]; | ||
}; | ||
|
||
// ---------- | ||
|
||
export class VError extends Error { | ||
private readonly issues: Issue[]; | ||
|
||
constructor(errors: Issue[]) { | ||
const message = JSON.stringify(errors, null, 2); | ||
|
||
super(message); | ||
this.issues = errors; | ||
this.name = 'VError'; | ||
} | ||
|
||
public errors(): Issue[] { | ||
return this.issues; | ||
} | ||
|
||
public flatten(): FlattenedIssue { | ||
return this.errors().reduce((flattened, error) => { | ||
const key = error.path || 'messages'; | ||
|
||
if (!flattened[key]) { | ||
flattened[key] = []; | ||
} | ||
|
||
flattened[key].push({ | ||
expected: error.expected, | ||
received: error.received, | ||
message: error.message | ||
}); | ||
|
||
return flattened; | ||
}, {} as FlattenedIssue); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,29 @@ | ||||||||||||||
function _includes(inputs: string[], value: string | undefined): boolean { | ||||||||||||||
return inputs?.includes(value); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+1
to
+3
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix type safety issue with undefined values. The function accepts Consider this fix to handle undefined values explicitly: -function _includes(inputs: string[], value: string | undefined): boolean {
- return inputs?.includes(value);
-}
+function _includes(inputs: string[], value: string | undefined): boolean {
+ return value !== undefined && inputs?.includes(value);
+} 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||
|
||||||||||||||
function _isNumber(input: string | undefined): boolean { | ||||||||||||||
return !Number.isNaN(Number(input)); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+5
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix edge case with whitespace-only strings. The current implementation considers whitespace-only strings as valid numbers because Apply this fix to handle whitespace-only strings properly: -function _isNumber(input: string | undefined): boolean {
- return !Number.isNaN(Number(input));
-}
+function _isNumber(input: string | undefined): boolean {
+ return input?.trim() !== '' && !Number.isNaN(Number(input));
+} 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||
|
||||||||||||||
function _isNotEmpty(input: string | undefined): boolean { | ||||||||||||||
return '' !== input?.trim(); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+9
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix incorrect handling of undefined inputs. The function returns Apply this fix to properly handle undefined inputs: -function _isNotEmpty(input: string | undefined): boolean {
- return '' !== input?.trim();
-}
+function _isNotEmpty(input: string | undefined): boolean {
+ return input !== undefined && '' !== input.trim();
+} 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||
|
||||||||||||||
function _isOptional(): boolean { | ||||||||||||||
return true; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
function _startsWith(input: string | undefined, prefix: string): boolean { | ||||||||||||||
return input?.startsWith(prefix); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// -------------------------------------------------------------------------------- | ||||||||||||||
|
||||||||||||||
export const _STR = { | ||||||||||||||
_includes, | ||||||||||||||
_isNotEmpty, | ||||||||||||||
_isNumber, | ||||||||||||||
_isOptional, | ||||||||||||||
_startsWith | ||||||||||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import validator from 'validator'; | ||
|
||
// -------------------------------------------------------------------------------- | ||
|
||
function _isDomain(input: string | undefined): boolean { | ||
const regex = /^[a-zA-Z0-9.-]+$/; | ||
return regex.test(input || ''); | ||
} | ||
|
||
function _isIP(input: string | undefined): boolean { | ||
return validator.isIP(input || ''); | ||
} | ||
|
||
function _isPort(input: string | undefined): boolean { | ||
const port = Number(input); | ||
return !Number.isNaN(port) && 1024 <= port && 65535 >= port; | ||
} | ||
|
||
function _isLocalhost(input: string | undefined): boolean { | ||
const regex = /^(http:\/\/)?(localhost|127\.0\.0\.1|::1)(:\d{1,5})?(\/)?$/; | ||
|
||
if (!regex.test(input || '')) { | ||
return false; | ||
} | ||
|
||
const port = input.split(':').at(-1).split('/').at(0); | ||
|
||
return _isPort(port); | ||
} | ||
|
||
function _isHost(input: string | undefined): boolean { | ||
return _isIP(input) || _isDomain(input) || _isLocalhost(input); | ||
} | ||
|
||
function _isEndpoint(input: string | undefined): boolean { | ||
const [host, port] = (input || '').split(':'); | ||
return _isHost(host) && _isPort(port); | ||
} | ||
|
||
function _isProtocol(input: string | undefined): boolean { | ||
const regex = /^(http|https)(:\/\/)?/; | ||
return regex.test(input || ''); | ||
} | ||
|
||
function _isURL(input: string | undefined): boolean { | ||
return validator.isURL(input || ''); | ||
} | ||
|
||
function _isEmail(input: string | undefined): boolean { | ||
return validator.isEmail(input || ''); | ||
} | ||
|
||
function _isMultipleURL(input: string | undefined): boolean { | ||
return input?.split(',').every((url) => _isURL(url.trim()) || _isLocalhost(url.trim())); | ||
} | ||
|
||
function _isPostgresURL(input: string | undefined): boolean { | ||
const regex = | ||
/^postgresql:\/\/([a-zA-Z0-9]+):([a-zA-Z0-9]+)@(localhost|[0-9.]+):(\d{1,5})\/([a-zA-Z0-9]+)(\?schema=)?([a-zA-Z0-9]+)/; | ||
|
||
return regex.test(input); | ||
} | ||
|
||
// -------------------------------------------------------------------------------- | ||
|
||
export const _URL = { | ||
_isDomain, | ||
_isEmail, | ||
_isEndpoint, | ||
_isHost, | ||
_isIP, | ||
_isLocalhost, | ||
_isPort, | ||
_isProtocol, | ||
_isMultipleURL, | ||
_isURL, | ||
_isPostgresURL | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove duplicate
IsUUID
from the import listIsUUID
is already imported on line 16. The second occurrence triggers a compilation error (TS2300: Duplicate identifier
) and linter failure. Delete the redundant entry to restore a valid import statement.🧰 Tools
🪛 Biome (1.9.4)
[error] 18-18: Declarations inside of a
import
declaration may not have duplicatesa second declaration of
IsUUID
is not allowedIsUUID
is first declared here(parse)
🤖 Prompt for AI Agents