MoonStack · Distribution · Plan

حزمة MoonStack «جاهزة-للجذر» (Docroot-Ready)
التشخيص الكامل + خطة الحل مرة واحدة

بُني من مسح متوازي بـ 5 وكلاء على كل نظام MoonStack (KB + Packaging + Installer + Updater + Build-modes)، مُتحقَّق بـ file:line. الهدف: زيب يُفكّ جاهز على الروت (install) ويعمل update لو نسخة موجودة — لـ build-mode الاتنين، من غير ما نكسر شريان تحديث الأسطول.

الجذر: PackageBuilder بيغلّف الزيب بمسارات جذر لارافيل الخام (public/ subfolder) — مفيش أي relayout. الحل الآمن: نحقن .htaccess في جذر الحزمة يحوّل كل الطلبات لـ public/ → الفك في public_html يبقى جاهز وآمن، والـ updater ما يتلمسش.

§1 إزاي MoonStack شغّال (end-to-end)

نظام توزيع/تحديث ذاتي‑الاستضافة لـ Moon‑ERP، معزول تمامًا عن نظام أحمد (Moon Central). كل المسارات تمرّ على نقطة تجميع واحدة.

1 · Buildmoonstack:build/shipPackageBuilder::build() يزيب الكود + vendor + الـFE المبني + manifest، ويوقّع (RSA) ويعمل sha256.
2 · PublishPublisher يكتب الزيب + sig + sha لـ moonui/public_html/moonstack/ ويحدّث versions.json.
3 · Install (fresh)public/moonstack-setup.phpApp\MoonStack\Installer\Installer (requirements → DB → .env/APP_KEY → migrate → seed → company/admin → finalize).
4 · Updatepublic/moonstack-update.php (delta-first، موقّع، backup → migrate → sync-reference seeders → health-check → finalize).
الوضعإزاي بيجمّع المصدرملاحظة
current server
moonstack-release.php
يزيب الشجرة الحيّة base_path() على moonui + الـ FE المبني مسبقًا /app (بدون node).الافتراضي للصفحة (CageFS كان يمنع node).
from git
scripts/moonstack-release-from-git.sh
clone نظيف + composer install --no-dev + ng buildmoonstack:ship --source=<be> --frontend=<fe>.يشحن المُلتزَم فقط؛ يتشغّل كـ root (node متاح).
نقطة محورية: الوضعين بيلتقوا على نفس PackageBuilder::build() — فإصلاح واحد هناك بيغطّي الاتنين. لا الـ script ولا الصفحة بيلمسوا الـ layout.

§2 السبب الجذري (مؤكد)

الـ layout بالكامل بيتحدّد من config/moonstack.php → package.include (سطور 149-156) اللي بيعدّد جذور لارافيل حرفيًا:

'include' => ['app','bootstrap','config','database','Modules','routes',
   'resources','lang','public','vendor','artisan','composer.json',
   'composer.lock','modules_statuses.json','.env.example']

PackageBuilder::addDir() (سطر 208) يحسب $rel = substr($abs, strlen($source)+1) ويضيفه بمساره الخام — بدون أي prefix. النتيجة: جذر الزيب = جذر لارافيل، public/ فولدر جوّه، والـ FE في public/app/. (مُتحقَّق من زيب moon-erp-v2.2.15.zip الحيّ: 15,978 ملف، top-level خام، vendor/ جوّاه 11,206 entry — يعني vendor مش ناقص في الـbuild؛ نقص prod كان فكّ ناقص/مقطوع لزيب 164MB).

سلسلة العَرَض على prod.elbaset.com

الحاجةالواقع
فكّيت الزيب في /home/prod/public_htmlنزل لارافيل خام في الـ webroot (app/ config/ public/ vendor/ .env.example …)
prod.elbaset.com/moonstack-setup.php404 — الملف فعليًا في /public/moonstack-setup.php (= 200)
الـ docroot = جذر لارافيلخطر أمني.env/config/storage/composer.json مكشوفين، مفيش root .htaccess
الخلاصة: الحزمة متصمّمة لـ «docroot = public/» (والدوكس بتأكّد إن التثبيت التجريبي تطلّب إعادة توجيه docroot يدويًا لـ …/publicself-hosted-distribution-plan). أنت فكّيت في public_html ومعملتش/ماتقدرش تعيد توجيه docroot → العَرَض حتمي.

