المصدر: apps/docs/content/ar/architecture.mdتعديل هذه الصفحة

نُطقي — البنية المعمارية

كيف يتركّب نُطقي: التطبيقات والحزم المشتركة، والمنظومة التقنية، والاصطلاحات العامة للمنصة، واستراتيجية ثنائية اللغة والاتجاه من اليمين إلى اليسار.

المنظومة في لمحة#

Rendering diagram…

تخطيط المستودع الموحّد#

nutqi/
  apps/
    web/    Next.js 15 App Router (ar RTL default + en), Tailwind v4, next-intl
    api/    NestJS 11 + Prisma + PostgreSQL 16, REST /api/v1
    docs/   this documentation site (statically rendered markdown)
  packages/
    ui/     design tokens + shared React components (Button, Card, StatusChip, …)
    shared/ TypeScript types + enums + zod schemas shared web↔api
  specs/         implementation specs (source of truth for the build)
  docs-content/  markdown sources rendered by the docs site

مساحة عمل واحدة بـ pnpm يقودها Turborepo، فيمرّ كل تطبيق وكل حزمة بالبناء والفحص والاختبار عبر خط الأنابيب نفسه.

المنظومة التقنية#

الطبقةالاختيار
الويبNext.js 15 (بموجّه App Router)، و Tailwind CSS v4، و next-intl — العربية (من اليمين إلى اليسار) هي الافتراضية، والإنجليزية ثانوية
الواجهة الخلفيةNestJS 11، وواجهة REST تحت /api/v1، وكائنات نقل مُتحقَّق منها، ورسائل خطأ ثنائية اللغة
البياناتPostgreSQL 16 مع Prisma؛ ومعرّفات من نوع cuid؛ وأعمدة تدقيق في كل جدول
المصادقةجلسات آمنة عبر كوكيز httpOnly؛ وتحكّم في الوصول قائم على الأدوار مع فحوص ملكية على مستوى الصف
المظهرخصائص CSS مخصّصة في @nutqi/ui؛ مظهران فاتح وداكن، والفاتح هو الافتراضي
التوثيقموقع ثابت بـ Next.js 15 يعرض ملفات markdown من المستودع؛ ومخططات Mermaid

الاصطلاحات العامة للمنصة#

  • المبالغ المالية تُخزَّن أعداداً صحيحة بالقروش (الجنيه المصري × 100) وتُنسَّق عبر أداة مشتركة — فلا وجود لأي عملة بفاصلة عائمة في أي موضع.
  • الطوابع الزمنية تُخزَّن بتوقيت UTC وتُعرض بتوقيت Africa/Cairo.
  • الحذف الناعم (deletedAt) يحفظ الملاحظات وقوالب الخطط ومدخلات معلومات العمل وإعلانات الوظائف.
  • كل جدول يحمل createdAt / updatedAt؛ والمعرّفات من نوع cuid.
  • الملفات المرفوعة تُقدَّم عبر الواجهة الخلفية (/api/v1/files/:id) مع طبقة تخزين مُجرَّدة تمهيداً للتخزين السحابي الكائني.
  • الزمن الحقيقي: الأحداث المُرسَلة من الخادم تغذّي شارة الإشعارات داخل التطبيق.

ثنائية اللغة والاتجاه من اليمين إلى اليسار#

  • العربية هي اللغة الافتراضية وتُعرض من اليمين إلى اليسار؛ والإنجليزية لغة ثانوية تُعرض من اليسار إلى اليمين. ورسائل التحقق وأخطاء الواجهة الخلفية ثنائية اللغة (message + messageAr، احتراماً لترويسة Accept-Language).
  • التخطيطات تستعمل خصائص CSS المنطقية في كل موضع، فينعكس كل مكوّن تلقائياً حين ينقلب الاتجاه.
  • يصدر هذا التوثيق في نسختين كاملتين غير مختلطتين — عربية (من اليمين إلى اليسار) وإنجليزية (من اليسار إلى اليمين) — فلا يلتقي القارئ بالخطّين في جملة واحدة. والاتجاه يأتي من <html dir> وترثه كل الكتل؛ ولا تُترك الكتل عمداً لتستنبط اتجاهها بنفسها، لأن ذلك الاستنباط يقوم على أول محرف قويّ فيقلب فقرةً عربيةً صادف أن بدأت بمعرّف أو برقم. ولا يُعزل إلا ما كان بالخط الغريب عن النسخة — اللاتيني داخل النثر العربي، والعربي داخل النثر الإنجليزي — فتبقى علامات الترقيم المحيطة به ملتصقةً بالجملة المضيفة. والمسرد هو الصفحة الوحيدة التي يظهر فيها الخطّان جنباً إلى جنب، في عمودين منفصلين، وخلاياه هي الموضع الوحيد الذي ما زال يستنبط اتجاه كل خلية على حدة.

