feat(privacy): keep venue phone numbers out of every public response
A doctor's office number sat next to the address on the public profile and in the anonymous API payload, so harvesting the phone number of every practice in the country was one unauthenticated request away. Street address and map coordinates stay public — a patient needs those to find the place — but the phone is now opt-in per caller: DoctorAddress::toArray() and the clinic serializers only emit it when told to, and the public doctor/clinic endpoints tell them to only when the caller may edit that profile (the same can_edit they already compute). Owner-facing address CRUD keeps returning it unchanged. The patient still gets the number where it is actually useful — their own appointment. That payload also stops guessing: it used to serialise the doctor's *first* address, so a booking made at the clinic or at a second office showed the wrong street entirely. It now resolves the address recorded on the appointment itself, which works the same for a personal office and a clinic branch, and falls back to the clinic's own number when the address has none. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -459,7 +459,13 @@ Get appointment detail.
|
||||
}
|
||||
}
|
||||
```
|
||||
> `doctor.specialties` آرایه (ممکن است خالی)؛ `address` اولین آدرس پزشک است (ممکن است `null` اگر پزشک آدرسی ندارد). `address.map.latitude/longitude` رشته یا `null`. تاریخها Unix.
|
||||
|
||||
> ℹ️ `address` is the venue recorded on this appointment (`address_id`) — the doctor's own
|
||||
> office or the clinic branch, whichever the booking was made at — and it is the only place
|
||||
> `telephone` is returned. When that record carries no number, the clinic's own number takes
|
||||
> its place. Public doctor and clinic responses never carry a phone number.
|
||||
|
||||
> `doctor.specialties` آرایه (ممکن است خالی)؛ `address` آدرسِ ثبتشدهٔ همین نوبت است و برای نوبتهای قدیمیِ بدون `address_id` ممکن است `null` باشد. `address.map.latitude/longitude` رشته یا `null`. تاریخها Unix.
|
||||
|
||||
### انتخاب بیمهٔ نوبت
|
||||
|
||||
|
||||
+3
-1
@@ -167,6 +167,8 @@ limited to the whitelist under `PATCH /api/v1/clinic/{uuid}`.
|
||||
}
|
||||
```
|
||||
|
||||
> 🔒 `phone`/`phone_number` are `null` unless the caller may edit the clinic (`can_edit: true`). The number is not public data; the patient sees the venue phone on their own appointment instead.
|
||||
>
|
||||
> `city`/`state`/`map`/`location`/`phone`/`phone_number` are all resolved from the clinic's **address** (`DoctorAddress` linked by `clinic_id`), not from columns on the clinic. `location` and `phone`/`phone_number` fall back to the deprecated `clinics.address` / `clinics.telephone` columns only when the address record has no value — reading them from different rows made one response describe two different places. Each is an array with a single object (or empty `[]` if the clinic has no address). `doctors` is a **count**; the actual doctor list comes from `GET /api/v1/clinic/doctor-list/{clinicUuid}` (`doctor_list` here is always `null`).
|
||||
|
||||
### معنای `is_active`
|
||||
@@ -330,7 +332,7 @@ List clinics with pagination.
|
||||
| `doctors_count` | integer | Number of doctors linked to the clinic |
|
||||
| `city` | string\|null | City name, resolved from the clinic's address (`DoctorAddress`) |
|
||||
| `state` | string\|null | Province name, resolved from the clinic's address (`DoctorAddress`) |
|
||||
| `phone` / `phone_number` | string\|null | Contact number from the clinic's address (`DoctorAddress`), falling back to the deprecated `clinics.telephone` column |
|
||||
| `phone` / `phone_number` | string\|null | Contact number from the clinic's address (`DoctorAddress`), falling back to the deprecated `clinics.telephone` column. `null` for anyone who cannot edit the clinic, and always `null` in the public list. |
|
||||
| `24_7` | boolean | Open 24/7 flag |
|
||||
| `field_working_days` | string\|null | Working days/hours description |
|
||||
|
||||
|
||||
+9
-2
@@ -133,7 +133,7 @@ limited to the whitelist under `PATCH /api/v1/doctor/{uuid}`.
|
||||
"address": [],
|
||||
"state": [],
|
||||
"city": [],
|
||||
"clinics": [{ "uuid": "...", "name": "کلینیک الوند", "address": "...", "telephone": "..." }],
|
||||
"clinics": [{ "uuid": "...", "name": "کلینیک الوند", "address": "...", "telephone": null }],
|
||||
"representation": { "id": 12, "uuid": "9c1...", "full_name": "علی محمدی" }
|
||||
}
|
||||
}
|
||||
@@ -143,6 +143,13 @@ limited to the whitelist under `PATCH /api/v1/doctor/{uuid}`.
|
||||
> ⚠️ **Double-nested:** Frontend extracts with `data?.data?.data`
|
||||
>
|
||||
> ℹ️ `representation` نمایندهی مالکِ پزشک است؛ برای پزشکِ بدون نماینده `null`.
|
||||
>
|
||||
> 🔒 **Phone numbers are not public.** `address[].telephone` and `clinics[].telephone`
|
||||
> are `null` for anonymous callers and only carry a value when `can_edit` is `true`
|
||||
> (the profile owner, its representative, or an admin). Street address and map
|
||||
> coordinates stay public — a patient needs them to find the place. The venue phone
|
||||
> reaches the patient through their own appointment (`GET /api/v1/appointments/user`),
|
||||
> not through the public profile.
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
@@ -171,7 +178,7 @@ Get doctor detail for clinic owner — only doctors who are members of the authe
|
||||
"uuid": "...",
|
||||
"title": "علی احمدی",
|
||||
"specialties": [...],
|
||||
"clinics": [{ "uuid": "...", "name": "کلینیک نور", "address": "...", "telephone": "..." }]
|
||||
"clinics": [{ "uuid": "...", "name": "کلینیک نور", "address": "...", "telephone": "021..." }]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user