إنتقل إلى المحتوى الرئيسي

الملف الشخصي والإعدادات

الخلفية​

على Shukah يغيّر التطبيق الملف الشخصي ومفتاح إشعارات push باستدعاء واحد، PUT /maeda/client/{client_id}، ويُبلغ Shukah بالهاتف عبر POST /maeda/client/add-device-id. وفي خدمة المصادقة ثلاثة routes لذلك: واحد للملف الشخصي، وواحد لمفتاح push، وroute لمعرّف الجهاز (device-id). وثلاثة أمور تستحق العناية: يحتاج مفتاح push إلى استدعاء خاص به لأن route الملف الشخصي يتجاهل push_enabled، ويرفض route الملف الشخصي هاتفًا تغيّر، وroute الجهاز مجرد stub لا يخزّن شيئًا.

الميزة​

الاستدعاءات الثلاثة كلها جزء من شاشات حساب العميل المسجَّل الدخول. وفي نسخة التطبيق اللحظية بتاريخ 2026-02-26 تحدث في هذه الأماكن:

الاستدعاءأين في التطبيق
تحديث الملف الشخصيفي شاشة المعلومات الشخصية، عندما ينقر العميل على «حفظ». يحمل استدعاء واحد client_id وcontact_phone وfirst_name وlast_name وemail_address وsex وreceive_offers وbirth_date (بالصيغة YYYY-MM-DD). حقل الهاتف ظاهر لكنه معطَّل، وقيمته هي الهاتف الذي حمّله التطبيق مع الملف الشخصي، لذا يعيد التطبيق إرسال الهاتف الذي استلمه.
تحديث الإعدادفي شاشة الإعدادات، عندما يقلب العميل مفتاح إشعارات push. يرسل التطبيق client_id وpush_enabled (0 أو 1). وعلى Shukah يمر هذا عبر route الملف الشخصي نفسه.
إضافة معرّف الجهازعند بدء التطبيق، بعد فحص الملف الشخصي (verify-token) مباشرة، عندما يكون العميل مسجَّل الدخول ولم يُجرَ الاستدعاء بعد على هذا التثبيت. يرسل المعرّف الفريد للجهاز مرة واحدة. وبعد إجابة 200 يسجّل التطبيق أنه أرسله ولا يرسله ثانية أبدًا.

يحتاج أول استدعاءين إلى JWT من Locate (انظر «الحصول على token تجريبي» في النظرة العامة للوحدة) ويجيبان بالملف الشخصي للعميل، الحقول الـ 13 نفسها في التحقق من الـ token، بعد التغيير. أما استدعاء معرّف الجهاز فلا يحتاج إلى token.

PUT /api/v1/clients/:client_id​

يستبدل ShukahPUT /maeda/client/:client_id✅ تم الاستبدال
يحتاج تعديل التطبيق1 breaking · 4 للمراقبة
  • 🔴 يحصل token من Shukah على 401 "Unauthorization"؛ أرسل JWT من Locate (من exchange-token؛ واحتفظ بـ token من Shukah).
  • 🟡 يُتجاهل push_enabled في هذا الـ route (ولا يزال التطبيق يرسله هنا: user.dto.ts:100-101)؛ راجع update-setting.
  • 🟡 يجب أن يساوي contact_phone الهاتف المخزَّن مع "+" وإلا 400؛ وقاعدة تغيير الهاتف في Shukah لم تُختبر.
  • 🟡 المدخلات الخاطئة تعطي 400 {status,message,stack} على Locate و422 [{field,message}] على Shukah؛ ويفقد التطبيق field.
  • 🟡 يبقى التعديل في Locate ولا يُدفع إلى Shukah (لا يوجد دفع في update-profile/index.js).

الطلب (Request)​

