Files
diyaa.de/docs/ARCHITECTURE.md
T

13 KiB

🏛️ المعمارية والاتفاقيات — diyaa.de

هاد الملف بيحدّد الشكل المستهدف للمشروع. أي feature جديد لازم يتبع هالاتفاقيات.


1) البنية العامة

الموقع بينقسم لـ واجهة عامة (public) + لوحة تحكم (admin) + API/Server Actions + قاعدة بيانات.

[ الزوّار ] ──> صفحات عامة (app/[locale]/…) ──┐
                                              ├──> Server Actions / Route Handlers ──> Prisma ──> PostgreSQL
[ المالك ] ──> لوحة التحكم (app/admin/…) ─────┘                                         │
                                                                          ملفات مرفوعة (volume على الـ VPS)

2) أقسام الموقع (من رؤية المالك)

كل هالأقسام (عدا الألحان) = صفوف بجدول Project واحد تتفلتر بحقل type. الفصل بالـ UI فقط.

  1. الأعمال / المعرض (Portfolio)type=PORTFOLIO بتصنيفات (شعارات، بروشورات، متاجر، أدوات ذكاء اصطناعي…).
  2. التطبيقات (Apps)type=APP تطبيقات Swift، مع سكرين شوتس + حقول iOS (App Store، TestFlight، الإصدار، رابط الدعم، سياسة خصوصية التطبيق).
  3. المواقع (Websites)type=WEBSITE مواقع Next.js/WordPress، مع سكرين شوتس ورابط حي.
  4. التصاميم (Designs)type=DESIGN (أو ضمن PORTFOLIO) — شعارات، هويات بصرية.
  5. الألحان (Melodies) — جدول Melody منفصل، عرض وتشغيل بتصنيفات (عربية، شرقية…) + تحميل اختياري.
  6. صفحات رسمية — سياسة الخصوصية، الشروط، Impressum (إلزامي قانونياً بألمانيا).
  7. تواصل — موجود، بنربطه بإرسال إيميل عبر SMTP + Honeypot + Rate limiting.
  8. لوحة تحكم — إضافة/تعديل/حذف كل ما سبق + التصنيفات.

3) مخطط قاعدة البيانات (Prisma) — الخيار A المعتمد

القرار (2026-08-05): جدول Project موحّد لكل الأعمال والتطبيقات والمواقع والتصاميم (يتميّزوا بحقل type)، و**Melody منفصل** (طبيعته صوتية). التقنيات والسكرين شوتس كـ String[] (بدون جداول join)؛ نضيف جداول Technology/Media منفصلة لاحقاً فقط لو لزم فلترة بالتقنية أو إدارة alt-text متقدمة. التصنيف علاقة واحد-لواحد (مش many-to-many).

⚠️ هاد يستبدل مخطط Work/App/Melody القديم يلي انبنى بـ T-03/T-06. الاستبدال آمن لأنو ما في داتا إنتاجية بعد. شوف مهمة الـ refactor بـ TODO.md.

enum ProjectType {
  PORTFOLIO   // شعار، بروشور، هوية بصرية… (سابقاً "أعمال/تصاميم")
  APP         // تطبيق Swift/iOS
  WEBSITE     // موقع Next.js أو WordPress
  DESIGN      // تصميم منفصل لو حبيت تفصله عن PORTFOLIO
}

enum CategoryKind {
  PROJECT     // تصنيفات المشاريع (شعارات، بروشورات، مواقع، متاجر، أدوات AI…)
  MELODY      // تصنيفات الألحان (عربي، شرقي…)
}

enum PublishStatus {
  DRAFT
  PUBLISHED
  ARCHIVED
}

model Category {
  id        String       @id @default(cuid())
  kind      CategoryKind
  slug      String       @unique
  nameAr    String
  nameEn    String
  order     Int          @default(0)
  projects  Project[]
  melodies  Melody[]
  createdAt DateTime     @default(now())
  updatedAt DateTime     @updatedAt
}

