diff --git a/ui/public/locales/en/translation.json b/ui/public/locales/en/translation.json index f7587be..ac25717 100644 --- a/ui/public/locales/en/translation.json +++ b/ui/public/locales/en/translation.json @@ -189,6 +189,8 @@ "description_kerberoshub": "Kerberos Agents can send heartbeats to a central", "description2_kerberoshub": "installation. Heartbeats and other relevant information are synced to Kerberos Hub to show realtime information about your video landscape.", "persistence": "Persistence", + "secondary_persistence": "Secondary Persistence", + "description_secondary_persistence": "Recordings will be sent to secondary persistence if the primary persistence is unavailable or fails. This can be useful for failover purposes.", "saasoffering": "Kerberos Hub (SAAS offering)", "description_persistence": "Having the ability to store your recordings is the beginning of everything. You can choose between our", "description2_persistence": ", or a 3rd party provider", diff --git a/ui/src/actions/agent.js b/ui/src/actions/agent.js index d933e62..71fc1c1 100644 --- a/ui/src/actions/agent.js +++ b/ui/src/actions/agent.js @@ -4,6 +4,7 @@ import { doVerifyOnvif, doVerifyHub, doVerifyPersistence, + doVerifySecondaryPersistence, doGetKerberosAgentTags, doGetDashboardInformation, doGetEvents, @@ -107,6 +108,28 @@ export const verifyPersistence = (config, onSuccess, onError) => { }; }; +export const verifySecondaryPersistence = (config, onSuccess, onError) => { + return (dispatch) => { + doVerifySecondaryPersistence( + config, + () => { + dispatch({ + type: 'VERIFY_SECONDARY_PERSISTENCE', + }); + if (onSuccess) { + onSuccess(); + } + }, + (error) => { + const { data } = error.response.data; + if (onError) { + onError(data); + } + } + ); + }; +}; + export const verifyHub = (config, onSuccess, onError) => { return (dispatch) => { doVerifyHub( diff --git a/ui/src/api/agent.js b/ui/src/api/agent.js index 369fb24..f6b1377 100644 --- a/ui/src/api/agent.js +++ b/ui/src/api/agent.js @@ -72,6 +72,25 @@ export function doVerifyPersistence(config, onSuccess, onError) { }); } +export function doVerifySecondaryPersistence(config, onSuccess, onError) { + const endpoint = API.post(`persistence/secondary/verify`, { + ...config, + }); + endpoint + .then((res) => { + if (res.status !== 200) { + throw new Error(res.data); + } + return res.data; + }) + .then((data) => { + onSuccess(data); + }) + .catch((error) => { + onError(error); + }); +} + export function doVerifyHub(config, onSuccess, onError) { const endpoint = API.post(`hub/verify`, { ...config, diff --git a/ui/src/pages/Settings/Settings.jsx b/ui/src/pages/Settings/Settings.jsx index d0e181d..cbbf410 100644 --- a/ui/src/pages/Settings/Settings.jsx +++ b/ui/src/pages/Settings/Settings.jsx @@ -33,6 +33,7 @@ import { verifyCamera, verifyHub, verifyPersistence, + verifySecondaryPersistence, getConfig, updateConfig, } from '../../actions/agent'; @@ -125,6 +126,8 @@ class Settings extends React.Component { this.onUpdateTimeline = this.onUpdateTimeline.bind(this); this.initialiseLiveview = this.initialiseLiveview.bind(this); this.verifyPersistenceSettings = this.verifyPersistenceSettings.bind(this); + this.verifySecondaryPersistenceSettings = + this.verifySecondaryPersistenceSettings.bind(this); this.verifyHubSettings = this.verifyHubSettings.bind(this); this.verifyCameraSettings = this.verifyCameraSettings.bind(this); this.verifySubCameraSettings = this.verifySubCameraSettings.bind(this); @@ -390,6 +393,8 @@ class Settings extends React.Component { configError: false, verifyPersistenceSuccess: false, verifyPersistenceError: false, + verifySecondaryPersistenceSuccess: false, + verifySecondaryPersistenceError: false, verifyHubSuccess: false, verifyHubError: false, verifyHubErrorMessage: '', @@ -441,6 +446,8 @@ class Settings extends React.Component { verifyHubError: false, verifyPersistenceSuccess: false, verifyPersistenceError: false, + verifySecondaryPersistenceSuccess: false, + verifySecondaryPersistenceError: false, persistenceSuccess: false, persistenceError: false, verifyCameraSuccess: false, @@ -477,6 +484,54 @@ class Settings extends React.Component { } } + verifySecondaryPersistenceSettings() { + const { config, dispatchVerifySecondaryPersistence } = this.props; + if (config) { + this.setState({ + configSuccess: false, + configError: false, + verifyHubSuccess: false, + verifyHubError: false, + verifyPersistenceSuccess: false, + verifyPersistenceError: false, + verifySecondaryPersistenceSuccess: false, + verifySecondaryPersistenceError: false, + persistenceSuccess: false, + persistenceError: false, + verifyCameraSuccess: false, + verifyCameraError: false, + verifyOnvifSuccess: false, + verifyOnvifError: false, + verifyCameraErrorMessage: '', + loading: true, + }); + + dispatchVerifySecondaryPersistence( + config.config, + () => { + this.setState({ + verifySecondaryPersistenceSuccess: true, + verifySecondaryPersistenceError: false, + verifySecondaryPersistenceMessage: '', + persistenceSuccess: false, + persistenceError: false, + loading: false, + }); + }, + (error) => { + this.setState({ + verifySecondaryPersistenceSuccess: false, + verifySecondaryPersistenceError: true, + verifySecondaryPersistenceMessage: error, + persistenceSuccess: false, + persistenceError: false, + loading: false, + }); + } + ); + } + } + verifySubCameraSettings(event) { this.verifyCameraSettings(event, 'secondary'); } @@ -2516,19 +2571,28 @@ class Settings extends React.Component {

{t('settings.persistence.secondary_persistence')}

+

+ {t( + 'settings.persistence.description_secondary_persistence' + )} +

this.onUpdateField( - 'kstorage', + 'kstorage_secondary', 'uri', value, - config.kstorage + config.kstorage_secondary ) } /> @@ -2538,13 +2602,17 @@ class Settings extends React.Component { placeholder={t( 'settings.persistence.kerberosvault_description_provider' )} - value={config.kstorage ? config.kstorage.provider : ''} + value={ + config.kstorage_secondary + ? config.kstorage_secondary.provider + : '' + } onChange={(value) => this.onUpdateField( - 'kstorage', + 'kstorage_secondary', 'provider', value, - config.kstorage + config.kstorage_secondary ) } /> @@ -2554,13 +2622,17 @@ class Settings extends React.Component { placeholder={t( 'settings.persistence.kerberosvault_description_directory' )} - value={config.kstorage ? config.kstorage.directory : ''} + value={ + config.kstorage_secondary + ? config.kstorage_secondary.directory + : '' + } onChange={(value) => this.onUpdateField( - 'kstorage', + 'kstorage_secondary', 'directory', value, - config.kstorage + config.kstorage_secondary ) } /> @@ -2571,13 +2643,17 @@ class Settings extends React.Component { placeholder={t( 'settings.persistence.kerberosvault_description_accesskey' )} - value={config.kstorage ? config.kstorage.access_key : ''} + value={ + config.kstorage_secondary + ? config.kstorage_secondary.access_key + : '' + } onChange={(value) => this.onUpdateField( - 'kstorage', + 'kstorage_secondary', 'access_key', value, - config.kstorage + config.kstorage_secondary ) } /> @@ -2589,14 +2665,16 @@ class Settings extends React.Component { 'settings.persistence.kerberosvault_description_secretkey' )} value={ - config.kstorage ? config.kstorage.secret_access_key : '' + config.kstorage_secondary + ? config.kstorage_secondary.secret_access_key + : '' } onChange={(value) => this.onUpdateField( - 'kstorage', + 'kstorage_secondary', 'secret_access_key', value, - config.kstorage + config.kstorage_secondary ) } /> @@ -2605,7 +2683,7 @@ class Settings extends React.Component {