تحديث جزئي: لا يتغير سوى المفاتيح الموجودة في الـ body. وتُرجع الخدمة أول مشكلة تجدها وتفحص بهذا الترتيب: client_id ثم contact_phone ثم first_name ثم last_name ثم email_address ثم birth_date.

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <locate-jwt>. وtoken من Shukah يحصل على 401.الـ JWT من Locate.
client_idpathintegerنعميجب أن يساوي معرّف العميل في الـ token، وإلا 400 مع client_id does not match the authenticated client.معرّف المستدعي نفسه.
client_idbodyinteger أو سلسلة رقميةلاإن أُرسل فيجب أن يساوي معرّف عميل الـ token أيضًا، مع 400 نفسه. وnull يفشل.معرّف المستدعي نفسه مرة أخرى، كما يرسله التطبيق.
contact_phonebodystringلالا يمكن تغييره. إن أُرسل فيجب أن تساوي القيمة بعد حذف المسافات الهاتف المخزَّن تمامًا، بما فيها + في البداية (966500000000 لا تطابق +966500000000)، وإلا 400 مع contact_phone cannot be changed. ولا يُكتب أبدًا.هاتف العميل.
first_name وlast_namebodystring أو nullلاnull أو سلسلة بحد أقصى 100 حرف بعد حذف المسافات الزائدة، وإلا 400 مع first_name is invalid أو last_name is invalid. تُخزَّن بعد حذف المسافات الزائدة. وnull تمسحها، وتُخزَّن "" كما هي "".الأسماء.
email_addressbodystring أو nullلاأي سلسلة أو null: لا فحص للصيغة ولا حد للطول، وإلا 400 مع email_address is invalid. تُخزَّن بعد حذف المسافات الزائدة.البريد الإلكتروني.
birth_datebodystring أو nullلاقيمة يستطيع new Date() في JavaScript قراءتها، وتُخزَّن بتاريخ UTC بالصيغة YYYY-MM-DD. وnull تمسحها. والقيمة غير المقروءة، بما فيها ""، تعطي 400 مع birth_date is invalid.تاريخ الميلاد. ويرسل التطبيق YYYY-MM-DD.
sexbodyintegerلالا يُتحقق منه. 1 ذكر و2 أنثى. وأي شيء آخر، بما فيه 0 و9 وnull، يُخزَّن «غير معروف» ويُقرأ لاحقًا 0.الجنس.
receive_offersbody0 أو 1 أو booleanلالا يُتحقق منه. فقط 1 و"1" وtrue تعني التفعيل. وأي شيء آخر يعني التعطيل.هل يتلقى العميل العروض.
push_enabled وأي مفتاح آخرbody—لايُتجاهل. فلمفتاح push route خاص به.—

طلب استُخدم للالتقاط أدناه، ويكتب القيمة الحالية للعميل مرة أخرى:

{
"client_id": 356259,
"receive_offers": 0
}

الاستجابة (Response)​

200 مع الملف الشخصي ذي الحقول الـ 13 بعد التحديث، دون غلاف. والحقول وقواعدها هي الجدول الوارد تحت التحقق من الـ token.

{
"client_id": 356259,
"odoo_id": "1342170",
"contact_phone": "+966500000000",
"status": "active",
"email_address": "user@example.com",
"first_name": "Test",
"last_name": "User",
"sex": 1,
"birth_date": "1990-01-01",
"receive_offers": 0,
"push_enabled": 0,
"is_food_truck_user": 1,
"is_cod_disabled": 0
}

التُقط على dev، 2026-10-01 (العميل 356259، كتابة مباشرة في Task 9، والقيمة الحالية أُعيدت كتابتها)؛ واستُبدلت الحقول الشخصية.

الأخطاء​

HTTPBodyمتى
400{"status":400,"message":"birth_date is invalid"}تعذّرت قراءة birth_date كتاريخ. ولرسائل الحقول الأخرى الشكل نفسه: client_id does not match the authenticated client وcontact_phone cannot be changed وfirst_name is invalid وlast_name is invalid وemail_address is invalid. وتُرجَع أول رسالة فاشلة فقط. وعلى dev يحمل الـ body أيضًا حقل stack (انظر أدناه).
401{"status":401,"message":"Unauthorization"}لا token، أو token ليس JWT من Locate، أو عميل غير نشط. وهذا يأتي قبل أي فحص للـ body.
500على dev: {"status":500,"message":"<raw error text>","stack":"<stack trace>"}. وفي production: {"status":500,"message":"Internal server error"}.فشل غير متوقع. لم يُلتقط. وعلى dev تكون الرسالة نص الخطأ الخام ويُضاف حقل stack؛ ولا تظهر Internal server error إلا في production (UAT وproduction: غير موثّق بعد).

