إدارة النظام والدعم
هذا هو دليل من ينشر النظام، ويشغّله، ويدعمه — مشغّل منصّة Waves Technology. خلافًا لكل دليل آخر في هذه المجموعة، لا يقتصر هذا الدليل على دور داخل منظمة غير حكومية: أنت تقع خارج أي منظمة، على مضيفك الخاص، ومهمّتك أن تُبقي المنصّة سليمة للجميع عليها دون أن تصل إلى بيانات أي مستأجر إلا حين تُدعى إلى ذلك صراحةً ومؤقّتًا.
يأتي هذا الدليل في نصفين:
- الجزء أ — وحدة تحكّم مشغّل المنصّة: عالَم التشغيل (ops) الذي تسجّل الدخول إليه، وطبقاته الثلاث من الوصول (تجميعي ← محجوب ← كسر الزجاج).
- الجزء ب — النشر والتشغيل والإدارة: إقامة الحزمة (stack)، والتزويد، وقاعدة البيانات، والتهيئة، وعالَم المرشّح، والصحّة، ودليل إجراءات الدعم.
المبادئ الأربعة — اعرف في أي عالَم أنت
لدى HeadHunter أربعة أنواع من المستخدمين، وجلساتهم مفصولة عمدًا حتى لا يعبر ضعف في أحدها إلى غيره:
| المبدأ | يسجّل الدخول على | ملفّ تعريف الجلسة | سرّ التوقيع |
|---|---|---|---|
| فريق المنظمة (org_admin، hr_manager، recruiter، compliance_officer) | app.<domain> |
hh_session (٨ ساعات) |
SESSION_SECRET |
| المراجِع الخارجي (hiring_manager) | app.<domain> |
hh_session (يركب عالَم المنظمة، مقيَّد بالوظيفة) |
SESSION_SECRET |
| مشغّل المنصّة (أنت) | ops.<domain> |
hh_ops_session (ساعتان) |
OPS_SESSION_SECRET |
| المرشّح (الباحث عن عمل) | نطاق التسويق الجذر <domain> |
hh_candidate_session |
CANDIDATE_SESSION_SECRET |
يخدم مونوليث واحد كل هذه المضيفات ويوجّه بحسب ترويسة Host. جلسة التشغيل منفصلة عمدًا — عمرها
الأقصر البالغ ساعتين وسرّها الخاص يعزلان دائرة تأثير وحدة تحكّم المشغّل عن عالَمَي المنظمة والمرشّح.
حسابك يعيش في platform_admins، لا في جدول users الخاص بأي منظمة.
الجزء أ — وحدة تحكّم مشغّل المنصّة
تسجيل الدخول
اذهب إلى /ops/login على مضيف التشغيل (ops.localhost في بيئة التطوير، وops.<domain> في
الإنتاج). تستوثق مقابل عالَم مشرفي المنصّة، وهو منفصل تمامًا عن تسجيل دخول أي منظمة — يعيد بريد خاطئ
وكلمة مرور خاطئة الخطأ العام نفسه، فلا تكشف الصفحة أبدًا ما إذا كان الحساب موجودًا. عند النجاح تحصل على
hh_ops_session مدّتها ساعتان؛ يُعيد كل طلب قراءة سجلّك ويؤكّد من جديد أن حسابك لا يزال نشطًا. لا
يوجد تسجيل ذاتي — يُزوَّد المشغّلون من سطر الأوامر (انظر التزويد في الجزء ب).
ملاحظة: لا يملك تسجيل دخول التشغيل بعد التحقّق بخطوتين ولا الحظر بعد محاولات كثيرة — وهذا متابعة سريعة مخطّطة. أبقِ الوصول إلى مضيف التشغيل محكمًا على مستوى الشبكة في هذه الأثناء.

