انتقل إلى المقال
Aniq-UI

Ticketoryانشر تيكيتوري

انشر تيكيتوري

ضع الموقع ولوحة التحكم وواجهة API على الإنترنت: Docker Compose على خادم واحد، أو تطبيق واحد لكل مضيف من Dockerfile، أو Node.js عادي.

لحزمة الحزمة الكاملة

ثلاث طرق للنشر

الطريقةمناسبة لـما تحتاجه
Docker Compose على خادم واحدخادم VPS تتحكم فيه، والتطبيقات الثلاثة معًاالحزمة الكاملة، وDocker على الخادم، وخادم وكيل عكسي لـ HTTPS
تطبيق واحد لكل مضيف، من Dockerfile الخاص بهRailway وRender وFly.io ومضيفات أخرى تبني من Dockerfileمجلد كل تطبيق؛ ووحدة تخزين أو MySQL وحاوية تخزين لواجهة API
Node.js عاديأي مضيف NodeNode.js 22 أو أحدث وYarn 4 على المضيف

مهما كانت الطريقة التي تختارها، يدمج الموقع ولوحة التحكم عنوان واجهة API في JavaScript عند بنائهما، لذلك ابنهما بالعناوين العامة وليس المحلية أبدًا.

Docker Compose على خادم واحد

انسخ مجلد الحزمة إلى الخادم وضع ملف .env بجانب docker-compose.yml فيه العناوين العامة الثلاثة. منها تُبنى الأصول المسموح بها في واجهة API، والعناوين التي تعيد إليها صفحات الدفع المشترين، وروابط تنزيل التذاكر، وعناوين الصور، وعنوان واجهة API في التطبيقين الأماميين. ويحلّ REVALIDATE_SECRET محل القيمة المحلية الافتراضية التي يتشاركها الموقع وواجهة API لتحديث صفحات الفعاليات فورًا.

.env بجانب docker-compose.yml
SITE_URL=https://www.your-domain.com
ADMIN_URL=https://dashboard.your-domain.com
API_URL=https://api.your-domain.com
MEDIA_HOSTNAME=api.your-domain.com
REVALIDATE_SECRET=a-long-random-string

MEDIA_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 واحدًا عند أول تشغيل وتحفظه في وحدة التخزين ticketory-data. وقاعدة البيانات والملفات المرفوعة وملفات CSV المصدّرة موجودة في وحدة التخزين نفسها، فاحتفظ بنسخة احتياطية منها.
  • أول تشغيل على وحدة تخزين جديدة يضيف المنظّمين التجريبيين ما لم يكن SEED_DEMO_DATA=false في .env. وفعالياتك الخاصة تبدأ بدونهم ومعك مالكك أنت، كما في «ابدأ بمساحة عملك الخاصة» أدناه.

واجهة API من Dockerfile

وجّه المضيف إلى مجلد واجهة API (back-end/ في الحزمة الكاملة). تستمع الصورة على PORT (القيمة 8000 ما لم يضبط المضيف غيرها)، وتجيب عن GET /api/health متى أجابت قاعدة بياناتها، وتحفظ SQLite والملفات المرفوعة وملفات CSV المصدّرة في /data.

  • اربط وحدة تخزين دائمة بالمسار /data. بدونها يبدأ كل نشر على قاعدة بيانات فارغة.
  • اضبط CORS_ORIGIN على عنواني الموقع واللوحة (مفصولين بفاصلة)، وFRONTEND_URL على عنوان اللوحة، وSTOREFRONT_URL على عنوان الموقع، وPUBLIC_API_URL على العنوان العام لواجهة API، وPUBLIC_MEDIA_URL على ذلك العنوان متبوعًا بـ /media.
  • القيمة الافتراضية للصورة هي SEED_DEMO_DATA=false: تحصل وحدة التخزين الجديدة على الجداول فقط، ثم ينشئ أول تشغيل لواجهة API مساحة عملك ومالكها من OWNER_EMAIL وOWNER_PASSWORD (انظر أدناه). اضبط SEED_DEMO_DATA=true بدلًا من ذلك للحصول على المنظّمين التجريبيين. ولا تُملأ قاعدة بيانات موجودة بالبيانات التجريبية مرة أخرى أبدًا.
  • اضبط 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_* الخمسة لحاوية تخزين.
  • اضبط REVALIDATE_SECRET على القيمة العشوائية الطويلة نفسها في واجهة API والموقع، واضبط STOREFRONT_INTERNAL_URL عندما تصل واجهة API إلى الموقع بعنوان غير STOREFRONT_URL.

