# Velody

## خطة المعمارية وبيئة المشروع

## Phase 1

هذه الوثيقة تغطي فقط:

- المعمارية الكاملة
- خطة التنفيذ
- هيكل المشروع
- استراتيجيات التخزين والمزامنة
- بيئة التطوير والتشغيل

ولا تبدأ بتنفيذ التطبيق الكامل.

## 0. قرارات تأسيسية

- النظام خاص جدًا لمالك واحد فقط.
- لا يوجد
  public multi-user auth
  في المرحلة الأولى.
- يوجد حد أدنى من الحماية التقنية بين الأجهزة والخادم عبر
  device install token
  أو
  shared secret
  مخزن في
  Keychain
  بدون بناء نظام حسابات.
- المصدر الحقيقي للمكتبة هو الخادم.
- تطبيق
  macOS
  هو مصدر الإدخال
  ingest source
  للملفات.
- تطبيق
  iPhone
  هو مستهلك للمكتبة مع تنزيل محلي كامل وتشغيل
  offline-first
  .
- الخلفية الموصى بها هي
  modular monolith
  باستخدام
  NestJS
  وليس
  microservices
  في البداية.
- التخزين الصوتي يكون على قرص
  VPS
  المحلي، وليس داخل
  PostgreSQL
  .
- المزامنة بين الأجهزة تبنى على
  append-only change log
  بدل إعادة تحميل كامل المكتبة كل مرة.

## 1. نظرة عامة على النظام

### الهدف العملي

النظام المطلوب هو مكتبة موسيقى خاصة تعمل كمنظومة شخصية شبيهة بخفة
Spotify
،
لكن بدون تعقيد المستخدمين العامين أو إدارة الحسابات أو المشاركة العامة.

### النتيجة المعمارية المطلوبة

1. المستخدم يضع ملفات
   MP3
   داخل مجلد مراقب على
   macOS
   .
2. تطبيق
   macOS
   يلتقط التغييرات، ينتظر استقرار الملف، يستخرج البيانات الوصفية، ثم يضع الملف في طابور رفع دائم.
3. الخادم يستقبل الملف بشكل قابل للاستئناف، يتحقق من سلامته، يحفظه على القرص، ويسجل بياناته الوصفية في قاعدة البيانات.
4. الخادم يضيف حدثًا جديدًا إلى سجل التغييرات.
5. تطبيق
   iPhone
   يقرأ التغييرات الجديدة، ينزل الملفات المطلوبة في الخلفية، يتحقق من سلامتها، ويحفظها محليًا للتشغيل بدون إنترنت.

### التوصية الأساسية

ابدأ بـ:

- تطبيقين أصليين
  SwiftUI
  .
- حزم مشتركة
  Swift Package
  للمنطق المشترك.
- خادم
  Node.js + TypeScript + NestJS
  .
- قاعدة
  PostgreSQL
  .
- تخزين ملفات على قرص
  VPS
  .
- واجهات
  REST API
  بإصدار
  versioned API
  من البداية.

## 2. مخطط نصي للنظام

```text
                           +----------------------+
                           |   Watched Folder     |
                           |      on macOS        |
                           +----------+-----------+
                                      |
                                      v
                           +----------------------+
                           | FSEvents Watcher     |
                           | + Stability Check    |
                           +----------+-----------+
                                      |
                                      v
                           +----------------------+
                           | Local Upload Queue   |
                           | + Metadata Extract   |
                           | + SHA-256 Hashing    |
                           +----------+-----------+
                                      |
                                      v
                     +----------------------------------------+
                     | HTTPS REST API via Nginx               |
                     | music.diyaa.de                         |
                     +----------------+-----------------------+
                                      |
                                      v
                     +----------------------------------------+
                     | NestJS Backend                         |
                     | - Upload Sessions                      |
                     | - Library Service                      |
                     | - Sync Events                          |
                     | - Validation / Metadata                |
                     +-----------+----------------------------+
                                 | 
                   +-------------+-------------+
                   |                           |
                   v                           v
        +-----------------------+   +---------------------------+
        | PostgreSQL            |   | VPS File Storage          |
        | metadata + sync log   |   | audio/artwork/incoming    |
        +-----------+-----------+   +-------------+-------------+
                    |                               |
                    +---------------+---------------+
                                    |
                                    v
                         +--------------------------+
                         | Sync Changes Endpoint    |
                         +------------+-------------+
                                      |
                                      v
                         +--------------------------+
                         | iPhone Sync Engine       |
                         | BGTasks + URLSession     |
                         +------------+-------------+
                                      |
                                      v
                         +--------------------------+
                         | Local Offline Store      |
                         | + Core Data catalog      |
                         | + local audio files      |
                         +------------+-------------+
                                      |
                                      v
                         +--------------------------+
                         | AVPlayer / AVQueuePlayer |
                         | background playback      |
                         +--------------------------+
```

## 3. هيكل المشروع الموصى به

