Compare commits

...

7 Commits

Author SHA1 Message Date
snyk-bot
27cb05e22a fix: Dockerfile to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE317-OPENSSL-3368755
- https://snyk.io/vuln/SNYK-ALPINE317-OPENSSL-3368755
2023-03-30 15:12:02 +00:00
EgleH
aba8b2be3a logout with one click (#223)
Co-authored-by: eglehelms <e.helms@cognigy.com>
2023-03-30 07:44:19 -04:00
EgleH
f4d7880ab7 Add Logout call to signout (#221)
* Add Logout call to signout

* clean local storage even on error

---------

Co-authored-by: eglehelms <e.helms@cognigy.com>
2023-03-29 08:54:17 -04:00
Dave Horton
7f93489580 bump version 2023-03-28 14:15:26 -04:00
Dave Horton
19fafdc908 minor label change 2023-03-25 12:13:13 -04:00
Hoan Luu Huu
a165bfc4d6 feat: onpremise nuance (#220)
Co-authored-by: Quan HL <quanluuhoang8@gmail.com>
2023-03-25 11:21:14 -04:00
Hoan Luu Huu
e26d9b95cb fix: edit application does not clear webhook user/pass when checkbox is uncheck (#217)
Co-authored-by: Quan HL <quanluuhoang8@gmail.com>
2023-03-21 20:14:16 -04:00
9 changed files with 198 additions and 39 deletions

View File

@@ -6,7 +6,7 @@ RUN npm install
RUN npm run build
RUN npm prune
FROM node:18.14.1-alpine as webapp
FROM node:18.15-alpine as webapp
RUN apk add curl
WORKDIR /opt/app
COPY . /opt/app

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "jambonz-webapp",
"version": "v1.0.0",
"version": "v0.8.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "jambonz-webapp",
"version": "v1.0.0",
"version": "v0.8.2",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,7 +1,7 @@
{
"name": "jambonz-webapp",
"description": "A simple provisioning web app for jambonz",
"version": "v1.0.0",
"version": "v0.8.2",
"license": "MIT",
"type": "module",
"engines": {

View File

@@ -189,6 +189,7 @@ export const CRED_NOT_TESTED = "not tested";
/** API base paths */
export const API_LOGIN = `${API_BASE_URL}/login`;
export const API_LOGOUT = `${API_BASE_URL}/logout`;
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`;

View File

@@ -18,6 +18,7 @@ import {
API_SIP_GATEWAY,
API_PASSWORD_SETTINGS,
USER_ACCOUNT,
API_LOGOUT,
} from "./constants";
import { ROUTE_LOGIN } from "src/router/routes";
import {
@@ -233,6 +234,10 @@ export const postLogin = (payload: UserLoginPayload) => {
});
};
export const postLogout = () => {
return postFetch<undefined>(API_LOGOUT);
};
/** Named wrappers for `postFetch` */
export const postServiceProviders = (payload: Partial<ServiceProvider>) => {

View File

@@ -303,6 +303,8 @@ export interface SpeechCredential {
custom_stt_endpoint: null | string;
client_id: null | string;
secret: null | string;
nuance_tts_uri: null | string;
nuance_stt_uri: null | string;
tts_api_key: null | string;
tts_region: null | string;
stt_api_key: null | string;

View File

@@ -72,11 +72,17 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
const [initialApplicationJson, setInitialApplicationJson] = useState(false);
const [accountSid, setAccountSid] = useState("");
const [callWebhook, setCallWebhook] = useState<WebHook>(DEFAULT_WEBHOOK);
const [tmpCallWebhook, setTmpCallWebhook] =
useState<WebHook>(DEFAULT_WEBHOOK);
const [initialCallWebhook, setInitialCallWebhook] = useState(false);
const [statusWebhook, setStatusWebhook] = useState<WebHook>(DEFAULT_WEBHOOK);
const [tmpStatusWebhook, setTmpStatusWebhook] =
useState<WebHook>(DEFAULT_WEBHOOK);
const [initialStatusWebhook, setInitialStatusWebhook] = useState(false);
const [messageWebhook, setMessageWebhook] =
useState<WebHook>(DEFAULT_WEBHOOK);
const [tmpMessageWebhook, setTmpMessageWebhook] =
useState<WebHook>(DEFAULT_WEBHOOK);
const [initialMessageWebhook, setInitialMessageWebhook] = useState(false);
const [synthVendor, setSynthVendor] =
useState<keyof SynthesisVendors>(VENDOR_GOOGLE);
@@ -97,7 +103,9 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
label: "Calling",
prefix: "call_webhook",
stateVal: callWebhook,
tmpStateVal: tmpCallWebhook,
stateSet: setCallWebhook,
tmpStateSet: setTmpCallWebhook,
initialCheck: initialCallWebhook,
required: true,
},
@@ -105,7 +113,9 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
label: "Call status",
prefix: "status_webhook",
stateVal: statusWebhook,
tmpStateVal: tmpStatusWebhook,
stateSet: setStatusWebhook,
tmpStateSet: setTmpStatusWebhook,
initialCheck: initialStatusWebhook,
required: true,
},
@@ -113,7 +123,9 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
label: "Messaging",
prefix: "message_webhook",
stateVal: messageWebhook,
tmpStateVal: tmpMessageWebhook,
stateSet: setMessageWebhook,
tmpStateSet: setTmpMessageWebhook,
initialCheck: initialMessageWebhook,
required: false,
},
@@ -242,6 +254,7 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
if (application.data.call_hook) {
setCallWebhook(application.data.call_hook);
setTmpCallWebhook(application.data.call_hook);
if (
application.data.call_hook.username ||
@@ -253,6 +266,7 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
if (application.data.call_status_hook) {
setStatusWebhook(application.data.call_status_hook);
setTmpStatusWebhook(application.data.call_status_hook);
if (
application.data.call_status_hook.username ||
@@ -264,6 +278,7 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
if (application.data.messaging_hook) {
setMessageWebhook(application.data.messaging_hook);
setTmpMessageWebhook(application.data.messaging_hook);
if (
application.data.messaging_hook.username ||
@@ -383,6 +398,18 @@ export const ApplicationForm = ({ application }: ApplicationFormProps) => {
name={webhook.prefix}
label="Use HTTP basic authentication"
initialCheck={webhook.initialCheck}
handleChecked={(e) => {
if (e.target.checked) {
webhook.stateSet(webhook.tmpStateVal);
} else {
webhook.tmpStateSet(webhook.stateVal);
webhook.stateSet({
...webhook.stateVal,
username: "",
password: "",
});
}
}}
>
<MS>{MSG_WEBHOOK_FIELDS}</MS>
<label htmlFor={`${webhook.prefix}_username`}>Username</label>

View File

@@ -89,6 +89,16 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
const [customVendorTtsUrl, setCustomVendorTtsUrl] = useState("");
const [tmpCustomVendorSttUrl, setTmpCustomVendorSttUrl] = useState("");
const [customVendorSttUrl, setCustomVendorSttUrl] = useState("");
const [initialOnPremNuanceTtsCheck, setInitialOnPremNuanceTtsCheck] =
useState(false);
const [onPremNuanceTtsCheck, setOnPremNuanceTtsCheck] = useState(false);
const [onPremNuanceTtsUrl, setOnPremNuanceTtsUrl] = useState("");
const [tmpOnPremNuanceTtsUrl, setTmpOnPremNuanceTtsUrl] = useState("");
const [initialOnPremNuanceSttCheck, setInitialOnPremNuanceSttCheck] =
useState(false);
const [onPremNuanceSttCheck, setOnPremNuanceSttCheck] = useState(false);
const [tmpOnPremNuanceSttUrl, setTmpOnPremNuanceSttUrl] = useState("");
const [onPremNuanceSttUrl, setOnPremNuanceSttUrl] = useState("");
const handleFile = (file: File) => {
const handleError = () => {
@@ -161,6 +171,12 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
custom_stt_url: customVendorSttUrl || null,
auth_token: customVendorAuthToken || null,
}),
...(vendor === VENDOR_NUANCE && {
client_id: clientId || null,
secret: secretKey || null,
nuance_tts_uri: onPremNuanceTtsUrl || null,
nuance_stt_uri: onPremNuanceSttUrl || null,
}),
};
if (credential && credential.data) {
@@ -197,8 +213,6 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
vendor === VENDOR_SONIOX
? apiKey
: null,
client_id: vendor === VENDOR_NUANCE ? clientId : null,
secret: vendor === VENDOR_NUANCE ? secretKey : null,
riva_server_uri: vendor == VENDOR_NVIDIA ? rivaServerUri : null,
})
.then(() => {
@@ -219,7 +233,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
if (credential.data.vendor) {
const v = credential.data.vendor.startsWith(VENDOR_CUSTOM)
? VENDOR_CUSTOM
: vendor;
: credential.data.vendor;
setVendor(v);
}
@@ -275,6 +289,24 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
setSecretKey(credential.data.secret);
}
if (credential.data.nuance_tts_uri) {
setOnPremNuanceTtsUrl(credential.data.nuance_tts_uri);
setInitialOnPremNuanceTtsCheck(true);
setOnPremNuanceTtsCheck(true);
} else {
setInitialOnPremNuanceTtsCheck(false);
setOnPremNuanceTtsCheck(false);
}
if (credential.data.nuance_stt_uri) {
setOnPremNuanceSttUrl(credential.data.nuance_stt_uri);
setInitialOnPremNuanceSttCheck(true);
setOnPremNuanceSttCheck(true);
} else {
setInitialOnPremNuanceSttCheck(false);
setOnPremNuanceSttCheck(false);
}
if (credential.data.tts_api_key) {
setTtsApiKey(credential.data.tts_api_key);
}
@@ -527,33 +559,108 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
</>
)}
{vendor === VENDOR_NUANCE && (
<fieldset>
<label htmlFor="nuance_client_id">
Client ID<span>*</span>
</label>
<input
id="nuance_client_id"
required
type="text"
name="nuance_client_id"
placeholder="Client ID"
value={clientId}
onChange={(e) => setClientId(e.target.value)}
disabled={credential ? true : false}
/>
<label htmlFor="nuance_secret">
Secret<span>*</span>
</label>
<Passwd
id="nuance_secret"
required
name="nuance_secret"
placeholder="Secret Key"
value={secretKey ? getObscuredSecret(secretKey) : secretKey}
onChange={(e) => setSecretKey(e.target.value)}
disabled={credential ? true : false}
/>
</fieldset>
<>
<fieldset>
<label htmlFor="nuance_client_id">
Client ID
{!onPremNuanceSttCheck && !onPremNuanceTtsCheck && (
<span>*</span>
)}
</label>
<input
id="nuance_client_id"
required={!onPremNuanceSttCheck && !onPremNuanceTtsCheck}
type="text"
name="nuance_client_id"
placeholder="Client ID"
value={clientId}
onChange={(e) => setClientId(e.target.value)}
disabled={credential ? true : false}
/>
<label htmlFor="nuance_secret">
Secret
{!onPremNuanceSttCheck && !onPremNuanceTtsCheck && (
<span>*</span>
)}
</label>
<Passwd
id="nuance_secret"
required={!onPremNuanceSttCheck && !onPremNuanceTtsCheck}
name="nuance_secret"
placeholder="Secret Key"
value={secretKey ? getObscuredSecret(secretKey) : secretKey}
onChange={(e) => setSecretKey(e.target.value)}
disabled={credential ? true : false}
/>
</fieldset>
<fieldset>
<>
<Checkzone
hidden
name="on_prem_nuance_use_tts"
label="Use on-prem TTS"
initialCheck={initialOnPremNuanceTtsCheck}
handleChecked={(e) => {
setOnPremNuanceTtsCheck(e.target.checked);
if (!e.target.checked) {
setTmpOnPremNuanceTtsUrl(onPremNuanceTtsUrl);
setOnPremNuanceTtsUrl("");
} else {
setOnPremNuanceTtsUrl(tmpOnPremNuanceTtsUrl);
}
}}
>
<label htmlFor="on_prem_nuance_use_tts">
TTS URI<span>*</span>
</label>
<input
id="on_prem_nuance_use_tts"
type="text"
name="on_prem_nuance_use_tts"
placeholder="ip:port"
pattern="(.*):([0-9]{0,6}$)"
required={onPremNuanceTtsCheck}
value={onPremNuanceTtsUrl}
onChange={(e) => {
setOnPremNuanceTtsUrl(e.target.value);
}}
/>
</Checkzone>
<Checkzone
hidden
name="on_prem_nuance_use_stt"
label="Use on-prem STT"
initialCheck={initialOnPremNuanceSttCheck}
handleChecked={(e) => {
setOnPremNuanceSttCheck(e.target.checked);
if (!e.target.checked) {
setTmpOnPremNuanceSttUrl(onPremNuanceSttUrl);
setOnPremNuanceSttUrl("");
} else {
setOnPremNuanceSttUrl(tmpOnPremNuanceSttUrl);
}
}}
>
<label htmlFor="on_prem_nuance_use_stt_lb">
STT URI<span>*</span>
</label>
<input
id="on_prem_nuance_use_stt"
type="text"
name="on_prem_nuance_use_stt"
placeholder="ip:port"
pattern="(.*):([0-9]{0,6}$)"
required={onPremNuanceSttCheck}
value={onPremNuanceSttUrl}
onChange={(e) => {
setOnPremNuanceSttUrl(e.target.value);
}}
/>
</Checkzone>
</>
</fieldset>
</>
)}
{vendor === VENDOR_AWS && (
<fieldset>

View File

@@ -4,13 +4,13 @@
import React, { useContext } from "react";
import { useNavigate } from "react-router-dom";
import { postLogin } from "src/api";
import { postLogin, postLogout } from "src/api";
import { StatusCodes } from "src/api/types";
import {
ROUTE_LOGIN,
ROUTE_CREATE_PASSWORD,
ROUTE_INTERNAL_ACCOUNTS,
ROUTE_INTERNAL_APPLICATIONS,
ROUTE_LOGIN,
} from "./routes";
import {
SESS_OLD_PASSWORD,
@@ -136,10 +136,27 @@ export const useProvideAuth = (): AuthStateContext => {
};
const signout = () => {
localStorage.clear();
sessionStorage.clear();
sessionStorage.setItem(SESS_FLASH_MSG, MSG_LOGGED_OUT);
window.location.href = ROUTE_LOGIN;
return new Promise((resolve, reject) => {
postLogout()
.then((response) => {
if (response.status === StatusCodes.OK) {
localStorage.clear();
sessionStorage.clear();
sessionStorage.setItem(SESS_FLASH_MSG, MSG_LOGGED_OUT);
resolve(response.json);
}
})
.catch((error) => {
localStorage.clear();
sessionStorage.clear();
sessionStorage.setItem(SESS_FLASH_MSG, MSG_LOGGED_OUT);
if (error) {
reject(error);
}
reject(MSG_SOMETHING_WRONG);
});
});
};
return {