§3 مشاكل ثانوية لازم نحلّها مع بعض («مرة واحدة»)

المشكلةالمكانالأثر
تضارب علامة التثبيت: المثبّت يكتب storage/moonstack/installed بينما index.php:19 يقرأ storage/installed القديمةInstaller.php:202 vs public/index.php:19كامن (مُقنّع بوجود .env) — يتصلّح بكتابة العلامتين في finalize
مفيش storage:link في مثبّت MoonStack (القديم install.php كان فيه)Installer.php (غايب)روابط public/storage للـ uploads مش بتتعمل
مفيش guard على vendor: لو with_vendor=true وvendor/ غايب، الـbuild بيعدّي بصمتPackageBuilder.php:74-86زيب بلا vendor → تثبيت ميّت عند الـboot. (RequirementsChecker مابيتشيكش على vendor → «أخضر» بعدين يموت)
seeders التحديث subset + non-fatalmoonstack.php:217-222reference data جديدة مش في القايمة دي ماتوصلش للعملاء (seeder-gap)

§4 القيود الحاكمة على أي حل

🔒 عزل مطلق عن نظام أحمد

أحمد يملك public/{install,update,deploy}.php + App\Services\* + config/{license,update}.php + moon:*. MoonStack يملك App\MoonStack\* + moonstack-*.php + moonstack:*. public/index.php مشترَك — أي تعديل فيه يخاطر بالتصادم → نتجنّبه.

🩸 ممنوع نكسر الـ Updater

الـ updater = شريان الأسطول؛ bug بيوقّف العملاء. بيكتب الـFE في public_path('app') = <root>/public/app ويطبّق الكود على base_path($rel) بمسارات نسبية لجذر لارافيل. أي حل يجب أن يُبقي مسارات الـzip نسبية لجذر لارافيل (un-wrapped) وإلا الـ delta/updater يتكسر.

+ نخليها بسيطة (لا licensing/telemetry) · نحافظ على حماية DATA (.env, storage/app, public/uploads never overwritten) + APP_KEY ثابت · plan-first إجباري لأي حاجة تلمس الـupdater.

§5 القرار: root .htaccess docroot-shim

نُبقي الحزمة لارافيل قياسي (مسارات نسبية لجذر لارافيل = الـupdater سليم 100%) ونضيف ملف واحد: .htaccess في جذر الحزمة يحوّل كل الطلبات لـ public/.

# .htaccess (جذر الحزمة = جذر لارافيل) — يخدم public/ كأنه الـ docroot
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_URI} !^/public/
  RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
# دفاع في العمق: امنع dotfiles/الأسرار من الجذر مباشرةً
RedirectMatch 404 /\.(?!well-known)
<FilesMatch "^(\.env.*|composer\.(json|lock)|artisan|.*\.sqlite)$">
  Require all denied
</FilesMatch>
ليه ده الصح:
  • تفك الزيب في public_htmlprod.elbaset.com/moonstack-setup.php يشتغل (يتحوّل لـ public/…). بدون تغيير docroot يدوي.
  • الكور محمي: كل طلب يتبادئ بـ public//.envpublic/.env (404)، /config/app.php→404. ملفات الكور غير قابلة للوصول.
  • الـupdater ما يتلمسش: مسارات الـzip تفضل نسبية لجذر لارافيل؛ الـ.htaccess الجديد مجرد ملف عادي — فعّال للتثبيت الجديد، وغير مؤذٍ للتثبيتات القديمة (لو docroot=public/ فالملف فوق الـdocroot = Apache يتجاهله).
  • إصلاح واحد في PackageBuilder::build() يغطّي build-mode الاتنين.

البدائل المرفوضة (وليه)

