Spaces:
Running
Running
File size: 4,410 Bytes
6e13570 4c6b1e8 6e13570 4c6b1e8 6e13570 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | require("dotenv").config();
// Fail fast if critical secrets are missing or left as placeholders — this
// is a cheap, high-value guard against accidentally deploying with
// insecure defaults.
const REQUIRED_IN_PRODUCTION = [
"MONGODB_URI",
"JWT_ACCESS_SECRET",
"JWT_REFRESH_SECRET",
"FIELD_ENCRYPTION_KEY",
"SMTP_HOST",
"SMTP_USER",
"SMTP_PASS",
"CLOUDINARY_CLOUD_NAME",
"CLOUDINARY_API_KEY",
"CLOUDINARY_API_SECRET",
];
const env = {
nodeEnv: process.env.NODE_ENV || "development",
port: parseInt(process.env.PORT, 10) || 5000,
clientOrigins: (process.env.CLIENT_ORIGINS || "http://localhost:5173")
.split(",")
.map((s) => s.trim())
.filter(Boolean),
// Base URL used to build absolute redirect links (e.g. Payoneer
// Checkout success/cancel URLs) sent to third-party APIs — falls
// back to the first allowed CORS origin if not set explicitly.
frontendUrl: process.env.FRONTEND_URL || (process.env.CLIENT_ORIGINS || "http://localhost:5173").split(",")[0].trim(),
mongoUri: process.env.MONGODB_URI,
jwtAccessSecret: process.env.JWT_ACCESS_SECRET,
jwtRefreshSecret: process.env.JWT_REFRESH_SECRET,
jwtAccessExpiresIn: process.env.JWT_ACCESS_EXPIRES_IN || "15m",
jwtRefreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || "7d",
fieldEncryptionKey: process.env.FIELD_ENCRYPTION_KEY,
smtp: {
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT, 10) || 587,
secure: process.env.SMTP_SECURE === "true",
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
emailFrom: process.env.EMAIL_FROM || "Aynwerd Academy <no-reply@aynwerd.com>",
adminNotifyEmail: process.env.ADMIN_NOTIFY_EMAIL,
bank: {
name: process.env.BANK_NAME || "",
accountName: process.env.BANK_ACCOUNT_NAME || "",
accountNumber: process.env.BANK_ACCOUNT_NUMBER || "",
routingNumber: process.env.BANK_ROUTING_NUMBER || "",
swiftBic: process.env.BANK_SWIFT_BIC || "",
iban: process.env.BANK_IBAN || "",
address: process.env.BANK_ADDRESS || "",
},
payoneer: {
paymentUrl: process.env.PAYONEER_PAYMENT_URL || "https://www.payoneer.com/",
accountEmail: process.env.PAYONEER_ACCOUNT_EMAIL || "",
},
// Payoneer Checkout (automated card/wallet checkout) — separate from
// the manual "payoneer" bank-style transfer above. These credentials
// are issued directly by Payoneer only after your business is
// approved as a Payoneer Checkout merchant; see
// utils/payoneerCheckout.js for what's still a placeholder pending
// that approval and your integration-specific API docs from them.
payoneerCheckout: {
apiBase: process.env.PAYONEER_CHECKOUT_API_BASE || "",
apiKey: process.env.PAYONEER_CHECKOUT_API_KEY || "",
apiSecret: process.env.PAYONEER_CHECKOUT_API_SECRET || "",
webhookSecret: process.env.PAYONEER_CHECKOUT_WEBHOOK_SECRET || "",
},
seedAdmin: {
name: process.env.SEED_ADMIN_NAME || "Site Admin",
email: process.env.SEED_ADMIN_EMAIL,
password: process.env.SEED_ADMIN_PASSWORD,
},
// Cloudinary: free-tier media storage used exclusively for admin-uploaded
// course videos/documents. Files are uploaded with delivery type
// "authenticated" so nothing is reachable without a freshly signed,
// short-lived URL minted server-side for a verified, paying learner.
cloudinary: {
cloudName: process.env.CLOUDINARY_CLOUD_NAME,
apiKey: process.env.CLOUDINARY_API_KEY,
apiSecret: process.env.CLOUDINARY_API_SECRET,
},
};
function assertProductionSafety() {
if (env.nodeEnv !== "production") return;
const missing = REQUIRED_IN_PRODUCTION.filter((key) => {
const value = process.env[key];
return !value || value.startsWith("replace_with_") || value.includes("<");
});
if (missing.length > 0) {
// eslint-disable-next-line no-console
console.error(
`FATAL: refusing to start in production with missing/placeholder env vars: ${missing.join(", ")}`
);
process.exit(1);
}
if (Buffer.from(env.fieldEncryptionKey || "", "hex").length !== 32) {
// eslint-disable-next-line no-console
console.error("FATAL: FIELD_ENCRYPTION_KEY must be a 32-byte (64 hex character) value.");
process.exit(1);
}
}
module.exports = { env, assertProductionSafety };
|