أدوار المشغّل الأربعة
تأتي صلاحياتك من دورك على المنصّة (platform_admins.role)، وهو خريطة تُبقى منفصلة تمامًا عن نظام
صلاحيات المنظمات (RBAC) حتى لا يرث المشغّل أبدًا صلاحية مقصورة على مستأجر:
| الدور | ما يمكنه فعله |
|---|---|
superadmin |
كل شيء — بما في ذلك إدارة مشرفي المنصّة الآخرين، وتعليق المنظمات، وطلب كسر الزجاج. |
support |
الطبقتان ١ و٢، ويمكنه طلب كسر الزجاج (لا تُفتح الطبقة ٣ إلا تحت منحة حيّة من المنظمة). |
readonly |
الطبقة ١ والطبقة ٢ للقراءة فقط. لا كسر زجاج أبدًا. |
ai_agent |
الطبقتان ١ و٢ برمجيًّا. غير مؤهّل أبدًا لكسر الزجاج، ولو بنيويًّا. |
القاعدة التي عليك استيعابها: لا يستطيع الوصول إلى بيانات المرشّحين الحقيقية سوى superadmin
وsupport، وذلك فقط تحت منحة نشطة. أما readonly وai_agent فمحظوران من كسر الزجاج لا بخريطة
الصلاحيات وحدها، بل بفحص بنيوي ثانٍ — فتخفيف الخريطة لن يُدخلهما.
طبقات الوصول الثلاث
بُنيت وحدة التحكّم على شكل ثلاث طبقات متراكزة. تقضي معظم وقتك في الطبقتين الخارجيّتين اللتين لا تكشفان بيانات شخصية أبدًا؛ أما أعمقها فاستثناء نادر مشروط بالموافقة.
الطبقة ١ — لوحة التجميع (/ops/dashboard)
نبض عرضك اليومي، خالية من البيانات الشخصية بحكم بنيتها — مصدر بياناتها الوحيد يحسب أعدادًا وتجميعات ولا يختار أي أعمدة شخصية (يفرض اختبار ألّا يظهر فيها حتى اسم عمود يحمل بيانات شخصية). تعرض:
- المنظمات — الإجمالي والنشِطة (النشِطة = لديها طلب واحد على الأقل في آخر ٣٠ يومًا).
- المستخدمون والوظائف (مسوّدة / مفتوحة / مغلقة).
- الطلبات — الإجمالي مقسَّمًا حسب الحالة.
- تسجيلات المنظمات الجديدة شهريًّا — خطّ بياني موجز لستّة أشهر.
- صحّة طابور التحليل — في الطابور / قيد التشغيل / خطأ، بالإضافة إلى الأخطاء الأخيرة في آخر ٢٤ ساعة.
- ملخّص الفوترة — المنظمات المخوّلة للتجمّع، والاشتراكات النشطة حسب الفئة، والمدفوعات اليدوية الأخيرة.
- صحّة النظام — إمكانية الوصول إلى قاعدة البيانات + عدد الترحيلات (migrations) المطبَّقة، وحيويّة المحلِّل.
قراءة الطبقة ١ مراقبة لا إجراء، لذا لا تكتب أي سطر في سجلّ التدقيق.
ما لا تعرضه الطبقة ١: لا توجد هنا أي مقاييس للمرشّحين — لا عدد لحسابات المرشّحين، ولا أعداد للتأكيد. الرقم الوحيد المتعلّق بـ«التسجيلات» هو المنظمات الجديدة شهريًّا. حسابات المرشّحين ذاتية الخدمة على موقع التسويق ولا تظهر في لوحة المشغّل.

الطبقة ٢ — تشخيصات المنظمة الواحدة (/ops/orgs، /ops/orgs/[id])
حين تحتاج منظمة إلى دعم، ابدأ هنا. يسرد الدليل (/ops/orgs) صفًّا واحدًا لكل مستأجر — الحالة،
وفئة الاشتراك، وهل هو معلَّق، وأعداد المستخدمين والوظائف المفتوحة والطلبات — مع بحث اختياري ?q= على
اسم المنظمة. وهو خالٍ من البيانات الشخصية، وقراءة الدليل غير مدوَّنة في التدقيق.
افتح منظمة لتتعمّق فيها. كل تعمّق يُدوَّن (org.viewed)، وما تراه محجوب عمدًا:
- مؤشّرات الميزات كقيَم منطقية فقط — مثل «تهيئة استقبال البريد: نعم/لا»، «تهيئة WhatsApp: نعم/لا». لا يظهر أبدًا مُعرِّف الاستقبال الخام (ingest slug) ولا رقم الهاتف.
- حسابات الفريق — يظهر بريد مستخدمي المنظمة فعلًا؛ فهم جهات اتصالك لدى العميل، لا مرشّحون.
- صحّة المحلِّل مع الإخفاقات الأخيرة — حيث تظهر سيرة ذاتية أخفق تحليلها، يُقنَّع المرشّح إلى أحرفه الأولى فقط. يُستخدم الاسم الخام لاشتقاق تلك الأحرف الأولى فحسب ولا يُعاد أبدًا؛ ولا يُكشف أي مسار ملف أو اسم ملف لسيرة ذاتية.
- سجلّ تدقيق المنظمة نفسها، مع ضمّ الفاعل من الفريق.
من هنا يستطيع superadmin أن يعلّق أو يعيد تنشيط مستأجر (مشروط بـplatform.org.suspend)؛
وكلا الإجراءين مدوَّن (org.suspended / org.reactivated).

