انتقل إلى المحتوى

البنية

تتّبع المنصّة البنية النظيفة: handler ← usecase ← repository ← domain. ولا تستورد نواة الأعمال إطارَ الويب إطلاقًا.

المكوّنات

الإنترنت ──► nginx (TLS)
   ├─ /oauth2/*, /.well-known/*, /userinfo ─► واجهة Go — مُصدِر OIDC مدمج
   ├─ /rp/sign/*   ─► مرحِّل توقيع eID (الواجهة الخلفية)
   ├─ /rp/eid/*     ─► وكيل خدمة eID — شخصي (الواجهة الخلفية)
   ├─ /rp/eid-org/* ─► وكيل خدمة eID — المنظمات (الواجهة الخلفية)
   └─ كلّ ما عدا ذلك ─► BFF بـ Next.js (web) ──► واجهة الخلفية (:8080)
   الشبكة الداخلية:  db (PostgreSQL) · redis

الطبقات

الطبقة التقنية ملاحظات
الواجهة الخلفية Go · chi (net/http) · pgx (دون ORM) بنية نظيفة، RLS، SQL مكتوب يدويًّا
الواجهة الأمامية Next.js 16 (BFF) يخاطب المتصفّح مسارات المصدر نفسه فقط؛ ولا تصل الرموز إلى شيفرة العميل
مزوّد OIDC مدمج (Go، usecases/oidc) تدير المنصّة بنفسها الدخول والموافقة والخروج
الهوية طرف معتمِد لدى eID Mongolia التحقّق بالهوية الإلكترونية
الذاكرة/الطابور Redis قائمة رفض الجلسات، الحالة المؤقّتة
الذكاء الاصطناعي Gemini (REST دون SDK) محادثة، صوت، ترجمة

الأمان

  • الأمان على مستوى الصفّ (RLS) — لا يرى المستخدم سوى صفوفه؛ مع حارس عند الإقلاع يتحقّق من قابلية التطبيق (يستلزم في الإنتاج دورًا غير خارق الصلاحيات).
  • نمط BFF — تعيش الرموز في كعكات httpOnly، ولا تظهر أبدًا في شيفرة المتصفّح.
  • حماية CSRF مزدوجة — ترويسة مخصّصة + تحقّق من المصدر.
  • ترويسات الأمان — CSP، وHSTS، وCOOP/COEP/CORP؛ وتحديد المعدّل حسب IP.
  • التدقيق — سجلّ مترابط بالبصمات، لا يقبل إلّا الإضافة.

الشيفرة المشتركة — ما ليس في هذا المستودع

لم تُكتب أيّ من القدرات أعلاه هنا. فهي تصل من طبقتين مشتركتين مُصدَّرتين بإصدارات، حتى لا تضطرّ أيّ منصّة إلى تكرار الإصلاح يدويًّا:

الطبقة المصدر الآلية
نواة الواجهة الخلفية — المصادقة، وRBAC، والبوّابة، والتدقيق، ومزوّد OIDC، وeID/SSO، والذكاء الاصطناعي open-gerege-core (وحدة Go) تبعية في go.mod
طبقة الواجهة الأمامية — lib/**، وcomponents/**، ومنطق 158 مسار BFF @gerege/ui-core (حزمة npm) تبعية في package.json

ولذلك لا يحتوي مجلّد backend/ في هذا المستودع سوى على ملفّ Go واحد:

backend/
├── cmd/api/main.go        # نحو 30 سطرًا: تشغيل النواة وإضافة مساراتك
├── deploy/                # Dockerfile، تهيئة قاعدة البيانات
└── .env.example           # قالب الإعدادات
func main() {
    server.ServiceName = "gerege-template"
    app, err := server.NewApp()          // ← كلّ القدرات من النواة
    // أضِف هنا مسارات هذا التطبيق الخاصّة:
    //   app.Router().Route("/api/xxx", xxx.Routes(app.Pool()))
    app.Run()
}

والواجهة الأمامية على النمط ذاته — إذ إنّ app/**/page.tsx أغلفة رفيعة حول عروض الحزمة، وapp/api/** إعادة تصدير من سطر واحد لكلّ مسار:

// src/app/api/org/[id]/route.ts
export { GET, PUT, DELETE } from '@gerege/ui-core/api/org/[id]';
export const dynamic = 'force-dynamic';

لماذا تبقى أغلفة المسارات

يمكن اختزال الملفّات الـ158 في مسار شامل واحد [...path]. لكنّ قائمة المسارات قائمة سماح أمنية — فهي تحدّد أيّ مسارات الواجهة الخلفية يمكن للمتصفّح بلوغها. والمسار الشامل يُلغيها ويفتح وكيلًا نحو كلّ المسارات. فالغلاف ثمن مقصود.

أمّا ما تملكه المنصّة فهو: brand.config.ts (الاسم · النطاق · الألوان · عنوان التوثيق)، وcomponents/landing/** (النصّ التسويقي)، وapp/globals.css (رموز ألوان العلامة)، وdeploy/** و.github/**.

وتمنع بوّابتا تكامل مستمر عودةَ التكرار — check-brand (يفشل البناء إذا ظهر اسم منصّة خارج brand.config.ts) وcheck-routes (يحتاج كلّ مسار في الحزمة إلى غلاف؛ وبدونه تختفي أيّ نقطة نهاية جديدة بصمت).