الموقع ولوحة التحكم من Dockerfile الخاص بكل منهما

وجّه المضيف إلى مجلد التطبيق وأعطه قيم NEXT_PUBLIC_* على أنها معاملات بناء، وليس متغيرات تشغيل: فهي تُدمج عند البناء. تقدّم كل صورة خادم Next.js المستقل على PORT (3030 للموقع و3031 للوحة التحكم، ما لم يحدد المضيف غيرهما).

معامل البناءالتطبيقالقيمة
NEXT_PUBLIC_API_BASE_URLكلاهماالعنوان العام لواجهة API متبوعًا بـ /api
NEXT_PUBLIC_SITE_URLكلاهماالعنوان العام للموقع: العناوين الأساسية وخريطة الموقع على الموقع، ورابط «عرض الموقع» ومعاينات الفعاليات في اللوحة
NEXT_PUBLIC_DASHBOARD_URLكلاهماالعنوان العام للوحة: المكان الذي يرسل إليه رابط «بِع التذاكر» في الموقع المنظّمين، وصورة المشاركة الخاصة باللوحة
NEXT_PUBLIC_MEDIA_HOSTNAMEكلاهمااختياري. مضيفو https الذين يقدّمون الصور (نطاق واجهة API أو نطاق حاويتك أو الاثنان)، مفصولين بفواصل
NEXT_PUBLIC_WEBSOCKET_BASE_URLلوحة التحكمالعنوان العام لواجهة API دون /api، للإشعارات المباشرة

يجلب خادم الموقع أيضًا من واجهة API أثناء عرض صفحات الفعاليات والأماكن والمنظّمين وخريطة الموقع. مع عنوان عام لواجهة API يصلح من الخادم أيضًا، لا حاجة إلى شيء آخر؛ ويتقدم عليه API_INTERNAL_URL وقت التشغيل عندما يجب أن يستخدم الخادم عنوانًا آخر. وREVALIDATE_SECRET متغير وقت تشغيل للموقع أيضًا، وليس وسيط بناء أبدًا.

Node.js عادي

ابنِ كل تطبيق مرة واحدة على المضيف، ثم شغّله. اضبط .env الخاص بالواجهتين الأماميتين على العناوين العامة قبل yarn build.

التطبيقالبناءالتشغيل
APIyarn build، ثم مرة واحدة yarn seed:prod (بيانات تجريبية) أو yarn db:sync:prod (جداول فارغة)yarn start:prod
الموقع العامyarn buildyarn start (المنفذ 3030)
لوحة المنظّمyarn buildyarn start (المنفذ 3031)

تربط واجهة API العنوان 0.0.0.0 وتقرأ PORT. أبقِ ملف SQLite وMEDIA_UPLOAD_DIR على تخزين يبقى بعد النشر، أو استخدم MySQL وحاوية تخزين.

ابدأ بمساحة عملك الخاصة