التُقط 400 الخاص بـ birth_date على dev في 2026-10-01، ومرة أخرى في 2026-10-02 مع "birth_date":"not-a-date". ورسائل 400 الأخرى مصدرها الشيفرة ووثيقة الخدمة، ولم تُلتقط. والتُقط body الخاص بـ 401 على dev في 2026-10-01 دون token. وعلى dev يحمل كل body من نوع 400 أيضًا حقل stack فيه stack trace للخادم كنص، مثل {"status":400,"message":"birth_date is invalid","stack":"<stack trace>"} (التُقط 2026-10-02). وتتركه العينات في الجدول.

جرّبه​

يكتب الاستدعاء الأول على dev، لذا أرسل القيمة الحالية للعميل، كما فعل الالتقاط. أما الاستدعاء الثاني فيُرفض ولا يكتب شيئًا.

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
export CLIENT_ID='<client-id-in-the-token>'
curl -s -X PUT "$AUTH_BASE_URL/api/v1/clients/$CLIENT_ID" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "{\"client_id\":$CLIENT_ID,\"receive_offers\":0}"
curl -s -X PUT "$AUTH_BASE_URL/api/v1/clients/$CLIENT_ID" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"birth_date":"not-a-date"}'

المتوقع: يعطي الاستدعاء الأول 200 والملف الشخصي المبيّن تحت «الاستجابة». ويعطي الثاني 400 مع birth_date is invalid.

الفروق عن Shukah​

الجانبShukahLocateالأثر على التطبيق
URLPUT $SHUKAH_BASE_URL/maeda/client/{client_id}PUT $AUTH_BASE_URL/api/v1/clients/{client_id}غيّر base URL والمسار.
الـ Tokentoken معتم (opaque) من Shukah.JWT من Locate فقط.احصل على الـ JWT عبر exchange-token أولًا، واحتفظ بـ token Shukah لـ order-v2. ولا تنقل تسجيل الدخول إلى Locate بعد.
push_enabledيقبل الـ route نفسه push_enabled أيضًا، ويرسله التطبيق هنا لمفتاح push.يُتجاهل. ويظل الاستدعاء يجيب 200 مع الملف الشخصي دون تغيير.انقل مفتاح push إلى استدعاء الإعداد أدناه، وإلا فلن يفعل شيئًا بصمت (قراءة شيفرة، لم يُلتقط).
contact_phoneهل تقبل Shukah هاتفًا تغيّر: غير موثّق بعد.يجب أن يساوي الهاتف المخزَّن تمامًا، وإلا 400.لا شيء ما دام التطبيق يعيد إرسال الهاتف الذي حمّله من Locate. لا تعِد تنسيقه.
التحقق والأخطاء422 مع مصفوفة أخطاء حقول: [{"field":"birth_date","message":"The birth date field must match the format Y-m-d."}] لـ "birth_date":"not-a-date" (التُقط على dev، 2026-10-02). وقواعد Shukah الأخرى: غير موثّق بعد.400 مع {"status":400,"message":"..."} (مع stack على dev)، والقواعد الواردة في جدول الطلب.يتغير الـ status من 422 إلى 400، ويختفي اسم الحقل. يعرض التطبيق message. افحص الصياغة في شاشة المعلومات الشخصية.
body النجاحالملف الشخصي ذو الحقول الـ 13 (التُقط على dev، 2026-10-02، بالطلب أعلاه).الملف الشخصي ذو الحقول الـ 13 (التُقط على dev، 2026-10-01 و2026-10-02).لا شيء: المفاتيح الـ 13 نفسها والأنواع والقيم نفسها للعميل 356259 في 2026-10-02.
sexيسمح نوع التطبيق بـ 9 لـ «غير منطبق».تُخزَّن 9 «غير معروف» وتُقرأ لاحقًا 0.العميل الذي في ملفه على Shukah القيمة 9 يُقرأ «غير معروف» بعد التحوّل. ولا يعرض نموذج المعلومات الشخصية سوى ذكر وأنثى.
النسخ إلى Shukahلا ينطبق.يبقى التعديل في Locate ولا يُنسخ إلى Shukah.أي شيء ما زال يقرأ الملف الشخصي من Shukah يحتفظ بالقيم القديمة.

PUT /api/v1/clients/setting​