```text
velody/
  apps/
    apple/
      Velody.xcworkspace
      VelodyMac/
        VelodyMacApp/
        VelodyMacTests/
      VelodyiPhone/
        VelodyiPhoneApp/
        VelodyiPhoneTests/

  packages/
    apple/
      VelodyDomain/
      VelodyNetworking/
      VelodyPersistence/
      VelodySync/
      VelodyPlayback/
      VelodyUtilities/

  backend/
    src/
      main.ts
      app.module.ts
      modules/
        health/
        config/
        devices/
        library/
        uploads/
        metadata/
        artwork/
        sync/
        storage/
      common/
        dto/
        pipes/
        filters/
        interceptors/
        utils/
      infrastructure/
        database/
        filesystem/
        logging/
    prisma/
      schema.prisma
      migrations/
    test/
      integration/
      e2e/
    Dockerfile
    package.json

  infra/
    docker/
      compose.local.yml
      compose.vps.yml
      env/
        backend.env.example
        postgres.env.example
    nginx/
      music.diyaa.de.conf
    scripts/
      deploy.sh
      backup-db.sh
      backup-storage.sh

  runtime/
    local-dropbox/
    storage/
    logs/

  docs/
    PROJECT_ENVIRONMENT_ARCHITECTURE.md
```

### لماذا هذا الهيكل مناسب

- يفصل بوضوح بين
  Apple apps
  و
  backend
  و
  infra
  .
- يسمح بمشاركة المنطق الحقيقي فقط بين
  macOS
  و
  iPhone
  بدل إجبار واجهات متطابقة.
- يبقي
  Docker
  وملفات التشغيل منفصلة عن كود التطبيق.
- يسمح بإضافة
  widgets
  أو
  watchOS
  أو
  CarPlay
  لاحقًا بدون إعادة تنظيم جذرية.

## 4. المعمارية الخلفية الموصى بها

### الأسلوب العام

التوصية هي:

modular monolith

باستخدام:

NestJS

داخل حاوية واحدة في البداية.

هذا الخيار يعطيك:

- هيكلة واضحة
- قابلية اختبار أعلى
- سهولة إضافة وحدات مستقبلية
- بساطة تشغيلية أكبر من
  microservices

### الوحدات الأساسية

#### 1. `HealthModule`

المسؤولية:

- فحص الجاهزية
- فحص الاتصال بقاعدة البيانات
- فحص إمكانية الكتابة على التخزين

#### 2. `ConfigModule`

المسؤولية:

- تحميل
  environment variables
- التحقق من القيم الإلزامية
- فصل إعدادات
  dev / staging / production

#### 3. `DevicesModule`

المسؤولية:

- تسجيل الأجهزة
- حفظ نوع الجهاز وإصداره وآخر نشاط
- إدارة
  install token
  أو
  device secret

#### 4. `UploadsModule`

المسؤولية:

- إنشاء جلسات رفع
  resumable upload sessions
- متابعة
  offset
  الحالي
- استقبال الأجزاء
  chunks
- إكمال الرفع أو رفضه

#### 5. `StorageModule`

المسؤولية:

- التعامل مع نظام الملفات
- الكتابة إلى
  incoming
  ثم النقل إلى التخزين النهائي
- إرجاع
  storage keys
- عزل منطق التخزين بحيث يمكن لاحقًا استبداله بـ
  S3-compatible storage
  دون كسر طبقة الأعمال

#### 6. `MetadataModule`

المسؤولية:

- استخراج بيانات
  MP3
  بعد الرفع
- تطبيع الحقول
  normalization
- استخراج الغلاف
  artwork
  إن وجد
- التحقق من مدة الملف وصيغته وخصائصه التقنية

#### 7. `LibraryModule`

المسؤولية:

- إنشاء أو تحديث السجل المنطقي للأغنية
- ربط السجل بالأصل الصوتي
  audio asset
- إدارة حالة الحذف المنطقي
  soft delete
- دعم الاستعلام عن المكتبة

#### 8. `ArtworkModule`

المسؤولية:

- تخزين الغلاف إن وجد
- إعادة تقديمه للأجهزة
- دعم
  caching headers
  و
  ETag

#### 9. `SyncModule`

المسؤولية:

- تسجيل أحداث التغيير
- تقديم
  bootstrap
  أولي للمكتبة
- تقديم
  incremental sync
  للأجهزة

### ما الذي لا أنصح به الآن

- لا
  Redis
  في البداية.
- لا
  Kafka
  أو
  RabbitMQ
  .
- لا تقسيم إلى عدة خدمات منفصلة.
- لا
  GraphQL
  لأن المطلوب بسيط وواضح ويخدمه
  REST
  بشكل ممتاز.

## 5. تصميم قاعدة البيانات

### المبدأ

افصل بين:

- الكيان المنطقي للأغنية
  track
- الملف الفعلي المخزن
  audio asset
- سجل التغييرات
  sync events

هذا يمنع ربط كل شيء مباشرة بالملف نفسه، ويسهل دعم إعادة الترميز، الغلاف، أو تعديلات metadata لاحقًا.

### الجداول الأساسية

#### `devices`

الغرض:

تعريف كل تثبيت شرعي للتطبيقات.

