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:
2026-08-05 20:53:40 +02:00
parent d87f3033c6
commit c26f41511f
122 changed files with 15068 additions and 41 deletions
+249
View File
@@ -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**.