الطبقة ٣ — كسر الزجاج (/ops/orgs/[id]/breakglass)
هذا هو المسار الوحيد إلى بيانات المرشّحين الحقيقية غير المحجوبة (الاسم، والبريد، والهاتف، والموقع، ووجود سيرة ذاتية من عدمه)، وهو مسيَّج من كل جانب. عامله كالاستثناء النادر الذي هو عليه.
- بوّابتان، لا واحدة. تحتاج إلى صلاحية
platform.breakglass.requestوأيضًا أن تجتاز إعادة فحص أهلية بنيوية.superadminوsupportوحدهما مؤهّلان؛ أماai_agentوreadonlyفلا يستطيعان الدخول أبدًا، مهما كانت خريطة الصلاحيات. - يجب أن توجد موافقة حيّة، ممنوحة من المنظمة، ومحدَّدة بمدّة. يُحسب الوصول عند وقت القراءة: لحظة أن تنتهي صلاحية المنحة أو تلغيها المنظمة، تعود الصفحة إلى حالة «لا وصول — اطلب الوصول من المنظمة» ولا تقرأ أي بيانات على الإطلاق.
- كل قراءة لبيانات حقيقية مدوَّنة، على الجانبين، ومختومة بمُعرِّف المنحة. لا منحة ← لا بيانات ← لا سطر تدقيق.
- سياسة المدّة (TTL): تدوم المنحة ساعة واحدة كحدٍّ أدنى، وتفترض ٢٤ ساعة، وتُسقَف عند ٧٢ ساعة (٣ أيام).
كيف تُنشأ المنحة (من جانب المنظمة). تفتح المنظمة الإعدادات ← وصول الدعم
(/settings/support-access على مضيفها app. — إجراء لـ org_admin). تختار مشغّلًا مسمّى ومؤهّلًا،
وتعطي سببًا ومرجع دعم اختياريًّا، وتضبط مدّة ضمن نافذة ١–٧٢ ساعة. يكتب ذلك منحةً بتاريخ انتهاء ويدوّنها
على الجانبين — سجلّ المنظمة نفسها (support_access.granted) ومرآة المشغّل (breakglass.granted).
وتستطيع المنظمة الإلغاء فورًا في أي وقت، وهو ما يُنعكس بالمثل. تكون المنحة «حيّة» فقط ما دامت غير
منتهية وغير ملغاة.