يستبدل ShukahPUT /maeda/client/:client_id✅ تم الاستبدال
يحتاج تعديل التطبيق2 breaking · 3 للمراقبة
  • 🔴 يحصل token من Shukah على 401 "Unauthorization"؛ أرسل JWT من Locate (من exchange-token؛ واحتفظ بـ token من Shukah).
  • 🔴 يبدّل التطبيق push عبر PUT client/:id؛ وتتجاهل Locate push_enabled هناك، لذا استدعِ PUT /clients/setting.
  • 🟡 client_id مطلوب في body ويجب أن يساوي client_id الخاص بالـ token؛ وغياب push_enabled يعطي 400.
  • 🟡 يبقى التغيير في Locate ولا يُدفع إلى Shukah.
  • 🟡 كتب الزوج القيمة الحالية (push_enabled 0)، لذا لم يثبت أن Shukah تطبّق push_enabled في هذا الـ route.

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <locate-jwt>. وtoken من Shukah يحصل على 401.الـ JWT من Locate.
client_idbodyinteger أو سلسلة رقميةنعميجب أن يساوي معرّف العميل في الـ token. والمفقود أو المختلف يعطي 400 مع client_id does not match the authenticated client.معرّف المستدعي نفسه. ولا معرّف في المسار.
push_enabledbody0 أو 1 أو booleanنعميجب أن يكون المفتاح موجودًا، وإلا 400 مع push_enabled is required. فقط 1 و"1" وtrue تفعّله. وكل شيء آخر يعطّله، بما فيه null و"true" و2.تشغيل إشعارات push أو إيقافها.

يُفحص client_id أولًا. ولا يُدعم في هذا الـ route اليوم سوى push_enabled.

طلب استُخدم للالتقاط أدناه، ويكتب القيمة الحالية مرة أخرى:

{
"client_id": 356259,
"push_enabled": 0
}

الاستجابة (Response)​

200 مع الملف الشخصي ذي الحقول الـ 13 بعد التحديث، دون غلاف، كما هو تحت التحقق من الـ token. ويُظهر push_enabled القيمة الجديدة.

{
"client_id": 356259,
"odoo_id": "1342170",
"contact_phone": "+966500000000",
"status": "active",
"email_address": "user@example.com",
"first_name": "Test",
"last_name": "User",
"sex": 1,
"birth_date": "1990-01-01",
"receive_offers": 0,
"push_enabled": 0,
"is_food_truck_user": 1,
"is_cod_disabled": 0
}

التُقط على dev، 2026-10-01 (العميل 356259، كتابة مباشرة في Task 9، والقيمة الحالية أُعيدت كتابتها)؛ واستُبدلت الحقول الشخصية.

الأخطاء​

HTTPBodyمتى
400{"status":400,"message":"client_id does not match the authenticated client"}client_id مفقود أو ليس عميل الـ token. ويشمل ذلك طلبًا بلا JSON body، تقرؤه الخدمة كـ {}. التُقط مع ضبط client_id على 1.
400{"status":400,"message":"push_enabled is required"}مفتاح push_enabled غائب وclient_id صحيح. لم يُلتقط.
401{"status":401,"message":"Unauthorization"}لا token، أو token ليس JWT من Locate، أو عميل غير نشط.
500على dev: {"status":500,"message":"<raw error text>","stack":"<stack trace>"}. وفي production: {"status":500,"message":"Internal server error"}.فشل غير متوقع. لم يُلتقط. وعلى dev تكون الرسالة نص الخطأ الخام ويُضاف حقل stack؛ ولا تظهر Internal server error إلا في production (UAT وproduction: غير موثّق بعد).

التُقط 400 الخاص بـ client_id و401 على dev في 2026-10-01 (والـ 401 دون token). أما 400 الخاص بـ push_enabled فمن الشيفرة ووثيقة الخدمة. وعلى dev يحمل body الخاص بـ 400 أيضًا حقل stack، وتتركه هذه الصفحة.

جرّبه​

يكتب الاستدعاء الأول على dev، لذا أرسل القيمة الحالية للعميل. أما الاستدعاء الثاني فيُرفض ولا يكتب شيئًا.

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
export CLIENT_ID='<client-id-in-the-token>'
curl -s -X PUT "$AUTH_BASE_URL/api/v1/clients/setting" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "{\"client_id\":$CLIENT_ID,\"push_enabled\":0}"
curl -s -X PUT "$AUTH_BASE_URL/api/v1/clients/setting" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"client_id":1,"push_enabled":0}'

