Address book
Bối cảnh
Trên Shukah, app quản lý các địa chỉ giao hàng đã lưu của client bằng năm lời gọi trên
/maeda/address-book (path không có phần /api/): list, get, create, update và delete. Auth
service có cùng năm route đó, nhưng không phải là swap drop-in. Có bốn điều cần chú ý: id của địa chỉ
là chuỗi 24 ký tự thay vì integer, câu trả lời của list và create dùng shape khác với
câu trả lời của get và update, create trả lời bên trong wrapper {status, address}, và
lời gọi create không tự điền street, city và các phần địa chỉ khác mà Shukah tự suy ra.
Tính năng
App cho phép một client đã đăng nhập lưu nhiều địa chỉ và chọn một địa chỉ làm mặc định. Trong snapshot app ngày 2026-02-26 các lời gọi xảy ra ở đây:
| Lời gọi | Ở đâu trong app |
|---|---|
| List | Khi một client đã đăng nhập mở màn hình hiển thị các địa chỉ đã lưu (location drawer và thanh user-location) và danh sách chưa được tải, khi app thiết lập địa chỉ giao hàng mặc định sau khi đăng nhập, khi pull-to-refresh trong danh sách địa chỉ đã lưu, và lại sau mỗi lần create, update hoặc delete. |
| Get | Khi client mở màn hình Edit address. |
| Create | Khi client lưu một vị trí từ bản đồ với "save for later" bật. Nếu đã có một địa chỉ đã lưu cùng tên, app xóa nó trước rồi mới tạo địa chỉ mới. |
| Update | Khi client lưu màn hình Edit address. App gửi location_name, area, is_default và image_urls. |
| Delete | Khi client xóa một địa chỉ trên màn hình Edit address. |
Cả năm lời gọi đều cần JWT Locate (xem "Lấy test token" ở trang tổng quan module). Các quy tắc áp dụng cho tất cả:
- Id. Mọi response đưa id dưới dạng Mongo ObjectId: một chuỗi hex 24 ký tự. Trong path của get, update và delete, một giá trị chỉ gồm chữ số trước tiên được thử như id địa chỉ Shukah cũ, rồi mới như ObjectId. Mọi giá trị khác chỉ được thử như ObjectId. Chỉ những địa chỉ đến từ address book của Shukah mới mang Shukah id, và không response nào hiển thị nó.
- Hai shape response. List và create dùng list shape:
is_defaultlà boolean,latvàlnglà số, field text bị thiếu lànull, và có các keysource,created_atvàupdated_at. Get và update dùng detail shape:is_defaultlà0hoặc1,latvàlnglà chuỗi, field text bị thiếu là"", và ba key đó vắng mặt. - Giá trị trống. Khi create và update, một field text được trim, và giá trị trống được lưu là
null.lathoặclngtrống hoặc không phải số được lưu lànull. Không có gì được validate: một body rỗng tạo ra một địa chỉ với mọi field rỗng, và không có giới hạn số lượng địa chỉ. - Update một phần. Update chỉ thay đổi các key có trong body. Một key có mặt với
null,""hoặc chỉ khoảng trắng sẽ xóa field (image_urlsthành[]).source, Shukah id vàcreated_atkhông bao giờ đổi. - Chỉ địa chỉ của chính mình. Id chỉ được tra trong các địa chỉ của người gọi. Một id thuộc về
người khác, hoặc không thuộc về ai, cho
404vớiAddress not found. - Một mặc định. Service giữ một địa chỉ mặc định khi app create, update hoặc delete:
| Lời gọi | Quy tắc |
|---|---|
| Create | is_default true xóa flag trên mọi địa chỉ khác. Nếu không, địa chỉ mới là mặc định chỉ khi sổ trống hoặc chưa có mặc định. |
| Update | Chỉ khi body có is_default. Đặt true: mọi địa chỉ khác mất flag. Đặt false khi không có địa chỉ nào khác là mặc định: địa chỉ khác đầu tiên trở thành mặc định. Nếu nó là địa chỉ duy nhất, nó vẫn là mặc định. |
| Delete | Nếu không còn mặc định nào và sổ không trống, địa chỉ đầu tiên theo thứ tự lưu trữ trở thành mặc định. Danh sách được sắp theo ngày, nên đó không phải lúc nào cũng là địa chỉ đầu tiên mà app thấy. |
Đảm bảo này chỉ đúng cho ba lời gọi này. Các địa chỉ copy từ Shukah không được kiểm tra, và trên dev cả 10 địa chỉ trong danh sách đã capture đều là mặc định (xem khối list).
GET /api/v1/clients/address-book
GET /maeda/address-book✅ Đã thay thế- 🔴 Token Shukah nhận 401 "Unauthorization"; hãy gửi JWT của Locate (lấy từ exchange-token; giữ lại token Shukah).
- 🔴 Địa chỉ tạo trên Locate có formatted_address/street/city là null; app gửi formatted_address làm c_address.
- 🟡 id là chuỗi 24 ký tự hex (Shukah là số nguyên); id Shukah đã lưu không còn khớp (initLocation.ts:78).
- 🟡 is_default là boolean, true ở 28 trên 30 (Shukah: 0 ở cả 15); initLocation lấy mục đầu tiên có giá trị true.
- 🟡 30 địa chỉ so với 15 trên Shukah: thao tác xóa trên Shukah không tới Locate, và "Saihat" xuất hiện 3 lần.
- 🟡 image_urls là [] trên Locate cho cả 4 địa chỉ Shukah có ảnh (Shukah trả về URL).
- 🟡 Locate xếp created_at mới nhất trước; Shukah xếp theo position (0..14), nên danh sách có thể đổi thứ tự.
Locate trả is_default dạng boolean và id dạng chuỗi 24 ký tự hex, trong khi Shukah trả 0 hoặc 1 và id là số nguyên (lat và lng là số ở cả hai phía); phần kiểm tra mặc định của app chấp nhận boolean, nhưng mọi chỗ coi id là số đều phải sửa.
Request
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <locate-jwt>. Token Shukah nhận 401. | JWT của Locate. |
| Bất kỳ query key hoặc header nào khác | query, header | — | không | Được chấp nhận và bỏ qua, ví dụ language. | — |
Không có phân trang, không có filter và không có tùy chọn sắp xếp.
Response
200 với một mảng theo list shape. Nó là [] khi client không có địa chỉ nào. created_at
mới nhất đứng đầu, và các địa chỉ không có created_at đứng cuối.
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
id | string | không | ObjectId hex 24 ký tự. Không bao giờ là integer id của Shukah. | Id địa chỉ cho get, update và delete. |
source | string | có | mt_client_inline, mt_address_book hoặc locate. | Địa chỉ đến từ đâu: locate cho địa chỉ tạo qua API này. |
is_default | boolean | không | true hoặc false. Có thể có nhiều hơn một địa chỉ true. | Địa chỉ mặc định. |
location_name, street, area, area_name, city, state, zipcode, country, country_code, formatted_address | string | có | null khi thiếu. Chuỗi rỗng đã được lưu vẫn là "" (capture có state: ""). | Các phần của địa chỉ. |
lat, lng | number | có | Số JSON. Không kiểm tra phạm vi. | Tọa độ. |
image_urls | array of string | không | Luôn là mảng, có thể rỗng. | URL ảnh. |
created_at, updated_at | string | có | ISO 8601 theo UTC, ví dụ 2026-09-29T17:01:10.000Z. | Khi địa chỉ được tạo và thay đổi lần cuối. |
[
{
"id": "6abbeed8a492ed4862500b74",
"source": "mt_address_book",
"is_default": true,
"location_name": "Saihat",
"street": "King Fahd Rd",
"area": "Eastern Province",
"area_name": "A12345",
"city": "Saihat",
"state": "Al-Qatif",
"zipcode": "32461",
"country": "Saudi Arabia",
"country_code": "SA",
"lat": 24.7136,
"lng": 46.6753,
"formatted_address": "King Fahd Rd, Al Olaya, Riyadh 12211",
"image_urls": [],
"created_at": "2026-09-29T17:01:10.000Z",
"updated_at": "2026-09-29T17:01:10.000Z"
}
]
Capture trên dev, 2026-10-01 (client 356259): mục đầu tiên trong 28 mục, với các field cá nhân và tọa độ đã được thay thế.
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
401 | {"status":401,"message":"Unauthorization"} | Không có token, token không phải JWT Locate, hoặc client thiếu hay không active. Cùng guard với Verify token. |
401 | Cùng body. | Token hợp lệ nhưng không tải được bản ghi client. |
500 | Trên dev: {"status":500,"message":"<raw error text>","stack":"<stack trace>"}. Trên production: {"status":500,"message":"Internal server error"}. | Lỗi không mong đợi. Chưa capture. Trên dev message là text lỗi gốc và có thêm field stack; Internal server error chỉ xuất hiện trên production (UAT và production: Chưa được tài liệu hóa). |
Body 401 được capture trên dev vào 2026-10-01 khi không có token.
Thử ngay
export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
curl -s "$AUTH_BASE_URL/api/v1/clients/address-book" -H "Authorization: Bearer $TOKEN"
Kỳ vọng: 200 và một mảng như ở mục Response. Không có token bạn nhận 401 như ở mục
Lỗi.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/address-book | GET $AUTH_BASE_URL/api/v1/clients/address-book | Đổi base URL và path. |
| Token | Token opaque của Shukah. | Chỉ JWT của Locate. | Lấy JWT bằng exchange-token trước, và giữ token Shukah cho order-v2. Chưa chuyển đăng nhập sang Locate. |
id | Một integer, ví dụ 162115 (capture trên dev, 2026-10-01). | Một chuỗi 24 ký tự, ví dụ 6abbeed8a492ed4862500b74. | Mọi chỗ lưu hoặc so sánh id dạng số đều phải đổi. App khai báo kiểu của nó là number. |
is_default | 0 hoặc 1 (capture trên dev, 2026-10-01). | true hoặc false. | App tìm mặc định bằng phép thử truthiness, chạy được với cả hai. Một phép so sánh strict với 1 thì không. |
| Key thêm | position (một integer). | source, created_at, updated_at; không có position. | Không có: type địa chỉ của app không có key nào trong bốn key này. |
image_urls | Một mảng hoặc null. Bốn trong 15 địa chỉ của client có URL ảnh (capture trên dev, 2026-10-02). | Luôn là mảng. Bản copy trên Locate của bốn địa chỉ đó có []. | Ảnh của các địa chỉ copy từ Shukah bị mất. Nguyên nhân: Chưa được tài liệu hóa. |
| Thứ tự | Theo position, từ 0 đến 14, trong khi các id không theo thứ tự (162115, 161996, 162101, …) (capture trên dev, 2026-10-02). | created_at mới nhất trước. | Nếu app hiển thị danh sách theo thứ tự của server, thứ tự sẽ đổi. |
| Số lượng cho client 356259 | 15 địa chỉ (capture trên dev, 2026-10-02; 13 vào 2026-10-01). | 30 địa chỉ, cùng client, cùng ngày (28 vào 2026-10-01). | Các địa chỉ đã xóa trên Shukah vẫn còn trên Locate, và một địa chỉ xuất hiện ba lần trên Locate. Hãy kiểm tra trùng lặp trước khi chuyển. |
| Địa chỉ mặc định | is_default là 0 ở cả 15 địa chỉ (capture trên dev, 2026-10-02). | true ở 28 trong 30 địa chỉ. | App lấy mặc định đầu tiên nó thấy, nên địa chỉ mặc định được chọn có thể khác. |
GET /api/v1/clients/address-book/:id
GET /maeda/address-book/:id✅ Đã thay thế- 🔴 Token Shukah nhận 401 "Unauthorization"; hãy gửi JWT của Locate (lấy từ exchange-token; giữ lại token Shukah).
- 🟡 id là chuỗi 24 ký tự hex; id số của Shukah (162115) vẫn được xử lý trong path.
- 🟡 lat/lng là chuỗi (Shukah trả số); app dùng Number() (locationPresetToSelectedAddress.ts:14).
- 🟡 Cùng một địa chỉ: is_default là 1 trên Locate so với 0 trên Shukah, và image_urls là [] so với một URL ảnh (162115).
- 🟡 id của người khác: Locate trả 404 "Address not found"; Shukah trả 403 "You can only view address that belong to you."
- 🟡 Body 404 của Locate có kèm stack của server (get.js:39:17) trên dev.
Request
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <locate-jwt>. | JWT của Locate. |
id | path | string | có | Giá trị chỉ gồm chữ số được thử trước như id địa chỉ Shukah, rồi như ObjectId. Mọi giá trị khác chỉ thử như ObjectId. | Id địa chỉ. |
| Bất kỳ query key hoặc header nào khác | query, header | — | không | Được chấp nhận và bỏ qua. | — |
Response
200 với một địa chỉ theo detail shape, không có wrapper:
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
id | string | không | ObjectId, kể cả khi dùng id số của Shukah trong path. | Id địa chỉ. |
location_name, formatted_address, area_name, street, city, area, state, zipcode, country, country_code | string | không | "" khi thiếu, không bao giờ là null. | Các phần của địa chỉ. |
is_default | integer | không | 0 hoặc 1. | Địa chỉ mặc định. |
lat, lng | string | không | Số dưới dạng text, ví dụ "24.7136". "" khi thiếu. | Tọa độ. |
image_urls | array of string | không | Có thể rỗng. | URL ảnh. |
Không có source, created_at hay updated_at.
{
"id": "6abe570b9ac75c8c65a5fa1a",
"location_name": "Docs capture",
"is_default": 0,
"formatted_address": "King Fahd Rd, Al Olaya, Riyadh 12211",
"area_name": "Olaya",
"street": "King Fahd Rd",
"city": "Riyadh",
"area": "Al Olaya",
"state": "",
"zipcode": "12211",
"country": "Saudi Arabia",
"country_code": "SA",
"lat": "24.7136",
"lng": "46.6753",
"image_urls": []
}
Capture trên dev, 2026-10-01 (client 356259): địa chỉ được tạo bởi sample create-address bên dưới, đọc ngay sau khi tạo và bị xóa ở cuối lần chạy. Text địa chỉ vốn đã là minh họa.
Cùng địa chỉ đó trả về từ create với state: null, và ở đây với state: "": đó là
khác biệt giữa hai shape.
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
401 | {"status":401,"message":"Unauthorization"} | Không có token, token không phải JWT Locate, client không active, hoặc không tải được bản ghi client. |
404 | {"status":404,"message":"Address not found"} | Không địa chỉ nào của client này khớp với id. Bao gồm địa chỉ đã bị xóa, và id thuộc về client khác. |
404 được capture trên dev vào 2026-10-01 bằng cách đọc địa chỉ mẫu sau khi nó bị xóa, và vào
2026-10-02 với một ObjectId không tồn tại. Một id thuộc về client khác nhận cùng câu trả lời
vì lookup chỉ tìm trong các địa chỉ của người gọi (đọc code; id của người khác chưa được thử live
trên Locate). Shukah trả lời id của người khác bằng 403 (capture trên dev, 2026-10-02; xem
Khác biệt so với Shukah). Trên dev các body lỗi cũng mang field stack với một stack trace của
server dạng text. Các sample trên trang này bỏ nó đi.
Thử ngay
export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
export ADDRESS_ID='<id-from-the-list>'
curl -s "$AUTH_BASE_URL/api/v1/clients/address-book/$ADDRESS_ID" -H "Authorization: Bearer $TOKEN"
Kỳ vọng: 200 và địa chỉ theo detail shape. Một id không tồn tại cho 404 như ở mục Lỗi.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/address-book/{id} | GET $AUTH_BASE_URL/api/v1/clients/address-book/{id} | Đổi base URL và path. |
id | Một integer (capture trên dev, 2026-10-01). | Một chuỗi 24 ký tự. Id số của Shukah vẫn chạy trong path với các địa chỉ đến từ address book của Shukah. | List giờ trả ObjectId. App so sánh id địa chỉ chọn lần cuối đã lưu với các id trong list dưới dạng chuỗi, nên một integer Shukah đã lưu không còn khớp và lựa chọn bị bỏ. |
lat, lng | Số JSON, ví dụ 24.7136 (capture trên dev, 2026-10-01). | Chuỗi, ví dụ "24.7136". | Không có: app chấp nhận cả hai và chuyển bằng Number() trước khi dùng. |
is_default | 0 hoặc 1. Địa chỉ 162115 là 0 (capture trên dev, 2026-10-02). | 0 hoặc 1. Cùng địa chỉ đó đọc qua Shukah id của nó là 1 (capture trên dev, 2026-10-02). | Type giống nhau, giá trị thì không: địa chỉ mặc định mà app hiển thị có thể đổi. |
image_urls | Một mảng hoặc null. Địa chỉ 162115 có một URL ảnh (capture trên dev, 2026-10-02). | Luôn là mảng. Cùng địa chỉ đó có []. | Ảnh của một địa chỉ copy từ Shukah bị mất. |
| Key thêm | position. | Không có. | Không có: app không đọc nó. |
| Địa chỉ của người khác | 403 {"name":"Forbidden","message":"You can only view address that belong to you.","code":0,"status":403,"type":"Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException"} (capture trên dev, 2026-10-02, id 1). | 404 Address not found, giống một id không tồn tại (đọc code). | Status và text mà app hiển thị sẽ đổi. |
Body 404 | {"name":"Not Found","message":"Object not found: 162119","code":0,"status":404,"type":"Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException"} (capture trên dev, 2026-10-01). | {"status":404,"message":"Address not found"} | App hiển thị message, nên text hiển thị sẽ đổi. |
POST /api/v1/clients/address-book
POST /maeda/address-book✅ Đã thay thế- 🔴 Token Shukah nhận 401 "Unauthorization"; hãy gửi JWT của Locate (lấy từ exchange-token; giữ lại token Shukah).
- 🔴 Response là {status,address:{...}}; app dựng preset từ cấp cao nhất nên id và lat/lng là NaN.
- 🔴 Không geocode: street, city, area, state, zipcode, country, formatted_address là null (Shukah điền các giá trị này).
- 🟡 address.is_default là boolean (Shukah là 0); lat/lng là số ở đây nhưng là chuỗi ở get và update.
- 🟡 Địa chỉ tạo trên Locate không bao giờ xuất hiện trên Shukah (push gọi /maeda/api/address-book, trả 404).
Request
Mọi field của body đều tùy chọn và không có gì được validate. Các key không được liệt kê bị bỏ qua.
Server đặt source là locate và các timestamp là bây giờ, nên body không thể set chúng.
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <locate-jwt>. | JWT của Locate. |
location_name, street, area, area_name, city, state, zipcode, country, country_code, formatted_address | body | string | không | Được trim. Trống hoặc null được lưu là null. Không giới hạn độ dài. | Các phần của địa chỉ. |
lat, lng | body | number hoặc numeric string | không | null, "" và giá trị không phải số được lưu là null. Không kiểm tra phạm vi. | Tọa độ. |
image_urls | body | array of string | không | Các chuỗi được trim và chuỗi trống bị bỏ. null cho []. Một chuỗi JSON-array hoặc chuỗi ngăn cách bằng dấu phẩy cũng được chấp nhận. | URL ảnh. |
is_default | body | boolean, 0 hoặc 1 | không | Chỉ 1, "1" và true nghĩa là mặc định. Mọi giá trị khác, kể cả "true" và 2, nghĩa là không mặc định. | Đặt địa chỉ này làm mặc định. |
Những gì app gửi trong snapshot ngày 2026-02-26 chỉ là location_name, area_name, lat và
lng (cả hai dạng chuỗi), is_default (0 hoặc 1) và image_urls (một mảng hoặc null).
Một request dùng cho capture bên dưới:
{
"location_name": "Docs capture",
"street": "King Fahd Rd",
"area": "Al Olaya",
"area_name": "Olaya",
"city": "Riyadh",
"zipcode": "12211",
"country": "Saudi Arabia",
"country_code": "SA",
"lat": 24.7136,
"lng": 46.6753,
"formatted_address": "King Fahd Rd, Al Olaya, Riyadh 12211",
"image_urls": [],
"is_default": false
}
Response
200 với một wrapper chứa địa chỉ mới theo list shape:
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
status | integer | không | Luôn là 200. | Echo status. |
address | object | không | List shape, với các field được liệt kê ở khối list. source là locate. is_default là true khi request yêu cầu hoặc khi client chưa có mặc định. | Địa chỉ đã được tạo. |
{
"status": 200,
"address": {
"id": "6abe570b9ac75c8c65a5fa1a",
"source": "locate",
"is_default": false,
"location_name": "Docs capture",
"street": "King Fahd Rd",
"area": "Al Olaya",
"area_name": "Olaya",
"city": "Riyadh",
"state": null,
"zipcode": "12211",
"country": "Saudi Arabia",
"country_code": "SA",
"lat": 24.7136,
"lng": 46.6753,
"formatted_address": "King Fahd Rd, Al Olaya, Riyadh 12211",
"image_urls": [],
"created_at": "2026-10-01T12:50:19.661Z",
"updated_at": "2026-10-01T12:50:19.661Z"
}
}
Capture trên dev, 2026-10-01 (client 356259, Task 9 live write). Địa chỉ bị xóa ở cuối lần
chạy. Text địa chỉ vốn đã là minh họa. is_default là false vì client
đã có các địa chỉ mặc định.
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
401 | {"status":401,"message":"Unauthorization"} | Không có token, token không phải JWT Locate, client không active, hoặc không tải được bản ghi client. |
500 | Trên dev: {"status":500,"message":"<raw error text>","stack":"<stack trace>"}. Trên production: {"status":500,"message":"Internal server error"}. | Lỗi không mong đợi, ví dụ một lần save xung đột với một bản copy dữ liệu của client từ Shukah đến cùng lúc. Chưa capture. Trên dev message là text lỗi gốc và có thêm field stack; Internal server error chỉ xuất hiện trên production (UAT và production: Chưa được tài liệu hóa). |
Không có 400: giá trị xấu được làm sạch hoặc lưu là null. Body 401 được capture trên dev vào
2026-10-01 khi không có token.
Thử ngay
Lời gọi này tạo một địa chỉ thật trên dev. Hãy xóa nó sau đó bằng lời gọi delete bên dưới.
export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
curl -s -X POST "$AUTH_BASE_URL/api/v1/clients/address-book" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"location_name":"Docs test","area_name":"Olaya","lat":"24.7136","lng":"46.6753","is_default":0,"image_urls":[]}'
Kỳ vọng: 200 và {"status":200,"address":{...}}. Body này là cái app gửi, nên street,
city, area, state, zipcode, country, country_code và formatted_address trả về là
null (capture trên dev, 2026-10-02, với đúng body này). Giữ address.id cho các lời gọi khác.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | POST $SHUKAH_BASE_URL/maeda/address-book | POST $AUTH_BASE_URL/api/v1/clients/address-book | Đổi base URL và path. |
| Response | Chính object địa chỉ, phẳng, với id integer và key position (capture trên dev, 2026-10-01). | {"status":200,"address":{...}} với id 24 ký tự. | App đọc id, lat, lng và phần còn lại từ cấp cao nhất của câu trả lời. Với wrapper, tất cả đều thiếu, và tọa độ nó dựng cho địa chỉ được chọn thành NaN. Hãy đọc address thay vào đó. |
| Các phần địa chỉ bị thiếu | Một request chỉ có lat, lng, area_name, location_name, is_default và image_urls trả về với street, area, state, zipcode, country, country_code và formatted_address đã được điền (capture trên dev, 2026-10-01, và lại vào 2026-10-02 với position 15). Shukah suy ra chúng bằng cách nào: Chưa được tài liệu hóa. | Chỉ lưu những gì được gửi. Cái gì không gửi là null: cùng request đó trả về cả tám giá trị là null (capture trên dev, 2026-10-02). | Một địa chỉ tạo bằng body hiện tại của app không có street, city hay formatted address. Màn hình bản đồ có các giá trị này, nên app có thể gửi chúng, hoặc backend phải điền. Có kế hoạch như vậy hay không: Chưa được tài liệu hóa. |
| Địa chỉ cùng tên | App xóa một địa chỉ hiện có cùng tên trước khi tạo địa chỉ mới. | Hai lời gọi đó vẫn chạy. Delete dùng id từ list, là một chuỗi. | Không có ở bước này. Xem khối delete cho lần xóa tường minh. |
| Địa chỉ được lưu ở đâu | Trong Shukah. | Trong Locate, sau đó là một bản copy best-effort sang Shukah. | Trên dev bản copy không tới được Shukah: list của Shukah vẫn có 13 địa chỉ sau create, update và delete ngày 2026-10-01, và vào 2026-10-02 địa chỉ tạo qua Locate cũng không bao giờ xuất hiện trên Shukah. Delete không bao giờ được copy. Không thể giả định một địa chỉ tạo qua Locate tồn tại trên Shukah. |
PUT /api/v1/clients/address-book/:id
PUT /maeda/address-book/:id✅ Đã thay thế- 🔴 Token Shukah nhận 401 "Unauthorization"; hãy gửi JWT của Locate (lấy từ exchange-token; giữ lại token Shukah).
- 🟡 id là chuỗi 24 ký tự hex và lat/lng là chuỗi; app đọc cùng các key nên không cần thay đổi.
- 🟡 Các phần địa chỉ chưa đặt trả về "" trên Locate (city, area, state ...); Shukah trả giá trị đã geocode.
- 🟡 Chỉnh sửa trên Shukah không bao giờ tới Locate ("S (edited)" vẫn là "Parity test S" ở đó).
- 🟡 Locate luôn giữ đúng một địa chỉ mặc định khi update; Shukah luôn trả is_default là 0.
- 🟡 Body 404 có kèm stack của server trên dev.
Body phẳng theo kiểu Shukah; id có thể là id 24 ký tự hex hoặc id số của Shukah.
Request
Cập nhật một phần: chỉ các key có trong body mới thay đổi.
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <locate-jwt>. | JWT của Locate. |
id | path | string | có | Chỉ chữ số: id địa chỉ Shukah trước, rồi ObjectId. Mọi giá trị khác: chỉ ObjectId. | Địa chỉ cần đổi. |
location_name, street, area, area_name, city, state, zipcode, country, country_code, formatted_address | body | string hoặc null | không | Làm sạch giống create. Một key có mặt với null, "" hoặc khoảng trắng đặt field là null. | Các phần của địa chỉ. |
lat, lng | body | number, numeric string hoặc null | không | Làm sạch giống create. | Tọa độ. |
image_urls | body | array of string hoặc null | không | Làm sạch giống create. null cho []. | URL ảnh. |
is_default | body | boolean, 0 hoặc 1 | không | Chỉ 1, "1" và true nghĩa là mặc định. Xem các quy tắc mặc định ở mục Tính năng. | Đặt địa chỉ này làm mặc định, hoặc không. |
source, created_at, bất kỳ key nào khác | body | — | không | Bị bỏ qua. | — |
Những gì app gửi trong snapshot ngày 2026-02-26: location_name, area, is_default (0 hoặc 1)
và image_urls.
{
"location_name": "Docs capture (edited)"
}
Response
200 với địa chỉ theo detail shape, giống như get: không có wrapper, is_default
là 0 hoặc 1, lat và lng là chuỗi.
{
"id": "6abe570b9ac75c8c65a5fa1a",
"location_name": "Docs capture (edited)",
"is_default": 0,
"formatted_address": "King Fahd Rd, Al Olaya, Riyadh 12211",
"area_name": "Olaya",
"street": "King Fahd Rd",
"city": "Riyadh",
"area": "Al Olaya",
"state": "",
"zipcode": "12211",
"country": "Saudi Arabia",
"country_code": "SA",
"lat": "24.7136",
"lng": "46.6753",
"image_urls": []
}
Capture trên dev, 2026-10-01 (client 356259, Task 9 live write): request ở trên, gửi tới địa chỉ từ sample create. Text địa chỉ vốn đã là minh họa.
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
401 | {"status":401,"message":"Unauthorization"} | Không có token, token không phải JWT Locate, client không active, hoặc không tải được bản ghi client. |
404 | {"status":404,"message":"Address not found"} | Không địa chỉ nào của client này khớp với id, kể cả id thuộc về client khác. |
500 | Trên dev: {"status":500,"message":"<raw error text>","stack":"<stack trace>"}. Trên production: {"status":500,"message":"Internal server error"}. | Lỗi không mong đợi, như với create. Chưa capture. Trên dev message là text lỗi gốc và có thêm field stack; Internal server error chỉ xuất hiện trên production (UAT và production: Chưa được tài liệu hóa). |
Không có 400. Body 401 được capture trên dev vào 2026-10-01 khi không có token. Body 404 là
cái đã capture cho get-address.
Thử ngay
Lời gọi này thay đổi một địa chỉ trên dev. Hãy dùng một địa chỉ bạn đã tạo cho bài test.
export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
export ADDRESS_ID='<id-from-the-create-call>'
curl -s -X PUT "$AUTH_BASE_URL/api/v1/clients/address-book/$ADDRESS_ID" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"location_name":"Office"}'
Kỳ vọng: 200 và địa chỉ theo detail shape với location_name mới.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | PUT $SHUKAH_BASE_URL/maeda/address-book/{id} | PUT $AUTH_BASE_URL/api/v1/clients/address-book/{id} | Đổi base URL và path. |
| Response | Địa chỉ, phẳng, với id integer, số cho lat và lng, và key position (capture trên dev, 2026-10-01 và 2026-10-02). | Địa chỉ, phẳng, với id 24 ký tự, chuỗi cho lat và lng, và không có position. Các phần địa chỉ chưa bao giờ được set trả về "" (capture trên dev, 2026-10-02). | Không có với các key app đọc (đã xác nhận vào 2026-10-02). App dựng địa chỉ đã lưu của nó từ các key cấp cao nhất, đều có mặt. |
| Quy tắc mặc định | Chưa được tài liệu hóa. | Các quy tắc ở mục Tính năng. | Nếu app dựa vào cách chọn mặc định của Shukah, hãy so sánh trước khi chuyển. |
| Copy sang Shukah | Không áp dụng. | Một bản copy best-effort của địa chỉ đã sửa được gửi sang Shukah. Khi địa chỉ không có Shukah id, bản copy tạo một địa chỉ mới ở đó, có thể gây trùng lặp. | Trên dev bản copy không xuất hiện trong list của Shukah vào 2026-10-01 và cả vào 2026-10-02 (xem create). Một lần sửa làm trên Shukah cũng không tới được Locate (2026-10-02). |
DELETE /api/v1/clients/address-book/:id
DELETE /maeda/address-book/:id✅ Đã thay thế- 🔴 Token Shukah nhận 401 "Unauthorization"; hãy gửi JWT của Locate (lấy từ exchange-token; giữ lại token Shukah).
- 🔴 App gửi +id, nên id 24 ký tự hex thành NaN và lệnh xóa trả 404; slice cũng so sánh bằng +id.
- 🟡 Response là 200 {"status":200}; Shukah trả 204 không có body; app bỏ qua body.
- 🟡 Xóa trên Locate không được đẩy sang Shukah, và xóa trên Shukah không tới Locate (địa chỉ "ma").
Request
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | Bearer <locate-jwt>. | JWT của Locate. |
id | path | string | có | Chỉ chữ số: id địa chỉ Shukah trước, rồi ObjectId. Mọi giá trị khác: chỉ ObjectId. | Địa chỉ cần xóa. |
Không có body.
Response
200 với một key:
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
status | integer | không | Luôn là 200. Không có key nào khác. | Echo status. |
{
"status": 200
}
Capture trên dev, 2026-10-01 (client 356259, Task 9 live write): lần xóa địa chỉ mẫu.
Việc xóa là vĩnh viễn. Nếu địa chỉ bị xóa là mặc định và vẫn còn địa chỉ khác, địa chỉ đầu tiên theo thứ tự lưu trữ trở thành mặc định.
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
401 | {"status":401,"message":"Unauthorization"} | Không có token, token không phải JWT Locate, client không active, hoặc không tải được bản ghi client. |
404 | {"status":404,"message":"Address not found"} | Không địa chỉ nào của client này khớp với id. Lần delete thứ hai của cùng id cũng nhận cái này, nên lời gọi không idempotent. |
500 | Trên dev: {"status":500,"message":"<raw error text>","stack":"<stack trace>"}. Trên production: {"status":500,"message":"Internal server error"}. | Lỗi không mong đợi. Chưa capture. Trên dev message là text lỗi gốc và có thêm field stack; Internal server error chỉ xuất hiện trên production (UAT và production: Chưa được tài liệu hóa). |
Body 401 được capture trên dev vào 2026-10-01 khi không có token. Body 404 là cái đã capture
cho get-address sau lần delete này.
Thử ngay
Lời gọi này xóa một địa chỉ trên dev. Hãy dùng một địa chỉ bạn đã tạo cho bài test.
export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
export ADDRESS_ID='<id-from-the-create-call>'
curl -s -X DELETE "$AUTH_BASE_URL/api/v1/clients/address-book/$ADDRESS_ID" -H "Authorization: Bearer $TOKEN"
Kỳ vọng: 200 và {"status":200}. Chạy lại cho 404 như ở mục Lỗi.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | DELETE $SHUKAH_BASE_URL/maeda/address-book/{id} | DELETE $AUTH_BASE_URL/api/v1/clients/address-book/{id} | Đổi base URL và path. |
| Câu trả lời thành công | 204 với body rỗng (capture trên dev, 2026-10-01). | 200 với {"status":200}. | Không có: app không đọc body. |
| Id mà app gửi | App biến id thành số bằng toán tử unary plus trước lời gọi. Điều đó chạy với integer của Shukah. | Một id 24 ký tự thành NaN, nên app gọi .../address-book/NaN. Đó không phải toàn chữ số, nên service tìm một ObjectId NaN và trả 404 Address not found (đọc code, chưa thử live). | Lần xóa tường minh trên màn hình Edit address sẽ lỗi cho đến khi app ngừng chuyển đổi id. Việc xóa địa chỉ cùng tên bên trong create không chuyển đổi nó. |
Body 404 | {"name":"Not Found","message":"Object not found: 162119",...} (capture trên dev, 2026-10-01). | {"status":404,"message":"Address not found"} | App hiển thị message, nên text hiển thị sẽ đổi. |
| Copy sang Shukah | Không áp dụng. | Delete không bao giờ được copy sang Shukah. | Một địa chỉ được tạo hoặc sửa qua Locate rồi xóa qua Locate có thể vẫn còn trên Shukah. |
Lưu ý
- App biến id địa chỉ thành số trước khi xóa, nên id Locate (24 ký tự hex) thành
NaNvà lần xóa lỗi với404. - Create trả
{status, address}thay vì chính địa chỉ, và nó không tự điền street, city hay formatted address từ tọa độ như Shukah làm. - List và create trả boolean và số, get và update trả
0/1và tọa độ dạng chuỗi, và list có thể chứa nhiều địa chỉ mặc định.
Chi tiết (cho backend)
Chỉ kiểm tra dev: host https://dev.authentication.locate.sa, prefix /api/v1. Host và prefix của UAT
và production: Chưa được tài liệu hóa. Body lỗi trên dev mang field stack (capture
2026-10-01 trên 404 của get-address và các 400 của profile và setting), nên dev không
chạy với NODE_ENV=production; handler chỉ thêm stack ngoài production
(src/middlewares/error-handler.js:12-35). Cùng handler đó làm cho một 500 không mong đợi mang text lỗi gốc và một stack trên dev, và Internal server error chỉ trên production (src/middlewares/error-handler.js:28-33).
Lookup id
findAddressIndexByApiId biến id trong path thành text. Nếu nó khớp ^\d+$ thì tìm một địa chỉ
có shukah_address_id bằng số đó, và nếu không có cái nào khớp thì fallback sang _id của
sub-document (src/controllers/client/address-book/lookup.js:13-25). Get, update và delete đều dùng nó.
Lookup chạy trên mảng addresses của chính người gọi, đó là lý do id của client khác cho 404
(src/controllers/client/address-book/get.js:11-14; tài liệu service liệt kê cùng 404 cho một id
không phải của user hiện tại, docs/api/client-auth-and-address-book.md:259-263). Shukah id được
copy từ mt_address_book.id cho các địa chỉ đến từ address book của Shukah, và là null cho
các địa chỉ tạo qua API và cho địa chỉ inline dựng từ dòng client
(src/models/client.js:19, src/sync/mappers/client-mapper.js:32-47,50-53).
Quy tắc ghi
- Create chỉ đọc các key trong bảng request của nó, nên các key lạ,
source,shukah_address_idvà các timestamp trong body bị bỏ qua. Nó setsource: 'locate',shukah_address_id: nullvà cả hai timestamp là bây giờ (src/controllers/client/address-book/parse-payload.js:4-21,src/controllers/client/address-book/create.js:13,18). - Các cast:
toStrtrim và biến trống hoặcnullthànhnull;toNumchonullvớinull,""và số không hữu hạn;toBoolchỉ true với1,"1"vàtrue;toImageUrlschấp nhận một mảng, một chuỗi JSON-array hoặc một chuỗi ngăn cách bằng dấu phẩy, trim, và bỏ giá trị trống (src/sync/lib/casts.js:1,56-81). - Update chỉ áp dụng một key khi body có nó (
hasOwnProperty), nên mộtnullcó mặt xóa field (src/controllers/client/address-book/parse-payload.js:39-57). Nó chỉ chạy quy tắc mặc định khi body cóis_default(src/controllers/client/address-book/update.js:22-24,src/controllers/client/address-book/parse-payload.js:60-75). - Quy tắc mặc định của create nằm trong
resolveDefaultOnCreate(src/controllers/client/address-book/parse-payload.js:24-37), và của delete trongresolveDefaultOnDelete, hàm promoteaddresses[0](parse-payload.js:78-82,src/controllers/client/address-book/remove.js:15-17). - Các handler tải lại client mà không dùng query cache, nên một lần đọc hay ghi không bao giờ dùng bản
cache 300 giây (
src/controllers/client/address-book/load-fresh-client.js:7-8). - Một lần save chạy đua với một thay đổi inbound từ Shukah có thể lỗi với Mongoose
VersionErrorvà cho500(đọc code; inbound push cố ý tăng version của document,docs/plans/2026-07-13-shukah-address-push-api.md:58-59).
Shape
Các câu trả lời list và create đến từ formatAddressForResponse, dùng ?? null cho text và
Boolean(is_default) (src/controllers/client/address-book/mapper.js:4-28). Get và update đến từ
formatAddressDetailForResponse, biến mọi text bị thiếu thành "", is_default thành 0 hoặc
1, và lat và lng thành text (src/controllers/client/address-book/mapper.js:30-51). List được
sắp theo created_at, mới nhất trước, với các địa chỉ không có ngày ở cuối
(src/controllers/client/address-book/handler.js:9-15).
Địa chỉ mặc định trong dữ liệu
Quy tắc "một mặc định" chỉ được thực thi bởi ba lời gọi app này. ETL chọn một mặc định cho mỗi
client khi nó dựng document (src/sync/mappers/client-mapper.js:80-87), nhưng inbound
push địa chỉ từ Shukah sang Locate không bầu chọn gì (src/controllers/admin/client-addresses/handler.js:67-68).
Đó là cách cả 10 mục trong list đã capture đều có thể là mặc định (2026-10-01), và 28 trong 30 (2026-10-02).
Vào 2026-10-02 bản copy của một địa chỉ tạo trên Shukah đến nơi với is_default là true, dù Shukah gửi 0
(nguyên nhân: Chưa được tài liệu hóa). Tài liệu service vẫn nói server luôn
giữ đúng một mặc định (docs/api/client-auth-and-address-book.md:291).
Push sang Shukah
Sau một lần save cục bộ, create và update đẩy địa chỉ sang Shukah, best effort và được await. Create
lưu id trả về làm shukah_address_id. Update đẩy tới {shukah_address_id} khi địa chỉ
có, và nếu không thì fallback sang create push. Delete không được đẩy
(src/services/shukah-sync.js:238-415; docs/plans/2026-09-22-shukah-address-outbound-push.md:106-110,149-151).
Push không làm gì khi sync tắt hoặc host hay admin token chưa được set. Nó có được bật trên
dev hay không: Chưa được tài liệu hóa. Các capture 2026-10-01 gợi ý nó không chạy với client này: sau
create, update và delete trên Locate, list của Shukah vẫn có 13 địa chỉ, trong khi một create được copy mà
không có delete được copy sẽ để lại 14.
Vào 2026-10-02 nguyên nhân đã được tìm thấy trong code: create push gửi tới /maeda/api/address-book
(src/services/shukah-sync.js:310), mà Shukah dev trả lời bằng 404; Shukah phục vụ
/maeda/address-book. Lỗi bị nuốt và chỉ được ghi vào sync_logs (:335). Update
push dùng /maeda/address-book/{id} (:356) nhưng chỉ cho địa chỉ đã có Shukah id;
các địa chỉ khác fallback sang create bị hỏng. Chiều ngược lại cũng hỏng: một create trên Shukah tới
Locate trong vài giây, nhưng update và delete trên Shukah không bao giờ tới. Locate có các receiver
(src/routes/admin.js:52-54), nên bản port Laravel của Shukah không gọi chúng (suy luận). Locate cũng
mất URL ảnh của 4 địa chỉ Shukah có ảnh; toImageUrls xử lý được mảng và chuỗi JSON
(src/sync/lib/casts.js:67-81), nên chỗ mất nằm ở upstream của nó. Nguyên nhân: Chưa được tài liệu hóa.
Khi tài liệu và code mâu thuẫn (code thắng)
- Tài liệu service nói
sourcecủa địa chỉ là"locate"hoặc"shukah"(docs/api/client-auth-and-address-book.md:224). Model cho phépmt_client_inline,mt_address_bookvàlocate(src/models/client.js:18), và capture cho thấymt_address_book. - Tài liệu service nói server đảm bảo đúng một mặc định
(
docs/api/client-auth-and-address-book.md:291). Điều đó chỉ đúng cho các lời gọi của app, như ở trên. - Một design doc ban đầu nói get trả
{status, address}(docs/plans/2026-07-07-shukah-address-book-api-local.md:13-14), và sửa thành object phẳng ở dòng 23-24. Code trả object phẳng. - Các design doc viết path Shukah là
/maeda/api/address-book(docs/plans/2026-07-07-shukah-address-book-api-local.md:49). App gọi/maeda/address-book, và Shukah dev trả404cho dạng/api/, nên các trang dùng path của app. - Các capture đã làm sạch cho thấy Shukah trả
latvàlngdạng số, trong khi type riêng của app khai báo là chuỗi (src/entities/location/model/types/location.server.ts:3-19). - 2026-10-02: trang này ghi 13 địa chỉ Shukah và 28 địa chỉ Locate, không có mặc định trên Shukah và cả 10 đều
mặc định trên Locate, và thứ tự Shukah chưa rõ. Các capture 2026-10-02 cho thấy 15 và 30, Shukah
0ở cả 15, Locatetrueở 28 trong 30, và Shukah sắp theoposition. - 2026-10-02: trang này kỳ vọng không có khác biệt
image_urlsở list và get. Locate đã mất URL ảnh của 4 địa chỉ Shukah có ảnh. - 2026-10-02: trang này ghi
is_defaultcủa get là giống nhau ở cả hai phía. Cùng một địa chỉ là0trên Shukah và1trên Locate. - 2026-10-02: trang này nói một id của người khác nhận cùng câu trả lời ở cả hai phía. Shukah trả
403; Locate trả404(đọc code). - 2026-10-02: trang này nói các phần địa chỉ
nullở create là đọc code. Giờ chúng đã được capture. - 2026-10-02: "bản copy best-effort sang Shukah" của create và update có trong code
(
src/controllers/client/address-book/create.js:23,src/controllers/client/address-book/update.js:31), nhưng vào 2026-10-02 cũng không có bản copy nào tới Shukah dev (xem Push sang Shukah).
Sources
locate-authentication-service@6757aa4:src/routes/client.js:17-21,src/controllers/client/address-book/handler.js:6-17,src/controllers/client/address-book/get.js:9-22,src/controllers/client/address-book/create.js:10-28,src/controllers/client/address-book/update.js:12-37,src/controllers/client/address-book/remove.js:8-21,src/controllers/client/address-book/lookup.js:13-25,src/controllers/client/address-book/load-fresh-client.js:7-8,src/controllers/client/address-book/mapper.js:4-51,src/controllers/client/address-book/parse-payload.js:4-82,src/sync/lib/casts.js:1,56-81,src/models/client.js:16-34,src/middlewares/error-handler.js:12-35,src/sync/mappers/client-mapper.js:32-87,src/controllers/admin/client-addresses/handler.js:67-68,src/services/shukah-sync.js:238-415- Tài liệu của
locate-authentication-service@6757aa4:docs/api/client-auth-and-address-book.md§4 đến §8 (dòng 187-370, 224, 259-263, 291; thay đổi lần cuối 2026-07-27),docs/plans/2026-07-07-shukah-address-book-api-local.md:13-24,49,226,docs/plans/2026-07-13-shukah-address-push-api.md:58-59,docs/plans/2026-09-22-shukah-address-outbound-push.md:106-110,149-151 - Snapshot mobile
mobile/locate-client@e960c3a(branchdevelopment, 2026-02-26; code của chính app, không phải Locate):src/entities/location/model/api/services/location.service.ts:21-24,37-41,55-59,73-78,92-96,src/entities/location/model/api/controllers/location.controller.ts:38-110,src/entities/location/model/api/dto/location.dto.ts:38-86,105-126,144-199,src/entities/location/model/types/location.server.ts:3-19,src/entities/location/model/constants/location.ts:15-18,src/entities/location/model/helpers/locationPresetToSelectedAddress.ts:3-20,src/entities/location/model/services/fetchLocationPresets/fetchLocationPresets.ts:17,src/entities/location/model/services/fetchLocationPresetById/fetchLocationPresetById.ts:17-19,src/entities/location/model/services/createLocationPreset/createLocationPreset.ts:50-92,src/entities/location/model/services/updateLocationPreset/updateLocationPreset.ts:18-20,src/entities/location/model/services/deleteLocationPreset/deleteLocationPreset.ts:20-21,src/entities/location/model/services/initLocation/initLocation.ts:33-38,78,src/entities/location/model/slice/location.slice.ts:56-58,src/features/SavedLocationsList/ui/SavedLocationsList/SavedLocationsList.tsx:70,src/features/UserLocationInfo/ui/UserLocationInfo/UserLocationInfo.container.tsx:44-46,src/widgets/LocationDrawer/ui/LocationDrawer/LocationDrawer.tsx:42-44,src/features/MapDrawer/model/services/createLocationPreset/saveLocation.ts:69-80,src/widgets/Map/ui/Map/Map.tsx:105,src/features/EditSavedAddress/model/services/init.ts:13-17,src/features/EditSavedAddress/model/services/updatePreset.ts:62-70,src/features/EditSavedAddress/model/services/deletePreset.ts:19,src/shared/api/reqError.ts:52-62 locate-authentication-service@6757aa4, push và sync:src/services/shukah-sync.js:310,335,356,src/routes/admin.js:52-54,src/sync/lib/casts.js:67-81- Capture dev, 2026-10-02 (parity audit):
addr-list-before--shukah,addr-list-before--locate-jwt,address-book-get--shukah,address-book-get--locate-jwt,address-book-get-shukahid--shukah,address-book-get-shukahid--locate-jwt,address-book-get-unknown--shukah,address-book-get-unknown--locate-jwt,address-create--shukah,address-create--locate-jwt,address-update--shukah,address-update--locate-jwt,address-delete--shukah,address-delete--locate-jwt,addr-list-after-create--shukah,addr-list-after-update--shukah,addr-list-after-delete--shukah,addr-list-after-delete--locate-jwt - Capture dev, 2026-10-01:
locate-address-list,locate-address-get,locate-address-create,locate-address-get-created,locate-address-update,locate-address-delete,locate-address-get-deleted,locate-address-list-after-shukah-writes,shukah-address-list,shukah-address-get,shukah-address-create,shukah-address-update,shukah-address-delete,shukah-address-get-deleted,shukah-address-list-after-locate-writes,probe-locate-address-list,probe-locate-address-get,probe-locate-address-create,probe-locate-address-update,probe-locate-address-delete