feat: full site build — Project/Melody schema (Option A), admin CRUD, public sections, uploads, email+SMTP, internal analytics, legal pages, docs
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
# 🏛️ المعمارية والاتفاقيات — 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**.
|
||||
Reference in New Issue
Block a user