المتوقع: يعطي الاستدعاء الأول 200 والملف الشخصي. ويعطي الثاني 400 مع client_id does not match the authenticated client.

الفروق عن Shukah​

الجانبShukahLocateالأثر على التطبيق
URLPUT $SHUKAH_BASE_URL/maeda/client/{client_id}، وهو route تحديث الملف الشخصي نفسه. والـ body هو {client_id, push_enabled}.PUT $AUTH_BASE_URL/api/v1/clients/setting، دون معرّف في المسار.يستدعي مفتاح push في التطبيق اليوم تحديث الملف الشخصي. وجّهه إلى هذا الـ route، وإلا تتجاهل Locate التغيير.
الـ Tokentoken معتم (opaque) من Shukah.JWT من Locate فقط.احصل على الـ JWT عبر exchange-token أولًا، واحتفظ بـ token Shukah لـ order-v2. ولا تنقل تسجيل الدخول إلى Locate بعد.
client_idيُرسل في المسار والـ body. وهل تفحصه Shukah مقابل الـ token: غير موثّق بعد.يجب أن يكون في الـ body وأن يساوي عميل الـ token.لا شيء: فالتطبيق يرسل client_id في الـ body بالفعل.
التحقق والأخطاءغير موثّق بعد.400 مع {"status":400,"message":"..."} لـ client_id خاطئ أو push_enabled مفقود.يرسل التطبيق المفتاحين بالفعل، لذا لا ينبغي أن يحدث ذلك.
body النجاحالملف الشخصي ذو الحقول الـ 13 مع push_enabled يساوي 0 (التُقط على dev، 2026-10-02، والـ body هو {"client_id":356259,"push_enabled":0}، أي أُعيدت كتابة القيمة الحالية).الملف الشخصي نفسه ذو الحقول الـ 13 (التُقط على dev، 2026-10-02).يقرأ التطبيق push_enabled من الإجابة ليضبط المفتاح. وقد كتب الالتقاط القيمة الحالية، فهو لا يثبت أن Shukah تطبّق التغيير على هذا الـ route.
النسخ إلى Shukahلا ينطبق.يبقى التغيير في Locate ولا يُنسخ إلى Shukah.تحتفظ Shukah بالقيمة القديمة.

POST /api/v1/clients/add-device-id​

يستبدل ShukahPOST /maeda/client/add-device-id✅ تم الاستبدال
استبدال مباشر0 breaking · 3 للمراقبة
  • 🟡 لا تخزّن Locate شيئًا؛ وحفظت Shukah صف جهاز (id 1074)؛ ويعلّم التطبيق المعرّف بأنه أُرسل على أي حال.
  • 🟡 تُعيد Locate القيمتين الثابتتين id 920 وclient_id 356315 للجميع؛ وتُرجع Shukah client_id الخاص بالمستدعي.
  • 🟡 لا تحتاج Locate إلى token (لا يوجد guard، routes/client.js:23)؛ ويمكن للتطبيق مواصلة إرسال token.

الطلب (Request)​

لا حاجة إلى token: فالـ token أو أي header آخر يُتجاهل.

الحقلالموضعالنوعمطلوبالقيودالوصف
device_idbodystringلالا يُتحقق منه. ويُعاد كما هو عند وجوده.المعرّف الفريد للجهاز.
أي مفتاح body أو query أو header آخرbody، query، header—لايُتجاهل، مثل language.—
{
"device_id": "00000000-0000-4000-8000-000000000000"
}

الاستجابة (Response)​

200 مع سجل جهاز. وهو stub، لذا فقيمتان من الثلاث ثابتتان:

الحقلالنوعيقبل nullالصيغة / القيودالوصف
idintegerلادائمًا 920، لكل مستدعٍ.معرّف سجل جهاز بديل (stand-in).
client_idintegerلادائمًا 356315، لكل مستدعٍ. وليس معرّف المستدعي.معرّف عميل بديل (stand-in).
device_idstringلاقيمة device_id من الطلب. وعندما لا يكون في الطلب واحد، معرّف نموذجي ثابت 46AE56EF-29BA-40AE-B436-AF22B119FE1E.معرّف الجهاز، كما أُعيد.
{
"id": 920,
"client_id": 356315,
"device_id": "00000000-0000-4000-8000-000000000000"
}

