Skip to content

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

Open
wants to merge 22 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,4 @@ module.exports = {
'prefer-template': 'error',
quotes: ['warn', 'single', { allowTemplateLiterals: true }]
}
};
};
1 change: 1 addition & 0 deletions apps/api-gateway/src/issuance/dtos/issuance.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
IsString,
IsUUID,
IsUrl,
IsUUID,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove duplicate IsUUID from the import list

IsUUID 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.

-  IsUUID,
-  IsUrl,
-  IsUUID,
+  IsUUID,
+  IsUrl,

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 18-18: Declarations inside of a import declaration may not have duplicates

a second declaration of IsUUID is not allowed

IsUUID is first declared here

(parse)

🤖 Prompt for AI Agents
In apps/api-gateway/src/issuance/dtos/issuance.dto.ts at line 18, remove the
duplicate import of IsUUID since it is already imported on line 16. Delete the
redundant IsUUID entry from the import list to fix the TS2300 duplicate
identifier compilation error and resolve the linter failure.

MaxLength,
ValidateNested
} from 'class-validator';
Expand Down
1 change: 1 addition & 0 deletions apps/api-gateway/src/issuance/issuance.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import { SchemaType } from '@credebl/enum/enum';
import { CommonConstants } from '../../../../libs/common/src/common.constant';
import { TrimStringParamPipe } from '@credebl/common/cast.helper';
import { NotFoundErrorDto } from '../dtos/not-found-error.dto';

@Controller()
@UseFilters(CustomExceptionFilter)
@ApiTags('credentials')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ export class VerificationController {
sortField,
sortBy
};

const proofPresentationDetails = await this.verificationService.getProofPresentations(
proofRequestsSearchCriteria,
user,
Expand Down
169 changes: 169 additions & 0 deletions config/config.ts
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 };
48 changes: 48 additions & 0 deletions config/core/error.ts
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);
}
}
29 changes: 29 additions & 0 deletions config/core/helpers/str-helpers.ts
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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix type safety issue with undefined values.

The function accepts string | undefined but inputs.includes(undefined) behavior depends on whether the array actually contains undefined values. This could lead to unexpected results.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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);
}
🤖 Prompt for AI Agents
In config/core/helpers/str-helpers.ts around lines 1 to 3, the function
_includes accepts a value that can be string or undefined, but calling includes
with undefined may cause unexpected behavior. Update the function to explicitly
check if the value is undefined and handle that case separately, returning false
or handling it as appropriate, before calling includes on the inputs array.


function _isNumber(input: string | undefined): boolean {
return !Number.isNaN(Number(input));
}
Comment on lines +5 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix edge case with whitespace-only strings.

The current implementation considers whitespace-only strings as valid numbers because Number(" ") returns 0.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function _isNumber(input: string | undefined): boolean {
return !Number.isNaN(Number(input));
}
function _isNumber(input: string | undefined): boolean {
return input?.trim() !== '' && !Number.isNaN(Number(input));
}
🤖 Prompt for AI Agents
In config/core/helpers/str-helpers.ts around lines 5 to 7, the _isNumber
function incorrectly treats whitespace-only strings as valid numbers because
Number("  ") returns 0. To fix this, add a check to return false if the input is
undefined or consists only of whitespace before converting it to a number. This
ensures that strings with only whitespace are not considered valid numbers.


function _isNotEmpty(input: string | undefined): boolean {
return '' !== input?.trim();
}
Comment on lines +9 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect handling of undefined inputs.

The function returns true for undefined inputs because '' !== undefined is true, which is likely not the intended behavior.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function _isNotEmpty(input: string | undefined): boolean {
return '' !== input?.trim();
}
function _isNotEmpty(input: string | undefined): boolean {
return input !== undefined && '' !== input.trim();
}
🤖 Prompt for AI Agents
In config/core/helpers/str-helpers.ts around lines 9 to 11, the function
_isNotEmpty incorrectly returns true for undefined inputs because it compares an
empty string to input?.trim(), which results in true when input is undefined. To
fix this, explicitly check if the input is defined before trimming and
comparing, ensuring the function returns false for undefined inputs and only
true for non-empty strings.


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
};
78 changes: 78 additions & 0 deletions config/core/helpers/url-helpers.ts
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
};
Loading