mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2026-01-25 02:08:19 +00:00
Compare commits
8 Commits
feature/up
...
v0.7.7-9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a67f6ad214 | ||
|
|
57dd168272 | ||
|
|
6b7e4a34bb | ||
|
|
50216d5345 | ||
|
|
4bdbd6fc0f | ||
|
|
ebcdefb945 | ||
|
|
6b9d206f4f | ||
|
|
68d88decb5 |
@@ -1,4 +1,4 @@
|
||||
FROM node:18.8.0-alpine as builder
|
||||
FROM node:18.9.0-alpine3.16 as builder
|
||||
RUN apk update && apk add --no-cache python3 make g++
|
||||
COPY . /opt/app
|
||||
WORKDIR /opt/app/
|
||||
|
||||
150
package-lock.json
generated
150
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "jambonz-webapp",
|
||||
"version": "v0.7.6",
|
||||
"version": "v0.7.7",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "jambonz-webapp",
|
||||
"version": "v0.7.6",
|
||||
"version": "v0.7.7",
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
@@ -2418,6 +2418,58 @@
|
||||
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.17",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
|
||||
"integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -13819,9 +13871,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/serve": {
|
||||
"version": "13.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serve/-/serve-13.0.2.tgz",
|
||||
"integrity": "sha512-71R6fKvNgKrqARAag6lYJNnxDzpH7DCNrMuvPY5PLVaC2PDhJsGTj/34o4o4tPWhTuLgEXqvgnAWbATQ9zGZTQ==",
|
||||
"version": "13.0.4",
|
||||
"resolved": "https://registry.npmjs.org/serve/-/serve-13.0.4.tgz",
|
||||
"integrity": "sha512-Lj8rhXmphJCRQVv5qwu0NQZ2h+0MrRyRJxDZu5y3qLH2i/XY6a0FPj/VmjMUdkJb672MBfE8hJ274PU6JzBd0Q==",
|
||||
"dependencies": {
|
||||
"@zeit/schemas": "2.6.0",
|
||||
"ajv": "6.12.6",
|
||||
@@ -14730,12 +14782,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
|
||||
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
|
||||
"version": "5.15.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
|
||||
"integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
@@ -14743,14 +14796,6 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"acorn": "^8.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"acorn": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin": {
|
||||
@@ -14791,14 +14836,6 @@
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"node_modules/terser/node_modules/source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/test-exclude": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
|
||||
@@ -17794,6 +17831,49 @@
|
||||
"chalk": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
|
||||
},
|
||||
"@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
|
||||
},
|
||||
"@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.17",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
|
||||
"integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -26006,9 +26086,9 @@
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"version": "13.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serve/-/serve-13.0.2.tgz",
|
||||
"integrity": "sha512-71R6fKvNgKrqARAag6lYJNnxDzpH7DCNrMuvPY5PLVaC2PDhJsGTj/34o4o4tPWhTuLgEXqvgnAWbATQ9zGZTQ==",
|
||||
"version": "13.0.4",
|
||||
"resolved": "https://registry.npmjs.org/serve/-/serve-13.0.4.tgz",
|
||||
"integrity": "sha512-Lj8rhXmphJCRQVv5qwu0NQZ2h+0MrRyRJxDZu5y3qLH2i/XY6a0FPj/VmjMUdkJb672MBfE8hJ274PU6JzBd0Q==",
|
||||
"requires": {
|
||||
"@zeit/schemas": "2.6.0",
|
||||
"ajv": "6.12.6",
|
||||
@@ -26710,12 +26790,13 @@
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
|
||||
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
|
||||
"version": "5.15.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
|
||||
"integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -26723,11 +26804,6 @@
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jambonz-webapp",
|
||||
"version": "v0.7.6",
|
||||
"version": "v0.7.7",
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
|
||||
@@ -22,7 +22,7 @@ import CopyableText from '../elements/CopyableText';
|
||||
import Span from '../elements/Span';
|
||||
import handleErrors from "../../helpers/handleErrors";
|
||||
import styled from 'styled-components/macro';
|
||||
import { APP_API_BASE_URL } from "../../constants";
|
||||
import { APP_API_BASE_URL, LIMITS } from "../../constants";
|
||||
|
||||
const StyledInputGroup = styled(InputGroup)`
|
||||
position: relative;
|
||||
@@ -94,6 +94,7 @@ const AccountForm = props => {
|
||||
const [ sbcs, setSbcs ] = useState([]);
|
||||
const [ subspaceSipRealmOtherValue, setSubspaceSipRealmOtherValue ] = useState('');
|
||||
const [ subspaceEnable, setSubspaceEnable ] = useState(false);
|
||||
const [localLimits, setLocalLimits] = useState([]);
|
||||
|
||||
// Invalid form inputs
|
||||
const [ invalidName, setInvalidName ] = useState(false);
|
||||
@@ -319,6 +320,16 @@ const AccountForm = props => {
|
||||
},
|
||||
});
|
||||
promiseList.push(applicationsPromise);
|
||||
|
||||
const limitsPromise = axios({
|
||||
method: 'get',
|
||||
baseURL: APP_API_BASE_URL,
|
||||
url: `/Accounts/${props.account_sid}/Limits`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
});
|
||||
promiseList.push(limitsPromise);
|
||||
}
|
||||
|
||||
const sbcsPromise = await axios({
|
||||
@@ -337,13 +348,16 @@ const AccountForm = props => {
|
||||
setAccounts(accountsData);
|
||||
|
||||
if (props.type === 'edit') {
|
||||
// Application Data
|
||||
const allApplications = (promiseAllValues[1] && promiseAllValues[1].data) || [];
|
||||
const accountApplicationsData = allApplications.filter(app => {
|
||||
return app.account_sid === props.account_sid;
|
||||
});
|
||||
setAccountApplications(accountApplicationsData);
|
||||
// Limits Data
|
||||
setLocalLimits(promiseAllValues[2]?.data);
|
||||
}
|
||||
setSbcs(promiseAllValues[2]?.data);
|
||||
setSbcs(promiseAllValues[3]?.data);
|
||||
|
||||
if (props.type === 'setup' && accountsData.length > 1) {
|
||||
history.push('/internal/accounts');
|
||||
@@ -558,7 +572,7 @@ const AccountForm = props => {
|
||||
? `/Accounts`
|
||||
: `/Accounts/${accountSid}`;
|
||||
|
||||
await axios({
|
||||
const accountResp = await axios({
|
||||
method: props.type === 'add' ? 'post' : 'put',
|
||||
baseURL: APP_API_BASE_URL,
|
||||
url,
|
||||
@@ -567,6 +581,23 @@ const AccountForm = props => {
|
||||
},
|
||||
data: axiosData,
|
||||
});
|
||||
// Update Limits
|
||||
const acc_sid = accountSid ? accountSid : accountResp.data.sid;
|
||||
await Promise.all(
|
||||
localLimits.map(l => {
|
||||
const method = l.quantity === "" ? 'delete' : 'post';
|
||||
const limitUrl = l.quantity === "" ? `/Accounts/${props.account_sid}/Limits?category=${l.category}` : `/Accounts/${acc_sid}/Limits`;
|
||||
return axios({
|
||||
method: method,
|
||||
baseURL: APP_API_BASE_URL,
|
||||
url: limitUrl,
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
...(method === 'post' && {data: l})
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
if (props.type === 'setup') {
|
||||
isMounted = false;
|
||||
@@ -892,6 +923,31 @@ const AccountForm = props => {
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{LIMITS.map(({ label, category }) => {
|
||||
const quantity = localLimits?.find(l => l.category === category)?.quantity;
|
||||
return <React.Fragment key={category}>
|
||||
<Label htmlFor={category}>{label}</Label>
|
||||
<Input
|
||||
name={category}
|
||||
id={category}
|
||||
type="number"
|
||||
placeholder="Enter Quantity (0=unlimited)"
|
||||
min="0"
|
||||
value={quantity >= 0 ? quantity : ""}
|
||||
onChange={e => {
|
||||
const limit = localLimits.find(l => l.category === category);
|
||||
const value = e.target.value ? Number(e.target.value) : "";
|
||||
if (limit) {
|
||||
setLocalLimits(localLimits.map(l => l.category === category ? {...l, quantity: value} : l));
|
||||
} else {
|
||||
setLocalLimits([...localLimits, {category, quantity: value}]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
</Input>
|
||||
|
||||
</React.Fragment>;
|
||||
})}
|
||||
{ process.env.REACT_APP_ENABLE_SUBSPACE ? (
|
||||
<>
|
||||
<Label htmlFor="subspaceId">Subspace</Label>
|
||||
@@ -1022,7 +1078,6 @@ const AccountForm = props => {
|
||||
)}
|
||||
</>
|
||||
) : null }
|
||||
|
||||
{errorMessage && (
|
||||
<FormError grid message={errorMessage} />
|
||||
)}
|
||||
|
||||
@@ -221,6 +221,8 @@ const CarrierForm = (props) => {
|
||||
const refUsername = useRef(null);
|
||||
const refPassword = useRef(null);
|
||||
const refRealm = useRef(null);
|
||||
const refFromUser = useRef(null);
|
||||
const refFromDomain = useRef(null);
|
||||
const refIp = useRef([]);
|
||||
const refPort = useRef([]);
|
||||
const refInbound = useRef([]);
|
||||
@@ -250,6 +252,8 @@ const CarrierForm = (props) => {
|
||||
const [ passwordInvalid, setPasswordInvalid ] = useState(false);
|
||||
const [ realm, setRealm ] = useState('');
|
||||
const [ realmInvalid, setRealmInvalid ] = useState(false);
|
||||
const [ fromUser, setFromUser ] = useState('');
|
||||
const [ fromDomain, setFromDomain ] = useState('');
|
||||
const [ sipGateways, setSipGateways ] = useState([
|
||||
{
|
||||
sip_gateway_sid: '',
|
||||
@@ -276,6 +280,7 @@ const CarrierForm = (props) => {
|
||||
const [suportSIP, setSupportSIP] = useState(false);
|
||||
const [diversion, setDiversion] = useState("");
|
||||
const [carrierActive, setCarrierActive] = useState(true);
|
||||
const [registerHasPublicIp, setRegisterHasPublicIp] = useState(false);
|
||||
const [predefinedCarriers, setPredefinedCarriers] = useState([]);
|
||||
const [activeTab, setActiveTab] = useState('1');
|
||||
const [sbcs, setSbcs] = useState([]);
|
||||
@@ -438,6 +443,8 @@ const CarrierForm = (props) => {
|
||||
setUsername(carrier.register_username || '');
|
||||
setPassword(carrier.register_password || '');
|
||||
setRealm(carrier.register_sip_realm || '');
|
||||
setFromUser(carrier.register_from_user || '');
|
||||
setFromDomain(carrier.register_from_domain || '');
|
||||
setSipGateways(currentSipGateways.map(s => ({
|
||||
sip_gateway_sid: s.sip_gateway_sid,
|
||||
ip: s.ipv4,
|
||||
@@ -473,6 +480,7 @@ const CarrierForm = (props) => {
|
||||
setSupportSIP(carrier.diversion ? true : false);
|
||||
setDiversion(carrier.diversion || '');
|
||||
setCarrierActive(carrier.is_active === 1);
|
||||
setRegisterHasPublicIp(carrier.register_public_ip_in_contact === 1);
|
||||
} else {
|
||||
const result = await axios({
|
||||
method: 'get',
|
||||
@@ -1028,6 +1036,9 @@ const CarrierForm = (props) => {
|
||||
register_username: username ? username.trim() : null,
|
||||
register_password: password ? password : null,
|
||||
register_sip_realm: register ? realm.trim() : null,
|
||||
register_from_user: register && fromUser ? fromUser.trim() : null,
|
||||
register_from_domain: register && fromDomain ? fromDomain.trim() : null,
|
||||
register_public_ip_in_contact: register && registerHasPublicIp ? 1 : 0,
|
||||
tech_prefix: techPrefix ? techPrefix.trim() : null,
|
||||
diversion: diversion ? diversion.trim() : null,
|
||||
is_active: carrierActive ? 1 : 0,
|
||||
@@ -1484,6 +1495,33 @@ const CarrierForm = (props) => {
|
||||
invalid={realmInvalid}
|
||||
ref={refRealm}
|
||||
/>
|
||||
<Label htmlFor="fromUser">SIP From User</Label>
|
||||
<Input
|
||||
name="fromUser"
|
||||
id="fromUser"
|
||||
value={fromUser}
|
||||
onChange={e => setFromUser(e.target.value)}
|
||||
placeholder="Optional: specify user part of SIP From header"
|
||||
ref={refFromUser}
|
||||
/>
|
||||
<Label htmlFor="fromDomain">SIP From Domain</Label>
|
||||
<Input
|
||||
name="fromDomain"
|
||||
id="fromDomain"
|
||||
value={fromDomain}
|
||||
onChange={e => setFromDomain(e.target.value)}
|
||||
placeholder="Optional: specify host part of SIP From header"
|
||||
ref={refFromDomain}
|
||||
/>
|
||||
<Label htmlFor="regPublicIp">Use Public IP in Contact</Label>
|
||||
<Checkbox
|
||||
noLeftMargin
|
||||
name="regPublicIp"
|
||||
id="regPublicIp"
|
||||
label=""
|
||||
checked={registerHasPublicIp}
|
||||
onChange={e => setRegisterHasPublicIp(e.target.checked)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
null
|
||||
|
||||
@@ -16,7 +16,7 @@ import Loader from '../blocks/Loader';
|
||||
import Modal from '../blocks/Modal';
|
||||
import { ServiceProviderValueContext } from '../../contexts/ServiceProviderContext';
|
||||
import handleErrors from "../../helpers/handleErrors";
|
||||
import { APP_API_BASE_URL } from "../../constants";
|
||||
import { APP_API_BASE_URL, LIMITS } from "../../constants";
|
||||
|
||||
const Td = styled.td`
|
||||
padding: 0.5rem 0;
|
||||
@@ -60,6 +60,19 @@ const SettingsForm = () => {
|
||||
const [serviceProviderSid, setServiceProviderSid] = useState('');
|
||||
const [serviceProviders, setServiceProviders] = useState([]);
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
const [localLimits, setLocalLimits] = useState([]);
|
||||
|
||||
const callApi = async (path, method, data) => {
|
||||
return await axios({
|
||||
method: method,
|
||||
baseURL: APP_API_BASE_URL,
|
||||
url: path,
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
||||
},
|
||||
...(data && {data})
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const getSettingsData = async () => {
|
||||
@@ -74,14 +87,7 @@ const SettingsForm = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const serviceProvidersResponse = await axios({
|
||||
method: 'get',
|
||||
baseURL: APP_API_BASE_URL,
|
||||
url: `/ServiceProviders`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
||||
},
|
||||
});
|
||||
const serviceProvidersResponse = await callApi(`/ServiceProviders`, 'get');
|
||||
|
||||
const sps = serviceProvidersResponse.data;
|
||||
const sp = sps.find(s => s.service_provider_sid === currentServiceProvider);
|
||||
@@ -91,6 +97,12 @@ const SettingsForm = () => {
|
||||
setServiceProviderSid(sp.service_provider_sid || '');
|
||||
setEnableMsTeams(sp.ms_teams_fqdn ? true : false);
|
||||
setSbcDomainName(sp.ms_teams_fqdn || '');
|
||||
|
||||
// Fetch Service provider Limits
|
||||
if (sp.service_provider_sid) {
|
||||
const serviceProvidersLimitsResponse = await callApi(`/ServiceProviders/${sp.service_provider_sid}/Limits`, 'get');
|
||||
setLocalLimits(serviceProvidersLimitsResponse.data);
|
||||
}
|
||||
} catch (err) {
|
||||
handleErrors({ err, history, dispatch });
|
||||
} finally {
|
||||
@@ -208,15 +220,12 @@ const SettingsForm = () => {
|
||||
name: serviceProviderName.trim(),
|
||||
};
|
||||
|
||||
await axios({
|
||||
method: 'put',
|
||||
baseURL: APP_API_BASE_URL,
|
||||
url: `/ServiceProviders/${serviceProviderSid}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
||||
},
|
||||
data,
|
||||
});
|
||||
await callApi(`/ServiceProviders/${serviceProviderSid}`, 'put', data);
|
||||
await Promise.all(
|
||||
localLimits.map(l => l.quantity === "" ?
|
||||
callApi(`/ServiceProviders/${serviceProviderSid}/Limits?category=${l.category}`, 'delete') :
|
||||
callApi(`/ServiceProviders/${serviceProviderSid}/Limits`, 'post', l))
|
||||
);
|
||||
|
||||
refreshMsTeamsData();
|
||||
|
||||
@@ -272,6 +281,7 @@ const SettingsForm = () => {
|
||||
invalid={invalidServiceProviderName}
|
||||
ref={refServiceProviderName}
|
||||
/>
|
||||
|
||||
<div>{/* needed for CSS grid layout */}</div>
|
||||
<Checkbox
|
||||
noLeftMargin
|
||||
@@ -297,6 +307,30 @@ const SettingsForm = () => {
|
||||
title={(!enableMsTeams && "You must enable Microsoft Teams Direct Routing in order to provide an SBC Domain Name") || ""}
|
||||
/>
|
||||
|
||||
{LIMITS.map(({ label, category }) => {
|
||||
const quantity = localLimits?.find(l => l.category === category)?.quantity;
|
||||
return <React.Fragment key={category}>
|
||||
<Label htmlFor={category}>{label}</Label>
|
||||
<Input
|
||||
name={category}
|
||||
id={category}
|
||||
type="number"
|
||||
placeholder="Enter Quantity (0=unlimited)"
|
||||
min="0"
|
||||
value={quantity >= 0 ? quantity : ""}
|
||||
onChange={e => {
|
||||
const limit = localLimits.find(l => l.category === category);
|
||||
const value = e.target.value ? Number(e.target.value) : "";
|
||||
if (limit) {
|
||||
setLocalLimits(localLimits.map(l => l.category === category ? {...l, quantity: value} : l));
|
||||
} else {
|
||||
setLocalLimits([...localLimits, {category, quantity: value}]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>;
|
||||
})}
|
||||
|
||||
{errorMessage && !confirmDelete && (
|
||||
<FormError grid message={errorMessage} />
|
||||
)}
|
||||
|
||||
@@ -79,6 +79,8 @@ const SpeechServicesAddEdit = (props) => {
|
||||
const refApiKey = useRef(null);
|
||||
const refRegion = useRef(null);
|
||||
const refAwsRegion = useRef(null);
|
||||
const refUseCustomTts = useRef(null);
|
||||
const refUseCustomStt = useRef(null);
|
||||
|
||||
// Form inputs
|
||||
const [vendor, setVendor] = useState('');
|
||||
@@ -93,6 +95,13 @@ const SpeechServicesAddEdit = (props) => {
|
||||
const [apiKey, setApiKey] = useState('');
|
||||
const [region, setRegion] = useState('');
|
||||
const [awsregion, setAwsRegion] = useState('');
|
||||
const [useCustomTts, setUseCustomTts] = useState(false);
|
||||
const [useCustomStt, setUseCustomStt] = useState(false);
|
||||
const [customTtsEndpoint, setCustomTtsEndpoint] = useState('');
|
||||
const [customSttEndpoint, setCustomSttEndpoint] = useState('');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [tmpCustomTtsEndpoint, setTmpCustomTtsEndpoint] = useState('');
|
||||
const [tmpCustomSttEndpoint, setTmpCustomSttEndpoint] = useState('');
|
||||
|
||||
// Invalid form inputs
|
||||
const [invalidVendorGoogle, setInvalidVendorGoogle] = useState(false);
|
||||
@@ -106,6 +115,9 @@ const SpeechServicesAddEdit = (props) => {
|
||||
const [invalidApiKey, setInvalidApiKey] = useState(false);
|
||||
const [invalidRegion, setInvalidRegion] = useState(false);
|
||||
const [invalidAwsRegion, setInvalidAwsRegion] = useState(false);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [invalidUseCustomTts, setInvalidUseCustomTts] = useState(false);
|
||||
const [invalidUseCustomStt, setInvalidUseCustomStt] = useState(false);
|
||||
|
||||
const [originalTtsValue, setOriginalTtsValue] = useState(null);
|
||||
const [originalSttValue, setOriginalSttValue] = useState(null);
|
||||
@@ -162,6 +174,10 @@ const SpeechServicesAddEdit = (props) => {
|
||||
setUseForStt(speechCredential.data.use_for_stt || false);
|
||||
setOriginalTtsValue(speechCredential.data.use_for_tts || false);
|
||||
setOriginalSttValue(speechCredential.data.use_for_stt || false);
|
||||
setUseCustomTts(speechCredential.data.use_custom_tts || false);
|
||||
setCustomTtsEndpoint(speechCredential.data.custom_tts_endpoint || '');
|
||||
setUseCustomStt(speechCredential.data.use_custom_stt || false);
|
||||
setCustomSttEndpoint(speechCredential.data.custom_stt_endpoint || '');
|
||||
}
|
||||
setShowLoader(false);
|
||||
} catch (err) {
|
||||
@@ -232,6 +248,8 @@ const SpeechServicesAddEdit = (props) => {
|
||||
setInvalidUseForTts(false);
|
||||
setInvalidUseForStt(false);
|
||||
setInvalidApiKey(false);
|
||||
setInvalidUseCustomTts(false);
|
||||
setInvalidUseCustomStt(false);
|
||||
let errorMessages = [];
|
||||
let focusHasBeenSet = false;
|
||||
|
||||
@@ -305,6 +323,24 @@ const SpeechServicesAddEdit = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (useCustomTts && !customTtsEndpoint) {
|
||||
errorMessages.push('Please provide a custom voice endpoint.');
|
||||
setInvalidUseCustomTts(true);
|
||||
if (!focusHasBeenSet) {
|
||||
refUseCustomTts.current.focus();
|
||||
focusHasBeenSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (useCustomStt && !customSttEndpoint) {
|
||||
errorMessages.push('Please provide a custom speech endpoint Id.');
|
||||
setInvalidUseCustomStt(true);
|
||||
if (!focusHasBeenSet) {
|
||||
refUseCustomStt.current.focus();
|
||||
focusHasBeenSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorMessages.length > 1) {
|
||||
setErrorMessage(errorMessages);
|
||||
return;
|
||||
@@ -333,16 +369,29 @@ const SpeechServicesAddEdit = (props) => {
|
||||
},
|
||||
data: {
|
||||
vendor,
|
||||
service_key: vendor === 'google' ? JSON.stringify(serviceKey) : null,
|
||||
access_key_id: vendor === 'aws' ? accessKeyId : null,
|
||||
secret_access_key: vendor === 'aws' ? secretAccessKey : null,
|
||||
aws_region: vendor === 'aws' ? awsregion : null,
|
||||
api_key: ['microsoft', 'wellsaid'].includes(vendor) ? apiKey : null,
|
||||
region: vendor === 'microsoft' ? region : null,
|
||||
use_for_tts: useForTts,
|
||||
use_for_stt: useForStt,
|
||||
service_provider_sid: accountSid ? null : currentServiceProvider,
|
||||
account_sid: accountSid || null,
|
||||
use_for_tts: useForTts,
|
||||
use_for_stt: useForStt,
|
||||
...(vendor === 'google' && method === 'post' && {
|
||||
service_key: serviceKey ? JSON.stringify(serviceKey) : null,
|
||||
}),
|
||||
...(vendor === 'aws' && {
|
||||
...(method === 'post' && {
|
||||
access_key_id: accessKeyId || null,
|
||||
secret_access_key: secretAccessKey || null}),
|
||||
aws_region: awsregion || null
|
||||
}),
|
||||
...(vendor === 'microsoft' && {
|
||||
region: region || null,
|
||||
use_custom_tts: useCustomTts ? 1 : 0,
|
||||
use_custom_stt: useCustomStt ? 1 : 0,
|
||||
custom_tts_endpoint: customTtsEndpoint || null,
|
||||
custom_stt_endpoint: customSttEndpoint || null,
|
||||
}),
|
||||
...(['wellsaid', 'microsoft'].includes(vendor) && method === 'post' && {
|
||||
api_key: apiKey || null,
|
||||
}),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -655,6 +704,72 @@ const SpeechServicesAddEdit = (props) => {
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
{/*
|
||||
<div />
|
||||
<Checkbox
|
||||
noLeftMargin
|
||||
name="useACustomVoice"
|
||||
id="useACustomVoice"
|
||||
label="Use a custom voice"
|
||||
checked={useCustomTts}
|
||||
onChange={e => {
|
||||
setUseCustomTts(e.target.checked);
|
||||
if (e.target.checked && tmpCustomTtsEndpoint) {
|
||||
setCustomTtsEndpoint(tmpCustomTtsEndpoint);
|
||||
}
|
||||
|
||||
if (!e.target.checked) {
|
||||
setTmpCustomTtsEndpoint(customTtsEndpoint);
|
||||
setCustomTtsEndpoint("");
|
||||
}
|
||||
}}
|
||||
invalid={invalidUseCustomTts}
|
||||
ref={refUseCustomTts}
|
||||
/>
|
||||
<Label htmlFor="customVoiceEndpoint">Custom voice endpoint</Label>
|
||||
<Input
|
||||
name="customVoiceEndpoint"
|
||||
id="customVoiceEndpoint"
|
||||
value={customTtsEndpoint}
|
||||
onChange={e => setCustomTtsEndpoint(e.target.value)}
|
||||
placeholder="Custom voice endpoint"
|
||||
invalid={invalidUseCustomTts}
|
||||
ref={refUseCustomTts}
|
||||
disabled={!useCustomTts}
|
||||
/>
|
||||
*/}
|
||||
<div />
|
||||
<Checkbox
|
||||
noLeftMargin
|
||||
name="useACustomSpeechModel"
|
||||
id="useACustomSpeechModel"
|
||||
label="Use a custom speech model"
|
||||
checked={useCustomStt}
|
||||
onChange={e => {
|
||||
setUseCustomStt(e.target.checked);
|
||||
if(e.target.checked && tmpCustomSttEndpoint) {
|
||||
setCustomSttEndpoint(tmpCustomSttEndpoint);
|
||||
}
|
||||
|
||||
if(!e.target.checked) {
|
||||
setTmpCustomSttEndpoint(customSttEndpoint);
|
||||
setCustomSttEndpoint("");
|
||||
}
|
||||
}}
|
||||
invalid={invalidUseCustomStt}
|
||||
ref={refUseCustomStt}
|
||||
/>
|
||||
<Label htmlFor="customSpeechEndpoint">Custom speech endpoint Id</Label>
|
||||
<Input
|
||||
name="customSpeechEndpoint"
|
||||
id="customSpeechEndpoint"
|
||||
value={customSttEndpoint}
|
||||
onChange={e => setCustomSttEndpoint(e.target.value)}
|
||||
placeholder="Custom speech endpoint Id"
|
||||
invalid={invalidUseCustomStt}
|
||||
ref={refUseCustomStt}
|
||||
disabled={!useCustomStt}
|
||||
/>
|
||||
</>
|
||||
) : vendor === 'wellsaid' ? (
|
||||
<>
|
||||
|
||||
@@ -1,2 +1,16 @@
|
||||
const { REACT_APP_API_BASE_URL } = process.env;
|
||||
export const APP_API_BASE_URL = (window.JAMBONZ) ? window.JAMBONZ.APP_API_BASE_URL : REACT_APP_API_BASE_URL;
|
||||
export const LIMITS = [
|
||||
// {
|
||||
// label: "Max registered devices (0=unlimited)",
|
||||
// category: "device",
|
||||
// },
|
||||
// {
|
||||
// label: "Max api calls per minute (0=unlimited)",
|
||||
// category: "api_rate",
|
||||
// },
|
||||
{
|
||||
label: "Max concurrent calls (0=unlimited)",
|
||||
category: "voice_call_session",
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user