أعمدة مقترحة:

- `id`
- `platform`
- `device_name`
- `app_version`
- `install_token_hash`
- `last_seen_at`
- `created_at`
- `updated_at`

ملاحظات:

- لا يمثل مستخدمًا.
- يمثل جهازًا موثوقًا داخل المنظومة الخاصة.

#### `tracks`

الغرض:

التمثيل المنطقي للأغنية داخل المكتبة.

أعمدة مقترحة:

- `id`
- `primary_audio_asset_id`
- `artwork_asset_id`
- `title`
- `artist`
- `album`
- `album_artist`
- `genre`
- `disc_number`
- `track_number`
- `year`
- `duration_ms`
- `status`
- `created_at`
- `updated_at`
- `deleted_at`

ملاحظات:

- هذا الجدول لا يخزن الملف نفسه.
- هذا هو الكيان الذي ستشير إليه
  playlists
  و
  favorites
  و
  playback history
  لاحقًا.

#### `audio_assets`

الغرض:

وصف الملف الصوتي الفعلي المخزن على الخادم.

أعمدة مقترحة:

- `id`
- `track_id`
- `sha256`
- `storage_key`
- `original_filename`
- `mime_type`
- `file_extension`
- `file_size_bytes`
- `bit_rate_kbps`
- `sample_rate_hz`
- `channels`
- `duration_ms`
- `source_device_id`
- `created_at`

قيود مهمة:

- `sha256` يجب أن يكون
  unique
  .
- `storage_key` يجب أن يكون
  unique
  .

#### `artwork_assets`

الغرض:

تخزين الغلاف المستخرج إن وجد.

أعمدة مقترحة:

- `id`
- `sha256`
- `storage_key`
- `mime_type`
- `width`
- `height`
- `file_size_bytes`
- `created_at`

#### `upload_sessions`

الغرض:

تمكين الرفع القابل للاستئناف.

أعمدة مقترحة:

- `id`
- `device_id`
- `expected_sha256`
- `expected_size_bytes`
- `received_bytes`
- `temp_storage_path`
- `status`
- `expires_at`
- `created_at`
- `updated_at`

#### `library_events`

الغرض:

تغذية المزامنة التزايدية
incremental sync
بين الخادم والأجهزة.

أعمدة مقترحة:

- `id`
  من نوع
  `bigserial`
- `entity_type`
- `entity_id`
- `action`
- `payload_version`
- `created_at`

فكرة الجدول:

- أي إنشاء أو تحديث أو حذف يضيف صفًا جديدًا.
- كل جهاز يحتفظ بآخر
  event id
  تمت معالجته.
- هذا أبسط وأمتن من إعادة جلب كامل المكتبة كل مرة.

#### `device_sync_cursors`

الغرض:

معرفة آخر نقطة مزامنة لكل جهاز.

أعمدة مقترحة:

- `device_id`
- `last_event_id`
- `last_full_sync_at`
- `updated_at`

### علاقات الجداول

```text
devices 1 --- n upload_sessions
devices 1 --- 1 device_sync_cursors
tracks 1 --- n audio_assets
tracks 0 --- 1 artwork_assets
tracks 1 --- n library_events
```

### جداول مستقبلية بدون كسر التصميم

- `playlists`
- `playlist_items`
- `favorites`
- `playback_positions`
- `recently_played`
- `device_content_state`

## 6. معمارية تخزين الملفات

### مبدأ التخزين

لا تستخدم اسم الملف الأصلي كمفتاح تخزين فعلي.

استخدم:

content-addressed storage

يعتمد على:

SHA-256

### تخطيط التخزين على
VPS

```text
/srv/velody/
  data/
    incoming/
      <upload-session-id>.part
    quarantine/
      <upload-session-id>/
    library/
      audio/
        ab/
          cd/
            <sha256>.mp3
      artwork/
        ef/
          12/
            <sha256>.jpg
    temp/
  backups/
    postgres/
    storage-manifests/
```

### لماذا التخزين بالهاش أفضل

- يمنع مشاكل تكرار الأسماء.
- يمنع مشاكل الأحرف الغريبة في أسماء الملفات.
- يجعل التحقق من السلامة أسهل.
- يسهل التبديل لاحقًا إلى تخزين سحابي.
- يحسن تنظيم المجلدات عبر توزيع الملفات على مستويات فرعية.

### استراتيجية التسمية

- اسم الملف النهائي يعتمد على
  SHA-256
  .
- الامتداد يعتمد على النوع الفعلي المكتشف، وليس على الاسم المرسل فقط.
- الاسم الأصلي يحفظ في قاعدة البيانات لأغراض العرض أو التتبع فقط.

### استراتيجية الكاش

#### على الخادم

- ملفات الصوت النهائية لا تعتبر
  cache
  بل هي أصل دائم.
- الغلاف يمكن تقديمه مع
  `ETag`
  و
  `Cache-Control`
  مناسب.

#### على
macOS

- مجلد محلي لطابور الرفع
- قاعدة بيانات محلية لحالة الملفات
- ملفات مؤقتة للتحليل فقط