التُقط على dev، 2026-10-01 (استدعاء مباشر في Task 9، بلا token؛ والطلب هو الوارد أعلاه). لا شيء يُخزَّن بهذا الاستدعاء.

الأخطاء​

لا شيء معرَّف. ولا يوجد 401، لأن الـ route لا يفحص الـ token. والطلب بلا JSON body يُقرأ كـ body فارغ ويظل يجيب 200 (التُقط الاستدعاء بـ body فارغ في 2026-10-01).

جرّبه​

export AUTH_BASE_URL=https://dev.authentication.locate.sa
curl -s -X POST "$AUTH_BASE_URL/api/v1/clients/add-device-id" -H "Content-Type: application/json" -d '{"device_id":"00000000-0000-4000-8000-000000000000"}'

المتوقع: 200 والسجل المبيّن تحت «الاستجابة». ويمكن استدعاؤه بأمان: فهو لا يكتب شيئًا.

الفروق عن Shukah​

الجانبShukahLocateالأثر على التطبيق
URLPOST $SHUKAH_BASE_URL/maeda/client/add-device-idPOST $AUTH_BASE_URL/api/v1/clients/add-device-idغيّر base URL والمسار.
ما يفعلهيخزّن سجل جهاز جديدًا للمستدعي في كل استدعاء (التُقط على dev، 2026-10-02: السجل 1074). ولا يوجد API يزيله.لا شيء: لا تخزين، ولا ربط بالعميل، ولا فحص للـ token.بعد التحوّل لا يُسجَّل أي جهاز بهذا الاستدعاء. وإن كان شيء على Shukah يعتمد على سجل الجهاز فسيتوقف: غير موثّق بعد.
الإجابة{"id":1074,"client_id":356259,"device_id":"<the id sent>"}: معرّف السجل الجديد ومعرّف المستدعي (التُقط على dev، 2026-10-02). ويتجاهل التطبيق الإجابة.{id: 920, client_id: 356315, device_id} للجميع.لا شيء بالنسبة إلى التطبيق.
الـ Tokenيرسل التطبيق token Shukah الخاص به. وهل تشترطه Shukah: غير موثّق بعد.غير مطلوب، ولا يُفحص.لا شيء.
إعادة المحاولةيسجّل التطبيق أنه أرسل المعرّف بعد إجابة 200.يجيب الـ stub دائمًا بـ 200.سيعلّم التطبيق الجهاز بأنه أُرسل ولن يحاول مرة أخرى أبدًا، مع أن شيئًا لم يُخزَّن.

تنبيه​

  • يتجاهل route الملف الشخصي push_enabled ويظل يجيب 200 مع القيمة القديمة، لذا يجب أن يستدعي مفتاح push المسار PUT /api/v1/clients/setting بدلًا منه.
  • يجب أن يساوي contact_phone الهاتف المخزَّن تمامًا، بما فيها + في البداية، وإلا يفشل الاستدعاء بـ 400.
  • يجيب add-device-id دائمًا بـ 200 ولا يخزّن شيئًا، فيعلّم التطبيق الجهاز بأنه أُرسل مع أنه لم يُسجَّل أي جهاز.

التفاصيل (للـ backend)​

فُحص dev فقط: الـ host https://dev.authentication.locate.sa والبادئة /api/v1. أما الـ host والبادئة في UAT وproduction: غير موثّق بعد. تحمل أجسام الأخطاء على dev حقل stack (التُقط في 2026-10-01 على 400 الخاصة بـ profile وsetting)، لذا فإن dev لا يعمل بـ NODE_ENV=production؛ والـ handler لا يضيف stack إلا خارج production (src/middlewares/error-handler.js:12-35). ويجعل الـ handler نفسه 500 غير المتوقع يحمل نص الخطأ الخام وstack على dev، وInternal server error في production فقط (src/middlewares/error-handler.js:28-33).

ترتيب الـ routes والحارس​

يُسجَّل PUT /clients/setting قبل PUT /clients/:client_id، لذا لا يمكن قراءة setting أبدًا كمعرّف عميل. ويشغّل كلاهما حارس client، فقواعد الـ token هي قواعد التحقق من الـ token. أما route الجهاز (device-id) فلديه الغلاف async فقط ولا حارس (src/routes/client.js:23-26).

