انشر Northstack
ضع الموقع ولوحة التحكم وواجهة API على الإنترنت: Docker Compose على خادم واحد، أو تطبيق واحد لكل مضيف من Dockerfile الخاص به، أو Node.js مباشرة، مع البدء فارغًا ومسؤول منصة خاص بك، وتحديث الجداول بعد كل تحديث.
لحزمة الحزمة الكاملة
ثلاث طرق للنشر
| الطريقة | مناسبة لـ | ما تحتاجه |
|---|---|---|
| Docker Compose على خادم واحد | خادم VPS تتحكم فيه، والتطبيقات الثلاثة معًا | الحزمة الكاملة، وDocker على الخادم، وخادم وكيل عكسي لـ HTTPS |
| تطبيق واحد لكل مضيف، من Dockerfile الخاص به | Railway وRender وFly.io ومضيفات أخرى تبني من Dockerfile | مجلد كل تطبيق؛ ووحدة تخزين أو MySQL، واختياريًا حاوية تخزين (bucket)، لواجهة API |
| Node.js عادي | أي مضيف Node | Node.js 22.13 أو إصدار أحدث من 22، أو 24.11 أو أحدث، وYarn 4 على المضيف |
مهما كانت الطريقة التي تختارها، يدمج الموقع ولوحة التحكم عنوان واجهة API في JavaScript عند بنائهما، لذلك ابنهما بالعناوين العامة وليس المحلية أبدًا.
Docker Compose على خادم واحد
انسخ مجلد الحزمة إلى الخادم وضع ملف .env بجانب docker-compose.yml يحمل العناوين العامة الثلاثة. منها تُبنى المصادر المسموح بها في واجهة API، والروابط التي تكتبها في الدعوات وإعادة تعيين بيانات الدخول وعودة الدفع، وروابط تنزيل الفواتير، وعنوان الوسائط، وعنوان واجهة API في الواجهتين الأماميتين.
SITE_URL=https://www.your-domain.com
ADMIN_URL=https://app.your-domain.com
API_URL=https://api.your-domain.comثم ابنِ وشغّل في الخلفية:
docker compose up --build -d- وجّه كل نطاق إلى الخادم عبر خادم وكيل عكسي يقدّم HTTPS (Caddy أو Nginx أو Traefik)، ويحوّل إلى المنافذ
3030و3031و8000. - انسخ
back-end/.env.exampleإلىback-end/.envلمفاتيح الدفع والبريد والتخزين والذكاء الاصطناعي وMCP: تقرؤه حاوية واجهة API إذا وُجد، ويحتفظdocker-compose.ymlبالقيم التي يجب أن تختلف داخل الحاوية. خلف الوكيل اضبطTRUST_PROXY=1هناك، ليحسب حد تسجيل الدخول كل زائر على حدة بدلًا من الوكيل. - من دون
JWT_SECRETخاص بك، تولّد واجهة API واحدًا عند أول تشغيل وتحفظه في وحدة التخزينnorthstack-data. وقاعدة البيانات والملفات المرفوعة في وحدة التخزين هذه أيضًا، فخذ منها نسخة احتياطية. - لتثبيت حقيقي، ابدأ وحدة التخزين الجديدة فارغة: ضع
SEED_SAMPLE_DATA=falseفي ملف.envبجانبdocker-compose.yml(وليسback-end/.env: يمرّر Compose هذه القيم الثلاث من هناك)، معADMIN_EMAILوADMIN_PASSWORDلأول مسؤول منصة لديك، قبل أولdocker compose up. أما ملف.envبجانبdocker-compose.ymlفلا يصل إلى واجهة API لهذا الغرض. راجع «البدء فارغًا» أدناه. - إذا تُركت على قيمتها الافتراضية (
SEED_SAMPLE_DATA=true)، فإن أول تشغيل على وحدة تخزين جديدة يملأ مساحات العمل التجريبية وحساباتها، وكلها بكلمة المرور المنشورة. اجعلها ملكك قبل أن يصبح المشروع متاحًا، كما في «البدء من البيانات التجريبية» أدناه.
واجهة API من Dockerfile
وجّه المضيف إلى مجلد واجهة API (back-end/ في الحزمة الكاملة). تعمل الصورة بـ NODE_ENV=production، وتستمع على PORT (القيمة 8000 ما لم يضبط المضيف غيرها)، وتجيب GET /api/health حين تحمل قاعدة بياناتها المخطط، وتحفظ SQLite والملفات المرفوعة في /data.
- اربط وحدة تخزين دائمة على
/data. من دونها يبدأ كل نشر على قاعدة بيانات جديدة. - اضبط
SEED_SAMPLE_DATA=falseمعADMIN_EMAILوADMIN_PASSWORD، لتبدأ وحدة التخزين الجديدة بمسؤول منصة خاص بك ومن دون بيانات تجريبية. وإذا تُركت علىtrue، وهي القيمة الافتراضية، فإن وحدة التخزين الجديدة تُملأ بالحسابات التجريبية وكلمة المرور المنشورة لها. - اضبط
CORS_ORIGINعلى عنواني الموقع ولوحة التحكم (مفصولين بفاصلة)، وFRONTEND_URLوDASHBOARD_URLعلى عنوان لوحة التحكم، وWEBSITE_URLعلى عنوان الموقع، وPUBLIC_API_URLعلى العنوان العام لواجهة API، وPUBLIC_MEDIA_URLعلى ذلك العنوان متبوعًا بـ/media. - على SQLite، تُنشأ وحدة التخزين التي لا قاعدة بيانات فيها مرة واحدة، كما يقول
SEED_SAMPLE_DATA؛ أما القاعدة الموجودة فلا تُملأ مرة أخرى أبدًا ولا تتغير جداولها عند التشغيل (راجع «التحديث» أدناه). - اضبط
JWT_SECRETخاصًا بك، أو دع الحاوية تولّد واحدًا وتحفظه في/data/.jwt-secret. - اضبط
TRUST_PROXY=1: فمنصة الاستضافة تضع وكيلًا أمام الحاوية. - على MySQL (
DB_TYPE=mysqlومتغيراتDB_*الخمسة)، لا تنشئ الحاوية الجداول. شغّلnode dist/database/create-admin.jsمرة واحدة بالبيئة نفسها (أوnode dist/database/seeder.jsلتثبيت تجريبي). - تبقى الملفات المرفوعة على وحدة التخزين بعد إعادة النشر؛ ولإبعادها عن الخادم اضبط متغيرات
R2_*الخمسة لحاوية تخزين.
الموقع ولوحة التحكم من Dockerfile الخاص بكل منهما
وجّه المضيف إلى مجلد التطبيق وأعطه قيم NEXT_PUBLIC_* على أنها معاملات بناء، وليس متغيرات تشغيل: فهي تُدمج عند البناء. تقدّم كل صورة خادم Next.js المستقل على PORT (3030 للموقع و3031 للوحة التحكم، ما لم يحدد المضيف غيرهما).
| معامل البناء | التطبيق | القيمة |
|---|---|---|
NEXT_PUBLIC_API_BASE_URL | كلاهما | العنوان العام لواجهة API متبوعًا بـ /api. إذا تُرك على قيمته الافتراضية http://localhost:8000/api، فإن التطبيق المنشور يستدعي جهاز الزائر نفسه. |
NEXT_PUBLIC_SITE_URL | الموقع | العنوان العام للموقع: الروابط الأساسية، وبدائل اللغات، وخريطة الموقع، وبطاقات المشاركة |
NEXT_PUBLIC_DASHBOARD_URL | الموقع | العنوان العام للوحة التحكم، الذي تفتحه «جرّب العرض الحي» و«استكشف العرض الحي» و«اطّلع على إدارة الأدوار» وزر تسجيل الدخول في /error/401 وعناوين /auth/* في الموقع |
NEXT_PUBLIC_BASE_URL | لوحة التحكم | العنوان العام للوحة التحكم: robots.txt وخريطة الموقع والبيانات الوصفية |
NEXT_PUBLIC_WEBSITE_URL | لوحة التحكم | العنوان العام للموقع، لروابط التوثيق والخصوصية والشروط |
NEXT_PUBLIC_WEBSOCKET_BASE_URL | لوحة التحكم | اختياري. العنوان العام لواجهة API من دون /api، لتحديثات الصلاحيات المباشرة؛ وإذا ترك فارغًا استخدمت لوحة التحكم NEXT_PUBLIC_API_BASE_URL من دون /api |
يقرأ خادم الموقع واجهة API أيضًا حين يعرض الدفع وإيصاله. وإذا كان عنوان واجهة API العام يعمل من الخادم أيضًا فلا حاجة إلى شيء آخر؛ ويتجاوزه API_INTERNAL_URL وقت التشغيل حين يجب أن يستخدم الخادم عنوانًا آخر.
Node.js عادي
ابنِ كل تطبيق مرة واحدة على المضيف، ثم شغّله. اضبط .env الخاص بالواجهتين الأماميتين على العناوين العامة قبل yarn build.
| التطبيق | البناء | التشغيل |
|---|---|---|
| API | yarn build، ثم مرة واحدة على قاعدة بيانات جديدة yarn admin:create:prod | yarn start:prod |
| لوحة تحكم مساحة العمل | yarn build | yarn start (المنفذ 3031، أو PORT في .env الخاص بها) |
| الموقع العام | yarn build | yarn start (المنفذ 3030) |
ترتبط واجهة API بالعنوان 0.0.0.0 وتقرأ PORT. أبقِ ملف SQLite وMEDIA_UPLOAD_DIR على تخزين يبقى بعد النشر، أو استخدم MySQL وحاوية تخزين. في الإنتاج لا تنشئ واجهة API الجداول ولا تغيّرها بنفسها: ينشئها yarn admin:create:prod مع أول مسؤول منصة لديك، ويحدّثها yarn schema:prod بعد التحديث.
البدء فارغًا، مع مسؤول منصة خاص بك
يبدأ التثبيت الحقيقي من دون بيانات تجريبية ومع حساب واحد لك. في مجلد واجهة API، بعد yarn build:
yarn admin:create:prod- ينشئ الجداول وأدوار المنصة وإعداداتها، وأول مسؤول منصة من
ADMIN_EMAILوADMIN_PASSWORD(من 8 إلى 128 حرفًا) في البيئة أوback-end/.env. ويسألك في الطرفية عن أيٍّ منهما كان فارغًا، وADMIN_NAMEاختياري. - لا ينشئ مساحة عمل ولا حسابًا تجريبيًا، ويرفض إذا كان هناك مسؤول منصة بالفعل. سجّل الدخول بهذا الحساب وأنشئ مساحة عملك في الإعداد الأولي.
- يفعل
yarn admin:createالشيء نفسه من المصدر، من دون بناء. - مع Docker، يفعل
SEED_SAMPLE_DATA=falseهذا عند أول تشغيل: إذا ضُبطADMIN_EMAILوADMIN_PASSWORDأنشأ المسؤول، وإلا أنشأ الجداول فقط وذكر سجله الأمر التالي الذي تشغّله،docker compose exec -u node api node dist/database/create-admin.js(ومعdocker run،docker exec -it -u node <container> node dist/database/create-admin.js).
لا تملأ خادمًا عامًا بالبيانات التجريبية أبدًا
yarn seed:prod وyarn seed لتثبيت تجريبي فقط: يحمّلان الحسابات التجريبية بكلمة المرور المنشورة في هذا الدليل. يستطيع أي شخص يقرؤه تسجيل الدخول بصفته مسؤول المنصة لديك.
التحديث: الجداول بعد التحديث
لا توجد ترحيلات، وفي الإنتاج لا تغيّر واجهة API الجداول بنفسها. بعد تحديث يغيّر كوده الجداول، خذ نسخة احتياطية من قاعدة البيانات، وابنِ، وارفع الجداول إلى مستوى الكود الجديد قبل تشغيل واجهة API:
yarn build
yarn schema:prod
yarn start:prod- لا يكتب
yarn schema:prodأي بيانات. يضيف ما هو جديد، ويحذف أيضًا العمود الذي لم يعد في الكود مع بياناته، لذلك تأتي النسخة الاحتياطية أولًا. - مع Docker تُترك وحدة التخزين الموجودة كما هي عند التشغيل. وحين تعمل الصورة الجديدة، طبّق المخطط فيها:
docker compose exec -u node api node dist/database/schema.js(ومعdocker run،docker exec -u node <container> node dist/database/schema.js).
البدء من البيانات التجريبية
كل عملية ملء بيانات تنشئ مساحات العمل التجريبية وأشخاصها، وتسجيلات الدخول الستة الموثقة وكل حساب تجريبي آخر بكلمة المرور المنشورة نفسها، وتملأ صورة Docker وحدة التخزين الجديدة بنفسها ما لم يكن SEED_SAMPLE_DATA=false. إذا بدأت من البيانات التجريبية، فاجعل التثبيت ملكك قبل أن يصبح متاحًا من الإنترنت:
- اجعل مسؤول المنصة التجريبي ملكك: سجّل الدخول بـ
platform@northstack.example، وغيّر اسمه وبريده في «الإعدادات» ثم «الملف الشخصي»، وكلمة المرور في «الإعدادات» ثم «الأمان». - لمساحة عملك الخاصة، أنشئ حسابًا في لوحة التحكم ببريدك أنت: ينشئ الإعداد الأولي مساحة العمل وتكون أنت مالكها. أو اجعل المالك التجريبي ملكك بالطريقة نفسها، وغيّر اسم Forma Studio في «الإعدادات» ثم «عام».
- في صفحة «المستخدمون» بالمنصة، احذف الحسابات التجريبية التي لا تحتاج إليها، أو استخدم تغيير كلمة المرور لأي حساب تُبقيه. يُرفض الحذف لحساب يملك مساحة عمل إلى أن يملكها عضو آخر (من صفحة «الفريق» في مساحة العمل تلك) أو تُحذف مساحة العمل («الإعدادات»، ثم «منطقة الخطر»).
- اضبط
JWT_SECRETخاصًا بك، حتى يتوقف عمل رمز تسجيل دخول صُنع قبل التبديل.
لا ينشئ تشغيل الملء الحسابات التجريبية ومساحات العمل إلا في قاعدة بيانات ليس فيها حساب بعد. وبعد وجود حسابات، لا يضيف تشغيل yarn seed أو yarn seed:prod مرة أخرى حسابًا ولا مساحة عمل، ويعيد فقط إلى أدوار المنصة المدمجة صلاحياتها، فلا يعود حساب تجريبي غيّرت اسمه أو حذفته بكلمة المرور المنشورة.
الإطلاق
قبل أن يصبح Northstack متاحًا من الإنترنت:
- في بيئة واجهة API،
NODE_ENV=productionوJWT_SECRETطويل وعشوائي خاص بك (يولّد Docker واحدًا إذا لم يُضبط). في الإنتاج ترفض واجهة API أن تبدأ بالسر النموذجي أو من دونCORS_ORIGIN، وتذكر أي قيمة خاطئة. CORS_ORIGINعلى الموقع ولوحة التحكم، وFRONTEND_URLوDASHBOARD_URLعلى لوحة التحكم، وWEBSITE_URLعلى الموقع، وPUBLIC_API_URLعلى واجهة API، وPUBLIC_MEDIA_URLعلى واجهة API متبوعة بـ/media.- مفتاح Stripe الحي وويب هوك الخاص به على
/api/billing/webhooks/stripeفي عنوان واجهة API، معSTRIPE_WEBHOOK_SECRET: ومن دونه تذكر واجهة API ذلك في سجلها عند التشغيل وترفض كل إشعار، فلا تُسجَّل التجديدات ولا المدفوعات الفاشلة أبدًا. وفي PayPal، مفاتيح التطبيق الحي معPAYPAL_ENV=live. - الضريبة مضبوطة لبلدك: لا تفرض Northstack أي ضريبة بنفسها.
- إعدادات
SMTP_*، وإلا لن يتلقى أحد الدعوات ولا إعادة تعيين بيانات الدخول ولا إيصالات التراخيص. - قاعدة البيانات والملفات المرفوعة على تخزين دائم، مع نسخة احتياطية.
- فحص الصحة الخاص بالمضيف على
/api/health، وTRUST_PROXY=1خلف وكيل. - بناء الواجهتين الأماميتين بقيم
NEXT_PUBLIC_*العامة. - علامتك التجارية في كل واجهة أمامية، والصفحات القانونية للموقع مكتوبة من جديد لعملك وبلدك.
- لا حسابات تجريبية: البدء فارغًا بـ
yarn admin:create:prodأوSEED_SAMPLE_DATA=false، أو حذف الحسابات التجريبية أو تغيير كلمات مرورها، كما سبق. DEMO_MODE=falseعلى واجهة API وNEXT_PUBLIC_DEMO_MODE=falseفي الواجهتين الأماميتين، كما تُشحن.