#### على
iPhone

- ملفات الصوت تحمل إلى
  `Application Support`
- ملفات الغلاف تحفظ في مسار منفصل
- الملفات المحلية تعامل كمكتبة
  offline library
  وليست مجرد
  streaming cache

### استراتيجية العمل بدون إنترنت

- التشغيل يجب أن يتم دائمًا من الملف المحلي المتحقق من سلامته.
- الواجهة تعرض آخر مكتبة معروفة محليًا حتى عند انعدام الشبكة.
- المزامنة لاحقًا تحاول فقط جلب التغييرات الجديدة بعد عودة الاتصال.

## 7. معمارية المزامنة

### أ. دورة رفع الملف من
macOS

1. يصل حدث تغيير من مجلد المراقبة.
2. يدخل الملف إلى مرحلة
   debounce
   وفحص الاستقرار.
3. إذا بقي الحجم ووقت التعديل ثابتين لعدة ثوانٍ، يعتبر جاهزًا.
4. يستخرج التطبيق بيانات أولية محليًا لتحسين تجربة المستخدم وسرعة التصنيف.
5. يحسب
   SHA-256
   عبر
   streaming
   وليس تحميل الملف كاملًا في الذاكرة.
6. يستعلم عن حالة الملف عبر
   `POST /api/v1/uploads/prepare`
   .
7. إذا كان الملف موجودًا مسبقًا بنفس الهاش:
   لا يعاد رفعه، بل يحدث الربط أو الحالة فقط.
8. إذا لم يكن موجودًا:
   ينشئ الخادم
   upload session
   ويعيد
   upload id
   و
   next offset
   .
9. يرفع العميل الملف على أجزاء متسلسلة.
10. عند اكتمال الرفع:
    يطلب العميل
    finalize
    .
11. الخادم يعيد حساب الهاش، ويتحقق من الحجم والصيغة.
12. إذا نجح التحقق:
    ينقل الملف من
    `incoming`
    إلى
    `library/audio`
    داخل عملية نهائية آمنة.
13. يحدث قاعدة البيانات داخل معاملة
    transaction
    واحدة.
14. يضاف حدث جديد إلى
    `library_events`
    .

### ب. دورة تنزيل الملف إلى
iPhone

1. التطبيق يحتفظ بآخر
   `event_id`
   تمت معالجته.
2. عند فتح التطبيق أو تشغيل
   background task
   يطلب:
   `GET /api/v1/sync/changes?after=<event_id>`
3. يحصل على:
   أغاني جديدة، تحديثات، وحذوفات.
4. ينشئ التطبيق مهام تنزيل عبر
   background URLSession
   .
5. ينزل الملف إلى مسار مؤقت.
6. بعد الاكتمال يتحقق من:
   الحجم، الهاش، وربما
   ETag
   .
7. ينقل الملف نقلاً ذريًا
   atomic move
   إلى موقعه النهائي.
8. يحدث السجل المحلي وقيمة
   sync cursor
   .
9. يصبح الملف قابلًا للتشغيل بالكامل بدون اتصال.

### ج. استراتيجية إعادة المحاولة

#### في
macOS

- خزّن طابور الرفع في قاعدة محلية دائمة.
- استخدم
  exponential backoff with jitter
  .
- مثال عملي:
  1 دقيقة، 5 دقائق، 15 دقيقة، ساعة، 6 ساعات.
- أخطاء
  5xx
  و
  network errors
  تعاد تلقائيًا.
- أخطاء
  4xx
  تعامل غالبًا كأخطاء دائمة وتظهر للمستخدم.

#### في
iPhone

- التنزيلات الكبيرة تتم عبر
  background session
  لتفادي توقفها عند انتقال التطبيق للخلفية.
- إذا انقطع التنزيل، يعتمد الاستئناف على دعم الخادم لطلبات
  `Range`
  وثبات
  `ETag`
  .

### د. معالجة التعارضات

في
Phase 1
لا يوجد تعارض متعدد المستخدمين عمليًا، لذلك نستخدم قواعد بسيطة وآمنة:

#### قاعدة الحقيقة

- الخادم هو المصدر النهائي للمكتبة.
- تطبيق
  iPhone
  لا يكتب إلى المكتبة في هذه المرحلة.

#### الملفات المكررة

- نفس
  `sha256`
  يعني نفس الأصل الصوتي.
- لا يعاد رفع الملف ولا تنشأ نسخة جديدة افتراضيًا.

#### نفس المسار المحلي مع ملف جديد

إذا كان تطبيق
macOS
يربط ذلك المسار سابقًا مع
track
معين ثم تغيرت محتوياته:

- يرفع الأصل الجديد
- ثم يرسل
  `existingTrackId`
  في مرحلة الإنهاء
- فيحدث السجل الحالي بدل إنشاء سجل جديد

هذا يحافظ على تحديث الأغنية نفسها عند تعديل الملف أو إعادة تصديره من نفس المصدر المحلي.

#### الحذف

- يفضل في البداية استخدام
  soft delete
  على الخادم.
