feat: added vobiz telephony (#65)

* feat: added vobiz telephony

* chore: run formatter

* chore: add migration

* Add tsclient

---------

Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
This commit is contained in:
Piyush Sahoo 2025-11-28 09:36:04 +05:30 committed by GitHub
parent 749a0c557f
commit 09897cb5d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 994 additions and 19 deletions

View file

@ -6,7 +6,7 @@ import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { getTelephonyConfigurationApiV1OrganizationsTelephonyConfigGet, saveTelephonyConfigurationApiV1OrganizationsTelephonyConfigPost } from "@/client/sdk.gen";
import type { TwilioConfigurationRequest, VonageConfigurationRequest } from "@/client/types.gen";
import type { TwilioConfigurationRequest, VobizConfigurationRequest,VonageConfigurationRequest } from "@/client/types.gen";
import { Button } from "@/components/ui/button";
import {
Card,
@ -37,6 +37,9 @@ interface TelephonyConfigForm {
private_key?: string;
api_key?: string;
api_secret?: string;
// Vobiz fields
auth_id?: string;
vobiz_auth_token?: string;
// Common field
from_number: string;
}
@ -99,6 +102,14 @@ export default function ConfigureTelephonyPage() {
if (response.data.vonage.from_numbers?.length > 0) {
setValue("from_number", response.data.vonage.from_numbers[0]);
}
} else if (response.data?.vobiz) {
setHasExistingConfig(true);
setValue("provider", "vobiz");
setValue("auth_id", response.data.vobiz.auth_id);
setValue("vobiz_auth_token", response.data.vobiz.auth_token);
if (response.data.vobiz.from_numbers?.length > 0) {
setValue("from_number", response.data.vobiz.from_numbers[0]);
}
}
}
} catch (error) {
@ -116,7 +127,7 @@ export default function ConfigureTelephonyPage() {
const accessToken = await getAccessToken();
// Build the request body based on provider
let requestBody: TwilioConfigurationRequest | VonageConfigurationRequest;
let requestBody: TwilioConfigurationRequest | VonageConfigurationRequest | VobizConfigurationRequest;
if (data.provider === "twilio") {
requestBody = {
@ -125,7 +136,7 @@ export default function ConfigureTelephonyPage() {
account_sid: data.account_sid,
auth_token: data.auth_token,
} as TwilioConfigurationRequest;
} else {
} else if (data.provider === "vonage") {
requestBody = {
provider: data.provider,
from_numbers: [data.from_number],
@ -134,6 +145,13 @@ export default function ConfigureTelephonyPage() {
api_key: data.api_key || undefined,
api_secret: data.api_secret || undefined,
} as VonageConfigurationRequest;
} else {
requestBody = {
provider: data.provider,
from_numbers: [data.from_number],
auth_id: data.auth_id,
auth_token: data.vobiz_auth_token,
} as VobizConfigurationRequest;
}
const response = await saveTelephonyConfigurationApiV1OrganizationsTelephonyConfigPost({
@ -223,6 +241,7 @@ export default function ConfigureTelephonyPage() {
<SelectContent>
<SelectItem value="twilio">Twilio</SelectItem>
<SelectItem value="vonage">Vonage</SelectItem>
<SelectItem value="vobiz">Vobiz</SelectItem>
</SelectContent>
</Select>
{hasExistingConfig && (
@ -381,6 +400,73 @@ export default function ConfigureTelephonyPage() {
</>
)}
{/* Vobiz-specific fields */}
{selectedProvider === "vobiz" && (
<>
<div className="space-y-2">
<Label htmlFor="auth_id">Auth ID</Label>
<Input
id="auth_id"
placeholder="MA_XXXXXXXX"
{...register("auth_id", {
required: selectedProvider === "vobiz" ? "Auth ID is required" : false,
})}
/>
{errors.auth_id && (
<p className="text-sm text-red-500">
{errors.auth_id.message}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="vobiz_auth_token">Auth Token</Label>
<Input
id="vobiz_auth_token"
type="password"
autoComplete="current-password"
placeholder={
hasExistingConfig
? "Leave masked to keep existing"
: "Enter your auth token"
}
{...register("vobiz_auth_token", {
required: selectedProvider === "vobiz" && !hasExistingConfig
? "Auth token is required"
: false,
})}
/>
{errors.vobiz_auth_token && (
<p className="text-sm text-red-500">
{errors.vobiz_auth_token.message}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="from_number">From Phone Number</Label>
<Input
id="from_number"
autoComplete="tel"
placeholder="918071387428 (E.164 without + prefix)"
{...register("from_number", {
required: "Phone number is required",
pattern: {
value: /^[1-9]\d{1,14}$/,
message:
"Enter a valid phone number without + prefix (e.g., 918071387428)",
},
})}
/>
{errors.from_number && (
<p className="text-sm text-red-500">
{errors.from_number.message}
</p>
)}
</div>
</>
)}
<div className="pt-4">
<Button
type="submit"

View file

@ -117,8 +117,8 @@ const WorkflowHeader = ({ isDirty, workflowName, rfInstance, onRun, workflowId,
});
// If no configuration exists, show configure dialog
// Check if any telephony provider is configured (Twilio or Vonage)
if (configResponse.error || (!configResponse.data?.twilio && !configResponse.data?.vonage)) {
// Check if any telephony provider is configured (Twilio, Vonage, or Vobiz)
if (configResponse.error || (!configResponse.data?.twilio && !configResponse.data?.vonage && !configResponse.data?.vobiz)) {
setConfigureDialogOpen(true);
return;
}

View file

@ -1,9 +1,8 @@
// This file is auto-generated by @hey-api/openapi-ts
import { type ClientOptions as DefaultClientOptions, type Config, createClient, createConfig } from '@hey-api/client-fetch';
import { createClientConfig } from '../lib/apiClient';
import type { ClientOptions } from './types.gen';
import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from '@hey-api/client-fetch';
import { createClientConfig } from '../lib/apiClient';
/**
* The `createClientConfig()` function will be called on client initialization
@ -17,4 +16,4 @@ export type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> =
export const client = createClient(createClientConfig(createConfig<ClientOptions>({
baseUrl: 'http://127.0.0.1:8000'
})));
})));

View file

@ -1,3 +1,3 @@
// This file is auto-generated by @hey-api/openapi-ts
export * from './sdk.gen';
export * from './types.gen';
export * from './sdk.gen';

File diff suppressed because one or more lines are too long

View file

@ -246,6 +246,8 @@ export type EmbedConfigResponse = {
position: string;
button_text: string;
button_color: string;
size: string;
auto_start: boolean;
};
export type EmbedTokenRequest = {
@ -451,6 +453,7 @@ export type SuperuserWorkflowRunsListResponse = {
export type TelephonyConfigurationResponse = {
twilio?: TwilioConfigurationResponse | null;
vonage?: VonageConfigurationResponse | null;
vobiz?: VobizConfigurationResponse | null;
};
export type TestSessionResponse = {
@ -563,6 +566,35 @@ export type ValidationError = {
type: string;
};
/**
* Request schema for Vobiz configuration.
*/
export type VobizConfigurationRequest = {
provider?: string;
/**
* Vobiz Account ID (e.g., MA_SYQRLN1K)
*/
auth_id: string;
/**
* Vobiz Auth Token
*/
auth_token: string;
/**
* List of Vobiz phone numbers (E.164 without + prefix)
*/
from_numbers: Array<string>;
};
/**
* Response schema for Vobiz configuration with masked sensitive fields.
*/
export type VobizConfigurationResponse = {
provider: string;
auth_id: string;
auth_token: string;
from_numbers: Array<string>;
};
/**
* Request schema for Vonage configuration.
*/
@ -812,6 +844,64 @@ export type HandleVonageEventsApiV1TelephonyVonageEventsWorkflowRunIdPostRespons
200: unknown;
};
export type HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostData = {
body?: never;
path: {
workflow_run_id: number;
};
query?: never;
url: '/api/v1/telephony/vobiz/hangup-callback/{workflow_run_id}';
};
export type HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostErrors = {
/**
* Not found
*/
404: unknown;
/**
* Validation Error
*/
422: HttpValidationError;
};
export type HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostError = HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostErrors[keyof HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostErrors];
export type HandleVobizHangupCallbackApiV1TelephonyVobizHangupCallbackWorkflowRunIdPostResponses = {
/**
* Successful Response
*/
200: unknown;
};
export type HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostData = {
body?: never;
path: {
workflow_run_id: number;
};
query?: never;
url: '/api/v1/telephony/vobiz/ring-callback/{workflow_run_id}';
};
export type HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostErrors = {
/**
* Not found
*/
404: unknown;
/**
* Validation Error
*/
422: HttpValidationError;
};
export type HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostError = HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostErrors[keyof HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostErrors];
export type HandleVobizRingCallbackApiV1TelephonyVobizRingCallbackWorkflowRunIdPostResponses = {
/**
* Successful Response
*/
200: unknown;
};
export type OfferApiV1PipecatRtcOfferPostData = {
body: RtcOfferRequest;
headers?: {
@ -2145,7 +2235,7 @@ export type GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetRespons
export type GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetResponse = GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetResponses[keyof GetTelephonyConfigurationApiV1OrganizationsTelephonyConfigGetResponses];
export type SaveTelephonyConfigurationApiV1OrganizationsTelephonyConfigPostData = {
body: TwilioConfigurationRequest | VonageConfigurationRequest;
body: TwilioConfigurationRequest | VonageConfigurationRequest | VobizConfigurationRequest;
headers?: {
authorization?: string | null;
};
@ -3184,4 +3274,4 @@ export type HealthApiV1HealthGetResponses = {
export type ClientOptions = {
baseUrl: 'http://127.0.0.1:8000' | (string & {});
};
};