متغيرات البيئة
ما يفعله كل إعداد في ملفات .env الخاصة بواجهة API والواجهتين الأماميتين، وأيها تحتاج إليه.
لحزمة الحزمة الكاملة
أين توجد الإعدادات
| الملف | يقرؤه | يحتوي على أسرار |
|---|---|---|
back-end/.env | واجهة API، مع yarn dev وداخل Docker | نعم. لا تضفه إلى المستودع أبدًا. |
frontend/.env | موقع الطلاب، أثناء البناء | لا. كل القيم عامة. |
admin-dashboard/.env | لوحة الإدارة، أثناء البناء | لا. كل القيم عامة. |
.env بجوار docker-compose.yml | Docker Compose، للحزمة الكاملة | لا |
أنشئ كل ملف من .env.example المجاور له، والذي يوثّق كل متغير: cp .env.example .env. تعمل الأمثلة كما هي للتشغيل المحلي.
أين توجد الإعدادات
ملف واحد، frontend/.env، يُقرأ عند بناء الموقع. كل القيم فيه عامة، لذا لا يحتوي على أي سر أبدًا. أنشئه من .env.example، الذي يوثّق كل متغير: cp .env.example .env.
أين توجد الإعدادات
ملف واحد، admin-dashboard/.env، يُقرأ عند بناء لوحة الإدارة. كل القيم فيه عامة، لذا لا يحتوي على أي سر أبدًا. لا تنشئه إن أردت العمل على المحاكاة؛ وأنشئه من .env.example عندما تربط واجهة API: cp .env.example .env.
أين توجد الإعدادات
ملف واحد، back-end/.env، تقرؤه واجهة API مع yarn dev وداخل Docker. يحتوي على أسرار: لا تضفه إلى المستودع أبدًا. أنشئه من .env.example، الذي يوثّق كل متغير ويعمل كما هو للتشغيل المحلي: cp .env.example .env.
أساسيات واجهة API
تتحقق واجهة API من هذه المتغيرات قبل أن تبدأ. إذا كان أحدها مفقودًا أو غير صالح، تتوقف وتعرض قائمة قصيرة بما يجب إصلاحه.
| المتغير | وظيفته |
|---|---|
NODE_ENV | development محليًا، وproduction على خادم مباشر. |
PORT | منفذ واجهة API، وهو 8000. تشير إليه الواجهتان الأماميتان. |
DB_TYPE | sqlite (الافتراضي) أو mysql. |
SQLITE_DATABASE | مسار ملف SQLite، وهو ./database.sqlite افتراضيًا. |
JWT_SECRET | يوقّع كل جلسة تسجيل دخول. مطلوب. القيمة المثال مقبولة في بيئة التطوير فقط. |
JWT_EXPIRATION | مدة صلاحية تسجيل الدخول، وهي 7d افتراضيًا. |
CORS_ORIGIN | عنوانا موقع الطلاب ولوحة الإدارة، مفصولين بفاصلة. مطلوب في بيئة الإنتاج. |
FRONTEND_URL | عنوان موقع الطلاب، ويُستخدم في الروابط التي ترسلها واجهة API. |
ولّد JWT_SECRET خاصًا بك بالأمر:
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"استخدم MySQL بدلًا من SQLite
أنشئ قاعدة بيانات فارغة، ثم اضبط برنامج التشغيل والاتصال في back-end/.env:
DB_TYPE=mysql
DB_HOST=your-mysql-host
DB_PORT=3306
DB_USERNAME=your-mysql-username
DB_PASSWORD=your-mysql-password
DB_DATABASE=your-database-nameثم شغّل yarn seed للبيانات التجريبية، أو yarn db:sync على موقع مباشر، وهو ينشئ الجداول دون كتابة أي صفوف. لا تحدّث واجهة API قيد التشغيل المخطط بنفسها إلا عندما يكون NODE_ENV=development.
موقع الطلاب ولوحة الإدارة
كل قيمة هنا من نوع NEXT_PUBLIC_*، وتُضمَّن في شيفرة JavaScript التي يحمّلها المتصفح. لا تضع أي سر في هذه الملفات أبدًا، وأعد البناء بعد تغيير أي قيمة.
| المتغير | التطبيق | وظيفته |
|---|---|---|
NEXT_PUBLIC_API_BASE_URL | كلاهما | عنوان واجهة API متضمنًا /api، مثل http://localhost:8000/api. إذا تُرك فارغًا، يستخدم التطبيق بياناته النموذجية. |
NEXT_PUBLIC_SITE_URL | كلاهما | العنوان العام لموقع الطلاب، ويُستخدم للروابط الأساسية (canonical) ومعاينات المشاركة وخريطة الموقع. اضبطه على نطاقك الحقيقي في بيئة الإنتاج. |
NEXT_PUBLIC_MEDIA_HOSTNAME | كلاهما | المضيف العام لمخزن الوسائط لديك، من دون https://. اتركه فارغًا حتى يتوفر لديك مخزن. |
NEXT_PUBLIC_WEBSOCKET_BASE_URL | لوحة الإدارة | عنوان واجهة API من دون /api، للإشعارات المباشرة. |
NEXT_PUBLIC_SAMPLE_DATA_NOTICE | كلاهما | اختياري. القيمة always تعرض إشعار "Sample data" في بناء الإنتاج أيضًا عندما تتوقف واجهة API عن الاستجابة. يضبطه ملف docker-compose.yml في الجذر، أما الموقع المنشور فيتركه عادة دون قيمة. |
API_INTERNAL_URL | موقع الطلاب | اختياري، للخادم فقط. العنوان الذي يصل منه خادم الموقع نفسه إلى واجهة API عندما يختلف عن عنوان المتصفح، كما في Docker Compose. |
خيارات Docker Compose
لا حاجة إلى ضبط أي شيء للتشغيل المحلي. لتغيير شيء، ضعه في ملف .env بجوار docker-compose.yml وشغّل docker compose up --build مرة أخرى: فالواجهتان الأماميتان تُضمّنان هذه القيم أثناء البناء.
| المتغير | وظيفته |
|---|---|
SITE_PORT, ADMIN_PORT, API_PORT | المنافذ على جهازك: 3030 و3031 و8000 افتراضيًا. اضبط أحدها حين يستخدم برنامج آخر ذلك المنفذ، مثل ADMIN_PORT=3041. وتتبعها العناوين أدناه وCORS_ORIGIN وFRONTEND_URL. |
SITE_URL, ADMIN_URL, API_URL | العنوان الذي يصل منه المتصفح إلى كل تطبيق. اضبط الثلاثة عند تشغيل التطبيقات على نطاقاتك الخاصة. |
MEDIA_HOSTNAME | المضيف العام لمخزنك، مع متغيرات R2_* في back-end/.env. |
SEED_DEMO_DATA | القيمة false تبدأ بجداول فارغة بدلًا من البيانات التجريبية. |
تقرأ حاوية واجهة API أيضًا back-end/.env عند وجوده، فتُضبط مفاتيح البريد والمدفوعات والوسائط والذكاء الاصطناعي في مكان واحد لكل من yarn dev و Docker.
تخزين الوسائط
تُرفع صور الدورات والصور الرمزية وصور المدونة وفيديوهات الدروس والمرفقات إلى مخزن Cloudflare R2، أو أي مخزن متوافق مع S3. اضبط المتغيرات الخمسة في back-end/.env وأعطِ الواجهتين الأماميتين المضيف العام للمخزن عبر NEXT_PUBLIC_MEDIA_HOSTNAME.
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_ENDPOINT=
R2_BUCKET_NAME=
R2_PUBLIC_URL=من دون مخزن، يحفظ سكربت البيانات التجريبية مسارات لملفات نموذجية ترفقها الواجهتان الأماميتان بنفسيهما، فتعمل كل صورة وكل فيديو درس محليًا. يتوقف رفع الوسائط الجديدة فقط.
البريد الإلكتروني
تُرسَل رسائل تأكيد البريد، وإعادة تعيين كلمة المرور، والإيصالات، وتأكيدات التسجيل عبر Resend. ومن دون RESEND_API_KEY يواصل التطبيق العمل ويكتب كل رسالة في سجله.
RESEND_API_KEY=
MAIL_FROM=Learnio <noreply@example.com>
MAIL_MAX_PER_ADDRESS_PER_DAY=5
MAIL_MAX_PER_SENDER_PER_DAY=20- استخدم مفتاحًا بصلاحية الإرسال فقط، لا مفتاحًا بصلاحية كاملة.
- يجب أن يكون
MAIL_FROMعلى نطاق تحققت منه في Resend، وإلا رُفض كل إرسال. - يحدد الحدّان عدد الرسائل التي يمكن أن يستقبلها عنوان واحد، وعدد الرسائل التي يمكن أن يطلقها مُرسِل واحد، في اليوم.
- اسم المنتج داخل كل رسالة وعنوان الرد عليها ليسا متغيرين: إنهما الإعدادان
site_nameوsupport_emailفي تبويب App Settings ضمن Settings في لوحة الإدارة، ويُقرآن لكل رسالة تُرسَل.
المدفوعات
افتراضيًا، تعمل صفحة الدفع على محاكي مدمج: لا يُتصل بأي معالج دفع ولا يُخصم أي مبلغ. يُرفض رقم البطاقة المنتهي بـ 0، لتجرّب مسار الفشل.
املأ مفاتيح معالج دفع لتنتقل طريقة الدفع تلك إلى المعالج الحقيقي. اضبط Stripe و PayPal كليهما قبل استقبال طلبات حقيقية: فصفحة الدفع تعرض دائمًا البطاقة و PayPal، والطلب المدفوع عبر المحاكي يكتمل دون خصم أي مبلغ من أحد.
لا تحتاج الدورات المجانية (السعر 0) إلى أي معالج دفع: يكتمل دفعها على واجهة API من دونه، أيًّا كانت المفاتيح المضبوطة.
| معالج الدفع | المتغيرات | نقطة نهاية الـ webhook |
|---|---|---|
| Stripe | STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET | POST <api>/api/webhooks/payments/stripe |
| PayPal | PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_WEBHOOK_ID, PAYPAL_ENV | POST <api>/api/webhooks/payments/paypal |
الـ webhook هو ما يمنح المقعد
يُسجَّل الطالب في الدورة عندما يؤكد معالج الدفع العملية عبر الـ webhook، لا عند عودته من صفحة الدفع. من دون الاشتراك في الـ webhook، تبقى الطلبات معلّقة.
- اشترك في أحداث Stripe التالية:
checkout.session.completedوpayment_intent.payment_failedوcharge.refunded. - اشترك في أحداث PayPal التالية:
PAYMENT.CAPTURE.COMPLETEDوPAYMENT.CAPTURE.DENIEDوPAYMENT.CAPTURE.REFUNDEDوPAYMENT.CAPTURE.REVERSED. - قيمة
PAYPAL_ENVهيsandboxافتراضيًا، ولا تأخذ أموالًا حقيقية. بيانات الاعتماد المباشرة تخص تطبيق PayPal مختلفًا، لذا يعني التحول إلى البيئة المباشرة مفاتيح جديدة إضافة إلىPAYPAL_ENV=live.
المساعد الذكي
مُضمَّن مع مشترياتك. سجّل الدخول لقراءته، أو افتحه في ملف التنزيل.
تشغيل المساعد الذكي: مفاتيح المزوّدين، وكيف يُعرض كل نموذج، وما يراه المدير بلا مفتاح.
الحصص المباشرة
مُضمَّن مع مشترياتك. سجّل الدخول لقراءته، أو افتحه في ملف التنزيل.
إعداد الفصول المباشرة: ربط خادم Jitsi Meet، ورموز الدخول الآمنة، وأدوات المضيف.
البيانات النموذجية في الواجهات الأمامية
تستمر الواجهتان الأماميتان في العمل عندما يتعذر الوصول إلى واجهة API: تقدّمان البيانات النموذجية المرفقة وتعرضان إشعار "Sample data" أسفل الصفحة. في بناء الإنتاج لا يظهر الإشعار إلا عندما لا تكون أي واجهة API مضبوطة على الإطلاق، ما لم يُبنَ التطبيق مع NEXT_PUBLIC_SAMPLE_DATA_NOTICE=always كما يفعل ملف docker-compose.yml في الجذر.
- تغطي البيانات النموذجية في لوحة الإدارة تسجيل الدخول، والطلاب، والطاقم، والأدوار، والفئات، والإعدادات، والإشعارات، والبحث، وسجل محادثات المساعد. أما شاشاتها الأخرى (أرقام النظرة العامة، والدورات، والمدرّسون، والتسجيلات، والطلبات، والتقييمات، والمدونة، والجدول، والاختبارات، والرسائل) فتحتاج إلى واجهة API.
- يقبل تسجيل الدخول في لوحة الإدارة أي بريد إلكتروني وأي كلمة مرور على البيانات النموذجية، ويحفظ تغييراتك في المتصفح. لتبدأ من جديد، شغّل
localStorage.removeItem("mock_db_v1"); location.reload();في وحدة تحكم المتصفح. - المساعد الذكي غير متاح على البيانات النموذجية، لأنه يحتاج إلى مفاتيح واجهة API.
- بمجرد أن تصبح واجهة API مباشرة، يحذف
yarn remove:mockفي أي من التطبيقين طبقة البيانات النموذجية.
كيف تعمل طبقة البيانات النموذجية
مُضمَّن مع مشترياتك. سجّل الدخول لقراءته، أو افتحه في ملف التنزيل.
كيف تُوجَّه الطلبات إلى البيانات المرفقة، وكيف تغيّرها أو توسّعها.
وضع العرض التجريبي
مُضمَّن مع مشترياتك. سجّل الدخول لقراءته، أو افتحه في ملف التنزيل.
تشغيل عرض تجريبي عام: مفتاح العرض التجريبي، وحسابات الزوار، وحصة الرسائل، وطريقة إزالة كل ذلك.