- يرسل حدث حذف إلى الأجهزة.
- تطبيق
  iPhone
  يزيل الملف المحلي عندما لا يكون قيد التشغيل النشط.

### هـ. كشف التكرار

المرحلة الأولى:

- المفتاح الأساسي هو
  full-file SHA-256
  .

ترقية مستقبلية اختيارية:

- إضافة
  audio fingerprint
  لتوحيد الملفات المعاد وسمها
  re-tagged files
  حتى لو تغيرت
  ID3 tags
  .

## 8. استراتيجية مراقبة المجلد في
macOS

### الخيار الأفضل

استخدم:

FSEvents

عبر:

CoreServices

### لماذا هذا هو الخيار الصحيح

- مراقبة مجلدات بشكل
  recursive
  وبكلفة منخفضة.
- مناسب للمجلدات التي تحتوي عددًا كبيرًا من الملفات.
- أفضل من
  DispatchSource
  عندما يكون المطلوب مراقبة شجرة مجلدات كاملة.

### الاستراتيجية العملية

1. يختار المستخدم مجلد المكتبة عبر
   `NSOpenPanel`
   .
2. يحفظ التطبيق إذن الوصول كمؤشر دائم:
   `security-scoped bookmark`
   إذا كان التطبيق
   sandboxed
   أو تريد أن يبقى جاهزًا للتوزيع الرسمي.
3. يبدأ
   `FSEventStream`
   على جذر المجلد.
4. الأحداث لا تؤدي مباشرة إلى رفع الملف، بل إلى إعادة فحص ذكية للمسار المتأثر.
5. لا يعالج التطبيق الملف قبل التأكد من استقراره.
6. عند تشغيل التطبيق أول مرة أو بعد انقطاع طويل، ينفذ
   full reconciliation scan
   للمجلد كاملًا.

### اعتبارات الاعتمادية

- تعامل مع حالات
  event coalescing
  و
  dropped events
  بإعادة مسح المسارات المتأثرة.
- إذا ظهر علم مثل:
  `MustScanSubDirs`
  أو تغير جذر المجلد، نفذ مسحًا كاملًا.
- تجاهل الملفات المخفية والمؤقتة مثل:
  `.part`
  و
  `.tmp`
  .
- اعتمد على قاعدة محلية تسجل آخر
  hash / mtime / size
  معروف لكل ملف.

### اعتبارات الأداء

- لا تحسب الهاش لكل الملفات في كل حدث.
- استخدم مرحلتين:
  فحص سريع
  `mtime + size`
  ثم
  hashing
  عند الحاجة فقط.
- اجعل الرفع منفصلًا عن خيط
  watcher
  نفسه.
- حد أقصى لعدد عمليات الرفع المتزامنة.

## 9. استراتيجية التخزين والعمل بدون إنترنت في
iPhone

### التخزين المحلي

الموصى به:

- قاعدة بيانات محلية
  Core Data
  للكتالوج والحالة
- ملفات الصوت داخل
  `Application Support`
- ملفات الغلاف داخل مسار منفصل

### لماذا
Core Data
هنا

- ناضج جدًا
- يدعم
  migrations
  بوضوح
- يعمل جيدًا مع مهام الخلفية
- مناسب أكثر من حلول أخف عندما تكون الأولوية للاستقرار طويل الأمد

### أفضل الممارسات

- لا تحفظ الملفات في
  `Documents`
  إلا إذا أردت إظهارها للمستخدم كملفات.
- استبعد ملفات الموسيقى من النسخ الاحتياطي السحابي إذا كانت قابلة لإعادة التنزيل:
  `isExcludedFromBackup`
  .
- لا تشغل من الملف المؤقت.
- شغّل فقط من الملف النهائي المتحقق من سلامته.

### مزامنة الخلفية

استخدم:

- `BGAppRefreshTask`
  لفحص تغييرات خفيفة
- `BGProcessingTask`
  لتنزيلات أكبر أو إعادة التحقق الدوري
- `URLSessionConfiguration.background(withIdentifier:)`
  للتنزيلات الفعلية

### معمارية التشغيل الصوتي

استخدم:

- `AVPlayer`
  للتشغيل الفردي
- `AVQueuePlayer`
  للطوابير المستقبلية
- `AVAudioSession`
  بفئة
  `.playback`
- `MediaPlayer`
  لدعم شاشة القفل وأوامر الوسائط

### سلوك التشغيل بدون إنترنت

- بمجرد اكتمال تنزيل الملف والتحقق منه، يصبح التشغيل مستقلًا عن الشبكة تمامًا.
- الاحتفاظ بمسار الملف المحلي في السجل المحلي.
- الاحتفاظ بآخر موضع تشغيل محليًا تمهيدًا لمزامنته لاحقًا.

## 10. معمارية
API

### الاستراتيجية العامة

- استخدم
  URI versioning
  من البداية.
- المسار الأساسي:
  `/api/v1`
- لا تخلط بين مسارات الإدارة، الرفع، والمزامنة.

### بنية المسارات المقترحة

#### صحة النظام

- `GET /api/v1/health`

#### الأجهزة

