متغيرات البيئة وحماية الأسرار — نشر المواقع والدومينات
متغيرات البيئة وحماية الأسرار
موقعك يحتاج إعدادات تختلف بين جهازك والإنترنت (رابط API، وضع التطوير) وأسراراً لا يجب أن يراها أحد (مفتاح قاعدة البيانات، مفتاح الدفع). متغيرات البيئة تفصل هذه القيم عن الكود.
ملف .env محلياً
# .env — لا يُرفع إلى Git أبداً (موجود في .gitignore)
DATABASE_URL=postgres://user:password@localhost:5432/app
STRIPE_SECRET_KEY=sk_test_xxxxx
VITE_API_URL=http://localhost:3000وأرفق ملف .env.example بلا قيم حقيقية ليعرف زملاؤك ما المتغيرات المطلوبة:
DATABASE_URL=
STRIPE_SECRET_KEY=
VITE_API_URL=http://localhost:3000القراءة في Node.js (الخلفية)
// Node 20.6+ يقرأ الملف بنفسه: node --env-file=.env server.js
const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) {
throw new Error("DATABASE_URL غير معرّف — راجع ملف .env");
}افحص المتغيرات الإلزامية عند بدء التشغيل: فشل واضح فوراً أفضل من خطأ غامض لاحقاً. (في الإصدارات الأقدم تُستخدم حزمة dotenv.)
القراءة في الواجهة (Vite)
const apiUrl = import.meta.env.VITE_API_URL;Vite يكشف للواجهة فقط المتغيرات التي تبدأ بـ VITE_، ويضعها حرفياً داخل ملفات JavaScript الناتجة عند البناء.
⚠️ أهم فكرة في الدرس: الواجهة لا تحفظ الأسرار
كل ما يصل إلى المتصفح يستطيع أي زائر قراءته: افتح أدوات المطور ← Sources وستجد قيمة VITE_API_URL مكتوبة. لذلك:
| يجوز في الواجهة (VITE_) | يجب أن يبقى في الخلفية فقط |
|---|---|
| رابط API العام | مفتاح قاعدة البيانات / كلمة مرورها |
| مفتاح Supabase العام (anon) — محمي بسياسات RLS | مفتاح Supabase الخدمي (service_role) |
| مفتاح خرائط مقيّد بنطاق موقعك | مفتاح OpenAI أو أي API مدفوع |
| مفتاح Stripe العلني pk_ | مفتاح Stripe السري sk_ |
تسمية متغير "سري" بـ VITE_ تنشره للعالم. إن احتاجت الواجهة خدمة بمفتاح سري، فالحل دالة خلفية (Serverless Function) تحمل المفتاح وتستدعي الخدمة نيابة عن الواجهة — وتتحقق من المستخدم وتحدّ عدد الطلبات.
على منصات النشر
لا ترفع .env؛ أدخل المتغيرات في لوحة المنصة:
| المنصة | المكان |
|---|---|
| Vercel | Project ← Settings ← Environment Variables |
| Netlify | Site configuration ← Environment variables |
| Render / Railway | تبويب Environment للخدمة |
| GitHub Actions | Settings ← Secrets and variables ← Actions |
- يمكن تعيين قيم مختلفة لبيئات الإنتاج و المعاينة و التطوير.
- بعد تغيير متغير يجب إعادة النشر: متغيرات الواجهة تُدمج عند البناء، وتغييرها لا يؤثر في نسخة مبنية سابقاً.
إذا تسرب سر
1. ألغِه فوراً من لوحة الخدمة وأنشئ بديلاً (rotate).
2. حدّث القيمة في المنصة وأعد النشر.
3. راجع سجلات الخدمة بحثاً عن استخدام غير معتاد.
4. أزل الملف من التتبع وأضفه إلى .gitignore. حذفه من التاريخ (أدوات مثل git filter-repo) خطوة إضافية، لكنها لا تغني عن الإلغاء.
تمرين
في مشروع Vite: أنشئ .env فيه VITE_APP_NAME واعرضه في الصفحة، ثم ابنِ المشروع (npm run build) وابحث عن القيمة داخل مجلد dist. أنشئ .env.example، وتحقق أن .env في .gitignore، ثم أضف المتغير في لوحة Vercel أو Netlify وأعد النشر.
// config.js — نقطة واحدة لقراءة الإعدادات والتحقق منها (Node.js)
// التشغيل: node --env-file=.env config.js
const REQUIRED = ["DATABASE_URL", "SESSION_SECRET"];
function loadConfig(env = process.env) {
const missing = REQUIRED.filter((name) => !env[name]);
if (missing.length > 0) {
throw new Error(`متغيرات بيئة ناقصة: ${missing.join(", ")}`);
}
return {
port: Number(env.PORT) || 3000,
isProduction: env.NODE_ENV === "production",
databaseUrl: env.DATABASE_URL,
sessionSecret: env.SESSION_SECRET,
};
}
// لا تطبع الأسرار أبداً في السجلات — اعرض أسماءها فقط
function describe(config) {
return {
port: config.port,
isProduction: config.isProduction,
databaseUrl: config.databaseUrl ? "[مخفي]" : "غير معرّف",
sessionSecret: config.sessionSecret ? "[مخفي]" : "غير معرّف",
};
}
try {
// قيم تجريبية لعرض المثال (في الواقع تأتي من .env أو لوحة المنصة)
const config = loadConfig({ DATABASE_URL: "postgres://demo", SESSION_SECRET: "demo-secret", PORT: "8080" });
console.log("الإعدادات:", describe(config));
loadConfig({ PORT: "8080" });
} catch (error) {
console.log("❌", error.message);
}اختبر فهمك