المنظّمون التجريبيون وبيانات دخولهم للتجربة فقط. لفعالياتك الخاصة ابدأ على قاعدة بيانات جديدة بدونهم: في قاعدة بيانات ليس فيها حساب موظف ولا مساحة عمل، ينشئ أول تشغيل لواجهة API البيانات الأساسية (الصلاحيات التسع والعشرون والأدوار الأربعة وإعدادات الموقع وأنواع الفعاليات الثلاثة)، ومساحة عمل واحدة فارغة ومالكها.

  1. اضبط متغيرات المالك

    في back-end/.env، أو في إعدادات مضيفك لواجهة API. مع Docker Compose ضعها في .env بجانب docker-compose.yml مع SEED_DEMO_DATA=false: يقرأ Compose ذلك الملف أولًا ثم back-end/.env، وتتقدم قيمة الأخير حيث يضبط الاثنان قيمة.

    back-end/.env
    OWNER_EMAIL=you@your-domain.com
    OWNER_PASSWORD=a-long-password-of-your-own
    OWNER_NAME=Your Name
    WORKSPACE_NAME=Your organisation

    النتيجة المتوقعة: OWNER_NAME وWORKSPACE_NAME اختياريان (Owner وTicketory عند عدم ضبطهما)، ويمكن تغييرهما لاحقًا، في ملفك الشخصي وفي الإعدادات ثم عام. تحتاج كلمة المرور إلى 10 أحرف على الأقل، ومع NODE_ENV=production تُرفض قيمة المثال من .env.example وكلمة مرور الحسابات التجريبية.

  2. ابدأ من دون البيانات التجريبية، على قاعدة بيانات جديدة

    من دون Docker، داخل مجلد واجهة API: yarn build، ثم yarn db:sync:prod (وليس yarn seed)، ثم yarn start:prod. مع Docker Compose، أبقِ SEED_DEMO_DATA=false في .env بجانب docker-compose.yml وشغّل docker compose up --build -d على وحدة تخزين جديدة (يحذف docker compose down -v وحدة التخزين الموجودة بكل بياناتها). وتبدأ حاوية واجهة API وحدها من دون -e SEED_DEMO_DATA=true، بالمتغيرات الممرَّرة بـ --env-file .env أو -e.

    النتيجة المتوقعة: يقول سجل واجهة API: Clean start: created the workspace "Your organisation" and its Owner، ومعه البريد الإلكتروني.

    تواجه مشكلة؟
  3. سجّل الدخول وجهّز مساحة العمل

    سجّل الدخول إلى اللوحة بـ OWNER_EMAIL وOWNER_PASSWORD. مساحة العمل فارغة: أضف مكانًا، ثم أول فعالية لك. عملتها USD إلى أن تغيّرها من الإعدادات ثم عام، ويمكنك ذلك حتى تُدفع أول تذكرة. بعدها ادعُ فريقك من صفحة الفريق، وأعطِ كلًّا منهم الدور الذي يحتاجه.

  • يحدث ذلك مرة واحدة. بمجرد وجود أي حساب موظف تُتجاهل متغيرات المالك، فلا تنشئ إعادة التشغيل أو نسخة ثانية من واجهة API شيئًا آخر، ويمكنك حذف OWNER_PASSWORD من الإعدادات بعد ذلك. ولا تُطبع كلمة المرور أبدًا.
  • لا يقرأها yarn seed ولا قاعدة بيانات مملوءة بالبيانات التجريبية: فهذه القاعدة فيها موظفون بالفعل.
  • ليس في اللوحة صفحة تسجيل. يُضاف كل حساب موظف آخر بالزر دعوة عضو في صفحة الفريق، بدور وكلمة مرور تشاركهما أنت بنفسك (لا يُرسل أي بريد).

البدء من البيانات التجريبية بدلًا من ذلك

تحتفظ قاعدة البيانات المملوءة بالبيانات التجريبية بمنظّميها الأربعة وحساباتهم. إذا بدأت منها، فاجعلها لك قبل أن تصبح متاحة من الإنترنت:

  1. اجعل المالك التجريبي لك: غيّر اسمه وبريده من الإعدادات ثم الملف الشخصي، وكلمة مروره من الإعدادات ثم الأمان. هو الحساب الوحيد الذي ينتمي إلى كل مساحات العمل التجريبية، ولا توجد شاشة تضيف عضوًا موجودًا إلى مساحة عمل أخرى، فأبقِه ولا تستبدله بحساب جديد.
  2. احذف الأعضاء التجريبيين الذين لا تحتاج إليهم من صفحة الفريق في كل مساحة عمل، أو غيّر كلمات مرورهم. إزالة تخرج العضو من مساحة العمل التي تعمل فيها؛ ويُحذف الحساب مع آخر مساحة عمل له. لا يمكنك إزالة نفسك، وتحتفظ كل مساحة عمل بمالك واحد.
  3. اضبط اسم مساحة العمل وبريد الدعم والعملة ورسوم الحجز والمنطقة الزمنية وإشعارات الفريق من الإعدادات ثم عام والإشعارات. تُقفل العملة بعد أول طلب مدفوع.
  4. أرشِف الفعاليات التجريبية أو ألغِها، واستبدل الأماكن التجريبية بأماكنك مع مخططات مقاعدها. لا توجد شاشة ولا مسار لإنشاء مساحة عمل أخرى، لذا أعد تسمية واحدة من الإعدادات ثم عام لتناسب عملك.
  5. ينشئ الحضور حساباتهم بأنفسهم على الموقع، أو يشترون كضيوف. الحاضر التجريبي بيانات تجريبية: احذفه مع الباقي.

تحديث نسخة تشغّلها بالفعل

