docs(spec): calendar_share co-editor/deputy and Time Arc alignment.

Back §2.1.3 API/ACL; Front §3.3 share busy/ghost. Refs EventHub/EventHubBack#73
This commit is contained in:
2026-08-15 23:20:39 +03:00
parent 1182568c9d
commit 4dd741ba48
7 changed files with 342 additions and 161 deletions
+38 -12
View File
@@ -13,8 +13,9 @@ EventHub — платформа для управления событиями
### 2.1. Календари
- CRUD календаря (название, описание, теги, владелец)
- Расшаривание по ссылке / приглашения с правами (`calendar_share`) — **фаза 2** (таблица есть;
user HTTP API и UI — Future, см. §2.1.3)
- Расшаривание по ссылке / приглашения с правами (`calendar_share`) — invite/accept/revoke +
ACL `read|write|admin` (Back#73); personal и commercial (заместитель); UI — Front;
не путать со `specialist_invite` / follow.
- Типы календарей: `personal` | `commercial` — семантика и коммерческий контур: **§2.1.2**
- Гибкое подтверждение заявок: `auto` | `manual` | `{timeout, N}` (секунды) — детали §2.1.2 / §2.3
- Теги календаря, рейтинг (средняя оценка, количество голосов)
@@ -68,7 +69,7 @@ EventHub — платформа для управления событиями
| | `personal` | `commercial` |
|---|---|---|
| Подписка владельца | не требуется | нужна active (trial или paid), иначе **restricted** |
| Видимость чужим | только владелец (+ share — фаза 2) | публичный просмотр; в search/discover — только если **не** restricted |
| Видимость чужим | только владелец или share grant | публичный просмотр; в search/discover — только если **не** restricted |
| Booking чужим | **запрещён** (бэкенд `403`, код `personal_calendar`) | разрешён при active подписке владельца |
| Follow | нет (нет доступа) | да |
| Специалисты | не используются | CRUD владельца; `specialist_id` на событии |
@@ -256,16 +257,33 @@ Legacy: прямой `POST /v1/calendars/:id/specialists` **не использ
**сделано** (Back#75): `GET/PUT /v1/notifications/prefs`,
`GET /v1/push/vapid-public-key`, `POST/DELETE /v1/push/subscriptions`;
UI — Front#71.
- `calendar_share` (`read` | `write` | `admin`): низкий приоритет; не путать с follow,
specialist_invite и платной subscription. User HTTP API и UI — до отдельной задачи
(таблица в схеме есть, см. §2.1.3).
- `calendar_share` (`read` | `write` | `admin`): соредактор personal / заместитель commercial;
invite/accept/revoke + grants; personal **не** в search/discovery. Не путать с follow,
specialist_invite и платной subscription.
- `client_type=mobile` — хвост, когда появится нативный клиент.
- **Не в фазе 2:** оплата услуги клиентом (B2C) — вне горизонта продукта.
### 2.1.3. Share / приглашения (фаза 2)
Таблица `calendar_share` (`calendar_id`, `user_id`, `rights`: `read` | `write` | `admin`)
существует в схеме. User HTTP API и клиентский UI — не стартовать, пока нет явного
сценария соредактора personal (совместная работа сейчас: specialist_invite + booking inbox).
### 2.1.3. Share / приглашения (соредактор и заместитель)
Таблица `calendar_share` (`id`, `calendar_id`, `user_id`, `rights`: `read` | `write` | `admin`,
`mirror_to_default`) и `calendar_share_invite` (зеркало specialist_invite + `rights`).
- Invite создаёт **owner** или share `admin` (personal **и** commercial).
- Accept → upsert grant; для personal `mirror_to_default` default `true` (выбор invitee);
commercial mirror игнорируется / false.
- ACL: `can_access` personal = owner | share; commercial public view + share edit;
`can_edit` = owner | share `write|admin`. DELETE calendar / смена type — только owner.
- `GET /v1/calendars` = owned shared (`role`, `share_rights`, `mirror_to_default`).
- Personal **никогда** не discoverable в `/v1/search`.
API:
- `GET/POST /v1/calendars/:id/share-invites`, `DELETE …/:invite_id`
- `GET /v1/calendars/:id/shares`, `DELETE/PUT …/shares/:user_id`
- `PUT /v1/user/shares/:calendar_id` `{mirror_to_default}`
- `GET /v1/user/share-invites`, `POST /v1/share-invites/:id/accept|decline`,
`POST /v1/share-invites/accept` `{token}`
Не путать со `specialist_invite` (booking team) и `calendar_follow` (публичный follow).
### 2.2. События (расширенная версия с повторяющимися событиями)
@@ -576,10 +594,12 @@ src/
- `GET /v1/user/reviews` — отзывы пользователя.
- `GET /v1/user/following` — календари, которые пользователь отслеживает (follow).
- `GET /v1/search` — поиск; **без токена** (гость) — только commercial по `can_access`;
пустой запрос + пагинация/`type` — discovery tops. С Bearer — как раньше (включая свои personal).
пустой запрос + пагинация/`type` — discovery tops. Personal **никогда** не в выдаче
(в т.ч. свои). С Bearer — commercial + доступные; busy для Time Arc берётся из personal отдельно.
В calendar-результатах — `image_url` (если задан на календаре). Upload/multipart
**не** входит в контракт: поле URL.
- `GET /v1/calendars` — список календарей (auth).
- `GET /v1/calendars` — список календарей (auth): owned shared (`role`, `share_rights`,
`mirror_to_default`).
- `POST /v1/calendars` — создать календарь (`commercial` → нужна уже active sub/trial;
иначе `402`; auto-start trial нет).
- `GET /v1/calendars/:id` — календарь **или unique `short_name`**. **Без токена** для active commercial (`following: false`);
@@ -598,6 +618,12 @@ src/
- `GET /v1/user/specialist-invites` — входящие приглашения.
- `POST /v1/specialist-invites/:id/accept` | `…/decline` — ответ invitee.
- `POST /v1/specialist-invites/accept` `{ token }` — accept по email deep-link.
- `GET/POST /v1/calendars/:id/share-invites` — исходящие share invite / создать (owner|admin).
- `DELETE /v1/calendars/:id/share-invites/:invite_id` — отменить pending.
- `GET /v1/calendars/:id/shares` — принятые grants; `DELETE/PUT …/shares/:user_id`.
- `PUT /v1/user/shares/:calendar_id` — свой `mirror_to_default`.
- `GET /v1/user/share-invites` — входящие share invites.
- `POST /v1/share-invites/:id/accept` | `…/decline`; `POST /v1/share-invites/accept` `{token}`.
- `GET /v1/calendars/:calendar_id/events` — события календаря (гость: commercial).
В JSON каждого события commercial-календаря — опциональное поле
`booking_occupancy`: `"free"` | `"pending"` | `"confirmed"`.