انشر الوكالة
ضع الموقع ولوحة التحكم وواجهة API على الإنترنت: Docker Compose على خادم واحد، أو تطبيق واحد لكل مضيف من Dockerfile، أو Node.js عادي.
لحزمة الحزمة الكاملة
ثلاث طرق للنشر
| الطريقة | مناسبة لـ | ما تحتاجه |
|---|---|---|
| Docker Compose على خادم واحد | خادم VPS تتحكم فيه، والتطبيقات الثلاثة معًا | الحزمة الكاملة، وDocker على الخادم، وخادم وكيل عكسي لـ HTTPS |
| تطبيق واحد لكل مضيف، من Dockerfile الخاص به | Railway وRender وFly.io ومضيفات أخرى تبني من Dockerfile | مجلد كل تطبيق؛ ووحدة تخزين أو MySQL وحاوية تخزين لواجهة API |
| Node.js عادي | أي مضيف Node | Node.js 22 أو أحدث وYarn 4 على المضيف |
مهما كانت الطريقة التي تختارها، يدمج الموقع ولوحة التحكم عنوان واجهة API في JavaScript عند بنائهما، لذلك ابنهما بالعناوين العامة وليس المحلية أبدًا.
Docker Compose على خادم واحد
انسخ مجلد الحزمة إلى الخادم وضع ملف .env بجانب docker-compose.yml يحوي العناوين العامة الثلاثة. تُبنى منها الأصول المسموح بها لواجهة API، وعنوان لوحة التحكم، وعناوين الصور، وعنوان واجهة API في الواجهتين الأماميتين.
SITE_URL=https://www.your-agency.com
ADMIN_URL=https://admin.your-agency.com
API_URL=https://api.your-agency.com
MEDIA_HOSTNAME=api.your-agency.comMEDIA_HOSTNAME اختياري، مضيف واحد أو عدة مضيفات مفصولة بفواصل (أضف مضيف حاوية التخزين عندما تذهب الملفات المرفوعة إليها): تُغيَّر أحجام الصور على مضيفات https هذه بواسطة Next.js، وتُحمَّل أي صورة أخرى كما هي. ثم ابنِ وشغّل في الخلفية:
docker compose up --build -d- وجّه كل نطاق إلى الخادم عبر خادم وكيل عكسي يقدّم HTTPS (Caddy أو Nginx أو Traefik)، ويحوّل إلى المنافذ
3030و3031و8000. - خلف ذلك الوكيل، اضبط
TRUST_PROXY=1فيback-end/.env، لتحسب حدود تسجيل الدخول ونموذج التواصل وطلب المعاينة كل زائر على حدة وليس الوكيل. تقرأ حاوية واجهة API الملفback-end/.envعند وجوده، وهناك أيضًا تُوضع المفاتيح الاختيارية. - بدون
JWT_SECRETخاص بك، تولّد واجهة API واحدًا عند أول تشغيل وتحفظه في وحدة التخزينvelra-data. وقاعدة البيانات والملفات المرفوعة في وحدة التخزين نفسها أيضًا، لذلك انسخها احتياطيًا. - يملأ التشغيل الأول الوكالة التجريبية ما لم يكن
SEED_DEMO_DATA=falseفي.env. أبقِه في التشغيل الأول: فالبيانات الأولية هي التي تنشئ الحسابات.
واجهة API من Dockerfile
وجّه المضيف إلى مجلد واجهة API (back-end/ في الحزمة الكاملة). تستمع الصورة على PORT (8000 ما لم يحدد المضيف غيره)، وتردّ على GET /api/health بمجرد أن تستجيب قاعدة بياناتها، وتحفظ SQLite والملفات المرفوعة في /data.
- اربط وحدة تخزين دائمة بالمسار
/data. بدونها يبدأ كل نشر على قاعدة بيانات فارغة. - اضبط
CORS_ORIGINعلى عنوانَي الموقع ولوحة التحكم (مفصولين بفاصلة)، وFRONTEND_URLعلى عنوان لوحة التحكم، وPUBLIC_MEDIA_URLعلى العنوان العام لواجهة API متبوعًا بـ/media. - اضبط
SEED_DEMO_DATA=trueفي أول تشغيل على وحدة تخزين جديدة: القيمة الافتراضية للصورة هيfalse، وهي تنشئ الجداول بلا حسابات. ولا تُملأ قاعدة بيانات موجودة بالبيانات الأولية مرة أخرى أبدًا. - اضبط
JWT_SECRETخاصًا بك، أو دع الحاوية تولّد واحدًا وتحفظه في/data/.jwt-secret. - اضبط
TRUST_PROXY=1: فمنصة الاستضافة تضع وكيلًا أمام الحاوية. - على MySQL (
DB_TYPE=mysqlومتغيراتDB_*الخمسة) لا تنشئ الحاوية الجداول. شغّلnode dist/database/seeder.js(الوكالة التجريبية) أوnode dist/database/sync-schema.js(جداول فارغة) مرة واحدة بالبيئة نفسها. - تبقى الملفات المرفوعة على وحدة التخزين بعد إعادة النشر؛ ولإبعادها عن الخادم اضبط متغيرات
R2_*الخمسة لحاوية تخزين.
الموقع ولوحة التحكم من Dockerfile الخاص بكل منهما
وجّه المضيف إلى مجلد التطبيق وأعطه قيم NEXT_PUBLIC_* على أنها معاملات بناء، وليس متغيرات تشغيل: فهي تُدمج عند البناء. تقدّم كل صورة خادم Next.js المستقل على PORT (3030 للموقع و3031 للوحة التحكم، ما لم يحدد المضيف غيرهما).
| معامل البناء | التطبيق | القيمة |
|---|---|---|
NEXT_PUBLIC_API_BASE_URL | كلاهما | العنوان العام لواجهة API متبوعًا بـ /api |
NEXT_PUBLIC_SITE_URL | كلاهما | العنوان العام للموقع: الروابط الأساسية وخريطة الموقع في الموقع، ورابط «عرض الموقع» في لوحة التحكم |
NEXT_PUBLIC_MEDIA_HOSTNAME | كلاهما | اختياري. مضيفات https التي تقدّم الصور (نطاق واجهة API، أو نطاق حاوية التخزين، أو كلاهما)، مفصولة بفواصل |
NEXT_PUBLIC_WEBSOCKET_BASE_URL | لوحة التحكم | العنوان العام لواجهة API دون /api، للإشعارات المباشرة |
يجلب خادم الموقع أيضًا من واجهة API أثناء عرض صفحات العقارات وخريطة الموقع. مع عنوان عام لواجهة API يعمل من الخادم أيضًا، لا يلزم شيء آخر؛ ويتغلب عليه API_INTERNAL_URL وقت التشغيل عندما يجب أن يستخدم الخادم عنوانًا آخر.
Node.js عادي
ابنِ كل تطبيق مرة واحدة على المضيف، ثم شغّله. اضبط .env الخاص بالواجهتين الأماميتين على العناوين العامة قبل yarn build.
| التطبيق | البناء | التشغيل |
|---|---|---|
| API | yarn build، ثم مرة واحدة yarn seed:prod (الوكالة التجريبية) أو yarn db:sync:prod (جداول فارغة) | yarn start:prod |
| الموقع العام | yarn build | yarn start (المنفذ 3030) |
| لوحة تحكم الوكالة | yarn build | yarn start (المنفذ 3031) |
تربط واجهة API العنوان 0.0.0.0 وتقرأ PORT. أبقِ ملف SQLite وMEDIA_UPLOAD_DIR على تخزين يبقى بعد النشر، أو استخدم MySQL وحاوية تخزين.
حساباتك الأولى
لا توجد صفحة تسجيل: حسابا لوحة التحكم يأتيان من البيانات الأولية. ابدأ أول مرة بالوكالة التجريبية، وسجّل الدخول، ثم اجعلها لك:
- غيّر بيانات تسجيل الدخول للحسابين في الإعدادات ثم الأمان، أو أنشئ مديرًا عامًا (Super Admin) خاصًا بك في إدارة المسؤولين واحذف الحسابين الأوليين.
- أرشف العقارات التجريبية أو احذفها، واحذف الأحياء التجريبية التي لا تحتاج إليها.
- أغلق الاستفسارات التجريبية وألغِ المعاينات التجريبية: فلا يمكن حذفها.
- اضبط اسم الوكالة وعبارتها التعريفية ووصفها وبريد التواصل في الإعدادات ثم الوكالة، والمنازل المميزة في الصفحة الرئيسية في الإعدادات ثم الصفحة الرئيسية.
الإطلاق
قبل أن تصبح الوكالة متاحة من الإنترنت:
- في بيئة واجهة API،
NODE_ENV=productionوJWT_SECRETطويل وعشوائي خاص بك (يولّد Docker واحدًا عند عدم ضبطه). في الإنتاج ترفض واجهة API البدء بدونCORS_ORIGIN، وتذكر القيمة الناقصة. CORS_ORIGINعلى عنوانَي الموقع ولوحة التحكم، وFRONTEND_URLعلى عنوان لوحة التحكم، وPUBLIC_MEDIA_URLعلى عنوان واجهة API متبوعًا بـ/media.- قاعدة البيانات والملفات المرفوعة على تخزين دائم، مع نسخة احتياطية.
- فحص الصحة الخاص بالمضيف على
/api/health، وTRUST_PROXY=1خلف وكيل. - بناء الواجهتين الأماميتين بقيم
NEXT_PUBLIC_*العامة. - في الموقع، علامتك التجارية في
src/config/brand.config.ts، وسياسة الخصوصية فيmessages/privacyمعاد كتابتها لوكالتك ولبلدك. - تغيير بيانات تسجيل الدخول للحسابين الأوليين، أو حذفهما.
DEMO_MODE=falseعلى واجهة API وNEXT_PUBLIC_DEMO_MODE=falseفي الواجهتين الأماميتين، كما تُشحن.
ليس لوكالة لها بيانات حقيقية
يبني yarn setup:fresh ويحذف كل جدول ويملأ البيانات الأولية في كل مرة يُشغَّل فيها، ويحذف yarn db:reset كل جدول. يناسبان بيئة جديدة تمامًا، وليس أمر تشغيل أو بناء أبدًا.