مع NODE_ENV=production لا تغيّر واجهة API الجداول بنفسها أبدًا. بعد تحديث يضيف عمودًا أو جدولًا، زامن البنية مرة واحدة على قاعدة البيانات الموجودة، قبل أن تشغّل النسخة الجديدة. هذا التحديث يضيف العمود locale في orders (لغة رسائل كل طلب) والجدول event_reminders.

من دون Docker، داخل مجلد واجهة API:

الطرفية
yarn build
yarn db:sync:prod

لا يكتب صفوفًا ويحتفظ ببياناتك. مع Docker تترك الحاوية قاعدة البيانات الموجودة كما هي، لذلك بعد docker compose up --build -d شغّل السكربت نفسه مرة واحدة داخل حاوية واجهة API العاملة، ثم أعد تشغيلها (لحاوية واجهة API وحدها: docker exec -u node <container> node dist/database/sync-schema.js، ثم docker restart <container>):

الطرفية
docker compose exec -u node api node dist/database/sync-schema.js
docker compose restart api

مع NODE_ENV=development، كما في yarn dev، تحدّث واجهة API الجداول بنفسها عند البدء.

الإطلاق

قبل أن تصبح تيكيتوري متاحة من الإنترنت:

  1. في بيئة واجهة API، NODE_ENV=production وJWT_SECRET طويل وعشوائي خاص بك (يولّد Docker واحدًا عند عدم ضبطه). في الإنتاج ترفض واجهة API البدء بدون CORS_ORIGIN، وتذكر القيمة الناقصة.
  2. CORS_ORIGIN على الموقع واللوحة، وFRONTEND_URL على اللوحة، وSTOREFRONT_URL على الموقع، وPUBLIC_API_URL على واجهة API، وPUBLIC_MEDIA_URL على واجهة API متبوعًا بـ /media.
  3. TICKET_SECRET يُضبط مرة واحدة، قبل أول بيع: فهو يوقّع رموز QR، وتغييره لاحقًا يُبطل كل تذكرة بيعت.
  4. مفاتيح Stripe أو PayPal الحية، وخطافات الويب المسجّلة لدى كل مزوّد على /api/webhooks/stripe و/api/webhooks/paypal في عنوان واجهة API. مع ضبط مفتاح حي يختفي دفع اختباري من صفحة الدفع.
  5. الضريبة مُعدّة لبلدك: لا تفرض تيكيتوري أي ضريبة بنفسها.
  6. RESEND_API_KEY وMAIL_FROM، وإلا لن يستلم الحضور رسائل التذاكر ولا رموز التسجيل ولا رسائل إعادة ضبط كلمة المرور.
  7. قاعدة البيانات والملفات المرفوعة وملفات CSV المصدّرة على تخزين دائم، مع نسخة احتياطية.
  8. فحص الصحة الخاص بالمضيف على /api/health، وTRUST_PROXY=1 خلف وكيل.
  9. بناء التطبيقين الأماميين بقيم NEXT_PUBLIC_* العامة، وREVALIDATE_SECRET نفسه على واجهة API والموقع.
  10. على الموقع: علامتك التجارية في src/config/brand.config.ts، ونصوص الخصوصية والشروط والإلغاء في messages/legal معاد كتابتها لعملك ولبلدك.
  11. مساحة عملك الخاصة من بداية نظيفة، أو كلمات المرور التجريبية مغيّرة والحسابات التجريبية التي لا تحتاج إليها محذوفة.
  12. DEMO_MODE=false على واجهة API وNEXT_PUBLIC_DEMO_MODE=false في الواجهتين الأماميتين، كما تُشحن.

ليس لبيانات مبيعات حية

يبني yarn setup:fresh ويحذف كل جدول ويملأ البيانات الأولية في كل مرة يُشغَّل فيها، ويحذف yarn db:reset كل جدول. يناسبان بيئة جديدة تمامًا، وليس أمر تشغيل أو بناء أبدًا.

توقفت عند خطوة؟

ابحث عن الحل قبل أن تبدأ من جديد.

حل المشكلات

تفضيلات ملفات تعريف الارتباط

نستخدم ملفات تعريف الارتباط لتعزيز تجربة التصفح الخاصة بك وتحليل حركة المرور على الموقع وتخصيص المحتوى. بالنقر على "قبول الكل"، فإنك توافق على استخدامنا لملفات تعريف الارتباط للتحليلات والإعلانات المخصصة.