model Project {            // جدول موحّد: أعمال + تطبيقات + مواقع + تصاميم
  id           String        @id @default(cuid())
  type         ProjectType
  slug         String        @unique
  titleAr      String
  titleEn      String
  summaryAr    String?
  summaryEn    String?
  descAr       String?
  descEn       String?
  coverImage   String?       // مسار الغلاف
  images       String[]      // معرض صور / سكرين شوتس
  technologies String[]      // التقنيات المستخدمة (بسيط، بدون جدول منفصل)
  externalUrl  String?       // رابط المشروع/الموقع الحي
  repoUrl      String?       // GitHub اختياري
  // ── حقول خاصة بالتطبيقات (تبقى فاضية لغير APP) ──
  platform     String?       // "iOS/Swift" | "Next.js" | "WordPress"…
  appStoreUrl  String?
  testflightUrl String?
  appVersion   String?
  supportUrl   String?
  appPrivacyUrl String?
  // ── حالة وترتيب ──
  status       PublishStatus @default(DRAFT)
  isFeatured   Boolean       @default(false)
  sortOrder    Int           @default(0)
  publishedAt  DateTime?
  categoryId   String?
  category     Category?     @relation(fields: [categoryId], references: [id])
  createdAt    DateTime      @default(now())
  updatedAt    DateTime      @updatedAt

  @@index([type, status])
}

model Melody {            // لحن (منفصل بسبب طبيعته الصوتية)
  id             String        @id @default(cuid())
  slug           String        @unique
  titleAr        String
  titleEn        String
  descAr         String?
  descEn         String?
  audioFile      String        // مسار ملف الصوت
  coverImage     String?       // artwork
  durationSec    Int?
  isDownloadable Boolean       @default(false)  // سماح/منع التحميل
  status         PublishStatus @default(DRAFT)
  isFeatured     Boolean       @default(false)
  sortOrder      Int           @default(0)
  categoryId     String
  category       Category      @relation(fields: [categoryId], references: [id])
  createdAt      DateTime      @default(now())
  updatedAt      DateTime      @updatedAt
}

model ContactMessage {    // رسائل نموذج التواصل
  id        String   @id @default(cuid())
  name      String
  email     String
  message   String
  createdAt DateTime @default(now())
}

enum AnalyticsEventType {
  PAGE_VIEW
  PROJECT_OPEN
  MELODY_PLAY
  PROJECT_LINK_CLICK
  CONTACT_SUBMITTED
}

model AnalyticsEvent {   // عدّادات داخلية بدون خدمة خارجية
  id        String             @id @default(cuid())
  type      AnalyticsEventType
  path      String?
  projectId String?
  melodyId  String?
  createdAt DateTime           @default(now())
}

model User {              // مستخدم لوحة التحكم (أنت)
  id        String   @id @default(cuid())
  email     String   @unique
  password  String    // hashed
  createdAt DateTime @default(now())
}

// (مؤجّل) إعدادات الموقع من الأدمن — نضيفه عند الحاجة
// model SiteSetting { id String @id key String @unique value String }

4) اتفاقيات المجلدات (الشكل المستهدف)

diyaa.de/
├── app/
│   ├── [locale]/                # الموقع العام
│   │   ├── page.tsx             # الرئيسية
│   │   ├── work/                # المعرض (PORTFOLIO) + [slug]
│   │   ├── apps/                # التطبيقات (APP) + [slug]
│   │   ├── websites/            # المواقع (WEBSITE) + [slug]
│   │   ├── designs/             # التصاميم (DESIGN) + [slug]
│   │   ├── melodies/            # الألحان + [slug]
│   │   ├── legal/               # privacy, terms, impressum
│   │   ├── about/ · contact/
│   ├── maintenance/ · coming-soon/  # صفحات الأوضاع
│   ├── admin/                   # 🔒 لوحة التحكم (محميّة بـ Auth)
│   │   ├── projects/ · melodies/ · categories/ · settings/
│   │   └── layout.tsx
│   ├── api/                     # Route Handlers (health, upload, …)
│   └── globals.css
├── components/
│   ├── ui/                      # 🧩 shadcn/ui (مكوّنات أساسية)
│   ├── public/                  # مكوّنات الواجهة العامة
│   ├── admin/                   # مكوّنات لوحة التحكم
│   └── shared/                  # مشتركة
├── lib/
│   ├── db.ts                    # Prisma client (singleton)
│   ├── auth.ts                  # إعداد Auth.js
│   ├── email.ts                 # Nodemailer + SMTP
│   ├── upload.ts                # منطق رفع الملفات
│   ├── validations/             # مخططات Zod
│   ├── i18n.ts · site.ts · metadata.ts
├── prisma/
│   └── schema.prisma            # 🎯 مصدر الحقيقة لبنية الداتا
├── content/                     # قواميس الترجمة الثابتة (ar/en)
├── docs/                        # 📚 التوثيق
└── (Docker, Makefile, …)

