mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2026-10-16 16:34:27 +00:00
jambonz webapp refresh (#64)
initial scaffold
switch to preact/compat
add feather icons dep
jambonz-ui, index.html
stub auth and store
readme tweaks
alias preact in vite config
more readme tweaks
Update README.md
lots of things
login flow...
add notes on apis by route
lots of work...
readmes
constants
Update login.tsx
Update index.ts
Update index.ts
Update create-password.tsx
Update actions.ts
Update index.tsx
Update index.tsx
Update actions.ts
Update index.ts
react version for eslint
some refactor and cleanup
Update api.ts
Update create-password.tsx
fetch transport wrapper
api util
toast time -- oops
msg constants
img path for docs/readmes
global dispatch, generic actions etc...
unreachable and stuff
properly wrap require-auth routes
support promise chain and async/await for api fetch transport
initial responsive navi menu
Update navi-data.ts
Update navi.tsx
Update styles.scss
Rename navi-data.ts to navi-items.ts
Update navi.tsx
Update index.ts
Update layout.tsx
Update index.ts
Update layout.tsx
Update index.tsx
Update index.tsx
Update actions.ts
Update index.tsx
Update index.tsx
Update create-password.tsx
Update login.tsx
Update create-password.tsx
move things around
access control interface
Update index.tsx
acl component etc
working on settings form..
more settings, forms, HOCs
service providers workflow
button up modals and toasts
mobile navi and toast timeout
Update index.tsx
Update index.ts
Update and rename index.ts to index.tsx
Update create-password.tsx
Update create-password.tsx
Update Dockerfile
Update entrypoint.sh
Update Dockerfile
Update navi.tsx
Update auth.tsx
Update auth.tsx
Update layout.tsx
Update layout.tsx
Update login.tsx
Update login.tsx
Update settings.tsx
Update index.tsx
Update index.ts
better lint-staged
fix sp undefined
toast dispatch helpers
sass vars -- no magic numbers
Update index.ts
Update create-password.tsx
Update login.tsx
Update index.ts
Update settings.tsx
Update accounts.tsx
working on settings...
Update index.ts
Update settings.tsx
Update index.tsx
more settings view...
get rid of most any usage
Update index.tsx
better api hook
get strong with types
obscured text component
HOC for dispatch type-safety
tweak api types
github icon on login layout
responsive grid -- api keys
better fetch transport with resolve/reject
fix generic action/dispatch typings
prefer interface for GlobalDispatch
Update index.ts
Update auth.tsx
Update auth.tsx
Update create-password.tsx
checkzones
wrap up checkzones
move styles around...
alias src
stub internal views
stub not found container
contrib readme and codeowners
Update README.md
Update and rename setup.md to environment.md
Update environment.md
Update environment.md
Update contrib.md
Update contrib.md
Update contrib.md
Update and rename contrib.md to contributors.md
Update contributors.md
Update index.ts
use api data hook
accounts stub, generic apikeys container
account edit form
Update edit.tsx
Update edit.tsx
add/edit for account form
lots of good refactors
check current sp on settings
grid stuff
Update index.scss
Update styles.scss
Update contributors.md
Update constants.ts
stubbing accounts as card view
Update types.ts
Update types.ts
Update auth.tsx
Update create-password.tsx
Update index.ts
Update index.tsx
fix enum status codes
component cleanup
delete account flow
Update types.ts
Update delete.tsx
Update use-mobile-media.ts
acl hoc
Update types.ts
Update index.ts
Update types.ts
fix generic useapidata
Update types.ts
Update types.ts
Update types.ts
Create index.tsx
Create types.ts
Update types.ts
Create types.ts
Update index.tsx
button up acl, feature flags and docs
subspace initial feature stub
fix some things
wrap up subspace feature
tooltip
Update subspace.tsx
Delete styles.scss
Update types.ts
Update auth.tsx
Update index.ts
some more type stuff
add react/jsx-key error for missing shorthand frag keys
basic spinner...
no accounts
data files for regions and speech
vendor selector logic
tighten up vendor stuff
bit more cleanup
Update types.ts
Update index.tsx
Update index.tsx
Update subspace.tsx
fix some type things
stub mock dev server implementation
add parity for account siprec_hook_sid
latest jambonz-ui update
cleanup package.json
fix docker stuff
docker notes in readme
adding github actions
package lock version
remove unused jest deps
update jambonz-ui
new new jambonz-ui
list view vs cards view
fix no accounts list view
fix prettier config
some house cleaning
file upload component
update pr-checks wildcard
wrappers for fetch transport -- any method (#78)
Refresh tweaks (#80)
* add alerts to mock api dev server
add webhook methods types
fix focus for file upload
update contrib readme
blob fetching
rest props spread for file-upload
* multi element fieldset structure, unique basic auth field names for accounts form
* Fix and simplify webhook state setting
* some ad-hoc cleanup for temp work
Adding generic account filter component (#82)
adding focused styles for account-filter
more robust account-filter props
updates to contrib readme
fix add service provider form a la new styles
required form field UI and labeling
Application page for refresh (#79)
* Adding barely working Application page (#70)
* resolve conflict and update, still barely working
* perfectly working application page
* Fix the duplicated name logic
* strip some comments
* changes to sync
* delete more condition
* some more changes for parity
* revert changes
* applying b1a9a77
* changes requested
* changes suggested
* changes suggested
* sync changes
organize some styles a bit more
refactor generic small selector styles
use portals for modals and toasts
add new classNames to applications form
handle applications view without accounts condition
sweep through with some cleanup
type-safety for :POST and :PUT api methods
Speech service page for refresh (#84)
* initial commit
* more update, probably one more
* properly rebase
* check box works okay
* properly rebase**2
* initial cleanup and ux-flow evaluation
* obscure secrets on frontend for local state
* refine ui for credential status checks
* ignore error set on unmount for CredentialStatus
* fix obscure field type crash bug
* Update utils.ts
* Update utils.ts
* Update constants.ts
* wrap up the speech credentials flow
Co-authored-by: kitajchuk <bk@kitajchuk.com>
tweaks to ui elements etc
tweak some typings and minor ui styles
better not tested messaging for TTS/STT
better placeholder feature flag for dev
Adding some conditional utilities (#87)
Microsoft Tenant page for refresh (#86)
* initial commit
* fix backend error with adding
* changes suggested
* changes requested
* use all accounts for ms teams tenants
* ui tweaks, add last ditch redirect back to form
Co-authored-by: kitajchuk <bk@kitajchuk.com>
Phone number page for refresh (#85)
* initial commit so I can hop back
* working properly
* carrier related change and mass edit
* mvoing around
* UI for mass edit
* unset selected for mass edit
* some minor ui cleanup
* fix empty/bad classNames on edit action icon
Co-authored-by: kitajchuk <bk@kitajchuk.com>
Update .dockerignore
cleanup and port some helpers
fix applications index useEffect
group synthesis fields and recognizer fields for application form
tweak contrib readme
just run tsc in pre-commit
update contrib readme
Carrier page for refresh (#89)
* initial commit
* working form but no put/post for gateways yet
* put/post for sip/smpp
* crud app done
* all the functionalities are here, unless it isnt
* changes suggested and delete sip/smpp when delete carrier
* Some initial UI cleanup etc...
* More UI cleanup and what not...
* No need for the 'Status' text here
* Remove the Grid component -- not reused
* Remove as much explicit null type as possible
* Use webhook methods constant in account form
* Some API constants and fix inbound/outbound smpp gateways delete with filter logic
* Tab handling logic for carrier form (#91)
* similar validation logic of sip for smpp
* Tech prefix tab validation
* revert to working sip gateway validation
* More validation cleanup
* Update index.ts
* Update index.ts
* More cleanup and form clarity for if/when required fields
* Fix some logic and reset gateways to delete when deleted
* use api data hooks for index partials
* smpp gateway validation and fqdn validations
* default application selector
* Fix up the SMPP dilemma...
* Typo and remove console log
* Tab switch for all validations
* Move empty SIP check to validation getter
* Render gateway validation messages near the invalid fields
* Explicit return on first active tab condition for browser constraints
* Use IP pattern for outbound smpp gateway since fqdn is disallowed here
* Add fqdn example to ip placeholders
* sticky tabs
* Tweak info text
* Gateway refetch code change
* delete gateways on demand
* move shared api fetching down into forms -- seems better actually
* Fix re-render glitch for gateways UI
Co-authored-by: kitajchuk <bk@kitajchuk.com>
Tweaks and minor cleanups
Few more minor tweaks
prettier package.json -- duh
Functional recent-calls dev server api
switch from moment to dayjs
Functional alerts dev server api
Dev server notes in readme
Fix applications bug which fixes current SP switch
Create hooks for vendor async data
No lazy load for routes (#97)
better speech hook
match dev mock paged response to api server paged response
Generic AccountSelect component
Generic useRedirect hook
Recent call page for refresh (#93)
* initial commit
* changes requested but yet to pcap
* pcap?
* Initial cleanup on RecentCalls
* Normalize set page number and fix status for mock dev server
* Listt item styles and details/pcap fix
* Refactor recent calls subcomponents
* Tighter section padding and smaller page titles
* Update _lists.scss
* Recent calls cleanup and some other tweaks
Co-authored-by: kitajchuk <bk@kitajchuk.com>
Implement proper Checkzone initial checked for carrier form
Add handleSelect prop to SelectFilter component and fix perPageFilter changes for recent calls
Alert view page for refresh (#99)
* initial commit
* changes suggested
* changes requested
* Style alerts UI
Co-authored-by: kitajchuk <bk@kitajchuk.com>
Refresh enhancement pagination logic (#101)
* initial commit
* Sort of secret props...
Co-authored-by: kitajchuk <bk@kitajchuk.com>
Quick small screen mobile sweep -- add logout button to mobile navi
Update index.tsx
Tweak some styles and restyle navi SP selector
Add key prop to ApiKeys on Settings so SP switch refetches data
fix checkbox margin now that grid-gap is used
Style tweaks
Update types.ts
Update delete.tsx
Update delete.tsx
Move some variable declarations around
Generic application select for forms
Fix issue #105 for carrier form applications
Use memo for filtered carriers on list view
Refactor generic application filter component
Update application-filter.tsx
Update types.ts
Update index.tsx
Update index.tsx
Update index.tsx
Update index.tsx
Cleanup some stuff -- add locked prop for Passwd
Fix unauthorized logout scenarios -- no react state errors :)
Normalize React types usage
Fuzzy search filter for collection lists (#106)
* Fuzzy search filter for collection lists
* Tweak some things for responsiveness
* Carrier preset label and fix All accounts filter for carriers list
Set text overflow on search filter
PR checklist items
Match 'No ...' text for speech services
Return rawCollection if hasLength check is false
Responsive styling for list item--action rows
Fix defaultOption for AccountSelector
Refresh: Add API limits for issue #109 (#111)
* Add API limits for issue #109
* Tighten up initial field renders
Cleanup for issue #104 (#108)
Co-authored-by: kitajchuk <bk@kitajchuk.com>
Tweak local limits
Generic local limits component
No default local limits -- move to forms components
Safe set values for limits -- maintain controlled inputs
Ref support for local limits form component
Handle empty data for local limits effect
Implement DELETE for limits
Singular nomenclature for post limit(s)
This commit is contained in:
1 parent
1276687cc0
commit
f381eba694
273 files changed
+23033
-42576
No files matched your search
@@ -0,0 +1,141 @@
|
||||
import type {
|
||||
LimitField,
|
||||
SipGateway,
|
||||
SmppGateway,
|
||||
WebHook,
|
||||
WebhookOption,
|
||||
} from "./types";
|
||||
|
||||
/** This window object is serialized and injected at docker runtime */
|
||||
/** The API url is constructed with the docker containers `ip:port` */
|
||||
interface JambonzWindowObject {
|
||||
API_BASE_URL: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
JAMBONZ: JambonzWindowObject;
|
||||
}
|
||||
}
|
||||
|
||||
/** https://vitejs.dev/guide/env-and-mode.html#env-files */
|
||||
export const API_BASE_URL =
|
||||
window.JAMBONZ?.API_BASE_URL || import.meta.env.VITE_API_BASE_URL;
|
||||
|
||||
/** Serves mock API responses from a local dev API server */
|
||||
export const DEV_BASE_URL = import.meta.env.VITE_DEV_BASE_URL;
|
||||
|
||||
/** TCP Max Port */
|
||||
export const TCP_MAX_PORT = 65535;
|
||||
|
||||
/** Tech Prefix minlength */
|
||||
export const TECH_PREFIX_MINLENGTH = 3;
|
||||
|
||||
/** IP Types for validations */
|
||||
export const IP = "ip";
|
||||
export const FQDN = "fqdn";
|
||||
export const FQDN_TOP_LEVEL = "fqdn-top-level";
|
||||
export const INVALID = "invalid";
|
||||
|
||||
/** Default API object models */
|
||||
export const DEFAULT_WEBHOOK: WebHook = {
|
||||
url: "",
|
||||
method: "POST",
|
||||
username: "",
|
||||
password: "",
|
||||
};
|
||||
|
||||
/** Default SIP/SMPP Gateways */
|
||||
export const DEFAULT_SIP_GATEWAY: SipGateway = {
|
||||
voip_carrier_sid: "",
|
||||
ipv4: "",
|
||||
port: 5060,
|
||||
netmask: 32,
|
||||
is_active: false,
|
||||
inbound: 1,
|
||||
outbound: 0,
|
||||
};
|
||||
|
||||
export const DEFAULT_SMPP_GATEWAY: SmppGateway = {
|
||||
voip_carrier_sid: "",
|
||||
ipv4: "",
|
||||
port: 2775,
|
||||
is_primary: false,
|
||||
use_tls: false,
|
||||
netmask: 32,
|
||||
inbound: 1,
|
||||
outbound: 1,
|
||||
};
|
||||
|
||||
/** Netmask Bits */
|
||||
export const NETMASK_BITS = Array(32)
|
||||
.fill(0)
|
||||
.map((_, index) => index + 1)
|
||||
.reverse();
|
||||
|
||||
export const NETMASK_OPTIONS = NETMASK_BITS.map((bit) => ({
|
||||
name: bit.toString(),
|
||||
value: bit.toString(),
|
||||
}));
|
||||
|
||||
/** List view filters */
|
||||
export const DATE_SELECTION = [
|
||||
{ name: "today", value: "today" },
|
||||
{ name: "last 7d", value: "7" },
|
||||
{ name: "last 14d", value: "14" },
|
||||
{ name: "last 30d", value: "30" },
|
||||
];
|
||||
|
||||
export const PER_PAGE_SELECTION = [
|
||||
{ name: "25 / page", value: "25" },
|
||||
{ name: "50 / page", value: "50" },
|
||||
{ name: "100 / page", value: "100" },
|
||||
];
|
||||
|
||||
/** Available webhook methods */
|
||||
export const WEBHOOK_METHODS: WebhookOption[] = [
|
||||
{
|
||||
name: "POST",
|
||||
value: "POST",
|
||||
},
|
||||
{
|
||||
name: "GET",
|
||||
value: "GET",
|
||||
},
|
||||
];
|
||||
|
||||
/** Various system limits */
|
||||
/** For now we are only supporting `voice_call_session` */
|
||||
export const LIMITS: LimitField[] = [
|
||||
{
|
||||
label: "Max concurrent calls (0=unlimited)",
|
||||
category: "voice_call_session",
|
||||
},
|
||||
// {
|
||||
// label: "Max registered devices (0=unlimited)",
|
||||
// category: "device",
|
||||
// },
|
||||
// {
|
||||
// label: "Max api calls per minute (0=unlimited)",
|
||||
// category: "api_rate",
|
||||
// },
|
||||
];
|
||||
|
||||
/** Speech credential test result status values */
|
||||
export const CRED_OK = "ok";
|
||||
export const CRED_FAIL = "fail";
|
||||
export const CRED_NOT_TESTED = "not tested";
|
||||
|
||||
/** API base paths */
|
||||
export const API_LOGIN = `${API_BASE_URL}/login`;
|
||||
export const API_SBCS = `${API_BASE_URL}/Sbcs`;
|
||||
export const API_USERS = `${API_BASE_URL}/Users`;
|
||||
export const API_API_KEYS = `${API_BASE_URL}/ApiKeys`;
|
||||
export const API_ACCOUNTS = `${API_BASE_URL}/Accounts`;
|
||||
export const API_APPLICATIONS = `${API_BASE_URL}/Applications`;
|
||||
export const API_PHONE_NUMBERS = `${API_BASE_URL}/PhoneNumbers`;
|
||||
export const API_MS_TEAMS_TENANTS = `${API_BASE_URL}/MicrosoftTeamsTenants`;
|
||||
export const API_SERVICE_PROVIDERS = `${API_BASE_URL}/ServiceProviders`;
|
||||
export const API_CARRIERS = `${API_BASE_URL}/VoipCarriers`;
|
||||
export const API_SMPP_GATEWAY = `${API_BASE_URL}/SmppGateways`;
|
||||
export const API_SIP_GATEWAY = `${API_BASE_URL}/SipGateways`;
|
||||
@@ -0,0 +1,587 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { useSelectState } from "src/store";
|
||||
import { getToken } from "src/router/auth";
|
||||
import {
|
||||
DEV_BASE_URL,
|
||||
API_BASE_URL,
|
||||
API_LOGIN,
|
||||
API_USERS,
|
||||
API_SERVICE_PROVIDERS,
|
||||
API_API_KEYS,
|
||||
API_ACCOUNTS,
|
||||
API_APPLICATIONS,
|
||||
API_MS_TEAMS_TENANTS,
|
||||
API_PHONE_NUMBERS,
|
||||
API_CARRIERS,
|
||||
API_SMPP_GATEWAY,
|
||||
API_SIP_GATEWAY,
|
||||
} from "./constants";
|
||||
import { ROUTE_LOGIN } from "src/router/routes";
|
||||
import {
|
||||
SESS_UNAUTHORIZED,
|
||||
MSG_SESS_EXPIRED,
|
||||
MSG_SERVER_DOWN,
|
||||
MSG_SOMETHING_WRONG,
|
||||
} from "src/constants";
|
||||
|
||||
import type {
|
||||
FetchError,
|
||||
FetchTransport,
|
||||
User,
|
||||
UserLogin,
|
||||
ServiceProvider,
|
||||
SidResponse,
|
||||
TokenResponse,
|
||||
EmptyResponse,
|
||||
SecretResponse,
|
||||
UseApiData,
|
||||
Alert,
|
||||
PagedResponse,
|
||||
RecentCall,
|
||||
UserLoginPayload,
|
||||
UserUpdatePayload,
|
||||
ApiKey,
|
||||
Account,
|
||||
Application,
|
||||
SpeechCredential,
|
||||
MSTeamsTenant,
|
||||
PhoneNumber,
|
||||
Carrier,
|
||||
SmppGateway,
|
||||
SipGateway,
|
||||
TotalResponse,
|
||||
CallQuery,
|
||||
PageQuery,
|
||||
Limit,
|
||||
LimitCategories,
|
||||
} from "./types";
|
||||
import { StatusCodes } from "./types";
|
||||
|
||||
/** Wrap all requests to normalize response handling */
|
||||
const fetchTransport = <Type>(
|
||||
url: string,
|
||||
options: RequestInit
|
||||
): Promise<FetchTransport<Type>> => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
const transport: FetchTransport<Type> = {
|
||||
status: response.status,
|
||||
json: <Type>{},
|
||||
};
|
||||
|
||||
// Redirect unauthorized
|
||||
if (response.status === StatusCodes.UNAUTHORIZED) {
|
||||
handleUnauthorized();
|
||||
reject();
|
||||
}
|
||||
|
||||
// API error handling returns { msg: string; }
|
||||
// See @type StatusJSON and StatusEmpty in ./types
|
||||
if (
|
||||
response.status >= StatusCodes.BAD_REQUEST &&
|
||||
response.status <= StatusCodes.INTERNAL_SERVER_ERROR
|
||||
) {
|
||||
try {
|
||||
const errJson = await response.json();
|
||||
reject(<FetchError>{
|
||||
status: response.status,
|
||||
...errJson,
|
||||
});
|
||||
} catch (error) {
|
||||
reject(<FetchError>{
|
||||
status: response.status,
|
||||
msg: MSG_SOMETHING_WRONG,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// API success handling returns a valid JSON response
|
||||
// This could either be a DTO object or a generic response
|
||||
// See types for various responses in ./types
|
||||
if (
|
||||
response.status === StatusCodes.OK ||
|
||||
response.status === StatusCodes.CREATED
|
||||
) {
|
||||
// Handle blobs -- e.g. pcap file API for RecentCalls
|
||||
if (
|
||||
options.headers!["Content-Type" as keyof HeadersInit] ===
|
||||
"application/octet-stream"
|
||||
) {
|
||||
const blob: Blob = await response.blob();
|
||||
|
||||
transport.blob = blob;
|
||||
} else {
|
||||
const json: Type = await response.json();
|
||||
|
||||
transport.json = json;
|
||||
}
|
||||
}
|
||||
|
||||
resolve(transport);
|
||||
// TypeError "Failed to fetch"
|
||||
// net::ERR_CONNECTION_REFUSED
|
||||
// This is the case if the server is unreachable...
|
||||
} catch (error: unknown) {
|
||||
// Caveat -- we don't kill the app if this is a bad request on local dev server
|
||||
if (!url.includes(DEV_BASE_URL)) {
|
||||
handleUnreachable();
|
||||
}
|
||||
|
||||
reject(<FetchError>{
|
||||
status: StatusCodes.INTERNAL_SERVER_ERROR,
|
||||
msg: (error as TypeError).message,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getAuthHeaders = () => {
|
||||
const token = getToken();
|
||||
|
||||
return {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
};
|
||||
|
||||
const getQuery = <Type>(query: Type) => {
|
||||
return decodeURIComponent(
|
||||
new URLSearchParams(query as unknown as Record<string, string>).toString()
|
||||
);
|
||||
};
|
||||
|
||||
/** Hard boot on 401 status code for unauthorized users */
|
||||
/** Since you're unauthorized there's no harm just reloading the app from "/" */
|
||||
/** We set a storage item for the dispatch message that is captured in the Login container */
|
||||
const handleBadRequest = (msg: string) => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
|
||||
if (window.location.pathname !== ROUTE_LOGIN) {
|
||||
sessionStorage.setItem(SESS_UNAUTHORIZED, msg);
|
||||
window.location.href = ROUTE_LOGIN;
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnauthorized = () => {
|
||||
handleBadRequest(MSG_SESS_EXPIRED);
|
||||
};
|
||||
|
||||
const handleUnreachable = () => {
|
||||
handleBadRequest(MSG_SERVER_DOWN);
|
||||
};
|
||||
|
||||
/** Wrapper for fetching Blobs -- API use case is RecentCalls pcap files */
|
||||
|
||||
export const getBlob = (url: string) => {
|
||||
return fetchTransport(url, {
|
||||
headers: {
|
||||
...getAuthHeaders(),
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/** Simple wrappers for fetchTransport calls to any API, :GET, :POST, :PUT, :DELETE */
|
||||
|
||||
export const getFetch = <Type>(url: string) => {
|
||||
return fetchTransport<Type>(url, {
|
||||
headers: getAuthHeaders(),
|
||||
});
|
||||
};
|
||||
|
||||
export const postFetch = <Type, Payload>(url: string, payload: Payload) => {
|
||||
return fetchTransport<Type>(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
headers: getAuthHeaders(),
|
||||
});
|
||||
};
|
||||
|
||||
export const putFetch = <Type, Payload>(url: string, payload: Payload) => {
|
||||
return fetchTransport<Type>(url, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(payload),
|
||||
headers: getAuthHeaders(),
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteFetch = <Type>(url: string) => {
|
||||
return fetchTransport<Type>(url, {
|
||||
method: "DELETE",
|
||||
headers: getAuthHeaders(),
|
||||
});
|
||||
};
|
||||
|
||||
/** All APIs need a wrapper utility that uses the FetchTransport */
|
||||
|
||||
export const postLogin = (payload: UserLoginPayload) => {
|
||||
return fetchTransport<UserLogin>(API_LOGIN, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/** Named wrappers for `postFetch` */
|
||||
|
||||
export const postServiceProviders = (payload: Partial<ServiceProvider>) => {
|
||||
return postFetch<SidResponse, Partial<ServiceProvider>>(
|
||||
API_SERVICE_PROVIDERS,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const postApiKey = (payload: Partial<ApiKey>) => {
|
||||
return postFetch<TokenResponse, Partial<ApiKey>>(API_API_KEYS, payload);
|
||||
};
|
||||
|
||||
export const postAccount = (payload: Partial<Account>) => {
|
||||
return postFetch<SidResponse, Partial<Account>>(API_ACCOUNTS, payload);
|
||||
};
|
||||
|
||||
export const postApplication = (payload: Partial<Application>) => {
|
||||
return postFetch<SidResponse, Partial<Application>>(
|
||||
API_APPLICATIONS,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const postSpeechService = (
|
||||
sid: string,
|
||||
payload: Partial<SpeechCredential>
|
||||
) => {
|
||||
return postFetch<SidResponse, Partial<SpeechCredential>>(
|
||||
`${API_SERVICE_PROVIDERS}/${sid}/SpeechCredentials`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const postMsTeamsTentant = (payload: Partial<MSTeamsTenant>) => {
|
||||
return postFetch<SidResponse, Partial<MSTeamsTenant>>(
|
||||
API_MS_TEAMS_TENANTS,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const postPhoneNumber = (payload: Partial<PhoneNumber>) => {
|
||||
return postFetch<SidResponse, Partial<PhoneNumber>>(
|
||||
API_PHONE_NUMBERS,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const postCarrier = (sid: string, payload: Partial<Carrier>) => {
|
||||
return postFetch<SidResponse, Partial<Carrier>>(
|
||||
`${API_SERVICE_PROVIDERS}/${sid}/VoipCarriers/`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const postSipGateway = (payload: Partial<SipGateway>) => {
|
||||
return postFetch<SidResponse, Partial<SipGateway>>(API_SIP_GATEWAY, payload);
|
||||
};
|
||||
|
||||
export const postSmppGateway = (payload: Partial<SmppGateway>) => {
|
||||
return postFetch<SidResponse, Partial<SmppGateway>>(
|
||||
API_SMPP_GATEWAY,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const postServiceProviderLimit = (
|
||||
sid: string,
|
||||
payload: Partial<Limit>
|
||||
) => {
|
||||
return postFetch<SidResponse, Partial<Limit>>(
|
||||
`${API_SERVICE_PROVIDERS}/${sid}/Limits`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const postAccountLimit = (sid: string, payload: Partial<Limit>) => {
|
||||
return postFetch<SidResponse, Partial<Limit>>(
|
||||
`${API_ACCOUNTS}/${sid}/Limits`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
/** Named wrappers for `putFetch` */
|
||||
|
||||
export const putUser = (sid: string, payload: UserUpdatePayload) => {
|
||||
return putFetch<EmptyResponse, UserUpdatePayload>(
|
||||
`${API_USERS}/${sid}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putServiceProvider = (
|
||||
sid: string,
|
||||
payload: Partial<ServiceProvider>
|
||||
) => {
|
||||
return putFetch<EmptyResponse, Partial<ServiceProvider>>(
|
||||
`${API_SERVICE_PROVIDERS}/${sid}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putAccount = (sid: string, payload: Partial<Account>) => {
|
||||
return putFetch<EmptyResponse, Partial<Account>>(
|
||||
`${API_ACCOUNTS}/${sid}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putApplication = (sid: string, payload: Partial<Application>) => {
|
||||
return putFetch<EmptyResponse, Partial<Application>>(
|
||||
`${API_APPLICATIONS}/${sid}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putSpeechService = (
|
||||
sid1: string,
|
||||
sid2: string,
|
||||
payload: Partial<SpeechCredential>
|
||||
) => {
|
||||
return putFetch<EmptyResponse, Partial<SpeechCredential>>(
|
||||
`${API_SERVICE_PROVIDERS}/${sid1}/SpeechCredentials/${sid2}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putMsTeamsTenant = (
|
||||
sid: string,
|
||||
payload: Partial<MSTeamsTenant>
|
||||
) => {
|
||||
return putFetch<EmptyResponse, Partial<MSTeamsTenant>>(
|
||||
`${API_MS_TEAMS_TENANTS}/${sid}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putPhoneNumber = (sid: string, payload: Partial<PhoneNumber>) => {
|
||||
return putFetch<EmptyResponse, Partial<PhoneNumber>>(
|
||||
`${API_PHONE_NUMBERS}/${sid}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putCarrier = (
|
||||
sid1: string,
|
||||
sid2: string,
|
||||
payload: Partial<Carrier>
|
||||
) => {
|
||||
return putFetch<EmptyResponse, Partial<Carrier>>(
|
||||
`${API_SERVICE_PROVIDERS}/${sid1}/VoipCarriers/${sid2}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putSipGateway = (sid: string, payload: Partial<SipGateway>) => {
|
||||
return putFetch<EmptyResponse, Partial<SipGateway>>(
|
||||
`${API_SIP_GATEWAY}/${sid}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
export const putSmppGateway = (sid: string, payload: Partial<SmppGateway>) => {
|
||||
return putFetch<EmptyResponse, Partial<SmppGateway>>(
|
||||
`${API_SMPP_GATEWAY}/${sid}`,
|
||||
payload
|
||||
);
|
||||
};
|
||||
|
||||
/** Named wrappers for `deleteFetch` */
|
||||
|
||||
export const deleteServiceProvider = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_SERVICE_PROVIDERS}/${sid}`);
|
||||
};
|
||||
|
||||
export const deleteApiKey = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_API_KEYS}/${sid}`);
|
||||
};
|
||||
|
||||
export const deleteAccount = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_ACCOUNTS}/${sid}`);
|
||||
};
|
||||
|
||||
export const deleteApplication = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_APPLICATIONS}/${sid}`);
|
||||
};
|
||||
|
||||
export const deleteSpeechService = (sid1: string, sid2: string) => {
|
||||
return deleteFetch<EmptyResponse>(
|
||||
`${API_SERVICE_PROVIDERS}/${sid1}/SpeechCredentials/${sid2}`
|
||||
);
|
||||
};
|
||||
|
||||
export const deleteMsTeamsTenant = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_MS_TEAMS_TENANTS}/${sid}`);
|
||||
};
|
||||
|
||||
export const deletePhoneNumber = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_PHONE_NUMBERS}/${sid}`);
|
||||
};
|
||||
|
||||
export const deleteCarrier = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_CARRIERS}/${sid}`);
|
||||
};
|
||||
|
||||
export const deleteSipGateway = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_SIP_GATEWAY}/${sid}`);
|
||||
};
|
||||
|
||||
export const deleteSmppGateway = (sid: string) => {
|
||||
return deleteFetch<EmptyResponse>(`${API_SMPP_GATEWAY}/${sid}`);
|
||||
};
|
||||
|
||||
export const deleteServiceProviderLimit = (
|
||||
sid: string,
|
||||
cat: LimitCategories
|
||||
) => {
|
||||
return deleteFetch<EmptyResponse>(
|
||||
`${API_SERVICE_PROVIDERS}/${sid}/Limits?category=${cat}`
|
||||
);
|
||||
};
|
||||
|
||||
export const deleteAccountLimit = (sid: string, cat: LimitCategories) => {
|
||||
return deleteFetch<EmptyResponse>(
|
||||
`${API_ACCOUNTS}/${sid}/Limits?category=${cat}`
|
||||
);
|
||||
};
|
||||
|
||||
/** Named wrappers for `getFetch` */
|
||||
|
||||
/** This is not a functioning API at the moment... */
|
||||
export const getUser = (sid: string) => {
|
||||
return getFetch<User>(`${API_USERS}/${sid}`);
|
||||
};
|
||||
|
||||
export const getServiceProviders = () => {
|
||||
return getFetch<ServiceProvider[]>(API_SERVICE_PROVIDERS);
|
||||
};
|
||||
|
||||
export const getAccountWebhook = (sid: string) => {
|
||||
return getFetch<SecretResponse>(
|
||||
`${API_ACCOUNTS}/${sid}/WebhookSecret?regenerate=true`
|
||||
);
|
||||
};
|
||||
|
||||
/** Wrappers for APIs that can have a mock dev server response */
|
||||
|
||||
export const getRecentCalls = (sid: string, query: Partial<CallQuery>) => {
|
||||
const qryStr = getQuery<Partial<CallQuery>>(query);
|
||||
|
||||
return getFetch<PagedResponse<RecentCall>>(
|
||||
import.meta.env.DEV
|
||||
? `${DEV_BASE_URL}/Accounts/${sid}/RecentCalls?${qryStr}`
|
||||
: `${API_ACCOUNTS}/${sid}/RecentCalls?${qryStr}`
|
||||
);
|
||||
};
|
||||
|
||||
export const getRecentCall = (sid: string, callSid: string) => {
|
||||
return getFetch<TotalResponse>(
|
||||
import.meta.env.DEV
|
||||
? `${DEV_BASE_URL}/Accounts/${sid}/RecentCalls/${callSid}`
|
||||
: `${API_ACCOUNTS}/${sid}/RecentCalls/${callSid}`
|
||||
);
|
||||
};
|
||||
|
||||
export const getPcap = (sid: string, callSid: string) => {
|
||||
return getBlob(
|
||||
import.meta.env.DEV
|
||||
? `${DEV_BASE_URL}/Accounts/${sid}/RecentCalls/${callSid}/pcap`
|
||||
: `${API_ACCOUNTS}/${sid}/RecentCalls/${callSid}/pcap`
|
||||
);
|
||||
};
|
||||
|
||||
export const getAlerts = (sid: string, query: Partial<PageQuery>) => {
|
||||
const qryStr = getQuery<Partial<PageQuery>>(query);
|
||||
|
||||
return getFetch<PagedResponse<Alert>>(
|
||||
import.meta.env.DEV
|
||||
? `${DEV_BASE_URL}/Accounts/${sid}/Alerts?${qryStr}`
|
||||
: `${API_ACCOUNTS}/${sid}/Alerts?${qryStr}`
|
||||
);
|
||||
};
|
||||
|
||||
/** Hooks for components to fetch data with refetch method */
|
||||
|
||||
/** :GET /{apiPath} -- this is generic for any fetch of data collections */
|
||||
export const useApiData: UseApiData = <Type>(apiPath: string) => {
|
||||
const [result, setResult] = useState<Type>();
|
||||
const [error, setError] = useState<FetchError>();
|
||||
const [refetch, setRefetch] = useState(0);
|
||||
|
||||
const refetcher = () => {
|
||||
setRefetch(refetch + 1);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
|
||||
getFetch<Type>(`${API_BASE_URL}/${apiPath}`)
|
||||
.then(({ json }) => {
|
||||
if (!ignore) {
|
||||
setResult(json!);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!ignore) {
|
||||
setError(error);
|
||||
}
|
||||
});
|
||||
|
||||
return function cleanup() {
|
||||
ignore = true;
|
||||
};
|
||||
}, [refetch]);
|
||||
|
||||
return [result, refetcher, error];
|
||||
};
|
||||
|
||||
/** Only for a couple routes but makes these fetches nice at the component level */
|
||||
/** Wrapping up the currentServiceProvider logic here also streamlines component use */
|
||||
/** :GET /ServiceProviders/:service_provider_sid/ApiKeys */
|
||||
/** :GET /ServiceProviders/:service_provider_sid/Accounts */
|
||||
export const useServiceProviderData: UseApiData = <Type>(apiPath: string) => {
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const [result, setResult] = useState<Type>();
|
||||
const [error, setError] = useState<FetchError>();
|
||||
const [refetch, setRefetch] = useState(0);
|
||||
|
||||
const refetcher = () => {
|
||||
setRefetch(refetch + 1);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
|
||||
if (currentServiceProvider) {
|
||||
getFetch<Type>(
|
||||
`${API_SERVICE_PROVIDERS}/${currentServiceProvider.service_provider_sid}/${apiPath}`
|
||||
)
|
||||
.then(({ json }) => {
|
||||
if (!ignore) {
|
||||
setResult(json!);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!ignore) {
|
||||
setError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return function cleanup() {
|
||||
ignore = true;
|
||||
};
|
||||
}, [currentServiceProvider, refetch]);
|
||||
|
||||
return [result, refetcher, error];
|
||||
};
|
||||
@@ -0,0 +1,329 @@
|
||||
import type { Vendor } from "src/vendor/types";
|
||||
|
||||
/** Simple types */
|
||||
|
||||
export type WebhookMethod = "POST" | "GET";
|
||||
|
||||
export type CredentialStatus = "ok" | "fail" | "not tested";
|
||||
|
||||
export type IpType = "ip" | "fqdn" | "fqdn-top-level" | "invalid";
|
||||
|
||||
export type LimitCategories = "api_rate" | "voice_call_session" | "device";
|
||||
|
||||
/** Status codes */
|
||||
|
||||
export enum StatusCodes {
|
||||
OK = 200,
|
||||
CREATED = 201,
|
||||
ACCEPTED = 202,
|
||||
NO_CONTENT = 204,
|
||||
BAD_REQUEST = 400,
|
||||
UNAUTHORIZED = 401,
|
||||
FORBIDDEN = 403,
|
||||
NOT_FOUND = 404,
|
||||
UNPROCESSABLE_ENTITY = 422,
|
||||
INTERNAL_SERVER_ERROR = 500,
|
||||
/** SMPP temporarily unavailable */
|
||||
TEMPORARILY_UNAVAILABLE = 480,
|
||||
}
|
||||
|
||||
/** Fetch transport interfaces */
|
||||
|
||||
export interface FetchTransport<Type> {
|
||||
status: StatusCodes;
|
||||
json: Type;
|
||||
blob?: Blob;
|
||||
}
|
||||
|
||||
export interface FetchError {
|
||||
status: StatusCodes;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
export interface UseApiData {
|
||||
<Type>(apiPath: string): [
|
||||
Type | undefined,
|
||||
() => void,
|
||||
FetchError | undefined
|
||||
];
|
||||
}
|
||||
|
||||
/** API related interfaces */
|
||||
|
||||
export interface UseApiDataMap<Type> {
|
||||
data?: Type;
|
||||
error?: FetchError;
|
||||
refetch: () => void;
|
||||
}
|
||||
|
||||
export interface WebhookOption {
|
||||
name: WebhookMethod;
|
||||
value: WebhookMethod;
|
||||
}
|
||||
|
||||
export interface Pcap {
|
||||
data_url: string;
|
||||
file_name: string;
|
||||
}
|
||||
|
||||
export interface CredentialTest {
|
||||
status: CredentialStatus;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export interface CredentialTestResult {
|
||||
stt: CredentialTest;
|
||||
tts: CredentialTest;
|
||||
}
|
||||
|
||||
export interface LimitField {
|
||||
label: string;
|
||||
category: LimitCategories;
|
||||
}
|
||||
|
||||
/** API responses/payloads */
|
||||
|
||||
export interface User {
|
||||
user_sid: string;
|
||||
}
|
||||
|
||||
export interface UserLogin {
|
||||
token: string;
|
||||
user_sid: string;
|
||||
force_change: boolean;
|
||||
}
|
||||
|
||||
export interface UserLoginPayload {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface UserUpdatePayload {
|
||||
old_password: string;
|
||||
new_password: string;
|
||||
}
|
||||
|
||||
export interface ServiceProvider {
|
||||
name: string;
|
||||
ms_teams_fqdn: null | string;
|
||||
service_provider_sid: string;
|
||||
}
|
||||
|
||||
export interface Limit {
|
||||
category: LimitCategories;
|
||||
/** Empty string signals :DELETE */
|
||||
/** @see src/components/forms/local-limits */
|
||||
/** @see src/containers/internal/views/accounts/form */
|
||||
/** @see src/containers/internal/views/settings/index */
|
||||
quantity: number | string;
|
||||
account_sid?: string;
|
||||
account_limits_sid?: string;
|
||||
service_provider_sid?: string;
|
||||
service_provider_limits_sid?: string;
|
||||
}
|
||||
|
||||
export interface ApiKey {
|
||||
token: string;
|
||||
last_used: null | string;
|
||||
expires_at: null | string;
|
||||
created_at: string;
|
||||
api_key_sid: string;
|
||||
account_sid: null | string;
|
||||
service_provider_sid: null | string;
|
||||
}
|
||||
|
||||
export interface WebHook {
|
||||
url: string;
|
||||
method: WebhookMethod;
|
||||
username: null | string;
|
||||
password: null | string;
|
||||
webhook_sid?: null | string;
|
||||
}
|
||||
|
||||
export interface Sbc {
|
||||
ipv4: string;
|
||||
port: number | string;
|
||||
sbc_address_sid: string;
|
||||
service_provider_sid: null | string;
|
||||
}
|
||||
|
||||
export interface Smpp {
|
||||
ipv4: string;
|
||||
port: number | string;
|
||||
use_tls: boolean;
|
||||
is_primary: boolean;
|
||||
smpp_address_sid: string;
|
||||
service_provider_sid: null | string;
|
||||
}
|
||||
|
||||
export interface Account {
|
||||
name: string;
|
||||
sip_realm: null | string;
|
||||
account_sid: string;
|
||||
webhook_secret: string;
|
||||
siprec_hook_sid: null | string;
|
||||
queue_event_hook: null | WebHook;
|
||||
registration_hook: null | WebHook;
|
||||
service_provider_sid: string;
|
||||
device_calling_application_sid: null | string;
|
||||
}
|
||||
|
||||
export interface Application {
|
||||
name: string;
|
||||
call_hook: null | WebHook;
|
||||
account_sid: null | string;
|
||||
messaging_hook: null | WebHook;
|
||||
application_sid: string;
|
||||
call_status_hook: null | WebHook;
|
||||
speech_synthesis_voice: null | string;
|
||||
speech_synthesis_vendor: null | Lowercase<Vendor>;
|
||||
speech_synthesis_language: null | string;
|
||||
speech_recognizer_vendor: null | Lowercase<Vendor>;
|
||||
speech_recognizer_language: null | string;
|
||||
}
|
||||
|
||||
export interface PhoneNumber {
|
||||
number: string;
|
||||
account_sid: null | string;
|
||||
application_sid: null | string;
|
||||
phone_number_sid: string;
|
||||
voip_carrier_sid: null | string;
|
||||
}
|
||||
|
||||
export interface MSTeamsTenant {
|
||||
tenant_fqdn: string;
|
||||
ms_teams_tenant_sid: string;
|
||||
account_sid: null | string;
|
||||
application_sid: null | string;
|
||||
service_provider_sid: string;
|
||||
}
|
||||
|
||||
export interface RecentCall {
|
||||
account_sid: string;
|
||||
call_sid: string;
|
||||
from: string;
|
||||
to: string;
|
||||
answered: boolean;
|
||||
sip_callid: string;
|
||||
sip_status: number;
|
||||
duration: number;
|
||||
attempted_at: number;
|
||||
answered_at: number;
|
||||
terminated_at: number;
|
||||
termination_reason: string;
|
||||
host: string;
|
||||
remote_host: string;
|
||||
direction: string;
|
||||
trunk: string;
|
||||
}
|
||||
|
||||
export interface SpeechCredential {
|
||||
speech_credential_sid: string;
|
||||
service_provider_sid: null | string;
|
||||
account_sid: null | string;
|
||||
vendor: Lowercase<Vendor>;
|
||||
use_for_tts: number;
|
||||
use_for_stt: number;
|
||||
last_used: null | string;
|
||||
region: null | string;
|
||||
aws_region: null | string;
|
||||
api_key: null | string;
|
||||
access_key_id: null | string;
|
||||
secret_access_key: null | string;
|
||||
service_key: null | string;
|
||||
}
|
||||
|
||||
export interface Alert {
|
||||
time: number;
|
||||
account_sid: string;
|
||||
alert_type: string;
|
||||
message: string;
|
||||
detail: string;
|
||||
}
|
||||
|
||||
export interface Carrier {
|
||||
voip_carrier_sid: string;
|
||||
name: string;
|
||||
description: null | string;
|
||||
is_active: boolean;
|
||||
service_provider_sid: string;
|
||||
account_sid: null | string;
|
||||
application_sid: null | string;
|
||||
e164_leading_plus: boolean;
|
||||
requires_register: boolean;
|
||||
register_username: null | string;
|
||||
register_sip_realm: null | string;
|
||||
register_password: null | string;
|
||||
tech_prefix: null | string;
|
||||
diversion: null | string;
|
||||
inbound_auth_username: string;
|
||||
inbound_auth_password: string;
|
||||
smpp_system_id: null | string;
|
||||
smpp_password: null | string;
|
||||
smpp_inbound_system_id: null | string;
|
||||
smpp_inbound_password: null | string;
|
||||
smpp_enquire_link_interval: number;
|
||||
}
|
||||
|
||||
export interface PredefinedCarriers extends Carrier {
|
||||
requires_static_ip: boolean;
|
||||
}
|
||||
|
||||
export interface ProtocolGateway {
|
||||
voip_carrier_sid: string;
|
||||
ipv4: string;
|
||||
port: number;
|
||||
netmask: number;
|
||||
inbound: number;
|
||||
outbound: number;
|
||||
}
|
||||
|
||||
export interface SipGateway extends ProtocolGateway {
|
||||
sip_gateway_sid?: null | string;
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
export interface SmppGateway extends ProtocolGateway {
|
||||
smpp_gateway_sid?: null | string;
|
||||
is_primary: boolean;
|
||||
use_tls: boolean;
|
||||
}
|
||||
|
||||
export interface PageQuery {
|
||||
page: number;
|
||||
count: number;
|
||||
start?: string;
|
||||
days?: number;
|
||||
}
|
||||
|
||||
export interface CallQuery extends PageQuery {
|
||||
direction?: string;
|
||||
answered?: string;
|
||||
}
|
||||
|
||||
export interface PagedResponse<Type> {
|
||||
page_size: number;
|
||||
total: number;
|
||||
page: number;
|
||||
data: Type[];
|
||||
}
|
||||
|
||||
export interface SidResponse {
|
||||
sid: string;
|
||||
}
|
||||
|
||||
export interface TokenResponse extends SidResponse {
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface SecretResponse {
|
||||
webhook_secret: string;
|
||||
}
|
||||
|
||||
export interface EmptyResponse {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface TotalResponse {
|
||||
total: number;
|
||||
}
|
||||
Reference in new issue
Block a user