انشر تيكيتوري
ضع الموقع ولوحة التحكم وواجهة 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 في التطبيقين الأماميين. ويحلّ REVALIDATE_SECRET محل القيمة المحلية الافتراضية التي يتشاركها الموقع وواجهة API لتحديث صفحات الفعاليات فورًا.
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-stringMEDIA_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.
| التطبيق | البناء | التشغيل |
|---|---|---|
| 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 وحاوية تخزين.
ابدأ بمساحة عملك الخاصة
المنظّمون التجريبيون وبيانات دخولهم للتجربة فقط. لفعالياتك الخاصة ابدأ على قاعدة بيانات جديدة بدونهم: في قاعدة بيانات ليس فيها حساب موظف ولا مساحة عمل، ينشئ أول تشغيل لواجهة API البيانات الأساسية (الصلاحيات التسع والعشرون والأدوار الأربعة وإعدادات الموقع وأنواع الفعاليات الثلاثة)، ومساحة عمل واحدة فارغة ومالكها.
اضبط متغيرات المالك
في
back-end/.env، أو في إعدادات مضيفك لواجهة API. مع Docker Compose ضعها في.envبجانبdocker-compose.ymlمعSEED_DEMO_DATA=false: يقرأ Compose ذلك الملف أولًا ثمback-end/.env، وتتقدم قيمة الأخير حيث يضبط الاثنان قيمة.back-end/.envOWNER_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وكلمة مرور الحسابات التجريبية.ابدأ من دون البيانات التجريبية، على قاعدة بيانات جديدة
من دون 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، ومعه البريد الإلكتروني.سجّل الدخول وجهّز مساحة العمل
سجّل الدخول إلى اللوحة بـ
OWNER_EMAILوOWNER_PASSWORD. مساحة العمل فارغة: أضف مكانًا، ثم أول فعالية لك. عملتها USD إلى أن تغيّرها من الإعدادات ثم عام، ويمكنك ذلك حتى تُدفع أول تذكرة. بعدها ادعُ فريقك من صفحة الفريق، وأعطِ كلًّا منهم الدور الذي يحتاجه.
- يحدث ذلك مرة واحدة. بمجرد وجود أي حساب موظف تُتجاهل متغيرات المالك، فلا تنشئ إعادة التشغيل أو نسخة ثانية من واجهة API شيئًا آخر، ويمكنك حذف
OWNER_PASSWORDمن الإعدادات بعد ذلك. ولا تُطبع كلمة المرور أبدًا. - لا يقرأها
yarn seedولا قاعدة بيانات مملوءة بالبيانات التجريبية: فهذه القاعدة فيها موظفون بالفعل. - ليس في اللوحة صفحة تسجيل. يُضاف كل حساب موظف آخر بالزر دعوة عضو في صفحة الفريق، بدور وكلمة مرور تشاركهما أنت بنفسك (لا يُرسل أي بريد).
البدء من البيانات التجريبية بدلًا من ذلك
تحتفظ قاعدة البيانات المملوءة بالبيانات التجريبية بمنظّميها الأربعة وحساباتهم. إذا بدأت منها، فاجعلها لك قبل أن تصبح متاحة من الإنترنت:
- اجعل المالك التجريبي لك: غيّر اسمه وبريده من الإعدادات ثم الملف الشخصي، وكلمة مروره من الإعدادات ثم الأمان. هو الحساب الوحيد الذي ينتمي إلى كل مساحات العمل التجريبية، ولا توجد شاشة تضيف عضوًا موجودًا إلى مساحة عمل أخرى، فأبقِه ولا تستبدله بحساب جديد.
- احذف الأعضاء التجريبيين الذين لا تحتاج إليهم من صفحة الفريق في كل مساحة عمل، أو غيّر كلمات مرورهم. إزالة تخرج العضو من مساحة العمل التي تعمل فيها؛ ويُحذف الحساب مع آخر مساحة عمل له. لا يمكنك إزالة نفسك، وتحتفظ كل مساحة عمل بمالك واحد.
- اضبط اسم مساحة العمل وبريد الدعم والعملة ورسوم الحجز والمنطقة الزمنية وإشعارات الفريق من الإعدادات ثم عام والإشعارات. تُقفل العملة بعد أول طلب مدفوع.
- أرشِف الفعاليات التجريبية أو ألغِها، واستبدل الأماكن التجريبية بأماكنك مع مخططات مقاعدها. لا توجد شاشة ولا مسار لإنشاء مساحة عمل أخرى، لذا أعد تسمية واحدة من الإعدادات ثم عام لتناسب عملك.
- ينشئ الحضور حساباتهم بأنفسهم على الموقع، أو يشترون كضيوف. الحاضر التجريبي بيانات تجريبية: احذفه مع الباقي.
تحديث نسخة تشغّلها بالفعل
مع 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 الجداول بنفسها عند البدء.
الإطلاق
قبل أن تصبح تيكيتوري متاحة من الإنترنت:
- في بيئة واجهة API،
NODE_ENV=productionوJWT_SECRETطويل وعشوائي خاص بك (يولّد Docker واحدًا عند عدم ضبطه). في الإنتاج ترفض واجهة API البدء بدونCORS_ORIGIN، وتذكر القيمة الناقصة. CORS_ORIGINعلى الموقع واللوحة، وFRONTEND_URLعلى اللوحة، وSTOREFRONT_URLعلى الموقع، وPUBLIC_API_URLعلى واجهة API، وPUBLIC_MEDIA_URLعلى واجهة API متبوعًا بـ/media.TICKET_SECRETيُضبط مرة واحدة، قبل أول بيع: فهو يوقّع رموز QR، وتغييره لاحقًا يُبطل كل تذكرة بيعت.- مفاتيح Stripe أو PayPal الحية، وخطافات الويب المسجّلة لدى كل مزوّد على
/api/webhooks/stripeو/api/webhooks/paypalفي عنوان واجهة API. مع ضبط مفتاح حي يختفي دفع اختباري من صفحة الدفع. - الضريبة مُعدّة لبلدك: لا تفرض تيكيتوري أي ضريبة بنفسها.
RESEND_API_KEYوMAIL_FROM، وإلا لن يستلم الحضور رسائل التذاكر ولا رموز التسجيل ولا رسائل إعادة ضبط كلمة المرور.- قاعدة البيانات والملفات المرفوعة وملفات CSV المصدّرة على تخزين دائم، مع نسخة احتياطية.
- فحص الصحة الخاص بالمضيف على
/api/health، وTRUST_PROXY=1خلف وكيل. - بناء التطبيقين الأماميين بقيم
NEXT_PUBLIC_*العامة، وREVALIDATE_SECRETنفسه على واجهة API والموقع. - على الموقع: علامتك التجارية في
src/config/brand.config.ts، ونصوص الخصوصية والشروط والإلغاء فيmessages/legalمعاد كتابتها لعملك ولبلدك. - مساحة عملك الخاصة من بداية نظيفة، أو كلمات المرور التجريبية مغيّرة والحسابات التجريبية التي لا تحتاج إليها محذوفة.
DEMO_MODE=falseعلى واجهة API وNEXT_PUBLIC_DEMO_MODE=falseفي الواجهتين الأماميتين، كما تُشحن.
ليس لبيانات مبيعات حية
يبني yarn setup:fresh ويحذف كل جدول ويملأ البيانات الأولية في كل مرة يُشغَّل فيها، ويحذف yarn db:reset كل جدول. يناسبان بيئة جديدة تمامًا، وليس أمر تشغيل أو بناء أبدًا.