الصورة التي عليك تذكّرها: المنظمة تمسك المفتاح. يمكنك أن تطلب، لكنك لا ترى بيانات مستأجر الحقيقية إلا ما دام ذلك المستأجر يسمح لك — بفعل نشط وواعٍ ومؤقّت — وكل ثانية من ذلك مسجَّلة لكليكما.
الجزء ب — النشر والتشغيل والإدارة
الحزمة والطوبولوجيا
HeadHunter مونوليث ممل يُشغَّل بـ Docker Compose — أربع خدمات، وأمر واحد لإقامته:
nginx(nginx:alpine) — حافّة TLS على المنفذين 80 و443. تُنهي HTTPS وتمرّرHostالحقيقي كـX-Forwarded-Hostوالمخطَّط كـX-Forwarded-Proto، حتى يخدم التطبيق الواحد المضيفات الثلاث.app— تطبيق SvelteKit 2 / adapter-node (مبنيّ من./app).parser— المحلِّل الجانبي الوحيد Python 3.12 FastAPI: عديم الحالة، يعمل على المعالج فقط، وغير مكشوف للعموم. يصل إليه التطبيق وحده، عبر شبكة compose الداخلية علىhttp://parser:8000.db—postgres:15-alpine.
مُصمَّم ليناسب خادمًا افتراضيًّا (VPS) متواضعًا على Hostinger في الاتحاد الأوروبي (الاستضافة في
الاتحاد الأوروبي هي أيضًا إشارة الثقة بموجب GDPR في وعد الخصوصية للمنتَج) — تكفي KVM 1 (1 vCPU /
4 GB) لبرنامج تجريبي؛ وتمنح KVM 2 (2 vCPU / 8 GB) هامشًا أكبر. تعيش الحالة الدائمة في وحدتَي تخزين
مسمّاتين: postgres_data (قاعدة البيانات) و**cv_storage** المركّبة على /app/uploads
(ملفات السير الذاتية الخام — بيانات شخصية، تُبقى خارج الصورة (image) وخارج git). يعمل التطبيق والمحلِّل
مُصلَّبين (no-new-privileges، ويُسقِط التطبيق كل قدرات Linux إلا القليل الذي يحتاجه).
دورة الحياة — ./headhunter.sh
كل شيء روتيني يمرّ عبر سكربت دورة الحياة في جذر المستودع:
./headhunter.sh setup # أول تشغيل: إنشاء .env (بأسرار عشوائية قوية) + شهادة موقَّعة ذاتيًا
./headhunter.sh up # بناء + تشغيل الحزمة (docker compose up -d --build)
./headhunter.sh status # حالة الحاويات (docker compose ps)
./headhunter.sh logs # متابعة سجلّات التطبيق (Ctrl-C للإيقاف)
./headhunter.sh down # إيقاف كل شيء — تُحفَظ وحدة تخزين postgres_data
./headhunter.sh restart # down + up
ينسخ setup الملف .env.example ← .env ويملأ DB_PASSWORD وSESSION_SECRET
وOPS_SESSION_SECRET بقيَم من openssl rand. في الإنتاج، اضبط CANDIDATE_SESSION_SECRET
صراحةً — انظر التهيئة والأسرار. مرِّر -y / --yes لتخطّي رسائل التأكيد في الاستخدام المُبرمَج.
التزويد
لا يوجد تسجيل ذاتي للمنظمات أو المشغّلين — كلاهما يُنشأ عمدًا من سطر الأوامر. شغّل هذه من /app مع ضبط
DATABASE_URL (أو عبر أغلفة headhunter.sh، التي تشغّلها مقابل حاوية قاعدة البيانات الحيّة).
منظمة جديدة و org_admin الأول لها:
npm run provision -- \
--org-name "Mercy NGO" --org-name-ar "منظمة الرحمة" \
--email admin@mercy.org --password 'StrongPass123' --full-name "Aisha Ali" \
[--role org_admin] [--locale ar] [--tier pilot] [--pool-entitlement]
# أو: ./headhunter.sh provision --org-name "Mercy NGO" --email admin@mercy.org ...
مشغّل منصّة (يُنشأ أول superadmin بهذه الطريقة؛ ثم يُدار المشغّلون التالون داخل وحدة التحكّم):
npm run provision-admin -- \
--email ops@waves.tech --password 'StrongPass123' --full-name "Operator Name" \
[--role superadmin|support|readonly|ai_agent]
# أو: ./headhunter.sh provision-admin --email ops@waves.tech ... [--role support]
الدور الافتراضي هو superadmin إن حُذف. تُرفَض إعادة التشغيل ببريد موجود (البُرُد فريدة عالميًّا).
تُجزَّأ كلمات المرور بـ Argon2id؛ ويُفرَض حدّ أدنى للطول قدره ٨.
قاعدة البيانات والترحيلات
تغييرات المخطَّط هي ترحيلات SQL مرقَّمة — 0NNN_name.sql مع 0NNN_name_down.sql مطابق — تحت
postgres/migrations/. أما init.sql فهو الأساس (baseline) فقط ولا يُعدَّل أبدًا للمخطَّط الحيّ.
npm run db:migrate # تطبيق الترحيلات المعلَّقة (tsx scripts/migrate.ts up) — يحتاج DATABASE_URL
npm run db:rollback # التراجع عن أحدث ترحيل (down)
# عبر الحزمة: ./headhunter.sh migrate | rollback | db-reset
على قاعدة بيانات جديدة، تشغّل حاوية db في compose السكربت apply-migrations.sh بعد init.sql،
مطبِّقةً كل ترحيل بالترتيب تلقائيًّا. تُبلِّغ نقطة /health بعدد الترحيلات المطبَّقة، فتؤكّد أن النشر
مُرحَّل بالكامل في لمحة. يمسح ./headhunter.sh db-reset وحدة تخزين قاعدة البيانات ويعيد بناء أساس
نظيف — إجراء مدمِّر؛ يطلب تأكيدًا.
التهيئة والأسرار
التهيئة عبارة عن متغيّرات بيئة في .env (لا يُلتزَم بها في git أبدًا). أهمّها:
| المتغيّر | الغرض |
|---|---|
DATABASE_URL |
سلسلة اتصال Postgres (يبنيها compose من DB_PASSWORD). |
SESSION_SECRET |
يوقّع جلسة المنظمة hh_session. وهو السرّ الأساس الذي تعود إليه البقية. |
OPS_SESSION_SECRET |
يوقّع جلسة المشغّل hh_ops_session. يعود إلى SESSION_SECRET إن كان غير مضبوط/فارغًا. |
CANDIDATE_SESSION_SECRET |
يوقّع جلسة المرشّح hh_candidate_session. اضبطه صراحةً في الإنتاج. |
APP_HOST / MARKETING_HOST / OPS_HOST |
المضيفات الثلاث التي يوجّه بها المونوليث (app. / الجذر / ops.). |
PARSER_URL |
المحلِّل الجانبي (http://parser:8000 على شبكة compose). |
STORAGE_DIR |
حيث تعيش ملفات السير الذاتية الخام على القرص (/app/uploads، وحدة تخزين cv_storage). |
ANTHROPIC_API_KEY |
مفتاح LLM اختياري (لمسوّدة الوصف الوظيفي فقط؛ تتدهور الميزة بلطف إن كان غير مضبوط). |
فخّان يستحقّان التنويه:
- لا تضبط
ORIGINأبدًا. يشتقّ adapter-node أصل (origin) كل طلب من ترويسات الوكيل المُمرَّرة حتى يخدم التطبيق الواحد مضيفات التسويق والتطبيق والتشغيل.ORIGINثابت سيسحق هذا الفصل ويكسر توجيه المضيفات. CANDIDATE_SESSION_SECRETوفخّ السلسلة الفارغة. يحقن docker-compose القيمةCANDIDATE_SESSION_SECRET=(سلسلة فارغة) حين يغيب المتغيّر عن.env؛ يعامل التطبيق الفارغ كغير مضبوط ويعود إلىSESSION_SECRETحتى يعمل نشرٌ حدّيّ. هذا مقبول في التطوير، لكن في الإنتاج اضبطه على قيمته الخاصة — فالهدف كلّه عزل دائرة تأثير عالَم المرشّح.
البريد الصادر / WhatsApp و LLM كلّها مستقلّة عن المزوِّد. للتطوير والبرنامج التجريبي دون مزوِّد
حيّ، تُكتب الرسائل الصادرة كأسطر JSON في صندوق صادر ملفّيّ (COMMS_OUTBOX_FILE) بدل إرسالها فعلًا
— مفيد، لكن انظر تحفّظ عالَم المرشّح أدناه.
عالَم المرشّح — تشغيل المبدأ الرابع
أضافت المرحلة ٥ حساب المرشّح: عالَمًا ذاتيّ الخدمة، مواجهًا للإنترنت، وعلى نطاق واسع، فيستحقّ عنايته التشغيلية الخاصة.
ما هو. الجداول
candidate_accountsوcandidate_profilesوcandidate_email_tokens(الترحيلات0041+،0043)، خلف لوحة الوظائف العامة/jobsووحدة التحكّم المسجَّل دخولها/me/*، مع ملفّ تعريف الجلسة المنفصلhh_candidate_session. يعيش على نطاق التسويق الجذر — لا علىapp.أوops.. وحدة تحكّم المشغّل لا تدير حسابات المرشّحين؛ فهي ذاتية الخدمة من طرف إلى طرف.بريد مؤكَّد إلزاميّ. يبقى الحساب الجديد
pendingحتى ينقر المرشّح على رابط التأكيد المُرسَل، ولا يمكنه التقديم ولا تلقّي مراسلات المنظمة حتى يُؤكَّد. وهذا يجعل مزوِّد بريد حيّ اعتماديّةً صارمة لعالَم المرشّح — لن يوصِّل صندوق التطوير/التجريب الملفّيّ رابط تأكيد حقيقيًّا، فخطّط لمرسِل حقيقي قبل فتح اللوحة لباحثين حقيقيين عن عمل. قابلية التوصيل إلى داخل السودان مسؤوليّتك أنت لمراقبتها.الاحتفاظ. يفرضه عملٌ عديم التأثير الجانبي (idempotent) على الخادم الافتراضي (يدويًّا أو عبر cron)، لا عاملٌ داخل التطبيق:
npm run candidate-retention -- [--days 30] [--dry-run] # من /app، يحتاج DATABASE_URLيمسح الحسابات غير المؤكَّدة أبدًا (
pending) الأقدم من النافذة (افتراضيًّا ٣٠ يومًا) مع ملفات سيرها الذاتية، كاتبًا سطر تدقيقcandidate.deleted(السببretention_unverified) قبل الحذف حتى يبقى الأثر منزوع الهويّة. يسرد--dry-runما سيُحذف دون حذف. الحسابات النشطة الخاملة خارج النطاق عمدًا — لا يوجد مسح تلقائي لحساب مؤكَّد؛ فذلك قرار سياسة منفصل ومقصود، لا افتراضًا صامتًا.التصدير والحذف (ذاتيّ الخدمة). ينزّل المرشّح بياناته من
/me/settings ← Export(GET /me/settings/export←my-data-export.json): الحساب، والملف، وبيانات السيرة الذاتية الوصفية (تبقى الملفات الثنائية قابلة للتنزيل منفصلةً، لا مُضمَّنة)، والطلبات بحالتها الآمنة للمرشّح، والوظائف المحفوظة. ويمحو حسابه من الإعدادات ← حذف الحساب (إعادة استيثاق + تأكيد مكتوبDELETE): يدوّنcandidate.deletedأولًا، ثم يزيل الصفوف المملوكة ذاتيًّا، ويفصل سجلّ استقبال المنظمة (applications.candidate_account_idيُضبَط على null، فيبقى سجلّ المنظمة دون الرابط الشخصي)، ويحذف ملفات السيرة الذاتية من القرص أفضلَ جهد. تفي هذه بوعد الخصوصية (الاستضافة في الاتحاد الأوروبي / عدم البيع). لا تتصرّف هنا إلا إن كان المرشّح عاجزًا حقًّا عن الخدمة الذاتية — فهذه التدفّقات له لا لك.موقف مواجهة الإساءة. التسجيل المواجِه للإنترنت محدود المعدّل (٥ تسجيلات لكل ١٠ دقائق لكل عنوان IP) ويحمل حقل مصيدة (honeypot) يُبتلَع — حين يقع فيه روبوت — كنجاح محايد (لا يُخبَر الروبوت أبدًا بأنه أخفق). كما تُخنَق إعادة إرسال بريد التأكيد وتغييرات البريد (٣ لكل ١٠ دقائق لكل حساب). راقب طفرات رموز
429أو أحجام التسجيل غير المعتادة.
الصحّة والمراقبة
/health(على التطبيق) هي مسبار الحيويّة. يُبلِّغ باتصال قاعدة البيانات مع عدد الترحيلات المطبَّقة، وبـ/healthالخاص بالمحلِّل الجانبي، ويتدهور بلطف — تُبلِّغ قاعدة بيانات أو محلِّل مفقود بحالة خطئه الخاصة بدل إسقاط المسبار بخطأ 500. كما أن لحاوية المحلِّل فحص صحّة compose خاصًّا بها مقابل/health.- طابور التحليل ظاهر على لوحة الطبقة ١ (في الطابور / قيد التشغيل / خطأ + أخطاء آخر ٢٤ ساعة) — إشارتك الأولى إلى أن تحليل السير الذاتية يتراكم أو يخفق.
- تذكّر: تسجيلات المرشّحين وتأكيدهم لا تظهر في الطبقة ١. إن احتجت إلى استقراء حجم حسابات المرشّحين، فذلك من قاعدة البيانات، لا من اللوحة.
دليل إجراءات الدعم
وجِّه كل طلب عبر المسار الأقلّ امتيازًا الذي يجيبه. الطبقة ٢ المحجوبة أولًا؛ وكسر الزجاج كملاذ أخير مشروط بالموافقة وحده.
| الطلب | من أين تبدأ |
|---|---|
| «مستخدم منظمة محبوس خارج حسابه / يحتاج إعادة تعيين» | org_admin الخاص بالمنظمة يدير مستخدميها. لا تصعّد إلى الطبقة ٢ إلا لتأكيد حالة المنظمة. |
| «لماذا لم تُحلَّل هذه السيرة الذاتية؟» | الطبقة ٢ إخفاقات المحلِّل للمنظمة (المرشّح مقنَّع إلى أحرفه الأولى) + صحّة طابور التحليل في الطبقة ١. |
| «توقّف وصولنا إلى التجمّع» | الطبقة ٢ — تحقّق من فئة اشتراك المنظمة / تخويلها؛ الفوترة يدوية (نقدًا/بنكك)، يسجّلها org_admin. |
| «نريدك أن تطّلع على بيانات مرشّح بعينه» | تمنح المنظمة وصول الدعم (/settings/support-access)؛ وعندئذٍ فقط، كسر الزجاج بالطبقة ٣ تحت تلك المنحة الحيّة. |
| مرشّح: «لم تصلني رسالة التأكيد» | إعادة الإرسال الذاتية على /me أولًا؛ تحقّق من قابلية توصيل بريدك إلى داخل السودان. تصرّف المشغّل ملاذ، لا الأصل. |
| مرشّح: «احذف حسابي» / «صدِّر بياناتي» | تدفّقا /me/settings الذاتيّان للتصدير والحذف. لا تتصرّف إلا إن عجز حقًّا. |
| اشتباه إساءة / فيضان تسجيل | حجم الطبقة ١ + حدّ المعدّل / المصيدة للتسجيل؛ يستطيع superadmin تعليق منظمة من الطبقة ٢ عند الحاجة. |
النسخ الاحتياطية. الحالة الدائمة هي وحدة تخزين postgres_data بالإضافة إلى ملفات السير الذاتية في
cv_storage (/app/uploads). انسخهما معًا احتياطيًّا على الخادم الافتراضي — فقاعدة البيانات وحدها،
دون ملفات السير الذاتية التي تشير إليها، استعادة ناقصة. (يحفظ ./headhunter.sh down وحدات التخزين؛
وdb-reset وحده هو ما يدمّر قاعدة البيانات.)
مرجع سريع
| أريد أن… | افعل هذا |
|---|---|
| أسجّل الدخول إلى وحدة تحكّم المشغّل | /ops/login على ops.<domain> |
| أرى صحّة المنصّة كلها | لوحة الطبقة ١ (/ops/dashboard) — خالية من البيانات الشخصية |
| أحقّق في منظمة واحدة | الطبقة ٢ (/ops/orgs/[id]) — محجوبة، مدوَّنة |
| أرى بيانات مرشّح الحقيقية | فقط عبر كسر الزجاج بالطبقة ٣، تحت منحة حيّة من المنظمة |
| أشغّل الحزمة / أوقفها | ./headhunter.sh up / down |
| أنشئ منظمة + مشرفها الأول | npm run provision -- --org-name … --email … --password … --full-name … |
| أنشئ مشغّلًا | npm run provision-admin -- --email … --password … --full-name … [--role …] |
| أطبّق المخطَّط / أتراجع عنه | npm run db:migrate / db:rollback (يحتاج DATABASE_URL) |
| أمسح المرشّحين غير المؤكَّدين أبدًا | npm run candidate-retention -- [--days 30] [--dry-run] |
| أفحص الحيويّة | GET /health (قاعدة البيانات + الترحيلات + المحلِّل) |
| أنسخ احتياطيًّا | وحدتا تخزين postgres_data و cv_storage (/app/uploads)، معًا |
أنت تشغّل المنصّة التي تستند إليها مسارات مِهَن الآخرين وقراراتهم في التوظيف. التصميم كلّه هنا يشير إلى اتجاه واحد: أبقِها سليمة من العرض التجميعي، وساعد المستأجرين من مسافة محجوبة، ولا تصل إلى البيانات الحقيقية إلا حين يُبقي المستأجر الباب مفتوحًا لك — بفعل نشط ومؤقّت.