- `POST /api/v1/devices/register`
- `POST /api/v1/devices/heartbeat`

#### الرفع

- `POST /api/v1/uploads/prepare`
- `GET /api/v1/uploads/:uploadId`
- `PATCH /api/v1/uploads/:uploadId`
- `POST /api/v1/uploads/:uploadId/finalize`

#### المكتبة

- `GET /api/v1/tracks`
- `GET /api/v1/tracks/:trackId`
- `GET /api/v1/tracks/:trackId/file`
- `GET /api/v1/artwork/:artworkId`

#### المزامنة

- `GET /api/v1/sync/bootstrap`
- `GET /api/v1/sync/changes`

### استراتيجية الرفع

لا أوصي في هذه المنظومة الخاصة بأن تبدأ بـ
multipart single-shot upload
فقط، لأن شرط الاستئناف مهم.

الأفضل:

- إنشاء جلسة رفع
- رفع متسلسل على أجزاء
- استعلام عن
  current offset
- إنهاء نهائي مع تحقق

### استراتيجية metadata

- التطبيق على
  macOS
  يمكنه إرسال metadata أولية.
- الخادم يجب أن يعيد استخراجها أو يتحقق منها بعد اكتمال الرفع.
- metadata النهائية المعتمدة يجب أن تكون من الخادم.

### استجابة المزامنة

يجب أن تعيد واجهة المزامنة:

- قائمة الأحداث الجديدة
- السجلات الكاملة المتأثرة
- الحذوفات كـ
  tombstones
- آخر
  `event_id`
  جديد ليحفظه العميل

### التوثيق

استخدم:

OpenAPI / Swagger

من البداية لتثبيت العقد
API contract
بين التطبيقات والخادم.

## 11. معمارية
Docker

### محليًا

الخدمات المطلوبة:

- `api`
- `postgres`
- `nginx`
  اختياري للمطابقة مع الإنتاج

### في
VPS

التوصية العملية:

- شغّل
  `api`
  و
  `postgres`
  عبر
  Docker Compose
  .
- شغّل
  Nginx
  على المضيف
  host
  مباشرة لسهولة إدارة
  TLS
  و
  Let's Encrypt
  .

هذا أكثر بساطة تشغيلية من حشر كل شيء داخل الحاويات في هذه المرحلة.

### الحاويات

#### `api`

- صورة
  Node.js
  إنتاجية
- يربط مجلد التخزين الدائم
- يستمع داخليًا على منفذ خاص مثل
  `3000`

#### `postgres`

- حاوية
  PostgreSQL
- بدون تعريض المنفذ للعالم الخارجي
- فقط على الشبكة الداخلية

### الأحجام
volumes

#### محليًا

- حجم أو
  bind mount
  لبيانات
  PostgreSQL
- مجلد محلي لاختبار تخزين
  MP3

#### إنتاجيًا

- `postgres_data`
- ربط:
  `/srv/velody/data`
  إلى داخل الحاوية

### الشبكات

- شبكة داخلية مثل:
  `velody_internal`
- قاعدة البيانات غير مكشوفة خارجيًا
- واجهة
  API
  لا تتعرض مباشرة للإنترنت
- المضيف فقط هو من يمرر الطلبات عبر
  Nginx

### استراتيجية النشر على
VPS

1. بناء صورة الخادم
2. رفع الصورة أو بناؤها على الخادم
3. تشغيل
   `docker compose up -d`
4. تمرير
   Nginx
   إلى
   `127.0.0.1:<api-port>`
5. تفعيل
   HTTPS
   عبر
   Let's Encrypt

### ما الذي يجب إضافته تشغيليًا

- نسخة احتياطية لقاعدة البيانات
- نسخة احتياطية لتخزين الملفات
- فحص
  healthcheck
  دوري
- سجلات منظمة
  structured logs

## 12. المكتبات والأدوات الموصى بها

### في تطبيقات
SwiftUI

#### الواجهة والحالة

- `SwiftUI`
- `Observation`
- `async/await`

#### الشبكات

- `URLSession`
- `URLSessionConfiguration.background`

#### التخزين المحلي

- `Core Data`

#### الصوت

- `AVFoundation`
- `AVPlayer`
- `AVQueuePlayer`
- `MediaPlayer`

#### مهام الخلفية

- `BackgroundTasks`

#### مراقبة المجلد في
macOS

- `FSEvents`
  عبر
  `CoreServices`

#### استخراج metadata والغلاف في
Apple side

- `AVFoundation`
- `AVAsset.commonMetadata`

### في الخلفية
Backend

#### الإطار الأساسي

- `NestJS`

#### الوصول إلى قاعدة البيانات

- `Prisma ORM`

#### التحقق من الطلبات

- `ValidationPipe`
- `class-validator`

#### رفع الملفات

- `@nestjs/platform-express`
- `multer`
  من خلال تكامل
  NestJS

#### التحقق من نوع الملف

- `file-type`

#### استخراج
MP3 metadata

- `music-metadata`

#### التشفير والتحقق

- مكتبة
  `crypto`
  المدمجة في
  Node.js

