FastComments.com


С FastComments можно вызывать конечную точку API всякий раз, когда комментарий добавляется, обновляется или удаляется в нашей системе.

Мы реализуем это с помощью асинхронных вебхуков по протоколам HTTP/HTTPS.


Что такое вебхуки Internal Link


Вебхук — это механизм, или интеграция, между двумя системами, где "производитель" (FastComments) генерирует событие которое "потребитель" (Вы) получает посредством вызова API.


Поддерживаемые события и ресурсы Internal Link

FastComments поддерживает вебхуки только для ресурса Comment.

Мы поддерживаем вебхуки для создания, удаления и обновления комментариев.

Каждое из них в нашей системе считается отдельным событием и, соответственно, имеет различную семантику и структуру для событий вебхука.

Тестирование Internal Link

В админке Webhooks есть кнопки Send Test Payload для каждого типа события (Create, Update, Delete). События Create и Update отправляют тестовый объект WebhookComment, а при тестировании Delete отправляется тестовое тело запроса, содержащее только ID.

Проверка полезной нагрузки

При тестировании интеграции вебхуков убедитесь, что входящие запросы содержат следующие заголовки:

  1. token - Ваш секрет API
  2. X-FastComments-Timestamp - метка времени Unix (в секундах)
  3. X-FastComments-Signature - подпись HMAC-SHA256

Используйте проверку подписи HMAC, чтобы убедиться, что полезные нагрузки подлинные.

Инструменты тестирования

Вы можете использовать такие инструменты, как webhook.site или ngrok, чтобы просматривать входящие полезные нагрузки вебхуков во время разработки.

Типы событий

  • Create Event: Вызывается при создании нового комментария. Метод по умолчанию: PUT
  • Update Event: Вызывается при редактировании комментария. Метод по умолчанию: PUT
  • Delete Event: Вызывается при удалении комментария. Метод по умолчанию: DELETE

Каждое событие включает полные данные комментария в теле запроса (см. Структуры данных для формата полезной нагрузки).

Структуры данных Internal Link

Единственная структура, отправляемая через вебхуки, — это объект WebhookComment, описанный ниже на TypeScript.

Структура объекта WebhookComment

Структура события "Create"

Тело запроса для события "create" — это объект WebhookComment.

Структура события "Update"

Тело запроса для события "update" — это объект WebhookComment.

Структура события "Delete"

Тело запроса для события "delete" — это объект WebhookComment.

Change as of Nov 14th 2023
Previously the "delete" event request body only contained the comment id. It now contains the full comment at the time of deletion.
Объект WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** Идентификатор комментария. **/
4 id: string
5 /** Идентификатор или URL, идентифицирующий тред комментариев. Нормализовано. **/
6 urlId: string
7 /** URL, указывающий, где был оставлен комментарий. **/
8 url?: string
9 /** Идентификатор пользователя, оставившего комментарий. Для SSO — с префиксом tenant id. **/
10 userId?: string
11 /** Email пользователя, оставившего комментарий. **/
12 commenterEmail?: string
13 /** Имя пользователя, отображаемое в виджете комментариев. Для SSO может быть displayName. **/
14 commenterName: string
15 /** Исходный текст комментария. **/
16 comment: string
17 /** Текст комментария после парсинга. **/
18 commentHTML: string
19 /** Внешний id комментария. **/
20 externalId?: string
21 /** Идентификатор родительского комментария. **/
22 parentId?: string | null
23 /** UTC-дата, когда был оставлен комментарий. **/
24 date: UTC_ISO_DateString
25 /** Комбинированная карма (за - против) голосов. **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** true, если пользователь был залогинен при написании комментария, либо комментарий был верифицирован, либо сессия была подтверждена при оставлении комментария. **/
30 verified: boolean
31 /** Дата верификации комментария. **/
32 verifiedDate?: number
33 /** Если модератор пометил комментарий как просмотренный. **/
34 reviewed: boolean
35 /** Местоположение или base64-кодирование аватара. Будет в base64 только если такое значение было передано через SSO. **/
36 avatarSrc?: string
37 /** Был ли комментарий помечен как спам вручную или автоматически? **/
38 isSpam: boolean
39 /** Был ли комментарий автоматически помечен как спам? **/
40 aiDeterminedSpam: boolean
41 /** Содержит ли комментарий изображения? **/
42 hasImages: boolean
43 /** Номер страницы, на которой находится комментарий при сортировке "Most Relevant". **/
44 pageNumber: number
45 /** Номер страницы, на которой находится комментарий при сортировке "Oldest First". **/
46 pageNumberOF: number
47 /** Номер страницы, на которой находится комментарий при сортировке "Newest First". **/
48 pageNumberNF: number
49 /** Был ли комментарий одобрен автоматически или вручную? **/
50 approved: boolean
51 /** Код локали (формат: en_us) пользователя в момент написания комментария. **/
52 locale: string
53 /** @упоминания в комментарии, которые были успешно распознаны. **/
54 mentions?: CommentUserMention[]
55 /** Домен, откуда пришел комментарий. **/
56 domain?: string
57 /** Необязательный список id групп модерации, связанных с этим комментарием. **/
58 moderationGroupIds?: string[]|null
59}
60