المظهر#

  • مظهران، فاتح وداكن، والفاتح هو الافتراضي: فالزائر لأول مرة يحصل على الفاتح دائماً، ولو كان نظام تشغيله مضبوطاً على الداكن. ولا تُطبَّق تفضيلات النظام تلقائياً بحال — فلا يبدّل المظهرَ إلا اختيار صريح.
  • المظهر النشط سمة واحدة على العنصر الجذر (data-theme)، ويُحفظ الاختيار لكل جهاز في تخزين المتصفح. ويطبّقه نصّ صغير قبل أول رسم للصفحة، فلا يرى القارئ العائد إلى الوضع الداكن وميضاً أبيض.
  • اللون يعيش في طبقة رموز دلالية واحدة: فسلالم الهوية البصرية لا تتغيّر بين المظهرين، ولا ينقلب إلا ما بُني فوقها من معانٍ — خلفية الصفحة، والأسطح، والحدود، ونص التمييز، ودرجات شارات الحالة، وألوان سلاسل الرسوم البيانية، والظلال. والمكوّنات تشير إلى المعاني لا إلى لون خام قط، وهذا بالضبط ما يجعل سمةً واحدةً تعيد طلاء المنتج كله.
  • ويستعمل المنتج وموقع التوثيق السمة نفسها والتفضيل المحفوظ نفسه، فيتعرّف كلٌّ منهما على التبديل الذي جرى في الآخر.

الوضع الأمني#

  • في المتصفح: سياسة أمن محتوى نافذة تحمل قيمة تعمية طازجة لكل طلب، فلا يعمل من النصوص البرمجية إلا ما ضمنه الخادم؛ ولا تُمنح unsafe-eval بحال. ومعها HSTS، وترويسات عزل المصادر المتقاطعة، وnosniff، وسياسة تمنع التأطير، وبلا لافتة لإطار العمل.
  • في الواجهة الخلفية: يُتحقَّق من كل متن طلب تحققاً صارماً وتُرفض الحقول غير المعروفة رفضاً قاطعاً، ويُحدّ حجم المتن، ويُحدّ معدّل الطلبات لكل عنوان شبكة بميزانية أضيق بكثير على مسارَي تسجيل الدخول ورمز التأكيد. وتوثيق الواجهة معطّل في الإنتاج ما لم يُفعَّل صراحةً.
  • عند الإقلاع: غياب مفتاح التوقيع أو فراغ قائمة المصادر المسموحة يرفض الإقلاع في الإنتاج بدل الارتداد إلى قيمة افتراضية غير آمنة، فيفشل النشر الخاطئ بصوتٍ عالٍ بدل أن يعمل مفتوحاً.
  • الصحة: يخدم GET /api/v1/health الوسيط العكسي وفحص صحة الحاوية — فحص حياة وفحص قاعدة بيانات حقيقي، وخالٍ عمداً من أرقام الإصدارات وسلاسل الاتصال ونصوص الأخطاء.

النشر#

  • ينشر تطبيق الويب وموقع التوثيق على Vercel من المستودع: رابط معاينة لكل طلب دمج، والإنتاج من main.
  • وتعمل الواجهة الخلفية على مضيف Docker خلف Nginx لا على منصة بلا خواديم، لأنها تمسك اتصالات قاعدة بيانات طويلة العمر، وتكتب الملفات المرفوعة على القرص، وتشغّل الترحيلات عند الإصدار. وتعيش العدّة في deploy/: حزمة Compose (الواجهة وPostgreSQL، مع فحوص صحة وخطوة ترحيل تُنفَّذ مرةً واحدة)، وقالب TLS لـNginx، ونصوص التهيئة والنشر والتراجع، ودليل تشغيل يغطي النسخ الاحتياطي والاستعادة وتجديد الشهادات.
  • ومنفذ الواجهة يرتبط بالمضيف المحلي وحده، وNginx هو السطح العام الوحيد.
للمطوّرين

التطوير المحلي (‏portless)#

كل خادم تطوير يعمل من خلال portless، وهو يمنح أسماء مضيفين محلية نظيفة بـ HTTPS ويحقن المتغيّر PORT — فلا يثبّت أي سكربت رقم منفذ (مثال: portless nutqi-docs -- pnpm --filter @nutqi/docs dev).

