init
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
import {
|
||||
AppConfig,
|
||||
CoingeckoConfig,
|
||||
EncryptionConfig,
|
||||
InvoiceConfig,
|
||||
JwtConfig,
|
||||
KrakenConfig,
|
||||
MulterConfig,
|
||||
OrderConfig,
|
||||
PostgresConfig,
|
||||
ShopSettingsConfig
|
||||
} from '../types/Config';
|
||||
import { MoneroNetwork } from '../types/MoneroNetwork';
|
||||
import { MoneroWalletConfig } from '../types/MoneroWalletConfig';
|
||||
import { MoneroConfirmationTier } from '../types/MoneroConfirmationTier';
|
||||
import { NodeEnv } from '../types/NodeEnv';
|
||||
import { ShopFiatCurrency } from '../types/ShopFiatCurrency';
|
||||
import { PaymentMethod } from '../modules/payment/types/PaymentMethod';
|
||||
import { SimplexConfig } from '../types/SimplexConfig';
|
||||
|
||||
const env = (key: string): string => process.env[key] || '';
|
||||
|
||||
const envInt = (key: string): number => parseInt(env(key), 10);
|
||||
|
||||
const isEnabled = (key: string): boolean => env(key) === 'true';
|
||||
|
||||
export const getPostgresConfig = (): PostgresConfig => {
|
||||
return {
|
||||
type: 'postgres',
|
||||
url: `postgres://${env('POSTGRES_USER')}:${env('POSTGRES_PASSWORD')}@${env('POSTGRES_HOST')}:${env('POSTGRES_PORT')}/${env('POSTGRES_DB')}`,
|
||||
host: env('POSTGRES_HOST'),
|
||||
username: env('POSTGRES_USER'),
|
||||
password: env('POSTGRES_PASSWORD'),
|
||||
port: envInt('POSTGRES_PORT'),
|
||||
database: env('POSTGRES_DB'),
|
||||
entities: [__dirname + '/../modules/**/entities/*{.ts,.js}'],
|
||||
migrations: [__dirname + '/../database/migrations/*{.ts,.js}'],
|
||||
migrationsRun: isEnabled('POSTGRES_MIGRATIONS_RUN')
|
||||
};
|
||||
};
|
||||
|
||||
export const getAppConfig = (): AppConfig => {
|
||||
const rawOrigins = env('CORS_ORIGINS');
|
||||
|
||||
return {
|
||||
port: envInt('BACKEND_PORT'),
|
||||
nodeEnv: env('NODE_ENV') as NodeEnv,
|
||||
corsOrigins: rawOrigins
|
||||
.split(',')
|
||||
.map(o => o.trim())
|
||||
.filter(Boolean),
|
||||
cmsPassword: env('CMS_PASSWORD'),
|
||||
captcha: {
|
||||
length: envInt('CAPTCHA_LENGTH')
|
||||
},
|
||||
throttle: {
|
||||
ttlMs: envInt('THROTTLE_TTL_MS'),
|
||||
limit: envInt('THROTTLE_LIMIT')
|
||||
},
|
||||
signedCookie: {
|
||||
jwtSecret: env('SIGNED_COOKIE_JWT_SECRET'),
|
||||
feedback: {
|
||||
cookieName: env('SIGNED_COOKIE_FEEDBACK_NAME'),
|
||||
expiresInMs: envInt('SIGNED_COOKIE_FEEDBACK_EXPIRES_IN_MS')
|
||||
},
|
||||
cart: {
|
||||
cookieName: env('SIGNED_COOKIE_CART_NAME'),
|
||||
expiresInMs: envInt('SIGNED_COOKIE_CART_EXPIRES_IN_MS')
|
||||
},
|
||||
captcha: {
|
||||
cookieName: env('SIGNED_COOKIE_CAPTCHA_NAME'),
|
||||
expiresInMs: envInt('SIGNED_COOKIE_CAPTCHA_EXPIRES_IN_MS')
|
||||
},
|
||||
discount: {
|
||||
cookieName: env('SIGNED_COOKIE_DISCOUNT_NAME'),
|
||||
expiresInMs: envInt('SIGNED_COOKIE_DISCOUNT_EXPIRES_IN_MS')
|
||||
},
|
||||
error: {
|
||||
cookieName: env('SIGNED_COOKIE_ERROR_NAME'),
|
||||
expiresInMs: envInt('SIGNED_COOKIE_ERROR_EXPIRES_IN_MS')
|
||||
},
|
||||
checkoutSession: {
|
||||
cookieName: env('SIGNED_COOKIE_CHECKOUT_SESSION_NAME'),
|
||||
expiresInMs: envInt('ORDER_CHECKOUT_VALIDITY_MS')
|
||||
},
|
||||
orderAuth: {
|
||||
cookieName: env('SIGNED_COOKIE_ORDER_AUTH_NAME'),
|
||||
expiresInMs: envInt('SIGNED_COOKIE_ORDER_AUTH_EXPIRES_IN_MS')
|
||||
},
|
||||
theme: {
|
||||
cookieName: env('SIGNED_COOKIE_THEME_NAME'),
|
||||
expiresInMs: envInt('SIGNED_COOKIE_THEME_EXPIRES_IN_MS')
|
||||
}
|
||||
},
|
||||
validation: {
|
||||
productTitleMaxLength: envInt('VALIDATION_PRODUCT_TITLE_MAX_LENGTH'),
|
||||
categoryNameMaxLength: envInt('VALIDATION_CATEGORY_NAME_MAX_LENGTH'),
|
||||
discountCodeMaxLength: envInt('VALIDATION_DISCOUNT_CODE_MAX_LENGTH'),
|
||||
variantImagesMax: envInt('VALIDATION_VARIANT_IMAGES_MAX'),
|
||||
digitalStockAttachmentsMax: envInt('VALIDATION_DIGITAL_STOCK_ATTACHMENTS_MAX'),
|
||||
shippingNoteMinLength: envInt('VALIDATION_SHIPPING_NOTE_MIN_LENGTH'),
|
||||
shippingNoteMaxLength: envInt('VALIDATION_SHIPPING_NOTE_MAX_LENGTH'),
|
||||
orderMessageMaxLength: envInt('VALIDATION_ORDER_MESSAGE_MAX_LENGTH')
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const getJwtConfig = (): JwtConfig => {
|
||||
return {
|
||||
secret: env('JWT_SECRET'),
|
||||
expiresInMs: envInt('JWT_EXPIRES_IN_MS')
|
||||
};
|
||||
};
|
||||
|
||||
export const getProductThumbMulterConfig = (): MulterConfig => {
|
||||
const allowedMimes = env('MULTER_PRODUCT_THUMB_ALLOWED_MIMES')
|
||||
.split(',')
|
||||
.map(v => v.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const maxFileBytes = envInt('MULTER_PRODUCT_THUMB_MAX_FILE_BYTES');
|
||||
|
||||
return { allowedMimes, maxFileBytes };
|
||||
};
|
||||
|
||||
export const getShopLogoMulterConfig = (): MulterConfig => {
|
||||
const allowedMimes = env('MULTER_SHOP_LOGO_ALLOWED_MIMES')
|
||||
.split(',')
|
||||
.map(v => v.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const maxFileBytes = envInt('MULTER_SHOP_LOGO_MAX_FILE_BYTES');
|
||||
|
||||
return { allowedMimes, maxFileBytes };
|
||||
};
|
||||
|
||||
export const getShopFaviconMulterConfig = (): MulterConfig => {
|
||||
const allowedMimes = env('MULTER_SHOP_FAVICON_ALLOWED_MIMES')
|
||||
.split(',')
|
||||
.map(v => v.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const maxFileBytes = envInt('MULTER_SHOP_FAVICON_MAX_FILE_BYTES');
|
||||
|
||||
return { allowedMimes, maxFileBytes };
|
||||
};
|
||||
|
||||
export const getDigitalStockAttachmentMulterConfig = (): MulterConfig => {
|
||||
const allowedMimes = env('MULTER_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES')
|
||||
.split(',')
|
||||
.map(v => v.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const maxFileBytes = envInt('MULTER_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES');
|
||||
|
||||
return { allowedMimes, maxFileBytes };
|
||||
};
|
||||
|
||||
export const getCoingeckoConfig = (): CoingeckoConfig => {
|
||||
return {
|
||||
apiBaseUrl: env('COINGECKO_API_BASE_URL'),
|
||||
xmrRateFetchTimeoutMs: envInt('COINGECKO_XMR_RATE_FETCH_TIMEOUT_MS')
|
||||
};
|
||||
};
|
||||
|
||||
export const getKrakenConfig = (): KrakenConfig => {
|
||||
return {
|
||||
apiBaseUrl: env('KRAKEN_API_BASE_URL'),
|
||||
xmrRateFetchTimeoutMs: envInt('KRAKEN_XMR_RATE_FETCH_TIMEOUT_MS')
|
||||
};
|
||||
};
|
||||
|
||||
export const getEncryptionConfig = (): EncryptionConfig => ({
|
||||
keyBase64: env('BASE64_ENCRYPTION_KEY')
|
||||
});
|
||||
|
||||
export const getShopSettingsConfig = (): ShopSettingsConfig => ({
|
||||
shopName: env('SHOP_NAME'),
|
||||
shopFiatCurrency: env('SHOP_FIAT_CURRENCY') as ShopFiatCurrency,
|
||||
monero: {
|
||||
confirmationTiers: JSON.parse(env('MONERO_CONFIRMATION_TIERS')) as MoneroConfirmationTier[]
|
||||
}
|
||||
});
|
||||
|
||||
export const getOrderConfig = (): OrderConfig => ({
|
||||
checkoutValidityMs: envInt('ORDER_CHECKOUT_VALIDITY_MS'),
|
||||
shippingPaymentValidityMs: envInt('ORDER_SHIPPING_PAYMENT_VALIDITY_MS'),
|
||||
checkoutStatusRefreshSec: envInt('ORDER_CHECKOUT_STATUS_REFRESH_SEC'),
|
||||
dataRetentionDays: envInt('ORDER_DATA_RETENTION_DAYS')
|
||||
});
|
||||
|
||||
export const getInvoiceConfig = (): InvoiceConfig => ({
|
||||
minByMethod: {
|
||||
[PaymentMethod.Xmr]: String(envInt('MONERO_MIN_INCOMING_ATOMIC'))
|
||||
}
|
||||
});
|
||||
|
||||
export const getMoneroWalletConfig = (): MoneroWalletConfig => ({
|
||||
network: env('MONERO_NETWORK') as MoneroNetwork,
|
||||
rpcUrl: `http://${env('MONERO_WALLET_RPC_HOST')}:${envInt('MONERO_WALLET_RPC_PORT')}/json_rpc`,
|
||||
daemonRpcUrl: `http://${env('MONERO_DAEMON_ADDRESS')}/json_rpc`,
|
||||
username: env('MONERO_WALLET_RPC_USERNAME'),
|
||||
password: env('MONERO_WALLET_RPC_PASSWORD'),
|
||||
rpcTimeoutMs: envInt('MONERO_WALLET_RPC_TIMEOUT_MS')
|
||||
});
|
||||
|
||||
export const getSimplexConfig = (): SimplexConfig => ({
|
||||
wsUrl: env('SIMPLEX_WS_URL'),
|
||||
botDisplayName: env('SIMPLEX_BOT_DISPLAY_NAME')
|
||||
});
|
||||
|
||||
export default () => ({
|
||||
postgres: getPostgresConfig(),
|
||||
app: getAppConfig(),
|
||||
jwt: getJwtConfig(),
|
||||
coingecko: getCoingeckoConfig(),
|
||||
kraken: getKrakenConfig(),
|
||||
encryption: getEncryptionConfig(),
|
||||
shopSettings: getShopSettingsConfig(),
|
||||
order: getOrderConfig(),
|
||||
invoice: getInvoiceConfig(),
|
||||
moneroWallet: getMoneroWalletConfig(),
|
||||
simplex: getSimplexConfig()
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { getAppConfig } from './index';
|
||||
|
||||
const {
|
||||
throttle: { ttlMs }
|
||||
} = getAppConfig();
|
||||
|
||||
export const throttleProfiles = {
|
||||
checkOrder: { default: { limit: 10, ttl: ttlMs } },
|
||||
cmsLogin: { default: { limit: 5, ttl: ttlMs } },
|
||||
walletWithdraw: { default: { limit: 5, ttl: ttlMs } },
|
||||
walletRevealSeed: { default: { limit: 5, ttl: ttlMs } },
|
||||
checkoutPay: { default: { limit: 5, ttl: ttlMs } },
|
||||
cartDiscount: { default: { limit: 10, ttl: ttlMs } },
|
||||
cartPage: { default: { limit: 40, ttl: ttlMs } }
|
||||
} as const;
|
||||
@@ -0,0 +1,56 @@
|
||||
import { join, resolve, sep } from 'node:path';
|
||||
|
||||
const uploadsRoot = (): string => join(process.cwd(), 'uploads');
|
||||
|
||||
const validatePathTraversal = (baseDir: string, absolutePath: string): void => {
|
||||
const resolvedBaseDir = resolve(baseDir);
|
||||
|
||||
if (!absolutePath.startsWith(`${resolvedBaseDir}${sep}`)) {
|
||||
throw new Error('Invalid storage key');
|
||||
}
|
||||
};
|
||||
|
||||
const resolvePathWithinDir = (baseDir: string, storageKey: string): string => {
|
||||
const resolvedBaseDir = resolve(baseDir);
|
||||
const absolutePath = resolve(resolvedBaseDir, storageKey);
|
||||
|
||||
validatePathTraversal(resolvedBaseDir, absolutePath);
|
||||
|
||||
return absolutePath;
|
||||
};
|
||||
|
||||
export const getPublicUploadsDir = (): string => join(uploadsRoot(), 'public');
|
||||
|
||||
export const publicUploadsUrlPrefix = '/uploads/public/';
|
||||
|
||||
export const getPrivateUploadsDir = (): string => join(uploadsRoot(), 'private');
|
||||
|
||||
export const getVariantImagesDir = (): string => join(getPublicUploadsDir(), 'variants', 'images');
|
||||
|
||||
export const variantImagesUrlPrefix = '/uploads/public/variants/images/';
|
||||
|
||||
export const getVariantImagePublicUrl = (filename: string): string => `${variantImagesUrlPrefix}${filename}`;
|
||||
|
||||
export const getShopBrandingDir = (): string => join(getPublicUploadsDir(), 'shop');
|
||||
|
||||
export const shopBrandingUrlPrefix = '/uploads/public/shop/';
|
||||
|
||||
export const getShopBrandingPublicUrl = (filename: string): string => `${shopBrandingUrlPrefix}${filename}`;
|
||||
|
||||
export const resolveShopBrandingPath = (storageKey: string): string => {
|
||||
const baseDir = getShopBrandingDir();
|
||||
|
||||
const absolutePath = resolvePathWithinDir(baseDir, storageKey);
|
||||
|
||||
return absolutePath;
|
||||
};
|
||||
|
||||
export const getDigitalStockAttachmentsDir = (): string => join(getPrivateUploadsDir(), 'digital-stock', 'attachments');
|
||||
|
||||
export const resolveDigitalStockAttachmentPath = (storageKey: string): string => {
|
||||
const baseDir = getDigitalStockAttachmentsDir();
|
||||
|
||||
const absolutePath = resolvePathWithinDir(baseDir, storageKey);
|
||||
|
||||
return absolutePath;
|
||||
};
|
||||
@@ -0,0 +1,351 @@
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import { IsBoolean, IsEnum, IsNotEmpty, IsNumber, IsString, Max, Min, validateSync } from 'class-validator';
|
||||
import { NodeEnv } from '../types/NodeEnv';
|
||||
import { ShopFiatCurrency } from '../types/ShopFiatCurrency';
|
||||
import { IsBase64 } from '../validation/decorators/isBase64';
|
||||
import { IsMoneroConfirmationTiers } from '../validation/decorators/isMoneroConfirmationTiers';
|
||||
import { MoneroNetwork } from '../types/MoneroNetwork';
|
||||
|
||||
class EnvironmentVariables {
|
||||
@IsNotEmpty()
|
||||
@IsEnum(NodeEnv)
|
||||
NODE_ENV: NodeEnv;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
BACKEND_PORT: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
POSTGRES_USER: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
POSTGRES_PASSWORD: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
POSTGRES_DB: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
POSTGRES_HOST: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
POSTGRES_PORT: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsBoolean()
|
||||
POSTGRES_MIGRATIONS_RUN: boolean;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
CORS_ORIGINS: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
JWT_SECRET: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
JWT_EXPIRES_IN_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
CMS_PASSWORD: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SHOP_NAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsEnum(ShopFiatCurrency)
|
||||
SHOP_FIAT_CURRENCY: ShopFiatCurrency;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
MULTER_PRODUCT_THUMB_ALLOWED_MIMES: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(10 * 1024 * 1024) // 10MB
|
||||
MULTER_PRODUCT_THUMB_MAX_FILE_BYTES: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
MULTER_SHOP_LOGO_ALLOWED_MIMES: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(10 * 1024 * 1024) // 10MB
|
||||
MULTER_SHOP_LOGO_MAX_FILE_BYTES: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
MULTER_SHOP_FAVICON_ALLOWED_MIMES: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(10 * 1024 * 1024) // 10MB
|
||||
MULTER_SHOP_FAVICON_MAX_FILE_BYTES: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(256)
|
||||
VALIDATION_PRODUCT_TITLE_MAX_LENGTH: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(256)
|
||||
VALIDATION_CATEGORY_NAME_MAX_LENGTH: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(64)
|
||||
VALIDATION_DISCOUNT_CODE_MAX_LENGTH: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(100)
|
||||
VALIDATION_VARIANT_IMAGES_MAX: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
MULTER_DIGITAL_STOCK_ATTACHMENT_ALLOWED_MIMES: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(50 * 1024 * 1024) // 50MB
|
||||
MULTER_DIGITAL_STOCK_ATTACHMENT_MAX_FILE_BYTES: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(20)
|
||||
VALIDATION_DIGITAL_STOCK_ATTACHMENTS_MAX: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(1000)
|
||||
VALIDATION_SHIPPING_NOTE_MIN_LENGTH: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(10000)
|
||||
VALIDATION_SHIPPING_NOTE_MAX_LENGTH: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(10000)
|
||||
VALIDATION_ORDER_MESSAGE_MAX_LENGTH: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(4)
|
||||
@Max(8)
|
||||
CAPTCHA_LENGTH: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1000)
|
||||
@Max(3_600_000)
|
||||
THROTTLE_TTL_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(10_000)
|
||||
THROTTLE_LIMIT: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIGNED_COOKIE_JWT_SECRET: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIGNED_COOKIE_FEEDBACK_NAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
SIGNED_COOKIE_FEEDBACK_EXPIRES_IN_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIGNED_COOKIE_CART_NAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
SIGNED_COOKIE_CART_EXPIRES_IN_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIGNED_COOKIE_CAPTCHA_NAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
SIGNED_COOKIE_CAPTCHA_EXPIRES_IN_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIGNED_COOKIE_DISCOUNT_NAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
SIGNED_COOKIE_DISCOUNT_EXPIRES_IN_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIGNED_COOKIE_ERROR_NAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
SIGNED_COOKIE_ERROR_EXPIRES_IN_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIGNED_COOKIE_CHECKOUT_SESSION_NAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIGNED_COOKIE_ORDER_AUTH_NAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
SIGNED_COOKIE_ORDER_AUTH_EXPIRES_IN_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIGNED_COOKIE_THEME_NAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
SIGNED_COOKIE_THEME_EXPIRES_IN_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
COINGECKO_API_BASE_URL: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
KRAKEN_API_BASE_URL: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
KRAKEN_XMR_RATE_FETCH_TIMEOUT_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
COINGECKO_XMR_RATE_FETCH_TIMEOUT_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@IsBase64({ byteLength: 32 })
|
||||
BASE64_ENCRYPTION_KEY: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@IsMoneroConfirmationTiers()
|
||||
MONERO_CONFIRMATION_TIERS: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(60000)
|
||||
ORDER_CHECKOUT_VALIDITY_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
ORDER_CHECKOUT_STATUS_REFRESH_SEC: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(60000)
|
||||
ORDER_SHIPPING_PAYMENT_VALIDITY_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
ORDER_DATA_RETENTION_DAYS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@Max(Number.MAX_SAFE_INTEGER)
|
||||
MONERO_MIN_INCOMING_ATOMIC: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
MONERO_DAEMON_ADDRESS: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsEnum(MoneroNetwork)
|
||||
MONERO_NETWORK: MoneroNetwork;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
MONERO_WALLET_RPC_HOST: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(65535)
|
||||
MONERO_WALLET_RPC_PORT: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
MONERO_WALLET_RPC_USERNAME: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
MONERO_WALLET_RPC_PASSWORD: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsNumber()
|
||||
@Min(1000)
|
||||
MONERO_WALLET_RPC_TIMEOUT_MS: number;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIMPLEX_WS_URL: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
SIMPLEX_BOT_DISPLAY_NAME: string;
|
||||
}
|
||||
|
||||
export const validate = (config: Record<string, unknown>) => {
|
||||
const validatedConfig = plainToClass(EnvironmentVariables, config, {
|
||||
enableImplicitConversion: true
|
||||
});
|
||||
|
||||
const errors = validateSync(validatedConfig, {
|
||||
skipMissingProperties: false
|
||||
});
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error(errors.toString());
|
||||
}
|
||||
|
||||
return validatedConfig;
|
||||
};
|
||||
Reference in New Issue
Block a user