تحديث الملف الشخصي​

  • الفحوص هي سلاسل express-validator تعمل بعد الحارس. ويُلقي الـ handler أول رسالة، لذا لا تعود سوى رسالة 400 واحدة (src/controllers/client/update-profile/validate.js:12-31، src/controllers/client/update-profile/index.js:12-15).
  • يبحث check('client_id') في body وcookies وheaders وparams وquery، لذا فإن client_id غير المطابق في سلسلة query أو في header يفشل بالرسالة نفسها أيضًا (فحص دون اتصال، 2026-10-01: شُغّلت سلاسل المدقّق الحقيقية على نحو 20 طلبًا مصطنعًا).
  • يُقارَن contact_phone بعد normalizePhone، الذي يحذف المسافات فقط. والقيمة null تفشل، ولا تُكتب القيمة أبدًا (src/controllers/client/update-profile/validate.js:20-23، src/controllers/client/update-profile/parse-payload.js:8-9، src/sync/lib/casts.js:40-41).
  • تُخزَّن الأسماء والبريد بعد حذف المسافات الزائدة. وتُخزَّن null كما هي null و"" كما هي "" (src/controllers/client/update-profile/parse-payload.js:5,11-13).
  • يمر birth_date عندما يكون null أو عندما تعطي toBirthDateString تاريخًا. وتُرجع تلك الدالة null للقيمة الفارغة وللقيمة غير المقروءة، والتاريخ الذي تحتفظ به هو تاريخ UTC (src/sync/lib/casts.js:2-11، src/controllers/client/update-profile/validate.js:27-30، src/controllers/client/update-profile/parse-payload.js:14-16).
  • يمر sex عبر mapGender: 1 أو "1" هي MALE، و2 أو "2" هي FEMALE، وكل شيء آخر هو UNKNOWN، وتحوّل الاستجابة UNKNOWN إلى 0. ويمر receive_offers عبر toBool (src/sync/lib/casts.js:1,14-18، src/controllers/client/update-profile/parse-payload.js:17-18، src/controllers/client/setting/mapper.js:14-18,34).
  • يغيّر الـ handler العميل الذي حمّله الحارس، ويضبط updated_at ويحفظ. ويمسح الحفظ العميل المخزَّن مؤقتًا (cache) (src/controllers/client/update-profile/index.js:17-20، src/models/client.js:158-161). ولا يوجد دفع (push) إلى Shukah (docs/plans/2026-07-16-shukah-update-profile-api-local.md:42,140).
  • لا يقرأ route الملف الشخصي push_enabled (docs/plans/2026-07-16-shukah-update-profile-api-local.md:39، src/controllers/client/update-profile/parse-payload.js:10-19).

تحديث الإعداد​

يشترط الـ handler أن يساوي client_id عميل الـ token، ثم ألا تكون push_enabled بقيمة undefined، ثم يضبط push_enabled بـ toBool ويحفظ. ولا يغيّر شيئًا آخر ولا يدفع إلى Shukah (src/controllers/client/setting/index.js:9-24، src/sync/lib/casts.js:1، docs/plans/2026-07-10-shukah-client-setting-api-local.md:100-106). والطلب بلا JSON body يصل كـ {}، فيعطي رسالة client_id أولًا.

Stub الخاص بمعرّف الجهاز​

يُرجع الـ handler {id: 920, client_id: 356315, device_id: req.body?.device_id ?? '<sample uuid>'} ولا يفعل شيئًا آخر. ويُتجاهل الـ query والـ headers (src/controllers/client/add-device-id/index.js:1-10، docs/plans/2026-07-15-shukah-add-device-id-api-local.md:15-16,65-69). ويثبّته اختبار وحدة (src/controllers/client/__tests__/add-device-id.test.js:11-35).

