2025-12-18 23:05:04 +05:30
|
|
|
export default () => ({
|
|
|
|
|
// Application
|
|
|
|
|
app: {
|
|
|
|
|
name: process.env.APP_NAME || 'Real Estate Agent Platform',
|
|
|
|
|
env: process.env.NODE_ENV || 'development',
|
|
|
|
|
port: parseInt(process.env.PORT || '3001', 10),
|
|
|
|
|
apiUrl: process.env.API_URL || 'http://localhost:3001',
|
|
|
|
|
frontendUrl: process.env.FRONTEND_URL || 'http://localhost:3000',
|
|
|
|
|
adminUrl: process.env.ADMIN_URL || 'http://localhost:3002',
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Database
|
|
|
|
|
database: {
|
|
|
|
|
url: process.env.DATABASE_URL,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// JWT Authentication
|
|
|
|
|
jwt: {
|
fix(security): resolve audit findings — secrets, env contract, migrations
Removes hardcoded fallback secrets and makes a misconfigured deploy fail
loudly instead of silently falling back to development defaults.
- Remove insecure JWT fallback secrets (messages.module, configuration)
- Remove the 'default-secret' fallback for the 2FA TOTP encryption key and
allow a dedicated TWO_FACTOR_ENCRYPTION_KEY so rotating JWT_SECRET no
longer locks out every 2FA user (see docs/2fa-key-rotation.md)
- Require EMAIL_API_URL; drop the hardcoded vendor email endpoint
- Drive WebSocket CORS from CORS_ORIGINS instead of origin:'*'
- Load .env before any Nest module is imported (src/load-env.ts). Decorator
arguments evaluate at import time, so the gateway previously froze its CORS
config to the localhost fallback even when CORS_ORIGINS was set
- Add boot-time env validation: missing required vars, weak JWT_SECRET, and
inverted access/refresh token lifetimes now abort startup
- Enable Redis TLS certificate verification
- Require ADMIN_EMAIL/ADMIN_PASSWORD for the seed; remove the published
default super-admin credentials and stop printing them
- Add the initial Prisma migration and stop gitignoring prisma/migrations
- Make .env.example an accurate configuration contract (admin bootstrap,
REDIS_TLS, S3_ENDPOINT, 2FA key, Firebase path; drop the dead SMTP block)
- Add handover documentation: architecture, ER model, sequence and data-flow
diagrams, 2FA key rotation runbook
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 11:30:36 +05:30
|
|
|
secret: process.env.JWT_SECRET,
|
|
|
|
|
accessExpiration: process.env.JWT_ACCESS_EXPIRATION || '15m',
|
|
|
|
|
refreshExpiration: process.env.JWT_REFRESH_EXPIRATION || '7d',
|
2025-12-18 23:05:04 +05:30
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Password Hashing
|
|
|
|
|
bcrypt: {
|
|
|
|
|
saltRounds: parseInt(process.env.BCRYPT_SALT_ROUNDS || '12', 10),
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Google OAuth
|
|
|
|
|
google: {
|
|
|
|
|
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
|
|
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
|
|
|
callbackUrl: process.env.GOOGLE_CALLBACK_URL,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Facebook OAuth
|
|
|
|
|
facebook: {
|
|
|
|
|
appId: process.env.FACEBOOK_APP_ID,
|
|
|
|
|
appSecret: process.env.FACEBOOK_APP_SECRET,
|
|
|
|
|
callbackUrl: process.env.FACEBOOK_CALLBACK_URL,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// AWS S3
|
|
|
|
|
aws: {
|
|
|
|
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
|
|
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
|
|
|
region: process.env.AWS_REGION || 'us-east-1',
|
|
|
|
|
s3Bucket: process.env.AWS_S3_BUCKET,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Email
|
|
|
|
|
mail: {
|
|
|
|
|
host: process.env.MAIL_HOST,
|
|
|
|
|
port: parseInt(process.env.MAIL_PORT || '587', 10),
|
|
|
|
|
user: process.env.MAIL_USER,
|
|
|
|
|
password: process.env.MAIL_PASSWORD,
|
|
|
|
|
from: process.env.MAIL_FROM,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Stripe
|
|
|
|
|
stripe: {
|
|
|
|
|
secretKey: process.env.STRIPE_SECRET_KEY,
|
|
|
|
|
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
|
|
|
|
|
publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Firebase
|
|
|
|
|
firebase: {
|
2026-02-25 06:45:27 +05:30
|
|
|
serviceAccountKeyPath: process.env.FIREBASE_SERVICE_ACCOUNT_KEY_PATH,
|
2025-12-18 23:05:04 +05:30
|
|
|
projectId: process.env.FIREBASE_PROJECT_ID,
|
|
|
|
|
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
|
|
|
|
|
privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Redis
|
|
|
|
|
redis: {
|
|
|
|
|
host: process.env.REDIS_HOST || 'localhost',
|
|
|
|
|
port: parseInt(process.env.REDIS_PORT || '6379', 10),
|
|
|
|
|
password: process.env.REDIS_PASSWORD || undefined,
|
|
|
|
|
db: parseInt(process.env.REDIS_DB || '0', 10),
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Rate Limiting
|
|
|
|
|
throttle: {
|
|
|
|
|
ttl: parseInt(process.env.THROTTLE_TTL || '60', 10),
|
|
|
|
|
limit: parseInt(process.env.THROTTLE_LIMIT || '100', 10),
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Logging
|
|
|
|
|
logging: {
|
|
|
|
|
level: process.env.LOG_LEVEL || 'debug',
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// CORS
|
|
|
|
|
cors: {
|
|
|
|
|
origins: process.env.CORS_ORIGINS?.split(',') || [
|
|
|
|
|
'http://localhost:3000',
|
|
|
|
|
'http://localhost:3002',
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
});
|