#### التوثيق

- `@nestjs/swagger`

### أدوات الاختبار

- `XCTest`
  لتطبيقات
  Apple
- `Jest`
  أو
  `Vitest`
  للخلفية
- اختبارات
  e2e
  لواجهات
  API

## 13. اعتبارات الأمان

حتى بدون نظام حسابات، لا ينبغي ترك النظام مكشوفًا بلا حماية.

### الحد الأدنى المطلوب

- `HTTPS only`
- عدم كشف
  PostgreSQL
  للعالم الخارجي
- عدم كشف منفذ الخادم مباشرة إن أمكن
- السماح بالوصول عبر
  Nginx
  فقط

### حماية الوصول

- استخدم
  install token
  أو
  shared secret
  لكل تطبيق مثبت
- خزنه داخل
  Keychain
  على
  Apple platforms
- خزن على الخادم فقط
  hash
  للرمز وليس الرمز الخام

### حماية الرفع

- تحقق من الحجم المتوقع
- تحقق من
  SHA-256
- تحقق من النوع الفعلي
  magic bytes
  وليس
  MIME
  المعلن فقط
- ارفض أي ملف ليس
  MP3
  في المرحلة الأولى
- انقل الملفات المشبوهة إلى
  quarantine

### حماية الملفات

- لا تستخدم المسارات المرسلة من العميل مباشرة على نظام الملفات
- لا تقدم الملفات من أسماء المستخدم الأصلية
- لا تسمح
  path traversal

### حماية البيانات

- استخدم مستخدم قاعدة بيانات بصلاحيات محددة
- افصل أسرار البيئة عن المستودع
- فعّل نسخًا احتياطية دورية
- راقب امتلاء القرص لأن التخزين الصوتي هو نقطة الفشل الأكثر واقعية

## 14. اعتبارات القابلية للتوسع

### ما الذي يجعل هذا التصميم قابلًا للنمو

#### 1. فصل
track
عن
asset

يسمح لاحقًا بإضافة:

- صيغ متعددة
- نسخ منخفضة الجودة
- نسخ
  lossless
- معاينات

#### 2. وجود
library_events

يسمح لاحقًا بـ:

- مزامنة أسرع
- عدة أجهزة
- متابعة
  continue listening
- مزامنة موجهة بالتغييرات بدل الفحص الكامل

#### 3. طبقة
StorageModule

تسمح لاحقًا بنقل التخزين من قرص
VPS
إلى تخزين كائني
object storage
بدون كسر
API
أو منطق الأعمال.

#### 4. طبقة
DevicesModule

تعطيك طريقًا نظيفًا لإضافة:

- أكثر من
  iPhone
- أكثر من
  Mac
- مزامنة متعددة الأجهزة

#### 5. بنية
NestJS modules

تسمح بإضافة:

- `playlists`
- `favorites`
- `search`
- `playback history`
- `lyrics`

كوحدات مستقلة نسبيًا.

### ما الذي لا يحتاج توسعة مبكرة

- لا تحتاج
  CDN
  الآن.
- لا تحتاج
  message broker
  الآن.
- لا تحتاج
  sharding
  أو
  distributed workers
  الآن.

## 15. خارطة طريق تنفيذية على مراحل

## Phase 1

الهدف:

تثبيت المعمارية، البيئة، العقود، والهيكل.

يشمل:

- إنشاء المستودع وهيكله
- تجهيز
  Xcode workspace
- تجهيز
  NestJS
  الأساسي
- تجهيز
  Docker Compose
- تصميم قاعدة البيانات
- تعريف
  API contracts
- توثيق كامل للمشروع

الناتج:

مرجع معماري واضح وقابل للتنفيذ.

## Phase 2

الهدف:

بناء الأساس الخلفي القابل للاختبار.

يشمل:

- إنشاء الخادم
  NestJS
- ربط
  PostgreSQL
- بناء
  Prisma schema
- إنشاء وحدات:
  `health`, `devices`, `uploads`, `storage`, `library`, `sync`
- تنفيذ رفع قابل للاستئناف
- حفظ ملفات
  MP3
  على القرص
- بناء
  sync events

الناتج:

خادم عامل محليًا مع مسار رفع وتخزين حقيقي.

## Phase 3

الهدف:

بناء تطبيق
macOS
كمصدر إدخال موثوق.

يشمل:

- اختيار مجلد المراقبة
- حفظ
  security-scoped bookmark
- بناء
  FSEvents watcher
- بناء طابور رفع دائم
- استخراج
  metadata
  والغلاف
- ربط الملف بـ
  existingTrackId
  عند التعديل من نفس المصدر المحلي

الناتج:

رفع تلقائي مستقر من
macOS
إلى الخادم.

## Phase 4

الهدف:

بناء تطبيق
iPhone
بمكتبة محلية وتشغيل بدون إنترنت.

يشمل:

- شاشة مكتبة محلية
- مزامنة أولية
  bootstrap sync
- مزامنة تزايدية
- تنزيلات خلفية
- تخزين محلي دائم
- تشغيل
  AVPlayer
  في الخلفية

