250 lines
13 KiB
Markdown
250 lines
13 KiB
Markdown
# 🏛️ المعمارية والاتفاقيات — 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`.
|
|
|
|
```prisma
|
|
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**.
|