حيث تختلف الوثائق عن الشيفرة (القول للشيفرة)​

  • تعطي وثيقة تصميم 2026-07-16 مسار تحديث Shukah بالشكل PUT /api/v1/clients/{client_id} (docs/plans/2026-07-16-shukah-update-profile-api-local.md:14-19). أما التطبيق فيستدعي PUT /maeda/client/{client_id}.
  • يُظهر سجل routes خدمة المصادقة route لـ Shukah هو PUT /maeda/api/setting استبدلته خدمة Locate (commit d837492). ولا يستدعيه التطبيق أبدًا: فمفتاح push يمر عبر route الملف الشخصي.
  • سلوك sex وreceive_offers وpush_enabled في استدعاء الملف الشخصي على Shukah غير موجود في أي مستودع: غير موثّق بعد.
  • 2026-10-02: لم يكن في هذه الصفحة body نجاح من Shukah لـ update profile وupdate setting، ولا body خطأ من Shukah. وقد التُقطت الآن: الملف الشخصي نفسه ذو الحقول الـ 13 على الجانبين، ومصفوفة أخطاء حقول 422 على Shukah حيث تجيب Locate بـ 400.
  • 2026-10-02: كتبت هذه الصفحة body 400 في Locate بالشكل {"status":400,"message":"..."}. وعلى dev يحمل أيضًا stack، وهو stack trace للخادم (src/middlewares/error-handler.js:33، يُضاف كلما كان NODE_ENV ليس production).
  • 2026-10-02: قالت هذه الصفحة إن ما تخزّنه Shukah لـ add-device-id غير موثّق، وإن إجابتها غير مؤكَّدة. تخزّن Shukah سجل جهاز جديدًا في كل استدعاء وتجيب بمعرّفه ومعرّف المستدعي.

Sources​

  • locate-authentication-service@6757aa4: src/routes/client.js:23-26، src/controllers/client/update-profile/validate.js:4-31، src/controllers/client/update-profile/index.js:11-23، src/controllers/client/update-profile/parse-payload.js:5-19، src/controllers/client/setting/index.js:9-24، src/controllers/client/setting/mapper.js:14-38، src/controllers/client/add-device-id/index.js:1-10، src/controllers/client/__tests__/add-device-id.test.js:11-35, src/sync/lib/casts.js:1-18,40-41، src/models/client.js:158-161, src/middlewares/error-handler.js:12-35
  • وثائق locate-authentication-service@6757aa4: docs/api/client-auth-and-address-book.md §10 و §11 (الأسطر 412-528؛ آخر تغيير 2026-07-27)، docs/plans/2026-07-16-shukah-update-profile-api-local.md:14-19,36-44,138-140، docs/plans/2026-07-10-shukah-client-setting-api-local.md:100-106، docs/plans/2026-07-15-shukah-add-device-id-api-local.md:12-18,63-69
  • نسخة الجوال اللحظية mobile/locate-client@e960c3a (الفرع development، 2026-02-26؛ شيفرة التطبيق نفسه، وليست من Locate): src/entities/user/model/api/services/user.service.ts:20-27,68-74، src/entities/user/model/api/dto/user.dto.ts:58-121,165-171، src/entities/user/model/types/user.server.ts:3-14، src/entities/user/model/constants/user.server.ts:1-6، src/entities/user/model/services/updateUser/updateUser.ts:13-42، src/features/UpdateProfileForm/model/services/updateProfile.ts:80-91، src/features/UpdateProfileForm/model/services/initUpdateProfileForm.ts:13-23، src/features/UpdateProfileForm/ui/UpdateProfileForm/UpdateProfileForm.tsx:42-45,73,155-160، platforms/mobile/products/locate/screens/home/PersonalInfo/ui/PersonalInfo/PersonalInfo.tsx:51، src/features/PushNotificationSettings/model/services/togglePushNotificationEnable.ts:22-26، src/features/PushNotificationSettings/ui/PushNotificationSettings/PushNotificationSettings.tsx:42-50، platforms/mobile/products/locate/screens/home/Settings/ui/Settings/Settings.tsx:37، src/entities/user/model/services/sendDeviceIdentificationData/sendDeviceIdentificationData.ts:18-30، src/entities/user/model/services/init/init.ts:21-23
  • التقاطات dev، 2026-10-02 (parity audit): update-profile--shukah وupdate-profile--locate-jwt و update-profile-bad--shukah وupdate-profile-bad--locate-jwt وupdate-setting--shukah و update-setting--locate-jwt وadd-device-id--shukah وadd-device-id--locate-jwt
  • التقاطات dev، 2026-10-01: locate-update-profile-unchanged وlocate-update-profile-bad-birth-date و locate-setting-unchanged وlocate-setting-mismatched-client وlocate-add-device-id و probe-locate-update-profile وprobe-locate-setting وprobe-locate-add-device-id