mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
feat(rooms): add webhook for transcript completion (#578)
* feat(rooms): add webhook notifications for transcript completion
- Add webhook_url and webhook_secret fields to rooms table
- Create Celery task with 24-hour retry window using exponential backoff
- Send transcript metadata, diarized text, topics, and summaries via webhook
- Add HMAC signature verification for webhook security
- Add test endpoint POST /v1/rooms/{room_id}/webhook/test
- Update frontend with webhook configuration UI and test button
- Auto-generate webhook secret if not provided
- Trigger webhook after successful file pipeline processing for room recordings
* style: linting
* fix: remove unwanted files
* fix: update openapi gen
* fix: self-review
* docs: add comprehensive webhook documentation
- Document webhook configuration, events, and payloads
- Include transcript.completed and test event examples
- Add security considerations and best practices
- Provide example webhook receiver implementation
- Document retry policy and signature verification
* fix: remove audio_mp3_url from webhook payload
- Remove audio download URL generation from webhook
- Update documentation to reflect the change
- Keep only frontend_url for accessing transcripts
* docs: remove unwanted section
* fix: correct API method name and type imports for rooms
- Fix v1RoomsRetrieve to v1RoomsGet
- Update Room type to RoomDetails throughout frontend
- Fix type imports in useRoomList, RoomList, RoomTable, and RoomCards
* feat: add show/hide toggle for webhook secret field
- Add eye icon button to reveal/hide webhook secret when editing
- Show password dots when webhook secret is hidden
- Reset visibility state when opening/closing dialog
- Only show toggle button when editing existing room with secret
* fix: resolve event loop conflict in webhook test endpoint
- Extract webhook test logic into shared async function
- Call async function directly from FastAPI endpoint
- Keep Celery task wrapper for background processing
- Fixes RuntimeError: event loop already running
* refactor: remove unnecessary Celery task for webhook testing
- Webhook testing is synchronous and provides immediate feedback
- No need for background processing via Celery
- Keep only the async function called directly from API endpoint
* feat: improve webhook test error messages and display
- Show HTTP status code in error messages
- Parse JSON error responses to extract meaningful messages
- Improved UI layout for webhook test results
- Added colored background for success/error states
- Better text wrapping for long error messages
* docs: adjust doc
* fix: review
* fix: update attempts to match close 24h
* fix: add event_id
* fix: changed to uuid, to have new event_id when reprocess.
* style: linting
* fix: alembic revision
This commit is contained in:
@@ -91,6 +91,14 @@ export const $CreateRoom = {
|
||||
type: "boolean",
|
||||
title: "Is Shared",
|
||||
},
|
||||
webhook_url: {
|
||||
type: "string",
|
||||
title: "Webhook Url",
|
||||
},
|
||||
webhook_secret: {
|
||||
type: "string",
|
||||
title: "Webhook Secret",
|
||||
},
|
||||
},
|
||||
type: "object",
|
||||
required: [
|
||||
@@ -103,6 +111,8 @@ export const $CreateRoom = {
|
||||
"recording_type",
|
||||
"recording_trigger",
|
||||
"is_shared",
|
||||
"webhook_url",
|
||||
"webhook_secret",
|
||||
],
|
||||
title: "CreateRoom",
|
||||
} as const;
|
||||
@@ -809,11 +819,11 @@ export const $Page_GetTranscriptMinimal_ = {
|
||||
title: "Page[GetTranscriptMinimal]",
|
||||
} as const;
|
||||
|
||||
export const $Page_Room_ = {
|
||||
export const $Page_RoomDetails_ = {
|
||||
properties: {
|
||||
items: {
|
||||
items: {
|
||||
$ref: "#/components/schemas/Room",
|
||||
$ref: "#/components/schemas/RoomDetails",
|
||||
},
|
||||
type: "array",
|
||||
title: "Items",
|
||||
@@ -869,7 +879,7 @@ export const $Page_Room_ = {
|
||||
},
|
||||
type: "object",
|
||||
required: ["items", "page", "size"],
|
||||
title: "Page[Room]",
|
||||
title: "Page[RoomDetails]",
|
||||
} as const;
|
||||
|
||||
export const $Participant = {
|
||||
@@ -969,6 +979,86 @@ export const $Room = {
|
||||
title: "Room",
|
||||
} as const;
|
||||
|
||||
export const $RoomDetails = {
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
title: "Id",
|
||||
},
|
||||
name: {
|
||||
type: "string",
|
||||
title: "Name",
|
||||
},
|
||||
user_id: {
|
||||
type: "string",
|
||||
title: "User Id",
|
||||
},
|
||||
created_at: {
|
||||
type: "string",
|
||||
format: "date-time",
|
||||
title: "Created At",
|
||||
},
|
||||
zulip_auto_post: {
|
||||
type: "boolean",
|
||||
title: "Zulip Auto Post",
|
||||
},
|
||||
zulip_stream: {
|
||||
type: "string",
|
||||
title: "Zulip Stream",
|
||||
},
|
||||
zulip_topic: {
|
||||
type: "string",
|
||||
title: "Zulip Topic",
|
||||
},
|
||||
is_locked: {
|
||||
type: "boolean",
|
||||
title: "Is Locked",
|
||||
},
|
||||
room_mode: {
|
||||
type: "string",
|
||||
title: "Room Mode",
|
||||
},
|
||||
recording_type: {
|
||||
type: "string",
|
||||
title: "Recording Type",
|
||||
},
|
||||
recording_trigger: {
|
||||
type: "string",
|
||||
title: "Recording Trigger",
|
||||
},
|
||||
is_shared: {
|
||||
type: "boolean",
|
||||
title: "Is Shared",
|
||||
},
|
||||
webhook_url: {
|
||||
type: "string",
|
||||
title: "Webhook Url",
|
||||
},
|
||||
webhook_secret: {
|
||||
type: "string",
|
||||
title: "Webhook Secret",
|
||||
},
|
||||
},
|
||||
type: "object",
|
||||
required: [
|
||||
"id",
|
||||
"name",
|
||||
"user_id",
|
||||
"created_at",
|
||||
"zulip_auto_post",
|
||||
"zulip_stream",
|
||||
"zulip_topic",
|
||||
"is_locked",
|
||||
"room_mode",
|
||||
"recording_type",
|
||||
"recording_trigger",
|
||||
"is_shared",
|
||||
"webhook_url",
|
||||
"webhook_secret",
|
||||
],
|
||||
title: "RoomDetails",
|
||||
} as const;
|
||||
|
||||
export const $RtcOffer = {
|
||||
properties: {
|
||||
sdp: {
|
||||
@@ -1351,6 +1441,14 @@ export const $UpdateRoom = {
|
||||
type: "boolean",
|
||||
title: "Is Shared",
|
||||
},
|
||||
webhook_url: {
|
||||
type: "string",
|
||||
title: "Webhook Url",
|
||||
},
|
||||
webhook_secret: {
|
||||
type: "string",
|
||||
title: "Webhook Secret",
|
||||
},
|
||||
},
|
||||
type: "object",
|
||||
required: [
|
||||
@@ -1363,6 +1461,8 @@ export const $UpdateRoom = {
|
||||
"recording_type",
|
||||
"recording_trigger",
|
||||
"is_shared",
|
||||
"webhook_url",
|
||||
"webhook_secret",
|
||||
],
|
||||
title: "UpdateRoom",
|
||||
} as const;
|
||||
@@ -1541,6 +1641,50 @@ export const $ValidationError = {
|
||||
title: "ValidationError",
|
||||
} as const;
|
||||
|
||||
export const $WebhookTestResult = {
|
||||
properties: {
|
||||
success: {
|
||||
type: "boolean",
|
||||
title: "Success",
|
||||
},
|
||||
message: {
|
||||
type: "string",
|
||||
title: "Message",
|
||||
default: "",
|
||||
},
|
||||
error: {
|
||||
type: "string",
|
||||
title: "Error",
|
||||
default: "",
|
||||
},
|
||||
status_code: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "integer",
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
title: "Status Code",
|
||||
},
|
||||
response_preview: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
type: "null",
|
||||
},
|
||||
],
|
||||
title: "Response Preview",
|
||||
},
|
||||
},
|
||||
type: "object",
|
||||
required: ["success"],
|
||||
title: "WebhookTestResult",
|
||||
} as const;
|
||||
|
||||
export const $WherebyWebhookEvent = {
|
||||
properties: {
|
||||
apiVersion: {
|
||||
|
||||
@@ -10,12 +10,16 @@ import type {
|
||||
V1RoomsListResponse,
|
||||
V1RoomsCreateData,
|
||||
V1RoomsCreateResponse,
|
||||
V1RoomsGetData,
|
||||
V1RoomsGetResponse,
|
||||
V1RoomsUpdateData,
|
||||
V1RoomsUpdateResponse,
|
||||
V1RoomsDeleteData,
|
||||
V1RoomsDeleteResponse,
|
||||
V1RoomsCreateMeetingData,
|
||||
V1RoomsCreateMeetingResponse,
|
||||
V1RoomsTestWebhookData,
|
||||
V1RoomsTestWebhookResponse,
|
||||
V1TranscriptsListData,
|
||||
V1TranscriptsListResponse,
|
||||
V1TranscriptsCreateData,
|
||||
@@ -118,7 +122,7 @@ export class DefaultService {
|
||||
* @param data The data for the request.
|
||||
* @param data.page Page number
|
||||
* @param data.size Page size
|
||||
* @returns Page_Room_ Successful Response
|
||||
* @returns Page_RoomDetails_ Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public v1RoomsList(
|
||||
@@ -158,12 +162,34 @@ export class DefaultService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rooms Get
|
||||
* @param data The data for the request.
|
||||
* @param data.roomId
|
||||
* @returns RoomDetails Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public v1RoomsGet(
|
||||
data: V1RoomsGetData,
|
||||
): CancelablePromise<V1RoomsGetResponse> {
|
||||
return this.httpRequest.request({
|
||||
method: "GET",
|
||||
url: "/v1/rooms/{room_id}",
|
||||
path: {
|
||||
room_id: data.roomId,
|
||||
},
|
||||
errors: {
|
||||
422: "Validation Error",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rooms Update
|
||||
* @param data The data for the request.
|
||||
* @param data.roomId
|
||||
* @param data.requestBody
|
||||
* @returns Room Successful Response
|
||||
* @returns RoomDetails Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public v1RoomsUpdate(
|
||||
@@ -227,6 +253,29 @@ export class DefaultService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rooms Test Webhook
|
||||
* Test webhook configuration by sending a sample payload.
|
||||
* @param data The data for the request.
|
||||
* @param data.roomId
|
||||
* @returns WebhookTestResult Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public v1RoomsTestWebhook(
|
||||
data: V1RoomsTestWebhookData,
|
||||
): CancelablePromise<V1RoomsTestWebhookResponse> {
|
||||
return this.httpRequest.request({
|
||||
method: "POST",
|
||||
url: "/v1/rooms/{room_id}/webhook/test",
|
||||
path: {
|
||||
room_id: data.roomId,
|
||||
},
|
||||
errors: {
|
||||
422: "Validation Error",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Transcripts List
|
||||
* @param data The data for the request.
|
||||
|
||||
@@ -24,6 +24,8 @@ export type CreateRoom = {
|
||||
recording_type: string;
|
||||
recording_trigger: string;
|
||||
is_shared: boolean;
|
||||
webhook_url: string;
|
||||
webhook_secret: string;
|
||||
};
|
||||
|
||||
export type CreateTranscript = {
|
||||
@@ -147,8 +149,8 @@ export type Page_GetTranscriptMinimal_ = {
|
||||
pages?: number | null;
|
||||
};
|
||||
|
||||
export type Page_Room_ = {
|
||||
items: Array<Room>;
|
||||
export type Page_RoomDetails_ = {
|
||||
items: Array<RoomDetails>;
|
||||
total?: number | null;
|
||||
page: number | null;
|
||||
size: number | null;
|
||||
@@ -176,6 +178,23 @@ export type Room = {
|
||||
is_shared: boolean;
|
||||
};
|
||||
|
||||
export type RoomDetails = {
|
||||
id: string;
|
||||
name: string;
|
||||
user_id: string;
|
||||
created_at: string;
|
||||
zulip_auto_post: boolean;
|
||||
zulip_stream: string;
|
||||
zulip_topic: string;
|
||||
is_locked: boolean;
|
||||
room_mode: string;
|
||||
recording_type: string;
|
||||
recording_trigger: string;
|
||||
is_shared: boolean;
|
||||
webhook_url: string;
|
||||
webhook_secret: string;
|
||||
};
|
||||
|
||||
export type RtcOffer = {
|
||||
sdp: string;
|
||||
type: string;
|
||||
@@ -281,6 +300,8 @@ export type UpdateRoom = {
|
||||
recording_type: string;
|
||||
recording_trigger: string;
|
||||
is_shared: boolean;
|
||||
webhook_url: string;
|
||||
webhook_secret: string;
|
||||
};
|
||||
|
||||
export type UpdateTranscript = {
|
||||
@@ -307,6 +328,14 @@ export type ValidationError = {
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type WebhookTestResult = {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
status_code?: number | null;
|
||||
response_preview?: string | null;
|
||||
};
|
||||
|
||||
export type WherebyWebhookEvent = {
|
||||
apiVersion: string;
|
||||
id: string;
|
||||
@@ -350,7 +379,7 @@ export type V1RoomsListData = {
|
||||
size?: number;
|
||||
};
|
||||
|
||||
export type V1RoomsListResponse = Page_Room_;
|
||||
export type V1RoomsListResponse = Page_RoomDetails_;
|
||||
|
||||
export type V1RoomsCreateData = {
|
||||
requestBody: CreateRoom;
|
||||
@@ -358,12 +387,18 @@ export type V1RoomsCreateData = {
|
||||
|
||||
export type V1RoomsCreateResponse = Room;
|
||||
|
||||
export type V1RoomsGetData = {
|
||||
roomId: string;
|
||||
};
|
||||
|
||||
export type V1RoomsGetResponse = RoomDetails;
|
||||
|
||||
export type V1RoomsUpdateData = {
|
||||
requestBody: UpdateRoom;
|
||||
roomId: string;
|
||||
};
|
||||
|
||||
export type V1RoomsUpdateResponse = Room;
|
||||
export type V1RoomsUpdateResponse = RoomDetails;
|
||||
|
||||
export type V1RoomsDeleteData = {
|
||||
roomId: string;
|
||||
@@ -377,6 +412,12 @@ export type V1RoomsCreateMeetingData = {
|
||||
|
||||
export type V1RoomsCreateMeetingResponse = Meeting;
|
||||
|
||||
export type V1RoomsTestWebhookData = {
|
||||
roomId: string;
|
||||
};
|
||||
|
||||
export type V1RoomsTestWebhookResponse = WebhookTestResult;
|
||||
|
||||
export type V1TranscriptsListData = {
|
||||
/**
|
||||
* Page number
|
||||
@@ -613,7 +654,7 @@ export type $OpenApiTs = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: Page_Room_;
|
||||
200: Page_RoomDetails_;
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
@@ -635,13 +676,26 @@ export type $OpenApiTs = {
|
||||
};
|
||||
};
|
||||
"/v1/rooms/{room_id}": {
|
||||
get: {
|
||||
req: V1RoomsGetData;
|
||||
res: {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: RoomDetails;
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HTTPValidationError;
|
||||
};
|
||||
};
|
||||
patch: {
|
||||
req: V1RoomsUpdateData;
|
||||
res: {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: Room;
|
||||
200: RoomDetails;
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
@@ -677,6 +731,21 @@ export type $OpenApiTs = {
|
||||
};
|
||||
};
|
||||
};
|
||||
"/v1/rooms/{room_id}/webhook/test": {
|
||||
post: {
|
||||
req: V1RoomsTestWebhookData;
|
||||
res: {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: WebhookTestResult;
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HTTPValidationError;
|
||||
};
|
||||
};
|
||||
};
|
||||
"/v1/transcripts": {
|
||||
get: {
|
||||
req: V1TranscriptsListData;
|
||||
|
||||
Reference in New Issue
Block a user