Когда пользователи отмечены в комментарии, информация хранится в списке с именем mentions. Каждый объект в этом списке имеет следующую структуру.

Объект упоминаний Webhook
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** Идентификатор пользователя. Для SSO-пользователей будет с префиксом tenant id. **/
4 id: string
5 /** Итоговый текст тега @mention, включая символ @. **/
6 tag: string
7 /** Оригинальный текст тега @mention, включая символ @. **/
8 rawTag: string
9 /** Тип отмеченного пользователя. user = аккаунт FastComments.com. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** Даже если пользователь отказывается от уведомлений, это все равно будет установлено в true. **/
12 sent: boolean
13}
14

HTTP-методы

Вы можете настроить HTTP-метод для каждого типа события вебхука в панели администратора:

  • Create Event: POST или PUT (по умолчанию: PUT)
  • Update Event: POST или PUT (по умолчанию: PUT)
  • Delete Event: DELETE, POST, или PUT (по умолчанию: DELETE)

Поскольку все запросы содержат ID, операции Create и Update по умолчанию идемпотентны (PUT). Повторная отправка того же запроса Create или Update не должна создавать дубликаты объектов на вашей стороне.

Заголовки запроса

Каждый запрос вебхука содержит следующие заголовки:

Заголовок Описание
Content-Type application/json
token Ваш секрет API
X-FastComments-Timestamp UNIX-временная метка (в секундах), когда запрос был подписан
X-FastComments-Signature Подпись HMAC-SHA256 (sha256=<hex>)

См. Security & API Tokens для информации о проверке подписи HMAC.


Безопасность и API-токены Internal Link

Запросы вебхуков FastComments включают несколько механизмов аутентификации для безопасности.

Headers Sent

Header Description
token Ваш API Secret (для обратной совместимости)
X-FastComments-Timestamp Unix-временная метка (в секундах), когда запрос был подписан
X-FastComments-Signature HMAC-SHA256 подпись полезной нагрузки

Мы настоятельно рекомендуем проверять HMAC-подпись, чтобы убедиться, что полезные данные вебхука подлинны и не были изменены.

Signature Format: sha256=<hex-encoded-signature>

How the signature is computed:

  1. Concatenate: timestamp + "." + JSON_payload_body
  2. Compute HMAC-SHA256 using your API Secret as the key
  3. Hex-encode the result

Example Verification (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(req, apiSecret) {
    const timestamp = req.headers['x-fastcomments-timestamp'];
    const signature = req.headers['x-fastcomments-signature'];

    if (!timestamp || !signature) {
        return false;
    }

    // Проверяет, что метка времени свежая (в пределах 5 минут)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Защита от повторного воспроизведения (replay-атаки)
    }

    // Проверка подписи
    const payload = JSON.stringify(req.body);
    const expectedSignature = crypto
        .createHmac('sha256', apiSecret)
        .update(`${timestamp}.${payload}`)
        .digest('hex');

    return signature === `sha256=${expectedSignature}`;
}

Example Verification (Python)

import hmac
import hashlib
import time
import json

def verify_webhook_signature(headers, body, api_secret):
    timestamp = headers.get('X-FastComments-Timestamp')
    signature = headers.get('X-FastComments-Signature')

    if not timestamp or not signature:
        return False

    # Проверяет, что метка времени свежая
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Проверка подписи
    payload = json.dumps(body, separators=(',', ':'))
    message = f"{timestamp}.{payload}"
    expected = hmac.new(
        api_secret.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()

    return signature == f"sha256={expected}"

Example Verification (PHP)

function verifyWebhookSignature($headers, $body, $apiSecret) {
    $timestamp = $headers['X-FastComments-Timestamp'] ?? null;
    $signature = $headers['X-FastComments-Signature'] ?? null;

    if (!$timestamp || !$signature) {
        return false;
    }

    // Проверяет, что метка времени свежая (в пределах 5 минут)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // Проверка подписи
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

Legacy Authentication

The token header containing your API Secret is still sent for backwards compatibility. However, we recommend migrating to HMAC verification for improved security as it protects against replay attacks.



В заключение

На этом завершается наша документация по Webhooks.

Мы надеемся, что интеграция FastComments Webhook окажется понятной и быстрой в настройке.

Если вы считаете, что обнаружили какие-либо пробелы в нашей документации, сообщите нам об этом ниже.