diff --git a/Backend/.env.example b/Backend/.env.example index f44a4aee..1055dd97 100644 --- a/Backend/.env.example +++ b/Backend/.env.example @@ -2,12 +2,15 @@ PORT=3000 NODE_ENV=development -# Database — match your local Postgres setup +# Database — defaults match docker-compose.yml DATABASE_HOST=localhost DATABASE_PORT=5432 -DATABASE_USER= postgres -DATABASE_PASSWORD= ******* -DATABASE_NAME= your-db-name +DATABASE_USER=gistpin +DATABASE_PASSWORD=gistpin +DATABASE_NAME=gistpin +# Connection pool size (defaults: max 10, min 2) +DB_POOL_MAX=10 +DB_POOL_MIN=2 # Soroban / Stellar SOROBAN_RPC_URL=https://soroban-testnet.stellar.org @@ -15,14 +18,32 @@ STELLAR_NETWORK_PASSPHRASE=Test SDF Network ; September 2015 # Set to the deployed GistRegistry contract to enable live mode. # Leave blank to run in mock mode (no blockchain calls). CONTRACT_ID_GIST_REGISTRY= +# Retries for Soroban RPC calls (default 3) +SOROBAN_RETRY_ATTEMPTS=3 +# Optional: Stellar public key (G...) reported as the moderator/admin in mock mode +# MODERATOR_ADDRESS= # IPFS (Pinata) — leave blank to use mock CIDs in dev PINATA_API_KEY= PINATA_SECRET_KEY= IPFS_GATEWAY=https://gateway.pinata.cloud/ipfs +# Retries for IPFS pin/fetch calls (default 3) +IPFS_RETRY_ATTEMPTS=3 # Optional: backend signing keypair for submitting txs to Soroban STELLAR_SECRET_KEY= +# HTTP +# Comma-separated allowed CORS origins (default http://localhost:3001,http://localhost:8081) +CORS_ORIGINS=http://localhost:3001,http://localhost:8081 +# Rate limiting: requests allowed per window (default 10) and window in ms (default 60000) +THROTTLE_LIMIT=10 +THROTTLE_TTL_MS=60000 +# Milliseconds to drain in-flight requests on shutdown (default 25000) +SHUTDOWN_TIMEOUT_MS=25000 + +# Stats endpoint cache TTL in ms (default 30000) +STATS_CACHE_TTL_MS=30000 + # Logging — directory for rotated log files (production only) LOG_DIR=logs diff --git a/Backend/src/app.module.ts b/Backend/src/app.module.ts index 2a6f342c..da919e48 100644 --- a/Backend/src/app.module.ts +++ b/Backend/src/app.module.ts @@ -13,6 +13,7 @@ import { GistsModule } from './gists/gists.module'; import { HealthModule } from './health/health.module'; import { MetricsModule } from './metrics/metrics.module'; import { ShutdownModule } from './common/shutdown/shutdown.module'; +import configuration from './config/configuration'; import { envValidationSchema } from './config/env.validation'; import { CorrelationIdMiddleware } from './common/middleware/correlation-id.middleware'; import { InFlightRequestMiddleware } from './common/shutdown/in-flight.middleware'; @@ -24,6 +25,7 @@ import { StatsModule } from './stats/stats.module'; imports: [ ConfigModule.forRoot({ isGlobal: true, + load: [configuration], validationSchema: envValidationSchema, validationOptions: { abortEarly: false, diff --git a/Backend/src/config/env.validation.ts b/Backend/src/config/env.validation.ts index 8c11d26c..353f3fe7 100644 --- a/Backend/src/config/env.validation.ts +++ b/Backend/src/config/env.validation.ts @@ -33,6 +33,16 @@ export const envValidationSchema = Joi.object({ PINATA_SECRET_KEY: Joi.string().allow('').default(''), IPFS_GATEWAY: Joi.string().uri().default('https://gateway.pinata.cloud/ipfs'), + // Retries and cache + IPFS_RETRY_ATTEMPTS: Joi.number().integer().min(1).max(10).default(3), + SOROBAN_RETRY_ATTEMPTS: Joi.number().integer().min(1).max(10).default(3), + STATS_CACHE_TTL_MS: Joi.number().integer().min(0).default(30000), + + // Moderator — optional Stellar public key (G... address); leave unset to use the default + MODERATOR_ADDRESS: Joi.string() + .pattern(/^G[A-Z2-7]{55}$/) + .messages({ 'string.pattern.base': 'MODERATOR_ADDRESS must be a Stellar public key' }), + // CORS CORS_ORIGINS: Joi.string().allow('').default(''),