306 lines
24 KiB
Markdown
306 lines
24 KiB
Markdown
# 34 — معمارية تعدد المستأجرين: سيرفر مستقل لكل مستأجر
|
||
|
||
> أُنشئ 2026-07-24. **حُدِّث في نفس اليوم بقرار المالك: سيرفر منفصل لكل مستأجر** (بدل قاعدة بيانات لكل مستأجر على سيرفر مشترك).
|
||
> كل رقم هنا مقروء من الكود مباشرة. المستودع: `~/development/App/Siro`.
|
||
> يُقرأ مع: `docs/21` (عيوب سيرو) · `docs/33` (الوحدات) · `docs/06` (نموذج المستأجر) · `docs/19` (الاستحقاقات).
|
||
|
||
---
|
||
|
||
## 0) القرار
|
||
|
||
**نسخة كاملة مستقلة لكل مستأجر على سيرفره الخاص.** لا كود تعدد مستأجرين إطلاقاً — لا `tenant_id`، ولا `TenantContext`، ولا حتى تعديل على `Database::get()`.
|
||
|
||
| البديل | الحكم |
|
||
|---|---|
|
||
| عمود `tenant_id` في 175+ جدول | ❌ مرفوض — أشهر عمل + خطر تسرّب دائم |
|
||
| قاعدة لكل مستأجر على سيرفر مشترك | 🟡 كان اقتراحي الأول (~5 أسابيع كود) |
|
||
| **سيرفر كامل لكل مستأجر** | ✅ **المعتمد — صفر كود تعدد مستأجرين** |
|
||
|
||
### لماذا هذا القرار صحيح فعلاً (وليس مجاملة)
|
||
|
||
1. **`docker/docker-compose.yml` موجود مسبقاً ومكتوب حرفياً لهذا الغرض** — التعليق في أول الملف: «سيرو في حاويات — **نسخة واحدة كاملة على سيرفر واحد**». البنية التي تحتاجها مبنية بالفعل.
|
||
2. **يُلغي كل كود تعدد المستأجرين** الذي قدّرته بـ5 أسابيع. تقديرك «يوم عمل لكل مستأجر» **واقعي — بشرط الأتمتة** (§3).
|
||
3. **العزل مطلق:** لا شرط `WHERE` منسي، لا بادئة Redis مشتركة، لا استعلام يخترق. المخاطرة الأمنية الأولى في أي SaaS تختفي كلياً.
|
||
4. **نصف قطر الانفجار = مستأجر واحد.** سيرفر يسقط أو يُهاجَم أو تمتلئ قاعدته → البقية لا يشعرون.
|
||
5. **يطابق وعد الموقع حرفياً:** «عزل بياناتك» لم يعد شعاراً تسويقياً بل حقيقة معمارية. **وباقة «سيادة» تصبح هي المعيار لا الاستثناء.**
|
||
6. **التصدير والنسخ الاحتياطي تافهان** — `mysqldump` واحد. أقوى نقطة بيع في العقد صارت أرخص ميزة عندك.
|
||
|
||
### التكلفة الحقيقية للقرار — وهي حقيقية
|
||
|
||
**أنت لم تُلغِ التعقيد، بل نقلته من الكود إلى التشغيل.**
|
||
بدل «كيف أعزل المستأجرين في كود واحد؟» صار السؤال **«كيف أدير 50 سيرفراً بلا فريق DevOps؟»**
|
||
هذا سؤال أسهل وأوضح — لكنه **يقتلك إن لم تحلّه بالأتمتة من المستأجر الثاني**، لا من العاشر. باقي هذا المستند هو الجواب.
|
||
|
||
---
|
||
|
||
## 1) 🔴 تصحيح فوري: «سيرفرات رخيصة» غير دقيق
|
||
|
||
جمعت حدود الذاكرة من `docker-compose.yml` الحالي:
|
||
|
||
| الحاوية | الحد |
|
||
|---|---|
|
||
| mysql | 2,560 م.ب |
|
||
| php-fpm | 2,048 |
|
||
| socket_driver | 768 |
|
||
| socket_passenger | 512 |
|
||
| redis | 512 |
|
||
| nginx | 256 |
|
||
| phpmyadmin | 256 |
|
||
| **المجموع** | **≈ 6.9 غ.ب** |
|
||
|
||
**سيرفر بـ2 غ.ب لن يشغّل هذه النسخة كما هي.** الحل ليس شراء سيرفرات ضخمة، بل **ثلاث وصفات compose حسب حجم المستأجر:**
|
||
|
||
| المقاس | الموارد | حدود مضبوطة | يناسب |
|
||
|---|---|---|---|
|
||
| **S** | 2 نواة / 4 غ.ب / 80 غ.ب SSD | mysql 1g · php 768m · redis 256m · بلا phpmyadmin | < 300 رحلة/يوم |
|
||
| **M** | 4 نواة / 8 غ.ب / 160 غ.ب | الحدود الحالية | < 3,000 رحلة/يوم |
|
||
| **L** | 8 نواة / 16 غ.ب + قرص منفصل لـMySQL | موسّعة + `pricing-engine` | > 3,000 رحلة/يوم |
|
||
|
||
**الوحدات تغيّر المقاس:** `pricing-engine` (Node) و`marketing_engine` يرفعان الحاجة درجة كاملة. اربط مقاس السيرفر بالباقة والوحدات في نفس عرض السعر.
|
||
|
||
**التكلفة التقديرية:** 50 مستأجراً على مزيج S/M ≈ **$700–1,200/شهر** مقابل إيراد $10k–35k/شهر. الهامش سليم تماماً — لكن **احسبها في التسعير من الآن**، لا لاحقاً.
|
||
|
||
> 🔴 **`phpmyadmin` يُحذف من وصفة الإنتاج.** لوحة قاعدة بيانات مكشوفة على 50 سيرفر = 50 باب خلفي. للصيانة: نفق SSH عند الحاجة فقط.
|
||
|
||
---
|
||
|
||
## 2) تقسيم الملفات: مشترك ↔ خاص بالمستأجر
|
||
|
||
سألت: هل نقدر نفصل المشترك عن الخاص بالمستأجر؟ **نعم — وهذا بالضبط الانضباط الوحيد الذي يجعل 50 سيرفراً قابلة للإدارة.**
|
||
|
||
```
|
||
/srv/tripz/
|
||
├── app/ ← من git. مشترك. ❌ لا يُعدَّل على السيرفر أبداً
|
||
│ ├── backend/ payment_server/ loction_server/ docker/
|
||
├── config/ ← خاص بالمستأجر. ليس في git إطلاقاً
|
||
│ ├── .env (DB · Redis · JWT · APP_DOMAIN · GLOBAL_COUNTRY)
|
||
│ ├── tenant.json (slug · اسم العرض · الباقة · الوحدات المفعّلة)
|
||
│ └── secrets/ (PayMob · Gemini · SMS · Firebase · مفاتيح JWT)
|
||
├── branding/ ← خاص: شعار · ألوان · اسم التطبيق · نصوص
|
||
└── storage/ ← خاص: uploads · logs · backups
|
||
```
|
||
|
||
### القاعدة الحديدية
|
||
|
||
> **إن عدّلت يوماً ملفاً داخل `app/` على سيرفر مستأجر — فقد صنعت فرعاً (fork)، والتحديث القادم إما يمسحه أو يفشل بتعارض.**
|
||
> أي سلوك خاص بمستأجر يُقاد من `config/` أو من أعلام الميزات — **لا بتعديل الكود. أبداً.**
|
||
|
||
هذه الجملة هي الفرق بين 50 سيرفراً تُحدَّث بأمر واحد، و50 فرعاً يستحيل صيانتها. اكتبها على الحائط.
|
||
|
||
الوضع اليوم جيد: `.gitignore` يستثني `.env` و`*.pem` و`service-account.json` بشكل صحيح. **الناقص:** فصل `config/` و`branding/` كمجلدات مستقلة خارج شجرة الكود.
|
||
|
||
---
|
||
|
||
## 3) التزويد: من «يوم عمل» إلى «أمر واحد»
|
||
|
||
**المستأجر الأول يدوياً — لكن وأنت تكتب كل خطوة.** ملاحظاتك هي السكربت. من المستأجر الثاني: السكربت فقط.
|
||
|
||
```bash
|
||
provision-tenant.sh --slug=acme --host=api.acme.com \
|
||
--size=M --plan=brand --country=EG \
|
||
--modules=ai,pricing_intel
|
||
```
|
||
ما يفعله بالترتيب:
|
||
1. إنشاء السيرفر عبر API المزوّد (Hetzner/DO) بالمقاس المطلوب
|
||
2. تقوية أساسية: مستخدم غير جذري · SSH بمفتاح فقط · جدار ناري (22/80/443 فقط) · fail2ban · تحديثات أمنية تلقائية
|
||
3. `git clone --branch <آخر وسم مستقر>` إلى `app/`
|
||
4. توليد `config/.env` بكلمات سرّ عشوائية + `tenant.json`
|
||
5. `docker compose up -d` بوصفة المقاس
|
||
6. تشغيل الهجرات + بذرة البيانات + `schema_version`
|
||
7. Let's Encrypt للنطاق
|
||
8. تركيب الكرونات (حسب الوحدات المفعّلة فقط)
|
||
9. تسجيل النسخ الاحتياطي اليومي
|
||
10. **تسجيل المستأجر في السجل المركزي** + توليد مفتاح التخاطر (§5)
|
||
11. **فحص دخان:** تسجيل دخول → عرض سعر → طلب رحلة وهمية → تأكيد وصول أول تخاطر
|
||
|
||
**الخطوة 11 غير قابلة للتفاوض.** «تم النشر» بلا فحص دخان يعني أن أول من يكتشف العطل هو العميل.
|
||
|
||
> 🔴 **مصيدة كلمات السرّ:** لا تستنسخ نفس `JWT_SECRET` أو كلمة سرّ MySQL عبر المستأجرين. اختراق واحد = سقوط الجميع. السكربت **يولّد** لكل مستأجر أسراره.
|
||
|
||
---
|
||
|
||
## 4) التحديثات عبر Git — النموذج الآمن
|
||
|
||
فكرتك صحيحة (git كمصدر واحد)، لكن `git pull` الساذج على 50 سيرفراً كارثة تنتظر. الفروق الأربعة التي تحوّلها لنظام:
|
||
|
||
### 4.1 وسوم لا فروع
|
||
**لا تنشر `main` أبداً.** انشر وسماً: `git fetch --tags && git checkout v1.4.2`. بلا هذا لا تعرف ما الذي يعمل عند أي مستأجر، ولا تستطيع التراجع.
|
||
|
||
### 4.2 نشر على حلقات (Rings)
|
||
```
|
||
الحلقة 0: سيرو (سيرفرك أنت) ← 24 ساعة مراقبة
|
||
الحلقة 1: 3 مستأجرين متعاونين ← 24 ساعة
|
||
الحلقة 2: الباقون ← دفعات من 10
|
||
```
|
||
**لا تنشر على الخمسين دفعة واحدة.** يوم واحد من الصبر يوفّر ليلة انهيار كامل.
|
||
|
||
### 4.3 هجرات + `schema_version`
|
||
كل سيرفر يسجّل نسخة مخططه. المنشِّط يرفض التشغيل إن كان الفارق أكثر من نسخة، ويوقف عند أول فشل بدل المتابعة.
|
||
**انجراف المخططات هو القاتل رقم واحد في هذا النموذج** — لأن كل سيرفر عنده MySQL خاص به.
|
||
|
||
### 4.4 تراجع جاهز قبل النشر لا بعده
|
||
`rollback-tenant.sh --slug=acme --to=v1.4.1` — يشمل هجرة عكسية. **إن لم يكن التراجع مجرَّباً فهو غير موجود.**
|
||
|
||
### 4.5 المنسّق
|
||
Ansible مع ملف جرد المستأجرين، أو سكربت bash + ssh حلقي إن أردت البقاء بسيطاً. الجوهر: **أمر واحد على جهازك ينفّذ على مجموعة محددة من السيرفرات ويعيد تقرير نجاح/فشل لكل واحد.**
|
||
|
||
> 🟠 **`deploy.sh` الحالي (`git add . && git push origin --all`) لا يصلح لهذا النموذج** — يدفع كل الفروع ويضيف كل شيء بلا تمييز. يُستبدل بمسار إصدار: فرع ميزة → مراجعة → دمج في main → وسم → نشر بالحلقات.
|
||
|
||
---
|
||
|
||
## 5) لوحة السوبر-أدمن والتخاطر المركزي
|
||
|
||
فكرتك: كل مستأجر يرسل بياناته لنقطة نهاية مركزية كل ساعتين. **الاتجاه صحيح تماماً** — دفع من المستأجر إلينا لا سحب: لا نحتاج منفذاً مفتوحاً في سيرفراتهم، يعمل خلف أي جدار ناري، وأنت لا تملك وصولاً دائماً لبياناتهم (وهذا **يقوّي** وعد الخصوصية على الموقع بدل أن يناقضه).
|
||
|
||
### 5.1 🔴 القاعدة التي تحمي الشركة: تجميعات فقط، لا بيانات شخصية
|
||
|
||
```jsonc
|
||
// ✅ يُرسَل
|
||
{ "slug":"acme", "period_start":"2026-07-24T10:00:00Z", "period_hours":2,
|
||
"trips":{"completed":412,"cancelled":37}, "gmv":{"amount":8420.50,"currency":"EGP"},
|
||
"drivers":{"active":88,"online_peak":61}, "passengers":{"new":24},
|
||
"health":{"version":"v1.4.2","schema_version":37,"uptime_s":864000,
|
||
"errors_5xx":3,"db_size_mb":2140,"disk_free_pct":62},
|
||
"modules":["ai","pricing_intel"] }
|
||
|
||
// ❌ لا يُرسَل أبداً
|
||
// أسماء · هواتف · إحداثيات · مسارات رحلات · صفوف مستخدمين · أي PII
|
||
```
|
||
|
||
سحب البيانات الخام لسيرفرك المركزي **يهدم وعد «بياناتك تبقى لك» حرفياً**، ويضعك تحت طائلة قوانين حماية بيانات في كل دولة يعمل فيها مستأجروك. **تجميعات فقط. بلا استثناء.**
|
||
|
||
### 5.2 عقد نقطة النهاية
|
||
|
||
| البند | القرار |
|
||
|---|---|
|
||
| المسار | `POST /telemetry/v1/report` |
|
||
| المصادقة | مفتاح لكل مستأجر + **توقيع HMAC** على الجسم + طابع زمني (حماية من إعادة الإرسال) |
|
||
| مفتاح التكرار | `(slug, period_start)` — إعادة الإرسال لا تُضاعف الحساب |
|
||
| التخزين | **append-only** للحمولة الخام + جداول مجمّعة مشتقة منها |
|
||
| فشل المركز | المستأجر **يصطفّ محلياً** ويعيد الإرسال لاحقاً — وإلا خسرت بيانات فوترة |
|
||
| فشل المستأجر | تنبيه بعد فوات نافذتين متتاليتين |
|
||
|
||
### 5.3 🟠 ساعتان بطيئة جداً للأعطال
|
||
|
||
الساعتان ممتازتان للفوترة والتحليل. **لكن انقطاع مستأجر يجب أن تعرفه خلال دقيقة لا ساعتين.**
|
||
➡️ **نبضة خفيفة كل 60 ثانية** (بضع بايتات: أنا حيّ + النسخة) + **الحمولة الكاملة كل ساعتين**. النبضة تكشف السقوط، والحمولة تروي القصة.
|
||
|
||
### 5.4 🔴 لا تفوتر من رقم يملكه العميل
|
||
|
||
المستأجر يملك سيرفره → يستطيع تعديل ما يرسله. **لا تبنِ فاتورة نسبة GMV على تخاطر غير موثوق وحده.**
|
||
الخيارات: ① مطابقة مع سجلات بوابة الدفع (المصدر المستقل الوحيد) · ② توقيع سجلات الرحلات بمفتاح لا يملكه المستأجر · ③ قبول المخاطرة صراحةً لباقة انطلاقة، والمطابقة للباقات الكبرى.
|
||
**اختر واحداً بوعي — لا تتركه للصدفة.**
|
||
|
||
### 5.5 الوصول للدعم
|
||
|
||
التخاطر يخبرك **أن** هناك مشكلة، لا **ما** هي. للتشخيص تحتاج مساراً استثنائياً:
|
||
مفتاح SSH دعم تحتفظ به + **بند صريح في العقد** يمنحك حق الدخول للصيانة، ويفضَّل أن يكون مؤقتاً ومسجّلاً. **لا تدخل سيرفر عميل بلا غطاء تعاقدي مكتوب.**
|
||
|
||
---
|
||
|
||
## 6) لوحات الأدمن وخدمة العملاء
|
||
|
||
فحصت `siro_admin` و`siro_service`: **كلاهما تطبيق Flutter وفيهما مجلد `web/`** — أي أن `flutter build web` يخرج صفحة ويب من نفس الكود مباشرة. **جوابك: نعم، ومن نفس المصدر بالضبط، بلا إعادة كتابة.**
|
||
|
||
المعمارية الصحيحة — **لوحتان منفصلتان تماماً، ولا تخلطهما أبداً:**
|
||
|
||
| | **لوحة المستأجر** | **لوحة السوبر-أدمن** |
|
||
|---|---|---|
|
||
| المصدر | `siro_admin` + `siro_service` (Flutter Web) | تطبيق مركزي جديد |
|
||
| أين تعمل | على سيرفر المستأجر نفسه | سيرفرك المركزي |
|
||
| ماذا ترى | بيانات هذا المستأجر فقط | تجميعات كل المستأجرين |
|
||
| العنوان | `admin.acme.com` · `support.acme.com` | `console.tripz.com` |
|
||
| العلامة | علامة المستأجر من `branding/` | علامتك |
|
||
|
||
> 🔴 **لا تبنِ لوحة واحدة تسجّل الدخول إلى كل سيرفرات المستأجرين.** ستحمل حينها مفاتيح دخول لكل عملائك في مكان واحد — وتُعيد بناء كل مخاطر تعدد المستأجرين التي تخلّصت منها للتوّ. السوبر-أدمن يقرأ من **قاعدة التخاطر المركزية فقط**.
|
||
|
||
**العمل المطلوب:** تعمية العلامة في `siro_admin`/`siro_service` (الاسم والشعار والألوان تُقرأ من `branding/` وقت البناء أو التشغيل) + بوابة استحقاقات تخفي الوحدات غير المشتراة (docs/19).
|
||
|
||
---
|
||
|
||
## 7) خطة العمل بالترتيب
|
||
|
||
| # | المهمة | المدة | البوابة |
|
||
|---|---|---|---|
|
||
| 1 | فصل `app/ ↔ config/ ↔ branding/ ↔ storage/` | 3 أيام | 🔴 كل ما بعده يعتمد عليها |
|
||
| 2 | ثلاث وصفات compose (S/M/L) + حذف phpmyadmin | 2 أيام | |
|
||
| 3 | **إصلاح عيوب المال في `docs/21`** | 5 أيام | 🔴 **قبل أول مستأجر مدفوع** |
|
||
| 4 | `provision-tenant.sh` + فحص الدخان | 4 أيام | |
|
||
| 5 | مسار الإصدار: وسوم + حلقات + `schema_version` + تراجع | 4 أيام | |
|
||
| 6 | التخاطر: مرسِل عند المستأجر + نقطة نهاية + نبضة 60ث | 4 أيام | |
|
||
| 7 | لوحة السوبر-أدمن (تقرأ التخاطر) | 5 أيام | |
|
||
| 8 | تعمية العلامة في admin/service + بوابة الاستحقاقات | 4 أيام | |
|
||
| | **المجموع** | **~5 أسابيع** | |
|
||
|
||
**لماذا ما زالت 5 أسابيع رغم أن كود تعدد المستأجرين اختفى؟** لأن العمل انتقل — من كتابة عزل في الكود، إلى بناء أدوات أسطول. **الفارق الحاسم: هذه الأدوات تُبنى مرة وتخدم كل مستأجر إلى الأبد، أما عزل الكود فكان سيبقى خطراً دائماً في كل استعلام جديد.** الصفقة رابحة.
|
||
|
||
**وضع الحد الأدنى (لأول 3 مستأجرين):** البنود 1 · 3 · 4 فقط ≈ **12 يوماً**. الباقي يُبنى بينما تبيع — لكن **لا تتجاوز الخامس** بلا البند 5 (التراجع)، ولا **العاشر** بلا البند 6 (التخاطر).
|
||
|
||
---
|
||
|
||
## 7.5) مدة التسليم وإيقاع التحديثات — مقارنة بالسوق
|
||
|
||
### ما يعد به المنافسون فعلياً (مقروء من مواقعهم 2026-07-24)
|
||
|
||
| المنصة | الوعد المعلن |
|
||
|---|---|
|
||
| **Onde** — الصفحة التسويقية | «أطلق خلال **14 يوماً**» |
|
||
| **Onde** — صفحة الأسئلة الشائعة | «إطلاق المنصة يستغرق **حوالي 4 أسابيع**» |
|
||
| متوسط القطاع (مزوّدو white-label) | **2 – 8 أسابيع** |
|
||
|
||
**الملاحظة الأهم:** Onde نفسها **تعد بـ14 يوماً في الإعلان و4 أسابيع في الأسئلة الشائعة.** هذا ليس تناقضاً عشوائياً — الرقم التسويقي يقيس *جاهزية المنصة*، والرقم الواقعي يشمل **مراجعة المتاجر** التي لا يملك أحد التحكم بها.
|
||
|
||
**وعدنا (30 يوماً) في الموضع الصحيح تماماً:** أصدق من «14 يوماً» وأسرع من سقف القطاع. **لا تخفّضه** — تأخير أسبوع على وعد 14 يوماً يحرق الثقة، بينما التسليم في 20 يوماً على وعد 30 يوماً يصنع بطلاً.
|
||
|
||
> **صياغة موصى بها في العقد:** «المنصة جاهزة وتعمل خلال 10 أيام عمل · النشر على المتجرين خلال 30 يوماً، وهو خاضع لمراجعة آبل وجوجل.» تفصل ما تتحكم به عمّا لا تتحكم به.
|
||
|
||
### إيقاع تحديثات المنافسين
|
||
Onde تقول: «تحديثات منتظمة لكلا النظامين بلا رسوم إضافية» — **بلا جدول معلن ولا ذكر لتحديث هوائي.** أي أن **التحديث الهوائي (Shorebird) تمايز حقيقي لك، لا لحاق بالركب.**
|
||
|
||
---
|
||
|
||
## 7.6) التحديث الهوائي (Shorebird) — الفرصة والمصيدة
|
||
|
||
### القاعدة التقنية الحاكمة
|
||
**Shorebird يرقّع كود Dart فقط.** أي تغيير في الطبقة الأصلية — Kotlin/Swift · إضافات (plugins) · أذونات · ترقية محرك Flutter — **يستوجب إصداراً كاملاً في المتجر.**
|
||
هذا يتطابق مع القرار المثبّت في `docs/22 §1.5`: **الطبقة الأصلية تبقى موحّدة عبر كل المستأجرين والمستويات** — وهذا بالضبط ما يجعل الترقيع الهوائي ممكناً أصلاً. **أي انحراف في الطبقة الأصلية بين مستأجر وآخر يقتل الميزة كلها.**
|
||
|
||
### 🔴 المصيدة: الرقعة تتضاعف بعدد المستأجرين
|
||
كل مستأجر = تطبيقان بهويتَي حزمة خاصتين = **تطبيقان مستقلان في Shorebird**.
|
||
50 مستأجراً = **100 تطبيق × كل إصلاح**. الترقيع اليدوي مستحيل عند هذا الحجم — ولا تكتشف ذلك عند المستأجر الأربعين.
|
||
|
||
**اللازم:** `patch-all.sh` يمرّ على جرد المستأجرين، ويرقّع بنفس الحلقات المعتمدة في §4.2 (سيرو أولاً → متعاونون → الباقون). **ونفس قاعدة التراجع: رقعة قابلة للسحب، مجرَّبة قبل الاعتماد عليها.**
|
||
**وتنبيه تكلفة:** تسعير Shorebird يقوم على التطبيقات وعمليات تثبيت الرقع — تكلفتك تنمو خطياً مع عدد المستأجرين. **احسبها ضمن الكلفة التشغيلية لكل مستأجر من الآن**، مع مقاسات السيرفرات في §1.
|
||
|
||
### 🟠 حدّ آبل
|
||
Shorebird مصمَّم للامتثال، لكن بند **App Store 3.3.2** يبقى منطقة الخطر: استعمل الرقع **لإصلاح الأعطال وتعديل السلوك**، لا لشحن ميزات تغيّر الغرض الأساسي للتطبيق. **الميزات الكبيرة تمرّ عبر المتجر.** انضباط بسيط يحميك من رفض جماعي يطال كل مستأجريك دفعة واحدة.
|
||
|
||
### الإيقاع المقترح
|
||
| النوع | القناة | التكرار |
|
||
|---|---|---|
|
||
| إصلاح عاجل (Dart) | رقعة Shorebird | خلال ساعات |
|
||
| تحسينات دورية (Dart) | رقعة | كل أسبوعين |
|
||
| ميزة كبيرة أو تغيير أصلي | إصدار متجر | كل 6–8 أسابيع |
|
||
| الباك إند | git بالحلقات (§4) | مستقل تماماً عن التطبيقات |
|
||
|
||
**الرسالة البيعية:** «إصلاح عندك خلال ساعات، لا خلال أسبوعين انتظاراً لمراجعة المتجر.» — **لا أحد في القطاع يقدر يقولها.**
|
||
|
||
---
|
||
|
||
## 8) القرارات المطلوبة منك
|
||
|
||
1. **المزوّد**: Hetzner (الأرخص بفارق كبير، أوروبا) · DigitalOcean · مزوّد محلي في كل دولة؟ *(للسيادة في مصر/السعودية قد يفرض العقد مزوّداً محلياً — احسبها الآن)*
|
||
2. ~~النطاقات~~ ✅ **محسوم (المالك 2026-07-24): نطاق فرعي لكل مستأجر تحت نطاقنا** — `acme.tripz.app` · `api.acme.tripz.app` · `admin.acme.tripz.app`. **لا IP في التطبيقات إطلاقاً.** الفوائد: شهادة wildcard واحدة (`*.tripz.app`) بدل 50 شهادة منفصلة تنتهي في 50 موعداً · إنشاء المستأجر = سجل DNS واحد بلا انتظار العميل · نقل السيرفر = تغيير سجل A بلا تحديث متجر. **يبقى مسموحاً** للمستأجر الكبير أن يوجّه `api.acme.com` بـCNAME إلى نطاقه الفرعي لاحقاً (تُعالج بشهادة منفصلة) — لكن الافتراضي هو نطاقنا.
|
||
3. **الفوترة**: تُبنى على التخاطر وحده أم بمطابقة بوابة الدفع؟ (§5.4)
|
||
4. **حق دخول الدعم**: بند تعاقدي دائم أم إذن مؤقت لكل حادثة؟
|
||
5. **مستودع Tripz أم مستودع سيرو؟** هل يصبح كود سيرو هو `backend/` في مستودع Tripz، أم يبقى مستودعاً مستقلاً ويُسحب كـsubmodule؟ *(توصيتي: مستودع واحد باسم Tripz — مصدر حقيقة واحد للنشر)*
|
||
|
||
---
|
||
|
||
## سجل التغييرات
|
||
|
||
| التاريخ | التغيير |
|
||
|---|---|
|
||
| 2026-07-24 | إنشاء الدراسة (DB لكل مستأجر) |
|
||
| 2026-07-24 | **تحديث جذري بقرار المالك: سيرفر كامل لكل مستأجر.** أُضيف: مقاسات السيرفرات، تقسيم الملفات، مسار الإصدار بالحلقات، عقد التخاطر، فصل لوحة المستأجر عن السوبر-أدمن |
|