الناتج:

تطبيق
iPhone
يعمل كمشغل موسيقى محلي متصل بالخادم عند الحاجة فقط.

## Phase 5

الهدف:

التقسية التشغيلية
hardening
.

يشمل:

- تحسين الاستئناف
- تحسين إعادة المحاولة
- تسجيلات منظمة
- مراقبة السعة
- نسخ احتياطية
- إدارة الحذف والمزامنة الدقيقة
- اختبارات
  e2e

## Phase 6

الهدف:

إضافة ميزات المنتج الأساسية.

يشمل:

- `playlists`
- `favorites`
- `search`
- `shuffle / repeat`
- `recently played`
- `continue listening`
- مزامنة مواضع التشغيل

## Phase 7

الهدف:

توسعة المنظومة بدون إعادة كتابة الأساس.

يشمل:

- `CarPlay`
- `Apple Watch`
- أكثر من جهاز لكل نوع
- مكتبات مشتركة مستقبلًا
- نظام صلاحيات حقيقي إذا لزم لاحقًا

## 16. بيئة التطوير والتشغيل المقترحة

### محليًا

#### تطبيقات
Apple

- `Xcode`
- `Swift 5.9+`
- `SwiftUI`
- `iOS Simulator`

#### الخلفية

- `Node.js 20+`
  ويفضل اعتماد إصدار
  LTS
  موحد في الفريق
- `npm`
  أو
  `pnpm`
- `Docker Desktop`
- `PostgreSQL`
  داخل
  Docker

#### مسارات محلية مفيدة

```text
runtime/local-dropbox/   -> مجلد إسقاط MP3 للتجربة
runtime/storage/         -> تخزين ملفات backend محليًا
runtime/logs/            -> سجلات التطوير
```

### إنتاجيًا على
VPS

- `Debian 12`
- `Docker Engine`
- `Docker Compose plugin`
- `PostgreSQL`
- حاوية
  backend
- تخزين محلي على القرص
- `Nginx`
- `Let's Encrypt`
- نطاق مثل:
  `music.diyaa.de`

### متغيرات بيئة أساسية

```text
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://...
STORAGE_ROOT=/srv/velody/data
PUBLIC_BASE_URL=https://music.diyaa.de
DEVICE_BOOTSTRAP_SECRET=...
MAX_UPLOAD_SIZE_BYTES=...
```

## 17. توصية ختامية

إذا كان الهدف هو بناء منظومة شخصية قوية، بسيطة، وتعيش طويلًا بدون إعادة كتابة لاحقًا، فهذه هي أفضل نقطة انطلاق عملية:

- `SwiftUI` native apps
- `NestJS` modular monolith
- `PostgreSQL` للبيانات الوصفية
- تخزين ملفات على قرص `VPS`
- مزامنة مبنية على `library_events`
- رفع قابل للاستئناف من `macOS`
- تنزيلات خلفية وتخزين محلي كامل على `iPhone`

هذه البنية ليست مبالغًا فيها، لكنها أيضًا ليست مؤقتة أو هشة. هي مناسبة جدًا لمنتج خاص يشبه
Spotify
من حيث الإحساس، لكن بدون تعقيد بنية خدمة عامة.

## 18. مراجع رسمية مفيدة

- Apple
  `FSEvents`
  https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html
- Apple
  `BackgroundTasks`
  https://developer.apple.com/documentation/uikit/using-background-tasks-to-update-your-app
- Apple
  `URLSession background transfers`
  https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1407496-background
- Apple
  `AVAudioSession`
  https://developer.apple.com/documentation/avfaudio/avaudiosession
- Apple
  `AVPlayer`
  https://developer.apple.com/documentation/avfoundation/avplayer
- Apple
  `AVQueuePlayer`
  https://developer.apple.com/documentation/avfoundation/avqueueplayer
- Apple
  `Core Data`
  https://developer.apple.com/documentation/coredata
- Apple
  `macOS App Sandbox and security-scoped bookmarks`
  https://developer.apple.com/documentation/security/app_sandbox/accessing_files_from_the_macos_app_sandbox
- NestJS
  `Modules`
  https://docs.nestjs.com/modules
- NestJS
  `Validation`
  https://docs.nestjs.com/techniques/validation
- NestJS
  `File upload`
  https://docs.nestjs.com/techniques/file-upload
- NestJS
  `Versioning`
  https://docs.nestjs.com/techniques/versioning
- NestJS
  `OpenAPI`
  https://docs.nestjs.com/openapi/introduction
- Prisma
  https://www.prisma.io/docs
- Docker Compose
  https://docs.docker.com/compose/
- Docker networks
  https://docs.docker.com/compose/how-tos/networking/
- Docker volumes
  https://docs.docker.com/reference/compose-file/volumes/
- PostgreSQL
  https://www.postgresql.org/docs/
- PostgreSQL
  `pgcrypto`
  https://www.postgresql.org/docs/16/pgcrypto.html
- `music-metadata`
  https://github.com/Borewit/music-metadata
- `file-type`
  https://github.com/sindresorhus/file-type
