mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2026-02-09 02:29:45 +00:00
1867 lines
65 KiB
JavaScript
1867 lines
65 KiB
JavaScript
import React, { useState, useEffect, useContext, useRef } from 'react';
|
|
import { useParams, useHistory } from 'react-router-dom';
|
|
import axios from 'axios';
|
|
import styled from "styled-components/macro";
|
|
import { Menu, Dropdown, Tabs, Switch } from "antd";
|
|
|
|
import { NotificationDispatchContext } from '../../contexts/NotificationContext';
|
|
import { ServiceProviderValueContext } from '../../contexts/ServiceProviderContext';
|
|
import Form from '../elements/Form';
|
|
import Input from '../elements/Input';
|
|
import PasswordInput from '../elements/PasswordInput';
|
|
import Label from '../elements/Label';
|
|
import Checkbox from '../elements/Checkbox';
|
|
import InputGroup from '../elements/InputGroup';
|
|
import FormError from '../blocks/FormError';
|
|
import Button from '../elements/Button';
|
|
import TrashButton from '../elements/TrashButton';
|
|
import Loader from '../blocks/Loader';
|
|
import sortSipGateways from '../../helpers/sortSipGateways';
|
|
import Select from '../elements/Select';
|
|
import handleErrors from "../../helpers/handleErrors";
|
|
import { LinkWithTooltip } from '../elements/Tooltip';
|
|
|
|
const { TabPane } = Tabs;
|
|
|
|
const StyledSection = styled.fieldset`
|
|
margin: auto;
|
|
width: calc(100% - 0.5rem);
|
|
margin-bottom: 1.5rem;
|
|
padding: 2rem;
|
|
border-radius: 0.5rem;
|
|
background: #FFF;
|
|
border:1px solid rgb(0 0 0 / 10%);
|
|
|
|
> *:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
> *:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
`;
|
|
|
|
const SMPPGatewaysInboundInputGroup = styled(InputGroup)`
|
|
grid-column: 2 / 3;
|
|
display: grid;
|
|
grid-gap: 1rem;
|
|
grid-template-columns: 1fr 80px 50px 70px;
|
|
`;
|
|
|
|
const SMPPGatewaysOutboundInputGroup = styled(InputGroup)`
|
|
grid-column: 2 / 3;
|
|
display: grid;
|
|
grid-gap: 1rem;
|
|
grid-template-columns: 1fr 80px 50px 20px 50px;
|
|
`;
|
|
|
|
const StyledLegend = styled.legend`
|
|
font-size: 16px;
|
|
color: #231f20;
|
|
padding: 0 0.5rem;
|
|
width: fit-content;
|
|
`;
|
|
|
|
const StyledForm = styled(Form)`
|
|
@media (max-width: 978.98px) {
|
|
flex-direction: column;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
|
|
& > * {
|
|
width: 100%;
|
|
}
|
|
|
|
& > hr {
|
|
width: calc(100% + 4rem);
|
|
}
|
|
}
|
|
`;
|
|
|
|
const StyledLabel = styled.div`
|
|
white-space: nowrap;
|
|
text-align: left;
|
|
color: #767676;
|
|
`;
|
|
|
|
const StyledButton = styled(Button)`
|
|
grid-column: 1 / 3;
|
|
`;
|
|
|
|
const SIPGatewaysInputGroup = styled(InputGroup)`
|
|
grid-column: 1 / 3;
|
|
display: grid;
|
|
grid-gap: 1rem;
|
|
grid-template-columns: 1fr 80px 80px auto;
|
|
|
|
@media (max-width: 978.98px) {
|
|
grid-template-columns: 1fr 80px 80px auto;
|
|
}
|
|
|
|
@media (max-width: 899.98px) {
|
|
grid-template-columns: 1fr 100px 80px;
|
|
}
|
|
|
|
@media (max-width: 767.98px) {
|
|
grid-template-columns: 1fr 80px 80px auto;
|
|
}
|
|
|
|
@media (max-width: 549.98px) {
|
|
grid-template-columns: 1fr 100px 80px;
|
|
}
|
|
`;
|
|
|
|
const SIPGatewaysChecboxGroup = styled.div`
|
|
display: flex;
|
|
|
|
@media (max-width: 978.98px) {
|
|
& > *:first-child {
|
|
margin-left: -0.5rem;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 549.98px) {
|
|
grid-column: 1 / 3;
|
|
}
|
|
`;
|
|
|
|
const StyledButtonGroup = styled(InputGroup)`
|
|
@media (max-width: 576.98px) {
|
|
width: 100%;
|
|
|
|
& > * {
|
|
width: 100%;
|
|
|
|
& > * {
|
|
width: 100%;
|
|
}
|
|
}
|
|
}
|
|
|
|
@media (max-width: 399.98px) {
|
|
flex-direction: column;
|
|
|
|
& > *:first-child {
|
|
margin-right: 0;
|
|
margin-bottom: 1rem;
|
|
}
|
|
}
|
|
`;
|
|
|
|
const NameFieldWrapper = styled.div`
|
|
${(props) => props.hasDropdown ? `
|
|
display: grid;
|
|
grid-template-columns: 75% 25%;
|
|
align-items: center;
|
|
` : `
|
|
width: 100%;
|
|
`}
|
|
`;
|
|
|
|
const CarrierSelect = styled.div`
|
|
margin-left: 1rem;
|
|
|
|
& > * {
|
|
width: 100%;
|
|
justify-content: flex-end;
|
|
}
|
|
`;
|
|
|
|
const CarrierItem = styled.div`
|
|
font-family: Objectivity;
|
|
font-size: 16px;
|
|
font-weight: 400;
|
|
color: #565656;
|
|
padding: 0.25rem 0.5rem;
|
|
|
|
${(props) => props.disabled ? 'opacity: 0.5;' : ''}
|
|
`;
|
|
|
|
const Section = styled.section`
|
|
padding: 0 2rem 2rem;
|
|
`;
|
|
|
|
const Subtitle = styled.p`
|
|
font-size: 14px;
|
|
color: #231f20;
|
|
margin: 1.5rem 0;
|
|
`;
|
|
|
|
const CarriersVoiceTipText = ({sbcs}) => {
|
|
const text = <>
|
|
Have your carriers send SIP calls to our servers at:
|
|
{sbcs.map((sbc) => <span key={sbc.ipv4}><br />{sbc.ipv4}:{sbc.port}</span>)}
|
|
</>;
|
|
|
|
return <span>Have your carriers whitelist our <LinkWithTooltip tipText={text}>SIP signaling IPs</LinkWithTooltip></span>;
|
|
};
|
|
|
|
const CarriersSmppTipText = ({smpps}) => {
|
|
const text = <>
|
|
Have your carriers send SMPP messages to our servers at:
|
|
{smpps.map((smpp) => <span key={smpp.ipv4}><br />{smpp.ipv4}:{smpp.port}{smpp.use_tls && ' (TLS)'}</span>)}
|
|
</>;
|
|
|
|
return <span>Have your carriers whitelist our <LinkWithTooltip tipText={text}>SMPP signaling IPs</LinkWithTooltip></span>;
|
|
};
|
|
|
|
const CarrierForm = (props) => {
|
|
const { voip_carrier_sid } = useParams();
|
|
const type = voip_carrier_sid ? 'edit' : 'add';
|
|
|
|
const currentServiceProvider = useContext(ServiceProviderValueContext);
|
|
const history = useHistory();
|
|
const dispatch = useContext(NotificationDispatchContext);
|
|
const jwt = localStorage.getItem('token');
|
|
|
|
// Refs
|
|
const refName = useRef(null);
|
|
const refUsername = useRef(null);
|
|
const refPassword = useRef(null);
|
|
const refRealm = useRef(null);
|
|
const refIp = useRef([]);
|
|
const refPort = useRef([]);
|
|
const refInbound = useRef([]);
|
|
const refOutbound = useRef([]);
|
|
const refTrash = useRef([]);
|
|
const refAdd = useRef(null);
|
|
const refTechPrefix = useRef(null);
|
|
|
|
const refSmppSystemId = useRef(null);
|
|
const refSmppPassword = useRef(null);
|
|
const refSmppInboundSystemId = useRef(null);
|
|
const refSmppInboundPassword = useRef(null);
|
|
const refSmppIp = useRef([]);
|
|
const refSmppPort = useRef([]);
|
|
const refSmppTrash = useRef([]);
|
|
|
|
// Form inputs
|
|
const [ name, setName ] = useState('');
|
|
const [ nameInvalid, setNameInvalid ] = useState(false);
|
|
const [ e164, setE164 ] = useState(false);
|
|
const [ application, setApplication ] = useState('');
|
|
const [ authenticate, setAuthenticate ] = useState(false);
|
|
const [ register, setRegister ] = useState(false);
|
|
const [ username, setUsername ] = useState('');
|
|
const [ usernameInvalid, setUsernameInvalid ] = useState(false);
|
|
const [ password, setPassword ] = useState('');
|
|
const [ passwordInvalid, setPasswordInvalid ] = useState(false);
|
|
const [ realm, setRealm ] = useState('');
|
|
const [ realmInvalid, setRealmInvalid ] = useState(false);
|
|
const [ sipGateways, setSipGateways ] = useState([
|
|
{
|
|
sip_gateway_sid: '',
|
|
ip: '',
|
|
port: 5060,
|
|
inbound: true,
|
|
outbound: false,
|
|
invalidIp: false,
|
|
invalidPort: false,
|
|
invalidInbound: false,
|
|
invalidOutbound: false,
|
|
}
|
|
]);
|
|
|
|
const [ applicationValues, setApplicationValues ] = useState([]);
|
|
const [ accounts, setAccounts ] = useState([]);
|
|
const [ accountSid, setAccountSid ] = useState('');
|
|
const [ carrierSid, setCarrierSid ] = useState('');
|
|
const [ showLoader, setShowLoader ] = useState(true);
|
|
const [ errorMessage, setErrorMessage ] = useState('');
|
|
const [requiredTechPrefix, setRequiredTechPrefix] = useState(false);
|
|
const [techPrefix, setTechPrefix] = useState('');
|
|
const [techPrefixInvalid, setTechPrefixInvalid ] = useState(false);
|
|
const [suportSIP, setSupportSIP] = useState(false);
|
|
const [diversion, setDiversion] = useState("");
|
|
const [carrierActive, setCarrierActive] = useState(true);
|
|
const [predefinedCarriers, setPredefinedCarriers] = useState([]);
|
|
const [activeTab, setActiveTab] = useState('1');
|
|
const [sbcs, setSbcs] = useState([]);
|
|
|
|
// SMPP state
|
|
const [ smpps, setSmpps ] = useState([]);
|
|
const [ smpp_system_id, setSmppSystemId ] = useState('');
|
|
const [ smpp_system_idInvalid, setSmppSystemIdInvalid ] = useState(false);
|
|
const [ smpp_password, setSmppPassword ] = useState('');
|
|
const [ smpp_passwordInvalid, setSmppPasswordInvalid ] = useState(false);
|
|
const [ smpp_inbound_system_id, setSmppInboundSystemId ] = useState('');
|
|
const [ smpp_inbound_system_idInvalid, setSmppInboundSystemIdInvalid ] = useState(false);
|
|
const [ smpp_inbound_password, setSmppInboundPassword ] = useState('');
|
|
const [ smpp_inbound_passwordInvalid, setSmppInboundPasswordInvalid ] = useState(false);
|
|
const [ smppGateways, setSmppGateways ] = useState([
|
|
{
|
|
smpp_gateway_sid: '',
|
|
ipv4: '',
|
|
port: 2775,
|
|
voip_carrier_sid: '',
|
|
inbound: false,
|
|
use_tls: 0,
|
|
outbound: true,
|
|
is_primary: 1,
|
|
invalidIp: false,
|
|
invalidPort: false,
|
|
},
|
|
{
|
|
smpp_gateway_sid: '',
|
|
ipv4: '',
|
|
port: 2775,
|
|
voip_carrier_sid: '',
|
|
inbound: true,
|
|
use_tls: 0,
|
|
outbound: false,
|
|
is_primary: 0,
|
|
invalidIp: false,
|
|
invalidPort: false,
|
|
}
|
|
]);
|
|
|
|
useEffect(() => {
|
|
const getAPIData = async () => {
|
|
let isMounted = true;
|
|
try {
|
|
const promises = [];
|
|
|
|
// Get Application Data
|
|
const applicationPromise = axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: '/Applications',
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
const accountsPromise = axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: '/Accounts',
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
const smppsPromise = axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: '/Smpps',
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
const sbcsPromise = await axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: '/Sbcs',
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
promises.push(applicationPromise); // 0
|
|
promises.push(accountsPromise); // 1
|
|
promises.push(smppsPromise); // 2
|
|
promises.push(sbcsPromise) // 3
|
|
|
|
if (type === 'edit') {
|
|
const carrierPromise = axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/VoipCarriers/${voip_carrier_sid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
|
|
const sipGatewaysPromise = axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/SipGateways?voip_carrier_sid=${voip_carrier_sid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
|
|
const smppGatewaysPromise = axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/SmppGateways?voip_carrier_sid=${voip_carrier_sid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
|
|
promises.push(carrierPromise); // 4
|
|
promises.push(sipGatewaysPromise); // 5
|
|
promises.push(smppGatewaysPromise); // 6
|
|
}
|
|
|
|
const promiseResponses = await Promise.all(promises);
|
|
|
|
setApplicationValues(promiseResponses[0].data);
|
|
setAccounts(promiseResponses[1].data);
|
|
setSmpps(promiseResponses[2].data);
|
|
setSbcs(promiseResponses[3].data);
|
|
|
|
if (type === 'edit') {
|
|
|
|
const carrier = promiseResponses[4].data;
|
|
const allSipGateways = promiseResponses[5].data;
|
|
const allSmppGateways = promiseResponses[6].data;
|
|
|
|
if (!carrier) {
|
|
isMounted = false;
|
|
history.push('/internal/carriers');
|
|
dispatch({
|
|
type: 'ADD',
|
|
level: 'error',
|
|
message: 'That carrier does not exist.',
|
|
});
|
|
return;
|
|
}
|
|
|
|
const currentSipGateways = allSipGateways.filter(s => {
|
|
return s.voip_carrier_sid === carrier.voip_carrier_sid;
|
|
});
|
|
|
|
const currentSmppGateways = allSmppGateways.filter(s => {
|
|
return s.voip_carrier_sid === carrier.voip_carrier_sid;
|
|
});
|
|
|
|
sortSipGateways(currentSipGateways);
|
|
sortSipGateways(currentSmppGateways);
|
|
|
|
setName(carrier.name || '');
|
|
setE164(carrier.e164_leading_plus === 1);
|
|
setApplication(carrier.application_sid || '');
|
|
setAccountSid(carrier.account_sid || '');
|
|
setAuthenticate(carrier.register_username ? true : false);
|
|
setRegister(carrier.requires_register === 1);
|
|
setUsername(carrier.register_username || '');
|
|
setPassword(carrier.register_password || '');
|
|
setRealm(carrier.register_sip_realm || '');
|
|
setSipGateways(currentSipGateways.map(s => ({
|
|
sip_gateway_sid: s.sip_gateway_sid,
|
|
ip: s.ipv4,
|
|
port: s.port,
|
|
netmask: s.netmask,
|
|
inbound: s.inbound === 1,
|
|
outbound: s.outbound === 1,
|
|
invalidIp: false,
|
|
invalidPort: false,
|
|
invalidInbound: false,
|
|
invalidOutbound: false,
|
|
})));
|
|
setSmppSystemId(carrier.smpp_system_id || '');
|
|
setSmppPassword(carrier.smpp_password || '');
|
|
setSmppInboundPassword(carrier.smpp_inbound_password || '');
|
|
setSmppInboundSystemId(carrier.smpp_inbound_system_id || '');
|
|
setSmppGateways(currentSmppGateways.map(s => ({
|
|
smpp_gateway_sid: s.smpp_gateway_sid,
|
|
ipv4: s.ipv4,
|
|
port: s.port,
|
|
netmask: s.netmask,
|
|
voip_carrier_sid: s.voip_carrier_sid,
|
|
inbound: s.inbound,
|
|
outbound: s.outbound,
|
|
use_tls: s.use_tls,
|
|
is_primary: s.is_primary,
|
|
invalidIp: false,
|
|
invalidPort: false,
|
|
})));
|
|
setCarrierSid(carrier.voip_carrier_sid);
|
|
setTechPrefix(carrier.tech_prefix || '');
|
|
setRequiredTechPrefix(carrier.tech_prefix ? true : false);
|
|
setSupportSIP(carrier.diversion ? true : false);
|
|
setDiversion(carrier.diversion || '');
|
|
setCarrierActive(carrier.is_active === 1);
|
|
} else {
|
|
const result = await axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/PredefinedCarriers`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
setPredefinedCarriers(
|
|
result.data
|
|
.map((item) => ({
|
|
...item,
|
|
value: item.predefined_carrier_sid,
|
|
text: item.requires_static_ip
|
|
? `${item.name} - requires static IP`
|
|
: item.name,
|
|
}))
|
|
.sort((a, b) => a.text.localeCompare(b.text))
|
|
);
|
|
}
|
|
|
|
} catch (err) {
|
|
if (err.response && err.response.status === 401) {
|
|
localStorage.clear();
|
|
sessionStorage.clear();
|
|
isMounted = false;
|
|
history.push('/');
|
|
dispatch({
|
|
type: 'ADD',
|
|
level: 'error',
|
|
message: 'Your session has expired. Please log in and try again.',
|
|
});
|
|
} else {
|
|
setErrorMessage('Something went wrong, please try again.');
|
|
dispatch({
|
|
type: 'ADD',
|
|
level: 'error',
|
|
message: (err.response && err.response.data && err.response.data.msg) || 'Unable to get carriers',
|
|
});
|
|
console.error(err.response || err);
|
|
}
|
|
} finally {
|
|
if (isMounted) {
|
|
setShowLoader(false);
|
|
}
|
|
}
|
|
};
|
|
getAPIData();
|
|
// eslint-disable-next-line
|
|
}, [history.location.pathname]);
|
|
|
|
const addSipGateway = () => {
|
|
const newSipGateways = [
|
|
...sipGateways,
|
|
{
|
|
sip_gateway_sid: '',
|
|
ip: '',
|
|
port: 5060,
|
|
inbound: true,
|
|
outbound: false,
|
|
invalidIp: false,
|
|
invalidPort: false,
|
|
invalidInbound: false,
|
|
invalidOutbound: false,
|
|
}
|
|
];
|
|
setSipGateways(newSipGateways);
|
|
};
|
|
|
|
const addSmppGateway = (inbound) => {
|
|
const newSmppGateways = [
|
|
...smppGateways,
|
|
{
|
|
smpp_gateway_sid: '',
|
|
ipv4: '',
|
|
port: 2775,
|
|
inbound: inbound,
|
|
outbound: !inbound,
|
|
voip_carrier_sid: '',
|
|
use_tls: 0,
|
|
is_primary: 0,
|
|
invalidIp: false,
|
|
invalidPort: false,
|
|
}
|
|
];
|
|
setSmppGateways(newSmppGateways);
|
|
};
|
|
|
|
const removeSipGateway = index => {
|
|
const newSipGateways = sipGateways.filter((s,i) => i !== index);
|
|
setSipGateways(newSipGateways);
|
|
setErrorMessage('');
|
|
};
|
|
|
|
const removeSmppGateway = index => {
|
|
const newSmppGateways = smppGateways.filter((s,i) => i !== index);
|
|
setSmppGateways(newSmppGateways);
|
|
setErrorMessage('');
|
|
};
|
|
|
|
const updateSipGateways = (e, i, key) => {
|
|
const newSipGateways = [...sipGateways];
|
|
const newValue =
|
|
key === 'invalidIp' ||
|
|
key === 'invalidPort' ||
|
|
key === 'invalidInbound' ||
|
|
key === 'invalidOutbound'
|
|
? true
|
|
: (key === 'inbound') || (key === 'outbound')
|
|
? e.target.checked
|
|
: e.target.value;
|
|
newSipGateways[i][key] = newValue;
|
|
|
|
if (key === "outbound" && newValue === true) {
|
|
newSipGateways[i]["netmask"] = 32;
|
|
}
|
|
setSipGateways(newSipGateways);
|
|
};
|
|
|
|
const updateSmppGateways = (e, i, key) => {
|
|
const newSmppGateways = [...smppGateways];
|
|
const newValue =
|
|
key === 'invalidIp' ||
|
|
key === 'invalidPort'
|
|
? true
|
|
: (key === 'use_tls') || (key === 'is_primary')
|
|
? e?1:0
|
|
: e.target.value;
|
|
newSmppGateways[i][key] = newValue;
|
|
|
|
if (key === "outbound" && newValue === true) {
|
|
newSmppGateways[i]["netmask"] = 32;
|
|
}
|
|
|
|
setSmppGateways(newSmppGateways);
|
|
};
|
|
|
|
const resetInvalidFields = () => {
|
|
setNameInvalid(false);
|
|
setUsernameInvalid(false);
|
|
setPasswordInvalid(false);
|
|
setRealmInvalid(false);
|
|
setTechPrefixInvalid(false);
|
|
const newSipGateways = [...sipGateways];
|
|
newSipGateways.forEach((s, i) => {
|
|
newSipGateways[i].invalidIp = false;
|
|
newSipGateways[i].invalidPort = false;
|
|
newSipGateways[i].invalidInbound = false;
|
|
newSipGateways[i].invalidOutbound = false;
|
|
});
|
|
setSipGateways(newSipGateways);
|
|
setSmppSystemIdInvalid(false);
|
|
setSmppPasswordInvalid(false);
|
|
setSmppInboundSystemIdInvalid(false);
|
|
setSmppInboundPasswordInvalid(false);
|
|
const newSmppGateways = [...smppGateways];
|
|
newSmppGateways.forEach((s, i) => {
|
|
s.invalidIp = false;
|
|
s.invalidPort = false;
|
|
});
|
|
setSmppGateways(newSmppGateways);
|
|
};
|
|
|
|
const handleSubmit = async e => {
|
|
let isMounted = true;
|
|
try {
|
|
setShowLoader(true);
|
|
e.preventDefault();
|
|
setErrorMessage('');
|
|
resetInvalidFields();
|
|
let errorMessages = [];
|
|
let focusHasBeenSet = false;
|
|
const regIp = /^((25[0-5]|2[0-4][0-9]|[0-1]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]?[0-9])$/;
|
|
const regFqdn = /^([a-zA-Z][^.]*)(\.[^.]+){2,}$/;
|
|
const regFqdnTopLevel = /^([a-zA-Z][^.]*)(\.[^.]+)$/;
|
|
const regPort = /^[0-9]+$/;
|
|
|
|
if (!name) {
|
|
errorMessages.push('Please provide a name.');
|
|
setNameInvalid(true);
|
|
if (!focusHasBeenSet) {
|
|
refName.current.focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
if (!register && ((username && !password) || (!username && password))) {
|
|
errorMessages.push('Username and password must be either both filled out or both empty.');
|
|
setUsernameInvalid(true);
|
|
setPasswordInvalid(true);
|
|
if (!focusHasBeenSet) {
|
|
if (!username) {
|
|
refUsername.current.focus();
|
|
} else {
|
|
refPassword.current.focus();
|
|
}
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
if (register && !username) {
|
|
errorMessages.push('If registration is required, you must provide a username.');
|
|
setUsernameInvalid(true);
|
|
if (!focusHasBeenSet) {
|
|
refUsername.current.focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
if (register && !password) {
|
|
errorMessages.push('If registration is required, you must provide a password.');
|
|
setPasswordInvalid(true);
|
|
if (!focusHasBeenSet) {
|
|
refPassword.current.focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
if (register && !realm) {
|
|
errorMessages.push('If registration is required, you must provide a SIP realm.');
|
|
setRealmInvalid(true);
|
|
if (!focusHasBeenSet) {
|
|
refRealm.current.focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
if (techPrefix && techPrefix.length < 2) {
|
|
errorMessages.push('If registration is required, you must provide a Tech prefix with more than 2 characters.');
|
|
setTechPrefixInvalid(true);
|
|
if (!focusHasBeenSet) {
|
|
refTechPrefix.current.focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
if (!sipGateways.length) {
|
|
errorMessages.push('You must provide at least one SIP Gateway.');
|
|
if (!focusHasBeenSet) {
|
|
refAdd.current.focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
sipGateways.forEach(async (gateway, i) => {
|
|
//-----------------------------------------------------------------------------
|
|
// IP validation
|
|
//-----------------------------------------------------------------------------
|
|
const type = regIp.test(gateway.ip.trim())
|
|
? 'ip'
|
|
: regFqdn.test(gateway.ip.trim())
|
|
? 'fqdn'
|
|
: regFqdnTopLevel.test(gateway.ip.trim())
|
|
? 'fqdn-top-level'
|
|
: 'invalid';
|
|
|
|
if (!gateway.ip) {
|
|
errorMessages.push('The IP Address cannot be blank. Please provide an IP address or delete the row.');
|
|
updateSipGateways(null, i, 'invalidIp');
|
|
if (!focusHasBeenSet) {
|
|
refIp.current[i].focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
else if (type === 'fqdn-top-level') {
|
|
errorMessages.push('When using an FQDN, you must use a subdomain (e.g. sip.example.com).');
|
|
updateSipGateways(null, i, 'invalidIp');
|
|
if (!focusHasBeenSet) {
|
|
refIp.current[i].focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
else if (type === 'invalid') {
|
|
errorMessages.push('Please provide a valid IP address or fully qualified domain name.');
|
|
updateSipGateways(null, i, 'invalidIp');
|
|
if (!focusHasBeenSet) {
|
|
refIp.current[i].focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Port validation
|
|
//-----------------------------------------------------------------------------
|
|
if (
|
|
gateway.port && (
|
|
!(regPort.test(gateway.port.toString().trim()))
|
|
|| (parseInt(gateway.port.toString().trim()) < 0)
|
|
|| (parseInt(gateway.port.toString().trim()) > 65535)
|
|
)
|
|
) {
|
|
errorMessages.push('Please provide a valid port number between 0 and 65535');
|
|
updateSipGateways(null, i, 'invalidPort');
|
|
if (!focusHasBeenSet) {
|
|
refPort.current[i].focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// inbound/outbound validation
|
|
//-----------------------------------------------------------------------------
|
|
if (type === 'fqdn' && (!gateway.outbound || gateway.inbound)) {
|
|
errorMessages.push('A fully qualified domain name may only be used for outbound calls.');
|
|
updateSipGateways(null, i, 'invalidIp');
|
|
if (gateway.inbound) updateSipGateways(null, i, 'invalidInbound');
|
|
if (!gateway.outbound) updateSipGateways(null, i, 'invalidOutbound');
|
|
if (!focusHasBeenSet) {
|
|
if (gateway.inbound) {
|
|
refInbound.current[i].focus();
|
|
} else {
|
|
refOutbound.current[i].focus();
|
|
}
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
else if (!gateway.inbound && !gateway.outbound) {
|
|
errorMessages.push('Each SIP Gateway must accept inbound calls, outbound calls, or both.');
|
|
updateSipGateways(null, i, 'invalidInbound');
|
|
updateSipGateways(null, i, 'invalidOutbound');
|
|
if (!focusHasBeenSet) {
|
|
refInbound.current[i].focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// duplicates validation
|
|
//-----------------------------------------------------------------------------
|
|
sipGateways.forEach((otherGateway, j) => {
|
|
if (i >= j) return;
|
|
if (!gateway.ip) return;
|
|
if (type === 'invalid') return;
|
|
if (gateway.ip === otherGateway.ip && gateway.port === otherGateway.port) {
|
|
errorMessages.push('Each SIP gateway must have a unique IP address.');
|
|
updateSipGateways(null, i, 'invalidIp');
|
|
updateSipGateways(null, i, 'invalidPort');
|
|
updateSipGateways(null, j, 'invalidIp');
|
|
updateSipGateways(null, j, 'invalidPort');
|
|
if (!focusHasBeenSet) {
|
|
refTrash.current[j].focus();
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
const assertEmptySmppIpsForAll = smppGateways.map((g) => g.ipv4.trim()).join('');
|
|
|
|
// These validations need to execute for SMS tab
|
|
if (smpp_system_id || smpp_password || smpp_inbound_password || assertEmptySmppIpsForAll !== '') {
|
|
// DH: allow creation of smpp gateways without system id/password, as we can get those
|
|
// aftewr the fact from TelecomxXchange
|
|
/*
|
|
if (!smpp_system_id) {
|
|
errorMessages.push('You must provide Outbound System ID.');
|
|
setSmppSystemIdInvalid(true);
|
|
if (!focusHasBeenSet) {
|
|
refSmppSystemId.current.focus();
|
|
setActiveTab('2');
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
if (!smpp_password) {
|
|
errorMessages.push('You must provide Outbound System Password.');
|
|
setSmppPasswordInvalid(true);
|
|
if (!focusHasBeenSet) {
|
|
refSmppPassword.current.focus();
|
|
setActiveTab('2');
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
*/
|
|
|
|
if(smpps && smpps.length > 0) {
|
|
const smppInboundGateways = smppGateways.filter((g) => g.inbound);
|
|
const smppOutboundGateways = smppGateways.filter((g) => g.outbound);
|
|
// const assertEmptySmppIpsForInbound = smppInboundGateways.map((g) => g.ipv4.trim()).join('');
|
|
|
|
// Validate Outbound Gateways -- At least one is required
|
|
if (!smppOutboundGateways.length) {
|
|
errorMessages.push('You must provide at least one Outbound Gateway.');
|
|
addSmppGateway(false); // False means NOT inbound, so YES it is outbound
|
|
setActiveTab('2'); // SMPP tab
|
|
}
|
|
|
|
// Validate Inbound Gateways -- Password required ONLY if adding Gateway(s)
|
|
if (smppInboundGateways.length) {
|
|
// We only need to validate this if the user entered something into an Inbound IP field
|
|
|
|
// DH: see above
|
|
/*
|
|
if (!smpp_inbound_password && assertEmptySmppIpsForInbound !== '') {
|
|
errorMessages.push('You must provide an Inbound System Password when adding IP Address(es) to whitelist.');
|
|
setSmppInboundPasswordInvalid(true);
|
|
if (!focusHasBeenSet) {
|
|
refSmppInboundPassword.current.focus();
|
|
setActiveTab('2');
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
smppGateways.forEach(async (gateway, i) => {
|
|
//-----------------------------------------------------------------------------
|
|
// IP validation
|
|
//-----------------------------------------------------------------------------
|
|
const type = regIp.test(gateway.ipv4.trim())
|
|
? 'ip'
|
|
: regFqdn.test(gateway.ipv4.trim())
|
|
? 'fqdn'
|
|
: regFqdnTopLevel.test(gateway.ipv4.trim())
|
|
? 'fqdn-top-level'
|
|
: (gateway.ipv4.trim() === '') ? 'empty' : 'invalid';
|
|
const gatewayTypeText = gateway.outbound ? 'Outbound' : 'Inbound';
|
|
if (!gateway.ipv4 && gateway.outbound) {
|
|
errorMessages.push(`The ${gatewayTypeText} IP Address cannot be blank. Please provide an IP address or delete the row.`);
|
|
updateSmppGateways(null, i, 'invalidIp');
|
|
if (!focusHasBeenSet) {
|
|
refSmppIp.current[i].focus();
|
|
setActiveTab('2');
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
else if (type === 'fqdn-top-level') {
|
|
errorMessages.push('When using an FQDN, you must use a subdomain (e.g. smpp.example.com).');
|
|
updateSipGateways(null, i, 'invalidIp');
|
|
if (!focusHasBeenSet) {
|
|
refSmppIp.current[i].focus();
|
|
setActiveTab('2');
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
else if (type === 'invalid') {
|
|
errorMessages.push(`Please provide a valid ${gatewayTypeText} IP address or fully qualified domain name.`);
|
|
updateSmppGateways(null, i, 'invalidIp');
|
|
if (!focusHasBeenSet) {
|
|
refSmppIp.current[i].focus();
|
|
setActiveTab('2');
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Port validation
|
|
//-----------------------------------------------------------------------------
|
|
if (
|
|
!gateway.port || (gateway.port && (
|
|
!(regPort.test(gateway.port.toString().trim()))
|
|
|| (parseInt(gateway.port.toString().trim()) < 0)
|
|
|| (parseInt(gateway.port.toString().trim()) > 65535))
|
|
)
|
|
) {
|
|
errorMessages.push(`Please provide a valid ${gatewayTypeText} port number between 0 and 65535`);
|
|
updateSmppGateways(null, i, 'invalidPort');
|
|
if (!focusHasBeenSet) {
|
|
refSmppPort.current[i].focus();
|
|
setActiveTab('2');
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// inbound/outbound validation
|
|
//-----------------------------------------------------------------------------
|
|
if (type === 'fqdn' && (!gateway.outbound || gateway.inbound)) {
|
|
errorMessages.push('A fully qualified domain name may only be used for outbound calls.');
|
|
updateSmppGateways(null, i, 'invalidIp');
|
|
if (!focusHasBeenSet) {
|
|
refSmppIp.current[i].focus();
|
|
setActiveTab('2');
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// duplicates validation
|
|
//-----------------------------------------------------------------------------
|
|
smppGateways.forEach((otherGateway, j) => {
|
|
if (i >= j) return;
|
|
if (!gateway.ip) return;
|
|
if (type === 'invalid') return;
|
|
if (gateway.ip === otherGateway.ip && gateway.port === otherGateway.port) {
|
|
errorMessages.push('Each SMPP gateway must have a unique IP address.');
|
|
updateSmppGateways(null, i, 'invalidIp');
|
|
updateSmppGateways(null, i, 'invalidPort');
|
|
updateSmppGateways(null, j, 'invalidIp');
|
|
updateSmppGateways(null, j, 'invalidPort');
|
|
if (!focusHasBeenSet) {
|
|
refSmppTrash.current[j].focus();
|
|
setActiveTab('2');
|
|
focusHasBeenSet = true;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// remove duplicate error messages
|
|
for (let i = 0; i < errorMessages.length; i++) {
|
|
for (let j = 0; j < errorMessages.length; j++) {
|
|
if (i >= j) continue;
|
|
if (errorMessages[i] === errorMessages[j]) {
|
|
errorMessages.splice(j, 1);
|
|
j = j - 1;
|
|
}
|
|
}
|
|
}
|
|
if (errorMessages.length > 1) {
|
|
setErrorMessage(errorMessages);
|
|
return;
|
|
} else if (errorMessages.length === 1) {
|
|
setErrorMessage(errorMessages[0]);
|
|
return;
|
|
}
|
|
|
|
//=============================================================================
|
|
// Submit
|
|
//=============================================================================
|
|
const creatingNewCarrier = type === 'add';
|
|
|
|
const method = creatingNewCarrier
|
|
? 'post'
|
|
: 'put';
|
|
|
|
const url = creatingNewCarrier
|
|
? `ServiceProviders/${currentServiceProvider}/VoipCarriers`
|
|
: `ServiceProviders/${currentServiceProvider}/VoipCarriers/${carrierSid}`;
|
|
|
|
// Create or update carrier
|
|
const voipCarrier = await axios({
|
|
method,
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
data: {
|
|
name: name.trim() || null,
|
|
e164_leading_plus: e164 ? 1 : 0,
|
|
application_sid: application || null,
|
|
account_sid: accountSid || null,
|
|
requires_register: register ? 1 : 0,
|
|
register_username: username ? username.trim() : null,
|
|
register_password: password ? password : null,
|
|
register_sip_realm: register ? realm.trim() : null,
|
|
tech_prefix: techPrefix ? techPrefix.trim() : null,
|
|
diversion: diversion ? diversion.trim() : null,
|
|
is_active: carrierActive ? 1 : 0,
|
|
smpp_system_id: smpp_system_id ? smpp_system_id.trim(): null,
|
|
smpp_password: smpp_password ? smpp_password.trim(): null,
|
|
smpp_inbound_system_id: smpp_inbound_system_id ? smpp_inbound_system_id.trim(): null,
|
|
smpp_inbound_password: smpp_inbound_password ? smpp_inbound_password.trim(): null
|
|
},
|
|
});
|
|
const voip_carrier_sid = voipCarrier.data.sid;
|
|
|
|
// get updated gateway info from API in order to delete ones that user has removed from UI
|
|
let sipGatewaysFromAPI;
|
|
let smppGatewaysFromAPI;
|
|
if (!creatingNewCarrier) {
|
|
let results = await axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/SipGateways?voip_carrier_sid=${carrierSid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
sipGatewaysFromAPI = results.data.filter(s => s.voip_carrier_sid === carrierSid);
|
|
if(smpps && smpps.length > 0) {
|
|
results = await axios({
|
|
method: 'get',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/SmppGateways?voip_carrier_sid=${voip_carrier_sid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
smppGatewaysFromAPI = results.data.filter(s => s.voip_carrier_sid === carrierSid);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Create or Update SIP Gateways
|
|
//-----------------------------------------------------------------------------
|
|
// Keeping track of created SIP gateways in case one throws an error, then all
|
|
// of the ones created before that (as well as the carrier) have to be deleted.
|
|
let completedSipGateways = [];
|
|
let completedSmppGateways = [];
|
|
try {
|
|
for (const s of sipGateways) {
|
|
const creatingNewGateway = creatingNewCarrier || s.sip_gateway_sid === '';
|
|
|
|
const method = creatingNewGateway
|
|
? 'post'
|
|
: 'put';
|
|
|
|
const url = creatingNewGateway
|
|
? '/SipGateways'
|
|
: `/SipGateways/${s.sip_gateway_sid}`;
|
|
|
|
const data = {
|
|
ipv4: s.ip.trim(),
|
|
port: s.port.toString().trim(),
|
|
netmask: s.netmask,
|
|
inbound: s.inbound,
|
|
outbound: s.outbound,
|
|
};
|
|
|
|
if (creatingNewGateway) {
|
|
data.voip_carrier_sid = voip_carrier_sid || carrierSid;
|
|
}
|
|
|
|
const result = await axios({
|
|
method,
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
data,
|
|
});
|
|
if (creatingNewGateway) {
|
|
completedSipGateways.push(result.data.sid);
|
|
}
|
|
};
|
|
if(smpps && smpps.length > 0) {
|
|
for (const s of smppGateways) {
|
|
const creatingNewGateway = creatingNewCarrier || s.smpp_gateway_sid === '';
|
|
|
|
const method = creatingNewGateway
|
|
? 'post'
|
|
: 'put';
|
|
|
|
const url = creatingNewGateway
|
|
? '/SmppGateways'
|
|
: `/SmppGateways/${s.smpp_gateway_sid}`;
|
|
|
|
const data = {
|
|
ipv4: s.ipv4.trim(),
|
|
port: s.port.toString().trim(),
|
|
netmask: s.netmask,
|
|
inbound: s.inbound,
|
|
outbound: s.outbound,
|
|
use_tls: s.use_tls,
|
|
is_primary: s.is_primary
|
|
};
|
|
|
|
if (creatingNewGateway) {
|
|
data.voip_carrier_sid = voip_carrier_sid || carrierSid;
|
|
}
|
|
|
|
const result = await axios({
|
|
method,
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
data,
|
|
});
|
|
if (creatingNewGateway) {
|
|
completedSmppGateways.push(result.data.sid);
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
if (completedSipGateways.length) {
|
|
for (const sid of completedSipGateways) {
|
|
await axios({
|
|
method: 'delete',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/SipGateways/${sid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
if(smpps && smpps.length > 0) {
|
|
for (const sid of completedSmppGateways) {
|
|
await axios({
|
|
method: 'delete',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/SmppGateways/${sid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
if (voip_carrier_sid) {
|
|
await axios({
|
|
method: 'delete',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/VoipCarriers/${voip_carrier_sid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
}
|
|
throw err;
|
|
}
|
|
|
|
// delete removed gateways (after add/update in case add/update caused errors)
|
|
if (!creatingNewCarrier) {
|
|
for (const remote of sipGatewaysFromAPI) {
|
|
const match = sipGateways.filter(local => local.sip_gateway_sid === remote.sip_gateway_sid);
|
|
if (!match.length) {
|
|
await axios({
|
|
method: 'delete',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/SipGateways/${remote.sip_gateway_sid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
if(smpps && smpps.length > 0) {
|
|
for (const remote of smppGatewaysFromAPI) {
|
|
const match = smppGateways.filter(local => local.smpp_gateway_sid === remote.smpp_gateway_sid);
|
|
if (!match.length) {
|
|
await axios({
|
|
method: 'delete',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/SmppGateways/${remote.smpp_gateway_sid}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
isMounted = false;
|
|
if (accountSid) {
|
|
history.push(`/internal/carriers?account_sid=${accountSid}`);
|
|
} else {
|
|
history.push('/internal/carriers');
|
|
}
|
|
const dispatchMessage = type === 'add'
|
|
? 'Carrier created successfully'
|
|
: 'Carrier updated successfully';
|
|
dispatch({
|
|
type: 'ADD',
|
|
level: 'success',
|
|
message: dispatchMessage
|
|
});
|
|
|
|
} catch (err) {
|
|
if (err.response && err.response.status === 401) {
|
|
localStorage.clear();
|
|
sessionStorage.clear();
|
|
isMounted = false;
|
|
history.push('/');
|
|
dispatch({
|
|
type: 'ADD',
|
|
level: 'error',
|
|
message: 'Your session has expired. Please log in and try again.',
|
|
});
|
|
} else {
|
|
setErrorMessage((err.response && err.response.data && err.response.data.msg) || 'Something went wrong, please try again.');
|
|
console.error(err.response || err);
|
|
}
|
|
} finally {
|
|
if (isMounted) {
|
|
setShowLoader(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
const pickupCarrier = async (value) => {
|
|
let isMounted = true;
|
|
try {
|
|
setShowLoader(true);
|
|
setErrorMessage("");
|
|
|
|
const result = await axios({
|
|
method: 'post',
|
|
baseURL: process.env.REACT_APP_API_BASE_URL,
|
|
url: `/ServiceProviders/${currentServiceProvider}/PredefinedCarriers/${value}`,
|
|
headers: {
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
|
|
const { sid } = result.data;
|
|
|
|
history.push(`/internal/carriers/${sid}/edit`);
|
|
} catch (err) {
|
|
handleErrors({ err, history, dispatch, setErrorMessage });
|
|
} finally {
|
|
if (isMounted) {
|
|
setShowLoader(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
const onChangeTab = activeKey => {
|
|
setActiveTab(activeKey);
|
|
};
|
|
|
|
return (
|
|
showLoader ? (
|
|
<Loader height="376px" />
|
|
) : (
|
|
<>
|
|
<StyledForm large>
|
|
<Label htmlFor="name">Name</Label>
|
|
<NameFieldWrapper hasDropdown={type === 'add'}>
|
|
<Input
|
|
name="name"
|
|
id="name"
|
|
value={name}
|
|
onChange={e => setName(e.target.value)}
|
|
placeholder="Carrier name"
|
|
invalid={nameInvalid}
|
|
autoFocus
|
|
ref={refName}
|
|
/>
|
|
{type === 'add' && (
|
|
<CarrierSelect>
|
|
<Dropdown
|
|
placement="bottomRight"
|
|
trigger="click"
|
|
overlay={
|
|
<Menu>
|
|
{predefinedCarriers.map((item) => {
|
|
const disabled = item.requires_static_ip;
|
|
return (
|
|
<Menu.Item key={item.value} disabled={disabled}>
|
|
<CarrierItem
|
|
disabled={disabled}
|
|
onClick={() => !disabled && pickupCarrier(item.value)}
|
|
>
|
|
{item.text}
|
|
</CarrierItem>
|
|
</Menu.Item>
|
|
);
|
|
})}
|
|
</Menu>
|
|
}
|
|
>
|
|
<Button text formLink type="button">
|
|
Select from list
|
|
</Button>
|
|
</Dropdown>
|
|
</CarrierSelect>
|
|
)}
|
|
</NameFieldWrapper>
|
|
|
|
<Label htmlFor="e164">active</Label>
|
|
<Checkbox
|
|
noLeftMargin
|
|
name="active"
|
|
id="active"
|
|
label=""
|
|
checked={carrierActive}
|
|
onChange={e => setCarrierActive(e.target.checked)}
|
|
/>
|
|
<hr style={{ margin: '0.5rem 0', maxWidth: '100%' }} />
|
|
</StyledForm>
|
|
|
|
<Section>
|
|
<Tabs activeKey={activeTab} onChange={onChangeTab} style={{overflow: 'visible'}}>
|
|
<TabPane tab="Voice" key="1">
|
|
<Subtitle>
|
|
<CarriersVoiceTipText sbcs={sbcs} />
|
|
</Subtitle>
|
|
<StyledForm large style={{paddingTop: 0}}>
|
|
<Label htmlFor="e164">E.164 Syntax</Label>
|
|
<Checkbox
|
|
noLeftMargin
|
|
name="e164"
|
|
id="e164"
|
|
label="prepend a leading + on origination attempts"
|
|
checked={e164}
|
|
onChange={e => setE164(e.target.checked)}
|
|
/>
|
|
|
|
<Label htmlFor="account">Used by</Label>
|
|
<Select
|
|
name="account"
|
|
id="account"
|
|
value={accountSid}
|
|
onChange={(e) => {
|
|
setAccountSid(e.target.value);
|
|
setApplication('');
|
|
}}
|
|
>
|
|
<option value="">
|
|
All accounts
|
|
</option>
|
|
{accounts.filter(a => a.service_provider_sid === currentServiceProvider).map(a => (
|
|
<option
|
|
key={a.account_sid}
|
|
value={a.account_sid}
|
|
>
|
|
{a.name}
|
|
</option>
|
|
))}
|
|
</Select>
|
|
|
|
{accountSid && (
|
|
<>
|
|
<Label htmlFor="application">Default Application</Label>
|
|
<Select
|
|
name="application"
|
|
id="application"
|
|
value={application}
|
|
onChange={e => setApplication(e.target.value)}
|
|
>
|
|
<option value="">
|
|
{type === 'add'
|
|
? '-- OPTIONAL: Application to invoke on calls arriving from this carrier --'
|
|
: '-- NONE --'
|
|
}
|
|
</option>
|
|
{applicationValues.filter((a) => {
|
|
// Map an application to a service provider through it's account_sid
|
|
const acct = accounts.find(ac => a.account_sid === ac.account_sid);
|
|
|
|
if (accountSid) {
|
|
return a.account_sid === accountSid;
|
|
}
|
|
|
|
return acct.service_provider_sid === currentServiceProvider;
|
|
}).map(a => (
|
|
<option
|
|
key={a.application_sid}
|
|
value={a.application_sid}
|
|
>
|
|
{a.name}
|
|
</option>
|
|
))}
|
|
</Select>
|
|
</>
|
|
)}
|
|
|
|
<hr style={{ margin: '0.5rem -2rem' }} />
|
|
|
|
{
|
|
!authenticate ? (
|
|
<>
|
|
<Button
|
|
text
|
|
formLink
|
|
type="button"
|
|
onClick={e => setAuthenticate(!authenticate)}
|
|
>
|
|
Does your carrier require authentication on outbound calls?
|
|
</Button>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Label htmlFor="username">Username</Label>
|
|
<Input
|
|
name="username"
|
|
id="username"
|
|
value={username}
|
|
onChange={e => setUsername(e.target.value)}
|
|
placeholder="SIP username for authenticating outbound calls"
|
|
invalid={usernameInvalid}
|
|
ref={refUsername}
|
|
/>
|
|
<Label htmlFor="password">Password</Label>
|
|
<PasswordInput
|
|
allowShowPassword
|
|
name="password"
|
|
id="password"
|
|
password={password}
|
|
setPassword={setPassword}
|
|
setErrorMessage={setErrorMessage}
|
|
placeholder="SIP password for authenticating outbound calls"
|
|
invalid={passwordInvalid}
|
|
ref={refPassword}
|
|
/>
|
|
<div></div>
|
|
<Checkbox
|
|
noLeftMargin
|
|
name="register"
|
|
id="register"
|
|
label="Carrier requires SIP Register before sending outbound calls"
|
|
checked={register}
|
|
onChange={e => setRegister(e.target.checked)}
|
|
/>
|
|
{
|
|
register ? (
|
|
<>
|
|
<Label htmlFor="realm">SIP Realm</Label>
|
|
<Input
|
|
name="realm"
|
|
id="realm"
|
|
value={realm}
|
|
onChange={e => setRealm(e.target.value)}
|
|
placeholder="SIP realm for registration"
|
|
invalid={realmInvalid}
|
|
ref={refRealm}
|
|
/>
|
|
</>
|
|
) : (
|
|
null
|
|
)
|
|
}
|
|
</>
|
|
)
|
|
}
|
|
|
|
<hr style={{ margin: '0.5rem -2rem' }} />
|
|
|
|
{
|
|
requiredTechPrefix ? (
|
|
<>
|
|
<Label htmlFor="techPrefix">Tech prefix</Label>
|
|
<Input
|
|
name="techPrefix"
|
|
id="techPrefix"
|
|
value={techPrefix}
|
|
onChange={e => setTechPrefix(e.target.value)}
|
|
placeholder="Tech Prefix"
|
|
invalid={techPrefixInvalid}
|
|
ref={refTechPrefix}
|
|
/>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Button
|
|
text
|
|
formLink
|
|
type="button"
|
|
onClick={e => setRequiredTechPrefix(!requiredTechPrefix)}
|
|
>
|
|
Does your carrier require a tech prefix on outbound calls?
|
|
</Button>
|
|
</>
|
|
)
|
|
}
|
|
|
|
<hr style={{ margin: '0.5rem -2rem' }} />
|
|
|
|
{
|
|
suportSIP ? (
|
|
<>
|
|
<Label htmlFor="diversion">Diversion</Label>
|
|
<Input
|
|
name="diversion"
|
|
id="diversion"
|
|
value={diversion}
|
|
onChange={e => setDiversion(e.target.value)}
|
|
placeholder="Phone number or SIP URI"
|
|
/>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Button
|
|
text
|
|
formLink
|
|
type="button"
|
|
onClick={() => setSupportSIP(!suportSIP)}
|
|
>
|
|
Does your carrier support the SIP Diversion header for authenticating the calling number?
|
|
</Button>
|
|
</>
|
|
)
|
|
}
|
|
|
|
<hr style={{ margin: '0.5rem -2rem' }} />
|
|
|
|
<StyledLabel>SIP Gateways</StyledLabel>
|
|
<SIPGatewaysInputGroup>
|
|
<StyledLabel>Network Address / Port / Netmask</StyledLabel>
|
|
</SIPGatewaysInputGroup>
|
|
{sipGateways.map((g, i) => (
|
|
<SIPGatewaysInputGroup key={i}>
|
|
<Input
|
|
name={`sipGatewaysIp[${i}]`}
|
|
id={`sipGatewaysIp[${i}]`}
|
|
value={sipGateways[i].ip}
|
|
onChange={e => updateSipGateways(e, i, 'ip')}
|
|
placeholder={'1.2.3.4'}
|
|
invalid={sipGateways[i].invalidIp}
|
|
ref={ref => refIp.current[i] = ref}
|
|
/>
|
|
<Input
|
|
width="5rem"
|
|
name={`sipGatewaysPort[${i}]`}
|
|
id={`sipGatewaysPort[${i}]`}
|
|
value={sipGateways[i].port}
|
|
onChange={e => updateSipGateways(e, i, 'port')}
|
|
placeholder="5060"
|
|
invalid={sipGateways[i].invalidPort}
|
|
ref={ref => refPort.current[i] = ref}
|
|
/>
|
|
<Select
|
|
name={`sipgatewaysNetmask[${i}]`}
|
|
id={`sipgatewaysNetmask[${i}]`}
|
|
value={sipGateways[i].netmask}
|
|
disabled={sipGateways[i].outbound}
|
|
onChange={e => updateSipGateways(e, i, 'netmask')}
|
|
>
|
|
{Array.from(Array(32 + 1).keys()).slice(1).reverse().map((item) => (
|
|
<option value={item} key={item}>{item}</option>
|
|
))}
|
|
</Select>
|
|
<SIPGatewaysChecboxGroup>
|
|
<Checkbox
|
|
id={`inbound[${i}]`}
|
|
label="Inbound"
|
|
tooltip="Sends us calls"
|
|
checked={sipGateways[i].inbound}
|
|
onChange={e => updateSipGateways(e, i, 'inbound')}
|
|
invalid={sipGateways[i].invalidInbound}
|
|
ref={ref => refInbound.current[i] = ref}
|
|
/>
|
|
<Checkbox
|
|
id={`outbound[${i}]`}
|
|
label="Outbound"
|
|
tooltip="Accepts calls from us"
|
|
checked={sipGateways[i].outbound}
|
|
onChange={e => updateSipGateways(e, i, 'outbound')}
|
|
invalid={sipGateways[i].invalidOutbound}
|
|
ref={ref => refOutbound.current[i] = ref}
|
|
/>
|
|
<TrashButton
|
|
onClick={() => removeSipGateway(i)}
|
|
ref={ref => refTrash.current[i] = ref}
|
|
/>
|
|
</SIPGatewaysChecboxGroup>
|
|
</SIPGatewaysInputGroup>
|
|
))}
|
|
<StyledButton
|
|
square
|
|
type="button"
|
|
onClick={addSipGateway}
|
|
ref={refAdd}
|
|
>
|
|
+
|
|
</StyledButton>
|
|
</StyledForm>
|
|
</TabPane>
|
|
<TabPane tab="SMS" disabled={smpps.length === 0} key="2">
|
|
<Subtitle>
|
|
<CarriersSmppTipText smpps={smpps} />
|
|
</Subtitle>
|
|
<StyledSection>
|
|
<StyledLegend>Outbound SMPP</StyledLegend>
|
|
<StyledForm
|
|
large
|
|
>
|
|
<Label htmlFor="smpp_system_id">System ID</Label>
|
|
<Input
|
|
name="smpp_system_id"
|
|
id="smpp_system_id"
|
|
value={smpp_system_id}
|
|
onChange={e => setSmppSystemId(e.target.value)}
|
|
placeholder="SMPP system id to authenticate with"
|
|
invalid={smpp_system_idInvalid}
|
|
ref={refSmppSystemId}
|
|
/>
|
|
<Label htmlFor="smpp_password">Password</Label>
|
|
<PasswordInput
|
|
allowShowPassword
|
|
name="smpp_password"
|
|
id="smpp_password"
|
|
password={smpp_password}
|
|
setPassword={setSmppPassword}
|
|
setErrorMessage={setErrorMessage}
|
|
placeholder="SMPP password to authenticate with"
|
|
invalid={smpp_passwordInvalid}
|
|
ref={refSmppPassword}
|
|
/>
|
|
|
|
<div
|
|
style={{
|
|
whiteSpace: 'nowrap',
|
|
textAlign: 'left',
|
|
color: '#231f20',
|
|
width: '170px'
|
|
}}
|
|
>Carrier SMPP Gateways</div>
|
|
<div/>
|
|
<div/>
|
|
<SMPPGatewaysOutboundInputGroup>
|
|
<Label>IP or DNS name</Label>
|
|
<Label>Port</Label>
|
|
<Label>Use TLS</Label>
|
|
</SMPPGatewaysOutboundInputGroup>
|
|
{smppGateways.map((g, i) => (
|
|
g.outbound?
|
|
(<SMPPGatewaysOutboundInputGroup key={i}>
|
|
<Input
|
|
name={`smppGatewaysIp[${i}]`}
|
|
id={`smppGatewaysIp[${i}]`}
|
|
value={g.ipv4}
|
|
onChange={e => updateSmppGateways(e, i, 'ipv4')}
|
|
placeholder={'1.2.3.4'}
|
|
invalid={g.invalidIp}
|
|
ref={ref => refSmppIp.current[i] = ref}
|
|
/>
|
|
<Input
|
|
width="5rem"
|
|
name={`smppGatewaysPort[${i}]`}
|
|
id={`smppGatewaysPort[${i}]`}
|
|
value={g.port}
|
|
onChange={e => updateSmppGateways(e, i, 'port')}
|
|
placeholder="2775"
|
|
invalid={g.invalidPort}
|
|
ref={ref => refSmppPort.current[i] = ref}
|
|
/>
|
|
<Switch
|
|
id={`tls[${i}]`}
|
|
label="TLS"
|
|
tooltip="Use TLS"
|
|
checked={g.use_tls === 1}
|
|
onChange={e => updateSmppGateways(e, i, 'use_tls')}
|
|
/>
|
|
{/* <Switch
|
|
id={`primary[${i}]`}
|
|
label="Primary"
|
|
tooltip="Is primary"
|
|
checked={g.is_primary === 1}
|
|
onChange={e => updateSmppGateways(e, i, 'is_primary')}
|
|
/> */}
|
|
<TrashButton
|
|
onClick={() => removeSmppGateway(i)}
|
|
ref={ref => refSmppTrash.current[i] = ref}
|
|
/>
|
|
</SMPPGatewaysOutboundInputGroup>)
|
|
:
|
|
null
|
|
))}
|
|
<div/>
|
|
<Button
|
|
square
|
|
type="button"
|
|
onClick={()=>addSmppGateway(false)}
|
|
ref={refAdd}
|
|
>
|
|
+
|
|
</Button>
|
|
</StyledForm>
|
|
</StyledSection>
|
|
<StyledSection>
|
|
<StyledLegend>Inbound SMPP</StyledLegend>
|
|
<StyledForm
|
|
large
|
|
>
|
|
<Label htmlFor="smpp_inbound_system_id">System ID</Label>
|
|
<Input
|
|
name="smpp_inbound_system_id"
|
|
id="smpp_inbound_system_id"
|
|
value={smpp_inbound_system_id}
|
|
onChange={e => setSmppInboundSystemId(e.target.value)}
|
|
placeholder="SMPP system id to authenticate with"
|
|
invalid={smpp_inbound_system_idInvalid}
|
|
ref={refSmppInboundSystemId}
|
|
/>
|
|
<Label htmlFor="smpp_inbound_password">Password</Label>
|
|
<PasswordInput
|
|
allowShowPassword
|
|
name="smpp_inbound_password"
|
|
id="smpp_inbound_password"
|
|
password={smpp_inbound_password}
|
|
setPassword={setSmppInboundPassword}
|
|
setErrorMessage={setErrorMessage}
|
|
placeholder="SMPP password for authenticating inbound messages"
|
|
invalid={smpp_inbound_passwordInvalid}
|
|
ref={refSmppInboundPassword}
|
|
/>
|
|
|
|
<div
|
|
style={{
|
|
whiteSpace: 'nowrap',
|
|
textAlign: 'left',
|
|
color: '#231f20',
|
|
width: '170px'
|
|
}}
|
|
>Carrier IP Address(es) to whitelist</div>
|
|
<div/>
|
|
<div/>
|
|
<SMPPGatewaysInboundInputGroup>
|
|
<Label>IP Address</Label>
|
|
<Label>Netmask</Label>
|
|
</SMPPGatewaysInboundInputGroup>
|
|
{smppGateways.map((g, i) => (
|
|
g.inbound?
|
|
(<SMPPGatewaysInboundInputGroup key={i}>
|
|
<Input
|
|
name={`smpppGatewaysIp[${i}]`}
|
|
id={`smpppGatewaysIp[${i}]`}
|
|
value={g.ipv4}
|
|
onChange={e => updateSmppGateways(e, i, 'ipv4')}
|
|
placeholder={'1.2.3.4'}
|
|
invalid={g.invalidIp}
|
|
ref={ref => refSmppIp.current[i] = ref}
|
|
/>
|
|
{/* <Input
|
|
width="5rem"
|
|
name={`smpppGatewaysPort[${i}]`}
|
|
id={`smpppGatewaysPort[${i}]`}
|
|
value={g.port}
|
|
onChange={e => updateSmppGateways(e, i, 'port')}
|
|
placeholder="2775"
|
|
invalid={g.invalidPort}
|
|
ref={ref => refSmppPort.current[i] = ref}
|
|
/> */}
|
|
<Select
|
|
name={`smppgatewaysNetmask[${i}]`}
|
|
id={`smppgatewaysNetmask[${i}]`}
|
|
value={smppGateways[i].netmask}
|
|
disabled={smppGateways[i].outbound}
|
|
onChange={e => updateSmppGateways(e, i, 'netmask')}
|
|
>
|
|
{Array.from(Array(32 + 1).keys()).slice(1).reverse().map((item) => (
|
|
<option value={item} key={item}>{item}</option>
|
|
))}
|
|
</Select>
|
|
<TrashButton
|
|
onClick={() => removeSmppGateway(i)}
|
|
ref={ref => refSmppTrash.current[i] = ref}
|
|
/>
|
|
<div></div>
|
|
</SMPPGatewaysInboundInputGroup>)
|
|
:
|
|
null
|
|
))}
|
|
<div/>
|
|
<Button
|
|
square
|
|
type="button"
|
|
onClick={()=>addSmppGateway(true)}
|
|
ref={refAdd}
|
|
>
|
|
+
|
|
</Button>
|
|
</StyledForm>
|
|
</StyledSection>
|
|
</TabPane>
|
|
</Tabs>
|
|
</Section>
|
|
|
|
{errorMessage && (
|
|
<Section>
|
|
<FormError grid message={errorMessage} />
|
|
</Section>
|
|
)}
|
|
|
|
<Section>
|
|
<StyledButtonGroup flexEnd spaced>
|
|
<Button
|
|
rounded="true"
|
|
gray
|
|
type="button"
|
|
onClick={() => {
|
|
history.push('/internal/carriers');
|
|
dispatch({
|
|
type: 'ADD',
|
|
level: 'info',
|
|
message: type === 'add' ? 'New carrier canceled' :'Changes canceled',
|
|
});
|
|
}}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
|
|
<Button rounded="true"
|
|
onClick={handleSubmit}
|
|
>
|
|
{type === 'add'
|
|
? 'Add Carrier'
|
|
: 'Save'
|
|
}
|
|
</Button>
|
|
</StyledButtonGroup>
|
|
</Section>
|
|
</>
|
|
)
|
|
);
|
|
};
|
|
|
|
export default CarrierForm;
|