5) اتفاقيات المكوّنات (Components)

  • كل شي كمكوّن قابل لإعادة الاستخدام. لا تكرّر UI.
  • مكوّنات components/ui/ = shadcn الأساسية (Button, Card, Dialog, Input…). لا تعدّل سلوكها الأساسي، ابنِ فوقها.
  • مكوّنات الأقسام (ProjectCard مشترك لكل الأنواع + MelodyCard + AudioPlayer) بتعيش بـ components/public/.
  • Server Components افتراضياً؛ استخدم "use client" فقط عند الحاجة للتفاعل (مشغّل صوت، فورمات، ثيم).
  • كل مكوّن عام لازم يدعم العربي والإنجليزي ويحترم اتجاه RTL/LTR.
  • الأنماط عبر Tailwind utilities + متغيّرات الثيم؛ لا CSS خام جديد (نحافظ على متغيّرات الثيم الموجودة لكن عبر إعداد Tailwind).

6) اتفاقيات Server Actions / API

  • عمليات لوحة التحكم (إنشاء/تعديل/حذف) → Server Actions بـ فاليديشن Zod.
  • كل action بيتحقق من الجلسة (Auth) قبل أي كتابة.
  • endpoints العامة (رفع، health، ربما RSS للألحان) → app/api/.
  • كل مدخلات المستخدم تمرّ بـ Zod schema من lib/validations/.
  • نموذج التواصل: يتحمّى بـ Honeypot (حقل مخفي) + Rate limiting (تحديد عدد الطلبات) لمنع السبام.
  • رفع الملفات: تحقّق من نوع الملف وحجمه، أسماء ملفات آمنة، altText للصور، وحذف الملف الفعلي من الـ volume عند حذف المحتوى.

7) الوسائط والملفات

  • الملفات المرفوعة تُخزّن على volume بالـ VPS (مش داخل صورة Docker).
  • الصور تُعالَج بـ sharp (تصغير + webp).
  • مسارات الملفات تُخزّن كـ نصوص بالداتابيس (مش الملف نفسه).
  • الرفع يتم عبر app/api/upload/ بعد التحقق من جلسة الأدمن، والملفات تُقدّم عبر app/api/uploads/.
  • مسار التخزين قابل للضبط عبر UPLOAD_DIR، والـ Docker volume الافتراضي هو /app/uploads.

8) الأوضاع (Site modes)

  • NEXT_PUBLIC_SITE_MODE بيقبل ثلاث قيم: full · maintenance · coming-soon.
    • coming-soon: يعرض ComingSoonPanel ويخفي الأقسام (موجود حالياً).
    • maintenance: يعرض صفحة صيانة (جديد — T-14).
    • full: الموقع الكامل.
  • T-17 منجز: .env.example يستخدم full بدل live، وlib/site.ts يعتمد coming-soon | maintenance | full. واجهة الصيانة الفعلية والاستثناء المرئي للأدمن ضمن T-14.
  • لوحة التحكم بتشتغل بغضّ النظر عن الوضع (محميّة بالـ Auth) عشان تحضّر المحتوى قبل الإطلاق.

9) البيئة والنشر

  • بيضاف خدمة postgres لـ docker-compose.yml + volume للبيانات + volume للملفات المرفوعة.
  • الـ Makefile بينضاف عليه أوامر: db-up, migrate, seed, studio (Prisma Studio).
  • migrations تنطبق تلقائياً عند النشر (خطوة بالـ start أو entrypoint).
  • أسرار (SMTP, DB URL, AUTH secret) بـ .env فقط — ولا تُرفع للـ git.