البديلليه اترفض
إعادة توجيه docroot لـ …/public (الموثّق)يتطلّب root/WHM (تعديل userdata + rebuild) — مش أوتوماتيك من الويب، والمالك مش عايز خطوة يدوية.
تغليف الكور في subfolder (public_html/core/) ونقل محتوى public للجذريكسر افتراض الـupdater public_path()=<root>/public، ويكسر توافق الـ delta (المسارات تتبدّل) → خطر على الأسطول. أنظف بصريًا لكن يلمس الشريان.
وضع الكور فوق الـwebroot (شقيق public_html)يتطلّب الفك في home + يكسر public_path للـupdater. خطر.
مقايضة بصرية: الحل ده يحمي لكن ملفات لارافيل تفضل ظاهرة في الـ webroot (File Manager). لو عايز «نضافة بصرية» (الكور في subfolder) أقدر أعملها كـ طور تانٍ اختياري بزيب-مثبّت منفصل (docroot-ready) مقابل زيب-تحديث (un-wrapped) — لكنها أكتر شغلًا واختبارًا وتلمس الـupdater. ترشيحي: نشحن الـ.htaccess دلوقتي (آمن وحلّ المشكلة الوظيفية)، والنضافة البصرية لاحقًا لو أصرّيت.

§6 التغييرات الدقيقة (مرة واحدة)

#التغييرالمكان (hook)
Aحقن root .htaccess (الـ shim أعلاه) في الـ full build، بجوار حقن manifest.json/skeletonPackageBuilder::build() ~:130-138 (addFromString('.htaccess',…))
Bguard على vendor: لو withVendor و!is_dir("$source/vendor") → فشل الـbuild بوضوحPackageBuilder::build() ~:74
Cstorage:link + كتابة العلامتين (storage/moonstack/installed وstorage/installed) في finalizeInstaller::install()/finalize() ~:54-64 / :189-198
DRequirementsChecker: شيك على vendor/autoload.php + layout (يفشل بصوت بدل ما يموت عند الـboot)RequirementsChecker::check() ~:29-60
E(اختياري) توسيع moonstack.updater.seeders لتغطية أي definition seeder جديد + جعلها fatal-on-errorconfig/moonstack.php:217-222 + moonstack-update.php:378-386

لا تغيير في scripts/moonstack-release-from-git.sh ولا moonstack-release.php — الاتنين بيلتقوا على PackageBuilder. ولا تغيير في public/index.php المشترَك (نحترم عزل أحمد): الـ layout الناتج لارافيل قياسي فالكشف $basePath=dirname(__DIR__) يشتغل صح.

§7 رَنبوك التثبيت الجديد (بعد الإصلاح)

  1. اعمل DB + DB user فاضيين في cPanel للحساب الجديد.
  2. نزّل moon-erp-v<ver>.zip جوّه public_html وفُكّه كامل (تأكد من اكتمال الفك — الزيب ~164MB).
  3. افتح https://<domain>/moonstack-setup.php → معالج WordPress-style (requirements → DB → admin) → تمام.

مفيش تغيير docroot. مفيش حفر في /public/. الكور محمي تلقائيًا.

§8 فكّ القفل الفوري لـ prod.elbaset.com (الحيّ)

prod دلوقتي عليه v2.2.15 مفكوك خام، docroot=جذر لارافيل، لسه مش متسطّب (مفيش .env/marker). نطبّق الإصلاح يدويًا عشان يشتغل حالًا:

prod حساب cPanel مستقل (user prod, IP 151.80.18.217). /home/whats/prod تطبيق تاني لا علاقة له.

§9 الطرح + التحقق

1 أطبّق A–D على /home/moonui/moon-erp-be + commit على hazemdev.
2 أبني نسخة جديدة (current-server) وأتأكد إن الزيب فيه .htaccess في الجذر + vendor + public/app.
3 اختبار فك في فولدر مؤقت → .htaccess + الـshim صح، والكشف يلاقي artisan.
4 أصلح prod + أكمّل تثبيته كـ«canary» للـ fresh-install.
5 أحدّث KB (dev-workflow/moonstack-update) بالرَنبوك الجديد + الـshim.

§10 قرارات محتاج موافقتك

1) أمشي بالحل المُرشَّح (root .htaccess shim) — آمن وبيحلّ المشكلة وظيفيًا وأمنيًا فورًا؟ ولا عايز الـ«نضافة البصرية» (الكور في subfolder، شغل أكتر + يلمس الـupdater)؟
2) أصلح prod الحيّ دلوقتي (أضيف الـ.htaccess وأكمّل التثبيت)، ولا تفضّل أبني/أنشر النسخة المُصلَّحة الأول وتفك من جديد؟
3) أضمّن إصلاح الـ seeders (E) في نفس الدُفعة، ولا أسيبه لدفعة منفصلة؟