الخدمةاسم المضيفملاحظات
الويبhttps://nutqi-web.localhostخادم التطوير الخاص بـ Next.js
الواجهة الخلفيةhttps://nutqi-api.localhostواجهة Swagger على /api/docs
التوثيقhttps://nutqi-docs.localhostهذا الموقع
  • يُبقي تطبيق الويب نداءات المتصفح على الأصل نفسه عبر إعادة كتابة في Next.js: /api/:path*${API_URL}/api/:path*، فتعمل المصادقة بكوكيز httpOnly رغم اختلاف أسماء المضيفين في بيئة التطوير. والمتغيّر API_URL يُوصَّل من portless get nutqi-api.
  • تضبط الواجهة الخلفية المتغيّر CORS_ORIGIN ليشمل اسم مضيف الويب.
  • قواعد البيانات: nutqi_dev للتطوير، و nutqi_test للاختبارات.
  • البناء لا يزعج خادم تطوير يعمل أبداً. فكلا تطبيقَي Next يثبّت خادم التطوير على مجلد إخراج خاص به (NEXT_DIST_DIR=.next-dev في سكربت dev)، ويترك .next للبناء وحده. فمشاركة مجلد واحد كانت تعني أن pnpm build يطمس القطع التي يقدّمها خادم تطوير حيّ، فيفشل كل طلب بعدها بخطأ MODULE_NOT_FOUND على قطعة webpack — وهو خطأ يُقرأ كعلة في الشيفرة وليس كذلك. وفصل التطوير لا البناء يُبقي مسار البناء مطابقاً حرفياً للتكامل المستمر ولـVercel، فيصير السلوك الآمن هو الافتراضي بلا رايةٍ تُتذكَّر.

خط أنابيب التكامل المستمر (‏GitHub Actions)#

يعمل .github/workflows/ci.yml عند الدفع إلى main وعند كل طلب دمج (وتُلغى التشغيلات التي تجاوزتها تشغيلات أحدث). وتعمل سبع مهامّ على التوازي، تعلن كل واحدة أدنى صلاحيات تحتاجها وتحمل مهلةً زمنية:

  1. البواباتpnpm وNode مع تخزين مؤقت للاعتماديات ولـTurborepo، وحاوية خدمة PostgreSQL 16 يُعِدّها prisma migrate deploy، ثم linttypechecktestbuild، وكل واحدة تتفرّع على جميع حزم مساحة العمل.
  2. الأسرار — مسح أسرار المستودع.
  3. الاعتماديات — تدقيق الثغرات وفحص سياسة التراخيص.
  4. التحليل الساكن — فحص الشيفرة.
  5. مراجعة الاعتماديات — على كل طلب دمج.
  6. الحاوية — تبني صورة الواجهة الخلفية، وتُصدر قائمة مكوّنات البرمجية، وتفشل عند الثغرات العالية أو الحرجة.
  7. الترحيلات — تلتقط انحراف المخطط قبل وصوله إلى أي بيئة.

ولا تعمل مهمة النشر الإنتاجي إلا على main بعد نجاح البوابات. أما النشرات التمهيدية فتأتي من تكامل Vercel مع المستودع، فلكل طلب دمج رابط يضغطه المراجع.

ويُتوقَّع أن تمرّ البوابات الأربع نفسها محلياً من جذر المستودع قبل الدفع.

بوابات جودة أخرى غير الأربع#

ثمة ثوابت لا يستطيع المراجع أن يتحقّق منها بعينه على نحو موثوق، فتُفرَض بنصوص برمجية واختبارات بدلاً من ذلك:

البوابةما تفرضه
check-logical.mjsألا توجد أدوات اتجاه فيزيائي (pl/pr، ml/mr، left/right) في مصادر الويب أو واجهة المستخدم، فينعكس الاتجاه تلقائياً
check-i18n.mjsتطابق مفاتيح ar وen في كل نطاق، وألا توجد قيم فارغة، وألا تبقى عربية في نص إنجليزي، وألا يوجد نسخ ولصق بلا ترجمة
check-literal-colors.mjsألا توجد ألوان حرفية في المكوّنات — فكل شيء يمرّ عبر طبقة الرموز الدلالية، وإلا انكسر المظهر الداكن صامتاً
no-mixing.test.tsأن تكون كل نسخة من التوثيق أحادية الخط، مع تأكيد المسرد استثناءً مقصوداً
direction.test.tsألا يُعزل إلا ما كان بالخط الغريب، وأن تُترك كتل الشيفرة وشأنها
smoke.test.tsأن يتطابق هيكل العناوين بين النسختين، وأن يحمل سجل القرارات المعرّفات نفسها بالترتيب نفسه

كيف يعمل موقع التوثيق هذا#

  • التطبيق apps/docs تطبيق Next.js 15 ثابت بالكامل. وهو يقرأ عند البناء ملفات markdown مباشرةً من docs-content/ و specs/02-api.md — فالمصادر لا تُنسخ ولا تُشتَقّ، ويبقى markdown المستودع هو المرجع الوحيد.
  • المخططات تُكتب في كتل شفرة من نوع mermaid وتُحوَّل إلى SVG داخل المتصفح بواسطة Mermaid 11 (مكوّن عميل يُحمَّل عند الطلب).
  • فهرس البحث يُولَّد عند البناء من المصادر نفسها ويُقدَّم بصيغة JSON ثابتة.
  • صفحة البنية المعمارية (apps/docs/content/architecture.md) وهذا الملحق هما المحتوى الوحيد المكتوب داخل تطبيق التوثيق نفسه.