@@ -746,7 +506,7 @@ const FirewallPage = () => {
'jetpack-protect'
) }
- { ( formState.jetpack_waf_ip_allow_list_enabled || ipAllowListHasContent ) && (
+ { ( jetpackWafIpAllowListEnabled || ipAllowListHasContent ) && (
) }
- { formState.jetpack_waf_ip_allow_list_enabled && (
+ { jetpackWafIpAllowListEnabled && (
@@ -783,15 +543,15 @@ const FirewallPage = () => {
variant={ 'secondary' }
size={ 'small' }
onClick={ addCurrentIpToAllowList }
- disabled={ ! canEditIpAllowList || isCurrentIpAllowed || formIsSubmitting }
+ disabled={ ! canEditIpAllowList || isCurrentIpAllowed || isUpdating }
>
{ __( '+ Add to Allow List', 'jetpack-protect' ) }
diff --git a/projects/plugins/protect/src/js/routes/scan/history/index.jsx b/projects/plugins/protect/src/js/routes/scan/history/index.jsx
index e30dc0c4c65bc..1c0014983d296 100644
--- a/projects/plugins/protect/src/js/routes/scan/history/index.jsx
+++ b/projects/plugins/protect/src/js/routes/scan/history/index.jsx
@@ -10,6 +10,7 @@ import ThreatsNavigation from '../../../components/threats-list/navigation';
import PaidList from '../../../components/threats-list/paid-list';
import useThreatsList from '../../../components/threats-list/use-threats-list';
import useAnalyticsTracks from '../../../hooks/use-analytics-tracks';
+import usePlan from '../../../hooks/use-plan';
import useProtectData from '../../../hooks/use-protect-data';
import ScanSectionHeader from '../scan-section-header';
import StatusFilters from './status-filters';
@@ -19,6 +20,7 @@ const ScanHistoryRoute = () => {
// Track page view.
useAnalyticsTracks( { pageViewEventName: 'protect_scan_history' } );
+ const { hasPlan } = usePlan();
const { filter = 'all' } = useParams();
const { item, list, selected, setSelected } = useThreatsList( {
@@ -26,7 +28,7 @@ const ScanHistoryRoute = () => {
status: filter,
} );
- const { counts, error, hasRequiredPlan } = useProtectData( {
+ const { counts, error } = useProtectData( {
sourceType: 'history',
filter: { status: filter },
} );
@@ -228,7 +230,7 @@ const ScanHistoryRoute = () => {
}, [ selected, list.length, filter, item?.name, item?.version ] );
// Threat history is only available for paid plans.
- if ( ! hasRequiredPlan ) {
+ if ( ! hasPlan ) {
return
;
}
diff --git a/projects/plugins/protect/src/js/routes/scan/index.jsx b/projects/plugins/protect/src/js/routes/scan/index.jsx
index b848b6cb2dc6c..ff93ac3b42353 100644
--- a/projects/plugins/protect/src/js/routes/scan/index.jsx
+++ b/projects/plugins/protect/src/js/routes/scan/index.jsx
@@ -1,9 +1,8 @@
import { AdminSectionHero, Container, Col, H3, Text } from '@automattic/jetpack-components';
import { useConnectionErrorNotice, ConnectionError } from '@automattic/jetpack-connection';
import { Spinner } from '@wordpress/components';
-import { useSelect, useDispatch } from '@wordpress/data';
import { __, sprintf } from '@wordpress/i18n';
-import React, { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
import inProgressImage from '../../../../assets/images/in-progress.png';
import AdminPage from '../../components/admin-page';
import ErrorScreen from '../../components/error-section';
@@ -12,17 +11,15 @@ import ScanFooter from '../../components/scan-footer';
import SeventyFiveLayout from '../../components/seventy-five-layout';
import Summary from '../../components/summary';
import ThreatsList from '../../components/threats-list';
-import { SCAN_STATUS_UNAVAILABLE } from '../../constants';
+import useScanStatusQuery, { isScanInProgress } from '../../data/scan/use-scan-status-query';
import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
import { OnboardingContext } from '../../hooks/use-onboarding';
+import usePlan from '../../hooks/use-plan';
import useProtectData from '../../hooks/use-protect-data';
import useWafData from '../../hooks/use-waf-data';
-import { STORE_ID } from '../../state/store';
import onboardingSteps from './onboarding-steps';
import ScanSectionHeader from './scan-section-header';
import styles from './styles.module.scss';
-import useCredentials from './use-credentials';
-import useStatusPolling from './use-status-polling';
const ConnectionErrorCol = () => {
const { hasConnectionError } = useConnectionErrorNotice();
@@ -73,7 +70,7 @@ const ErrorSection = ( { errorMessage, errorCode } ) => {
};
const ScanningSection = ( { currentProgress } ) => {
- const { hasRequiredPlan } = useProtectData();
+ const { hasPlan } = usePlan();
const { globalStats } = useWafData();
const totalVulnerabilities = parseInt( globalStats?.totalVulnerabilities );
const totalVulnerabilitiesFormatted = isNaN( totalVulnerabilities )
@@ -91,7 +88,7 @@ const ScanningSection = ( { currentProgress } ) => {
-
+
{
{ __( 'Your results will be ready soon', 'jetpack-protect' ) }
- { hasRequiredPlan && currentProgress !== null && currentProgress >= 0 && (
-
- ) }
+ { hasPlan && }
{ sprintf(
// translators: placeholder is the number of total vulnerabilities i.e. "22,000".
@@ -129,6 +124,7 @@ const ScanningSection = ( { currentProgress } ) => {
}
preserveSecondaryOnMobile={ false }
+ fluid={ true }
/>
@@ -153,20 +149,12 @@ const DefaultSection = () => {
};
const ScanPage = () => {
- const { lastChecked, hasRequiredPlan } = useProtectData();
- const { refreshStatus } = useDispatch( STORE_ID );
- const { scanInProgress, statusIsFetching, scanIsUnavailable, status, scanError } = useSelect(
- select => ( {
- scanError: select( STORE_ID ).scanError(),
- scanInProgress: select( STORE_ID ).scanInProgress(),
- scanIsUnavailable: select( STORE_ID ).getScanIsUnavailable(),
- status: select( STORE_ID ).getStatus(),
- statusIsFetching: select( STORE_ID ).getStatusIsFetching(),
- } )
- );
+ const { hasPlan } = usePlan();
+ const { lastChecked } = useProtectData();
+ const { data: status } = useScanStatusQuery( { usePolling: true } );
let currentScanStatus;
- if ( scanError ) {
+ if ( status.error ) {
currentScanStatus = 'error';
} else if ( ! lastChecked ) {
currentScanStatus = 'in_progress';
@@ -179,31 +167,21 @@ const ScanPage = () => {
pageViewEventName: 'protect_admin',
pageViewEventProperties: {
check_status: currentScanStatus,
- has_plan: hasRequiredPlan,
+ has_plan: hasPlan,
},
} );
- useStatusPolling();
- useCredentials();
-
- // retry fetching status if it is not available
- useEffect( () => {
- if ( ! statusIsFetching && SCAN_STATUS_UNAVAILABLE === status.status && ! scanIsUnavailable ) {
- refreshStatus( true );
- }
- }, [ statusIsFetching, status.status, refreshStatus, scanIsUnavailable ] );
-
const renderSection = useMemo( () => {
- if ( scanInProgress ) {
- return
;
+ if ( isScanInProgress( status ) ) {
+ return
;
}
- if ( scanError ) {
- return
;
+ if ( status.error ) {
+ return
;
}
return
;
- }, [ scanInProgress, status.currentProgress, scanError ] );
+ }, [ status ] );
return (
diff --git a/projects/plugins/protect/src/js/routes/scan/onboarding-steps.jsx b/projects/plugins/protect/src/js/routes/scan/onboarding-steps.jsx
index 9e113d104679c..0e85aa56d9289 100644
--- a/projects/plugins/protect/src/js/routes/scan/onboarding-steps.jsx
+++ b/projects/plugins/protect/src/js/routes/scan/onboarding-steps.jsx
@@ -1,11 +1,10 @@
import { Button, Text, getRedirectUrl } from '@automattic/jetpack-components';
-import { useProductCheckoutWorkflow } from '@automattic/jetpack-connection';
-import { createInterpolateElement } from '@wordpress/element';
+import { createInterpolateElement, useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
-import { JETPACK_SCAN_SLUG } from '../../constants';
import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
+import usePlan from '../../hooks/use-plan';
-const { adminUrl, siteSuffix } = window.jetpackProtectInitialState;
+const { siteSuffix } = window.jetpackProtectInitialState;
const scanResultsTitle = __( 'Your scan results', 'jetpack-protect' );
const scanResultsDescription = (
@@ -17,12 +16,12 @@ const scanResultsDescription = (
);
const UpgradeButton = props => {
- const { run } = useProductCheckoutWorkflow( {
- productSlug: JETPACK_SCAN_SLUG,
- redirectUrl: adminUrl,
- } );
- const { recordEventHandler } = useAnalyticsTracks();
- const getScan = recordEventHandler( 'jetpack_protect_onboarding_get_scan_link_click', run );
+ const { upgradePlan } = usePlan();
+ const { recordEvent } = useAnalyticsTracks();
+ const getScan = useCallback( () => {
+ recordEvent( 'jetpack_protect_onboarding_get_scan_link_click' );
+ upgradePlan();
+ }, [ recordEvent, upgradePlan ] );
return ;
};
diff --git a/projects/plugins/protect/src/js/routes/scan/scan-section-header.tsx b/projects/plugins/protect/src/js/routes/scan/scan-section-header.tsx
index d91bf435f1720..d1196f9bb051b 100644
--- a/projects/plugins/protect/src/js/routes/scan/scan-section-header.tsx
+++ b/projects/plugins/protect/src/js/routes/scan/scan-section-header.tsx
@@ -1,6 +1,6 @@
import { Container, Col, Text, Title, getIconBySlug } from '@automattic/jetpack-components';
import React from 'react';
-import useProtectData from '../../hooks/use-protect-data';
+import usePlan from '../../hooks/use-plan';
import ScanSectionNavigation from './scan-section-navigation';
import styles from './styles.module.scss';
@@ -12,7 +12,7 @@ type Props = {
const ScanSectionHeader = ( { title, subtitle, controls }: Props ) => {
const Icon = getIconBySlug( 'protect' );
- const { hasRequiredPlan } = useProtectData();
+ const { hasPlan } = usePlan();
return (
@@ -34,7 +34,7 @@ const ScanSectionHeader = ( { title, subtitle, controls }: Props ) => {
{ title }
) }
- { !! hasRequiredPlan && }
+ { !! hasPlan && }
{ controls }
diff --git a/projects/plugins/protect/src/js/routes/scan/use-credentials.js b/projects/plugins/protect/src/js/routes/scan/use-credentials.js
deleted file mode 100644
index 77f787153f8e2..0000000000000
--- a/projects/plugins/protect/src/js/routes/scan/use-credentials.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { useDispatch, useSelect } from '@wordpress/data';
-import { useEffect } from 'react';
-import { STORE_ID } from '../../state/store';
-
-const useCredentials = () => {
- const { checkCredentials } = useDispatch( STORE_ID );
- const credentials = useSelect( select => select( STORE_ID ).getCredentials() );
-
- useEffect( () => {
- if ( ! credentials ) {
- checkCredentials();
- }
- }, [ checkCredentials, credentials ] );
-};
-
-export default useCredentials;
diff --git a/projects/plugins/protect/src/js/routes/scan/use-status-polling.js b/projects/plugins/protect/src/js/routes/scan/use-status-polling.js
deleted file mode 100644
index ad662bc0ca4b4..0000000000000
--- a/projects/plugins/protect/src/js/routes/scan/use-status-polling.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import apiFetch from '@wordpress/api-fetch';
-import { useDispatch, useSelect } from '@wordpress/data';
-import camelize from 'camelize';
-import { useEffect } from 'react';
-import {
- SCAN_IN_PROGRESS_STATUSES,
- SCAN_STATUS_IDLE,
- SCAN_STATUS_UNAVAILABLE,
-} from '../../constants';
-import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
-import { STORE_ID } from '../../state/store';
-
-/**
- * Use Status Polling
- *
- * When the status is 'scheduled' or 'scanning', re-checks the status periodically until it isn't.
- */
-const useStatusPolling = () => {
- const { recordEvent } = useAnalyticsTracks();
- const status = useSelect( select => select( STORE_ID ).getStatus() );
- const { setStatus, setStatusProgress, setStatusIsFetching, setScanIsUnavailable } =
- useDispatch( STORE_ID );
- useEffect( () => {
- let pollTimeout;
- const pollDuration = 10000;
-
- const statusIsInProgress = currentStatus =>
- SCAN_IN_PROGRESS_STATUSES.indexOf( currentStatus ) >= 0;
-
- // if there has never been a scan, and the scan status is idle, then we must still be getting set up
- const scanIsInitializing = ( currentStatus, lastChecked ) =>
- ! lastChecked && SCAN_STATUS_IDLE === currentStatus;
-
- const pollStatus = () => {
- return new Promise( ( resolve, reject ) => {
- apiFetch( {
- path: 'jetpack-protect/v1/status?hard_refresh=true',
- method: 'GET',
- } )
- .then( newStatus => {
- if ( newStatus?.error ) {
- throw newStatus?.error_message;
- }
-
- if (
- statusIsInProgress( newStatus?.status ) ||
- scanIsInitializing( newStatus?.status, newStatus?.last_checked )
- ) {
- setStatusProgress( newStatus?.current_progress );
- pollTimeout = setTimeout( () => {
- pollStatus()
- .then( result => resolve( result ) )
- .catch( error => reject( error ) );
- }, pollDuration );
- return;
- }
-
- resolve( newStatus );
- } )
- .catch( () => {
- // Keep trying when unable to fetch the status.
- setTimeout( () => {
- pollStatus()
- .then( result => resolve( result ) )
- .catch( error => reject( error ) );
- }, 5000 );
- } );
- } );
- };
-
- if (
- ! statusIsInProgress( status?.status ) &&
- ! scanIsInitializing( status?.status, status?.lastChecked )
- ) {
- return;
- }
-
- pollTimeout = setTimeout( () => {
- setStatusIsFetching( true );
- pollStatus()
- .then( newStatus => {
- setScanIsUnavailable( SCAN_STATUS_UNAVAILABLE === newStatus.status );
- setStatus( camelize( newStatus ) );
- recordEvent( 'jetpack_protect_scan_completed', {
- scan_status: newStatus.status,
- } );
- } )
- .finally( () => {
- setStatusIsFetching( false );
- } );
- }, pollDuration );
-
- return () => clearTimeout( pollTimeout );
- }, [
- status?.status,
- status?.lastChecked,
- setScanIsUnavailable,
- setStatus,
- setStatusProgress,
- setStatusIsFetching,
- recordEvent,
- ] );
-};
-
-export default useStatusPolling;
diff --git a/projects/plugins/protect/src/js/components/interstitial-page/index.jsx b/projects/plugins/protect/src/js/routes/setup/index.jsx
similarity index 70%
rename from projects/plugins/protect/src/js/components/interstitial-page/index.jsx
rename to projects/plugins/protect/src/js/routes/setup/index.jsx
index f51f9354550e7..3e82bf582e41c 100644
--- a/projects/plugins/protect/src/js/components/interstitial-page/index.jsx
+++ b/projects/plugins/protect/src/js/routes/setup/index.jsx
@@ -7,22 +7,14 @@ import {
} from '@automattic/jetpack-components';
import { createInterpolateElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
-import React from 'react';
+import Logo from '../../components/logo';
+import ConnectedPricingTable from '../../components/pricing-table';
import useAnalyticsTracks from '../../hooks/use-analytics-tracks';
-import Logo from '../logo';
-import ConnectedPricingTable from '../pricing-table';
import styles from './styles.module.scss';
const ACTIVATE_LICENSE_URL = 'admin.php?page=my-jetpack#/add-license';
-/**
- * Interstitial Page
- *
- * @param {object} props - Component props
- * @param {Function} props.onScanAdd - Callback when adding paid protect product successfully
- * @return {React.Component} Interstitial react component.
- */
-const InterstitialPage = ( { onScanAdd } ) => {
+const SetupRoute = () => {
// Track view for Protect WAF page.
useAnalyticsTracks( {
pageViewEventName: 'protect_interstitial',
@@ -51,7 +43,7 @@ const InterstitialPage = ( { onScanAdd } ) => {
-
+
@@ -59,4 +51,4 @@ const InterstitialPage = ( { onScanAdd } ) => {
);
};
-export default InterstitialPage;
+export default SetupRoute;
diff --git a/projects/plugins/protect/src/js/components/interstitial-page/stories/broken/index.stories.jsx b/projects/plugins/protect/src/js/routes/setup/stories/broken/index.stories.jsx
similarity index 100%
rename from projects/plugins/protect/src/js/components/interstitial-page/stories/broken/index.stories.jsx
rename to projects/plugins/protect/src/js/routes/setup/stories/broken/index.stories.jsx
diff --git a/projects/plugins/protect/src/js/components/interstitial-page/stories/broken/mock.js b/projects/plugins/protect/src/js/routes/setup/stories/broken/mock.js
similarity index 100%
rename from projects/plugins/protect/src/js/components/interstitial-page/stories/broken/mock.js
rename to projects/plugins/protect/src/js/routes/setup/stories/broken/mock.js
diff --git a/projects/plugins/protect/src/js/components/interstitial-page/styles.module.scss b/projects/plugins/protect/src/js/routes/setup/styles.module.scss
similarity index 100%
rename from projects/plugins/protect/src/js/components/interstitial-page/styles.module.scss
rename to projects/plugins/protect/src/js/routes/setup/styles.module.scss
diff --git a/projects/plugins/protect/src/js/state/actions.js b/projects/plugins/protect/src/js/state/actions.js
deleted file mode 100644
index 6a56452681951..0000000000000
--- a/projects/plugins/protect/src/js/state/actions.js
+++ /dev/null
@@ -1,544 +0,0 @@
-import apiFetch from '@wordpress/api-fetch';
-import { sprintf, _n, __ } from '@wordpress/i18n';
-import camelize from 'camelize';
-import API from '../api';
-import { SCAN_STATUS_UNAVAILABLE } from '../constants';
-
-const SET_CREDENTIALS_STATE_IS_FETCHING = 'SET_CREDENTIALS_STATE_IS_FETCHING';
-const SET_CREDENTIALS_STATE = 'SET_CREDENTIALS_STATE';
-const SET_SCAN_HISTORY = 'SET_SCAN_HISTORY';
-const SET_STATUS = 'SET_STATUS';
-const SET_STATUS_PROGRESS = 'SET_STATUS_PROGRESS';
-const START_SCAN_OPTIMISTICALLY = 'START_SCAN_OPTIMISTICALLY';
-const SET_STATUS_IS_FETCHING = 'SET_STATUS_IS_FETCHING';
-const SET_SCAN_IS_UNAVAILABLE = 'SET_SCAN_IS_UNAVAILABLE';
-const SET_SCAN_IS_ENQUEUING = 'SET_SCAN_IS_ENQUEUING';
-const SET_INSTALLED_PLUGINS = 'SET_INSTALLED_PLUGINS';
-const SET_INSTALLED_THEMES = 'SET_INSTALLED_THEMES';
-const SET_WP_VERSION = 'SET_WP_VERSION';
-const SET_JETPACK_SCAN = 'SET_JETPACK_SCAN';
-const SET_PRODUCT_DATA = 'SET_PRODUCT_DATA';
-const SET_THREAT_IS_UPDATING = 'SET_THREAT_IS_UPDATING';
-const SET_THREATS_ARE_FIXING = 'SET_THREATS_ARE_FIXING';
-const SET_MODAL = 'SET_MODAL';
-const SET_NOTICE = 'SET_NOTICE';
-const CLEAR_NOTICE = 'CLEAR_NOTICE';
-const SET_HAS_REQUIRED_PLAN = 'SET_HAS_REQUIRED_PLAN';
-const SET_ONBOARDING_PROGRESS = 'SET_ONBOARDING_PROGRESS';
-
-const SET_WAF_IS_SEEN = 'SET_WAF_IS_SEEN';
-const SET_WAF_UPGRADE_IS_SEEN = 'SET_WAF_UPGRADE_IS_SEEN';
-const SET_WAF_IS_ENABLED = 'SET_WAF_IS_ENABLED';
-const SET_WAF_IS_UPDATING = 'SET_WAF_IS_UPDATING';
-const SET_WAF_IS_TOGGLING = 'SET_WAF_IS_TOGGLING';
-const SET_WAF_CONFIG = 'SET_WAF_CONFIG';
-const SET_WAF_STATS = 'SET_WAF_STATS';
-
-const setScanHistory = scanHistory => {
- return { type: SET_SCAN_HISTORY, scanHistory };
-};
-
-const setStatus = status => {
- return { type: SET_STATUS, status };
-};
-
-const setStatusProgress = currentProgress => {
- return { type: SET_STATUS_PROGRESS, currentProgress };
-};
-
-const startScanOptimistically = () => {
- return { type: START_SCAN_OPTIMISTICALLY };
-};
-
-const refreshPlan =
- () =>
- ( { dispatch } ) => {
- apiFetch( {
- path: 'jetpack-protect/v1/check-plan',
- method: 'GET',
- } ).then( hasRequiredPlan => dispatch( setHasRequiredPlan( hasRequiredPlan ) ) );
- };
-
-/**
- * Fetch Status
- *
- * @param {boolean} hardRefresh - Clears the status cache before fetching, when enabled.
- * @return {Promise} - Promise which resolves with the status request results.
- */
-const fetchStatus = hardRefresh =>
- apiFetch( {
- path: `jetpack-protect/v1/status${ hardRefresh ? '?hard_refresh=true' : '' }`,
- method: 'GET',
- } );
-
-/**
- * Side effect action which will fetch the status from the server
- *
- * @param {boolean} hardRefresh - Clears the status cache before fetching, when enabled.
- * @return {Promise} - Promise which resolves when the status is refreshed from an API fetch.
- */
-const refreshStatus =
- ( hardRefresh = false ) =>
- async ( { dispatch } ) => {
- dispatch( setStatusIsFetching( true ) );
- return await new Promise( ( resolve, reject ) => {
- return fetchStatus( hardRefresh )
- .then( checkStatus )
- .then( status => {
- dispatch( setScanIsUnavailable( SCAN_STATUS_UNAVAILABLE === status.status ) );
- dispatch( setStatus( camelize( status ) ) );
- resolve( status );
- } )
- .catch( error => {
- reject( error );
- } )
- .finally( () => {
- dispatch( setStatusIsFetching( false ) );
- } );
- } );
- };
-
-/**
- * Refresh Scan History
- * @return {Promise} - Promise which resolves with the scan history once it has been fetched.
- */
-const refreshScanHistory = () => {
- return async ( { dispatch } ) => {
- return API.fetchScanHistory()
- .then( scanHistory => camelize( scanHistory ) )
- .then( scanHistory => {
- dispatch( setScanHistory( scanHistory ) );
- } );
- };
-};
-
-/**
- * Check Status
- *
- * @param {object} currentStatus - The status.
- * @param {number} attempts - The amount of recursive attempts that have already been made.
- * @return {Promise} - Promise which resolves with the status once it has been checked.
- */
-const checkStatus = ( currentStatus, attempts = 0 ) => {
- return new Promise( ( resolve, reject ) => {
- if ( SCAN_STATUS_UNAVAILABLE === currentStatus.status && attempts < 3 ) {
- fetchStatus( true )
- .then( newStatus => {
- setTimeout( () => {
- checkStatus( newStatus, attempts + 1 )
- .then( result => resolve( result ) )
- .catch( error => reject( error ) );
- }, 5000 );
- } )
- .catch( reject );
- } else {
- resolve( currentStatus );
- }
- } );
-};
-
-/**
- * Side effect action which will fetch the credential status from the server
- *
- * @return {Promise} - Promise which resolves when the status is refreshed from an API fetch.
- */
-const checkCredentials =
- () =>
- async ( { dispatch } ) => {
- return await new Promise( ( resolve, reject ) => {
- dispatch( setCredentialsIsFetching( true ) );
- return apiFetch( {
- path: 'jetpack-protect/v1/check-credentials',
- method: 'POST',
- } )
- .then( credentials => {
- dispatch( setCredentials( credentials ) );
- resolve( credentials );
- } )
- .catch( error => {
- reject( error );
- } )
- .finally( () => {
- dispatch( setCredentialsIsFetching( false ) );
- } );
- } );
- };
-
-const setCredentialsIsFetching = isFetching => {
- return { type: SET_CREDENTIALS_STATE_IS_FETCHING, isFetching };
-};
-
-const setCredentials = credentials => {
- return { type: SET_CREDENTIALS_STATE, credentials };
-};
-
-const setStatusIsFetching = status => {
- return { type: SET_STATUS_IS_FETCHING, status };
-};
-
-const setScanIsUnavailable = status => {
- return { type: SET_SCAN_IS_UNAVAILABLE, status };
-};
-
-const setScanIsEnqueuing = isEnqueuing => {
- return { type: SET_SCAN_IS_ENQUEUING, isEnqueuing };
-};
-
-const setInstalledPlugins = plugins => {
- return { type: SET_INSTALLED_PLUGINS, plugins };
-};
-
-const setInstalledThemes = themes => {
- return { type: SET_INSTALLED_THEMES, themes };
-};
-
-const setwpVersion = version => {
- return { type: SET_WP_VERSION, version };
-};
-
-const setJetpackScan = scan => {
- return { type: SET_JETPACK_SCAN, scan };
-};
-
-const setThreatIsUpdating = ( threatId, isUpdating ) => {
- return { type: SET_THREAT_IS_UPDATING, payload: { threatId, isUpdating } };
-};
-
-const setThreatsAreFixing = threatIds => {
- return { type: SET_THREATS_ARE_FIXING, threatIds };
-};
-
-const ignoreThreat =
- ( threatId, callback = () => {} ) =>
- async ( { dispatch } ) => {
- dispatch( setThreatIsUpdating( threatId, true ) );
- return await new Promise( () => {
- return apiFetch( {
- path: `jetpack-protect/v1/ignore-threat?threat_id=${ threatId }`,
- method: 'POST',
- } )
- .then( () => {
- return dispatch( refreshStatus() );
- } )
- .then( () => {
- return dispatch( refreshScanHistory() );
- } )
- .then( () => {
- return dispatch(
- setNotice( { type: 'success', message: __( 'Threat ignored', 'jetpack-protect' ) } )
- );
- } )
- .catch( () => {
- return dispatch(
- setNotice( {
- type: 'error',
- message: __( 'An error ocurred ignoring the threat.', 'jetpack-protect' ),
- } )
- );
- } )
- .finally( () => {
- dispatch( setThreatIsUpdating( threatId, false ) );
- callback();
- } );
- } );
- };
-
-const unignoreThreat =
- ( threatId, callback = () => {} ) =>
- async ( { dispatch } ) => {
- dispatch( setThreatIsUpdating( threatId, true ) );
- return await new Promise( () => {
- return apiFetch( {
- path: `jetpack-protect/v1/unignore-threat?threat_id=${ threatId }`,
- method: 'POST',
- } )
- .then( () => {
- return dispatch( refreshScanHistory() );
- } )
- .then( () => {
- return dispatch( refreshStatus() );
- } )
- .then( () => {
- return dispatch(
- setNotice( { type: 'success', message: __( 'Threat unignored', 'jetpack-protect' ) } )
- );
- } )
- .catch( () => {
- return dispatch(
- setNotice( {
- type: 'error',
- message: __( 'An error ocurred unignoring the threat.', 'jetpack-protect' ),
- } )
- );
- } )
- .finally( () => {
- dispatch( setThreatIsUpdating( threatId, false ) );
- callback();
- } );
- } );
- };
-
-const getFixThreatsStatus =
- threatIds =>
- async ( { dispatch } ) => {
- const path = threatIds.reduce( ( carryPath, threatId ) => {
- return `${ carryPath }threat_ids[]=${ threatId }&`;
- }, 'jetpack-protect/v1/fix-threats-status?' );
-
- dispatch( setThreatsAreFixing( threatIds ) );
-
- return await apiFetch( {
- path,
- method: 'GET',
- } )
- .then( async response => {
- const threatArray = Object.values( response.threats );
- const inProgressThreats = threatArray.filter( threat => 'in_progress' === threat.status );
-
- if ( inProgressThreats.length > 0 ) {
- // fix still in progress - try again in another second
- return await new Promise( () => {
- setTimeout( () => {
- dispatch( getFixThreatsStatus( threatIds ) );
- }, 1000 );
- } );
- }
-
- // throw an error if not all threats were fixed
- const fixedThreats = threatArray.filter( threat => threat.status === 'fixed' );
- if ( ! fixedThreats.length === threatIds.length ) {
- throw 'Not all threats could be fixed.';
- }
- } )
- .then( () => {
- // threats fixed - refresh the status
- dispatch( refreshStatus() );
- dispatch( refreshScanHistory() );
- dispatch(
- setNotice( {
- type: 'success',
- message: sprintf(
- // translators: placeholder is the number amount of fixed threats.
- _n(
- '%s threat was fixed successfully',
- '%s threats were fixed successfully',
- threatIds.length,
- 'jetpack-protect'
- ),
- threatIds.length
- ),
- } )
- );
- } )
- .catch( () => {
- dispatch(
- setNotice( {
- type: 'error',
- message: __(
- 'Not all threats could be fixed. Please contact our support.',
- 'jetpack-protect'
- ),
- } )
- );
- } )
- .finally( () => {
- dispatch( setThreatsAreFixing( [] ) );
- } );
- };
-
-const fixThreats =
- ( threatIds, callback = () => {} ) =>
- async ( { dispatch } ) => {
- threatIds.forEach( threatId => {
- dispatch( setThreatIsUpdating( threatId, true ) );
- } );
- return await new Promise( () => {
- return apiFetch( {
- path: `jetpack-protect/v1/fix-threats?threat_ids=${ threatIds }`,
- method: 'POST',
- data: { threatIds },
- } )
- .then( () => {
- return dispatch(
- setNotice( {
- type: 'success',
- message: __(
- "We're hard at work fixing this threat in the background. Please check back shortly.",
- 'jetpack-protect'
- ),
- } )
- );
- } )
- .then( () => {
- // wait one second, then start checking if the threats have been fixed
- setTimeout( () => dispatch( getFixThreatsStatus( threatIds ) ), 1000 );
- } )
- .catch( () => {
- return dispatch(
- setNotice( {
- type: 'error',
- message: __( 'Error fixing threats. Please contact support.', 'jetpack-protect' ),
- } )
- );
- } )
- .finally( () => {
- threatIds.forEach( threatId => {
- dispatch( setThreatIsUpdating( threatId, false ) );
- } );
- callback();
- } );
- } );
- };
-
-const scan =
- ( callback = () => {} ) =>
- async ( { dispatch } ) => {
- dispatch( setScanIsEnqueuing( true ) );
- return await new Promise( () => {
- return apiFetch( {
- path: `jetpack-protect/v1/scan`,
- method: 'POST',
- } )
- .then( () => {
- dispatch( startScanOptimistically() );
- setTimeout( () => dispatch( refreshStatus( true ) ), 5000 );
- } )
- .catch( () => {
- return dispatch(
- setNotice( {
- type: 'error',
- message: __( 'An error ocurred enqueuing the scan', 'jetpack-protect' ),
- } )
- );
- } )
- .finally( () => {
- dispatch( setScanIsEnqueuing( false ) );
- callback();
- } );
- } );
- };
-
-/**
- * Set Modal
- *
- * @param {object} modal - The modal payload to set in state.
- * @param {null|string} modal.type - The modal slug, or null to display no modal.
- * @param {object} modal.props - The props to pass to the modal component.
- * @return {object} The modal action object.
- */
-const setModal = modal => {
- return { type: SET_MODAL, payload: modal };
-};
-
-const setNotice = notice => {
- return { type: SET_NOTICE, payload: notice };
-};
-
-const clearNotice = () => {
- return { type: CLEAR_NOTICE };
-};
-
-const setHasRequiredPlan = hasRequiredPlan => {
- return { type: SET_HAS_REQUIRED_PLAN, hasRequiredPlan };
-};
-
-const setOnboardingProgress = progress => {
- return { type: SET_ONBOARDING_PROGRESS, progress };
-};
-
-const setWafIsEnabled = isEnabled => {
- return { type: SET_WAF_IS_ENABLED, isEnabled };
-};
-
-const setWafIsSeen = isSeen => {
- return { type: SET_WAF_IS_SEEN, isSeen };
-};
-
-const setWafUpgradeIsSeen = upgradeIsSeen => {
- return { type: SET_WAF_UPGRADE_IS_SEEN, upgradeIsSeen };
-};
-
-const setWafIsUpdating = isUpdating => {
- return { type: SET_WAF_IS_UPDATING, isUpdating };
-};
-
-const setWafIsToggling = isToggling => {
- return { type: SET_WAF_IS_TOGGLING, isToggling };
-};
-
-const setWafConfig = config => {
- return { type: SET_WAF_CONFIG, config };
-};
-
-const setWafStats = stats => {
- return { type: SET_WAF_STATS, stats };
-};
-
-const actions = {
- checkCredentials,
- setCredentials,
- setCredentialsIsFetching,
- setScanHistory,
- setStatus,
- setStatusProgress,
- startScanOptimistically,
- refreshStatus,
- refreshScanHistory,
- setStatusIsFetching,
- setScanIsEnqueuing,
- setInstalledPlugins,
- setInstalledThemes,
- setwpVersion,
- setJetpackScan,
- ignoreThreat,
- unignoreThreat,
- setModal,
- setNotice,
- clearNotice,
- fixThreats,
- scan,
- setThreatsAreFixing,
- refreshPlan,
- setHasRequiredPlan,
- setScanIsUnavailable,
- setOnboardingProgress,
- setWafIsEnabled,
- setWafIsSeen,
- setWafUpgradeIsSeen,
- setWafIsUpdating,
- setWafIsToggling,
- setWafConfig,
- setWafStats,
-};
-
-export {
- SET_CREDENTIALS_STATE,
- SET_CREDENTIALS_STATE_IS_FETCHING,
- SET_SCAN_HISTORY,
- SET_STATUS,
- SET_STATUS_PROGRESS,
- START_SCAN_OPTIMISTICALLY,
- SET_STATUS_IS_FETCHING,
- SET_SCAN_IS_UNAVAILABLE,
- SET_SCAN_IS_ENQUEUING,
- SET_INSTALLED_PLUGINS,
- SET_INSTALLED_THEMES,
- SET_WP_VERSION,
- SET_JETPACK_SCAN,
- SET_PRODUCT_DATA,
- SET_THREAT_IS_UPDATING,
- SET_MODAL,
- SET_NOTICE,
- CLEAR_NOTICE,
- SET_THREATS_ARE_FIXING,
- SET_HAS_REQUIRED_PLAN,
- SET_ONBOARDING_PROGRESS,
- SET_WAF_IS_SEEN,
- SET_WAF_UPGRADE_IS_SEEN,
- SET_WAF_IS_ENABLED,
- SET_WAF_IS_UPDATING,
- SET_WAF_IS_TOGGLING,
- SET_WAF_CONFIG,
- SET_WAF_STATS,
- actions as default,
-};
diff --git a/projects/plugins/protect/src/js/state/reducers.js b/projects/plugins/protect/src/js/state/reducers.js
deleted file mode 100644
index ebf4208200ea6..0000000000000
--- a/projects/plugins/protect/src/js/state/reducers.js
+++ /dev/null
@@ -1,228 +0,0 @@
-import { combineReducers } from '@wordpress/data';
-import camelize from 'camelize';
-import { SCAN_STATUS_OPTIMISTICALLY_SCANNING } from '../constants';
-import {
- SET_CREDENTIALS_STATE,
- SET_CREDENTIALS_STATE_IS_FETCHING,
- SET_SCAN_HISTORY,
- SET_STATUS,
- SET_STATUS_PROGRESS,
- START_SCAN_OPTIMISTICALLY,
- SET_STATUS_IS_FETCHING,
- SET_SCAN_IS_UNAVAILABLE,
- SET_SCAN_IS_ENQUEUING,
- SET_INSTALLED_PLUGINS,
- SET_INSTALLED_THEMES,
- SET_WP_VERSION,
- SET_JETPACK_SCAN,
- SET_THREAT_IS_UPDATING,
- SET_MODAL,
- SET_NOTICE,
- CLEAR_NOTICE,
- SET_THREATS_ARE_FIXING,
- SET_HAS_REQUIRED_PLAN,
- SET_ONBOARDING_PROGRESS,
- SET_WAF_IS_SEEN,
- SET_WAF_UPGRADE_IS_SEEN,
- SET_WAF_IS_ENABLED,
- SET_WAF_IS_UPDATING,
- SET_WAF_IS_TOGGLING,
- SET_WAF_CONFIG,
- SET_WAF_STATS,
-} from './actions';
-
-const credentials = ( state = null, action ) => {
- switch ( action.type ) {
- case SET_CREDENTIALS_STATE:
- return action.credentials;
- }
- return state;
-};
-
-const credentialsIsFetching = ( state = false, action ) => {
- switch ( action.type ) {
- case SET_CREDENTIALS_STATE_IS_FETCHING:
- return action.isFetching;
- }
- return state;
-};
-
-const scanHistory = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_SCAN_HISTORY:
- return camelize( action.scanHistory );
- }
- return state;
-};
-
-const status = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_STATUS:
- return action.status;
- case SET_STATUS_PROGRESS:
- return { ...state, currentProgress: action.currentProgress };
- case START_SCAN_OPTIMISTICALLY:
- return { ...state, currentProgress: 0, status: SCAN_STATUS_OPTIMISTICALLY_SCANNING };
- }
- return state;
-};
-
-const statusIsFetching = ( state = false, action ) => {
- switch ( action.type ) {
- case SET_STATUS_IS_FETCHING:
- return action.status;
- }
- return state;
-};
-
-const scanIsUnavailable = ( state = false, action ) => {
- switch ( action.type ) {
- case SET_SCAN_IS_UNAVAILABLE:
- return action.status;
- }
- return state;
-};
-
-const scanIsEnqueuing = ( state = false, action ) => {
- switch ( action.type ) {
- case SET_SCAN_IS_ENQUEUING:
- return action.isEnqueuing;
- }
- return state;
-};
-
-const installedPlugins = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_INSTALLED_PLUGINS:
- return action.plugins;
- }
- return state;
-};
-
-const installedThemes = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_INSTALLED_THEMES:
- return action.themes;
- }
- return state;
-};
-
-const wpVersion = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_WP_VERSION:
- return action.version;
- }
- return state;
-};
-
-const jetpackScan = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_JETPACK_SCAN:
- return action.scan;
- }
- return state;
-};
-
-const threatsUpdating = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_THREAT_IS_UPDATING:
- return { ...state, [ action.payload.threatId ]: action.payload.isUpdating };
- }
- return state;
-};
-
-const threatsAreFixing = ( state = [], action ) => {
- switch ( action.type ) {
- case SET_THREATS_ARE_FIXING:
- return action.threatIds;
- }
- return state;
-};
-
-const modal = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_MODAL:
- return { ...state, ...action.payload };
- }
- return state;
-};
-
-const notice = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_NOTICE:
- return { ...state, ...action.payload };
- case CLEAR_NOTICE:
- return {};
- }
- return state;
-};
-
-const hasRequiredPlan = ( state = false, action ) => {
- switch ( action.type ) {
- case SET_HAS_REQUIRED_PLAN:
- return action.hasRequiredPlan;
- }
- return state;
-};
-
-const onboardingProgress = ( state = null, action ) => {
- switch ( action.type ) {
- case SET_ONBOARDING_PROGRESS:
- return action.progress;
- }
- return state;
-};
-
-const defaultWaf = {
- wafSupported: null,
- bruteForceSupported: null,
- isSeen: false,
- upgradeIsSeen: false,
- isEnabled: false,
- isUpdating: false,
- isToggling: false,
- config: undefined,
- stats: undefined,
-};
-const waf = ( state = defaultWaf, action ) => {
- switch ( action.type ) {
- case SET_WAF_IS_SEEN:
- return { ...state, isSeen: action.isSeen };
- case SET_WAF_UPGRADE_IS_SEEN:
- return { ...state, upgradeIsSeen: action.upgradeIsSeen };
- case SET_WAF_IS_ENABLED:
- return { ...state, isEnabled: action.isEnabled };
- case SET_WAF_CONFIG:
- return { ...state, config: action.config };
- case SET_WAF_STATS:
- return { ...state, stats: action.stats };
- case SET_WAF_IS_UPDATING:
- return { ...state, isUpdating: action.isUpdating };
- case SET_WAF_IS_TOGGLING:
- return { ...state, isToggling: action.isToggling };
- }
- return state;
-};
-
-const reducers = combineReducers( {
- credentials,
- credentialsIsFetching,
- scanHistory,
- status,
- statusIsFetching,
- scanIsUnavailable,
- scanIsEnqueuing,
- installedPlugins,
- installedThemes,
- wpVersion,
- jetpackScan,
- threatsUpdating,
- modal,
- notice,
- threatsAreFixing,
- hasRequiredPlan,
- onboardingProgress,
- waf,
-} );
-
-export default reducers;
diff --git a/projects/plugins/protect/src/js/state/resolvers.js b/projects/plugins/protect/src/js/state/resolvers.js
deleted file mode 100644
index 40f916ce02375..0000000000000
--- a/projects/plugins/protect/src/js/state/resolvers.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import apiFetch from '@wordpress/api-fetch';
-import actions from './actions';
-
-const resolvers = {
- getJetpackScan: {
- isFulfilled: state => {
- return Object.keys( state?.jetpackScan ).length > 0;
- },
-
- fulfill:
- () =>
- async ( { dispatch } ) => {
- const response = await apiFetch( {
- path: '/my-jetpack/v1/site/products/scan',
- method: 'GET',
- } );
-
- dispatch( actions.setJetpackScan( response ) );
- },
- },
-};
-
-export default resolvers;
diff --git a/projects/plugins/protect/src/js/state/selectors.js b/projects/plugins/protect/src/js/state/selectors.js
deleted file mode 100644
index 307bfdaffecd5..0000000000000
--- a/projects/plugins/protect/src/js/state/selectors.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import { __ } from '@wordpress/i18n';
-import { SCAN_IN_PROGRESS_STATUSES, SCAN_STATUS_OPTIMISTICALLY_SCANNING } from '../constants';
-
-/**
- * Scan in progress selector.
- *
- * @param {object} state - The current state.
- * @return {boolean} Whether a scan is in progress.
- */
-const scanInProgress = state => {
- const { status, lastChecked, error } = selectors.getStatus( state );
-
- // When "optimistically" scanning, ignore any other status or error.
- if ( SCAN_STATUS_OPTIMISTICALLY_SCANNING === status ) {
- return true;
- }
-
- // If the scan is unavailable, scanning is not in progress.
- const unavailable = selectors.getScanIsUnavailable( state );
- if ( unavailable ) {
- return false;
- }
-
- // If the status is one of the scanning statuses, we are scanning.
- if ( SCAN_IN_PROGRESS_STATUSES.includes( status ) ) {
- return true;
- }
-
- // If we have no record of a previous scan, we must be queueing up the initial scan.
- if ( ! lastChecked && ! error ) {
- return true;
- }
-
- return false;
-};
-
-/**
- * Scan error selector.
- *
- * @param {object} state - The current state.
- *
- * @typedef {object} ScanError
- * @property {string} code - The code identifying the type of error.
- * @property {string} message - A message describing the error.
- *
- * @return {ScanError|null} The error object or null.
- */
-const scanError = state => {
- const { status, error, errorCode, errorMessage } = selectors.getStatus( state );
-
- // If the scan results include an error, return it.
- if ( error ) {
- return { code: errorCode, message: errorMessage };
- }
-
- // If the scan is unavailable, return an error.
- const unavailable = selectors.getScanIsUnavailable( state );
- if ( unavailable ) {
- return {
- code: 'scan_unavailable',
- message: __( 'We are having problems scanning your site.', 'jetpack-protect' ),
- };
- }
-
- // If there is no status and we are not requesting it, return an error.
- const isFetching = selectors.getStatusIsFetching( state );
- if ( ! status && ! isFetching ) {
- return {
- code: 'scan_unavailable',
- message: __( 'We are having problems scanning your site.', 'jetpack-protect' ),
- };
- }
-
- return null;
-};
-
-const selectors = {
- getCredentials: state => state.credentials || null,
- getCredentialsIsFetching: state => state.credentialsIsFetching || false,
- getInstalledPlugins: state => state.installedPlugins || {},
- getInstalledThemes: state => state.installedThemes || {},
- getScanHistory: state => state.scanHistory || {},
- getStatus: state => state.status || {},
- getStatusIsFetching: state => state.statusIsFetching || false,
- getScanIsUnavailable: state => state.scanIsUnavailable || false,
- getScanIsEnqueuing: state => state.scanIsEnqueuing || false,
- scanInProgress,
- scanError,
- getWpVersion: state => state.wpVersion || '',
- getJetpackScan: state => state.jetpackScan || {},
- getThreatsUpdating: state => state.threatsUpdating || {},
- getModalType: state => state.modal?.type || null,
- getModalProps: state => state.modal?.props || {},
- getNotice: state => state.notice || null,
- getThreatsAreFixing: state => state.threatsAreFixing || [],
- hasRequiredPlan: state => state.hasRequiredPlan || false,
- getOnboardingProgress: state => state.onboardingProgress || null,
- getWaf: state => state.waf,
-};
-
-export default selectors;
diff --git a/projects/plugins/protect/src/js/state/store-holder.js b/projects/plugins/protect/src/js/state/store-holder.js
deleted file mode 100644
index 9956b859ebb31..0000000000000
--- a/projects/plugins/protect/src/js/state/store-holder.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { createReduxStore, register } from '@wordpress/data';
-
-class storeHolder {
- static store = null;
-
- static mayBeInit( storeId, storeConfig ) {
- if ( null === storeHolder.store ) {
- storeHolder.store = createReduxStore( storeId, storeConfig );
- register( storeHolder.store );
- }
- }
-}
-
-export default storeHolder;
diff --git a/projects/plugins/protect/src/js/state/store.js b/projects/plugins/protect/src/js/state/store.js
deleted file mode 100644
index c1112e549c975..0000000000000
--- a/projects/plugins/protect/src/js/state/store.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import camelize from 'camelize';
-import actions from './actions';
-import reducer from './reducers';
-import resolvers from './resolvers';
-import selectors from './selectors';
-import storeHolder from './store-holder';
-
-const STORE_ID = 'jetpack-protect';
-
-/**
- * Inits redux store for Jetpack Protect
- */
-function initStore() {
- storeHolder.mayBeInit( STORE_ID, {
- __experimentalUseThunks: true, // never stop experiment :sweat_smile:
- reducer,
- actions,
- selectors,
- resolvers,
- initialState: camelize( window.jetpackProtectInitialState ) || {},
- } );
-}
-
-export { STORE_ID, initStore };
diff --git a/projects/plugins/protect/src/js/types/fixers.ts b/projects/plugins/protect/src/js/types/fixers.ts
new file mode 100644
index 0000000000000..fc43d944a5830
--- /dev/null
+++ b/projects/plugins/protect/src/js/types/fixers.ts
@@ -0,0 +1,9 @@
+export type FixerStatus = 'not_started' | 'in_progress' | 'fixed' | 'not_fixed';
+
+export type FixersStatus = {
+ threats: {
+ [ key: number ]: {
+ status: FixerStatus;
+ };
+ };
+};
diff --git a/projects/plugins/protect/src/js/types/global.d.ts b/projects/plugins/protect/src/js/types/global.d.ts
new file mode 100644
index 0000000000000..5fc0a2444334d
--- /dev/null
+++ b/projects/plugins/protect/src/js/types/global.d.ts
@@ -0,0 +1,34 @@
+import { PluginData, ThemeData } from './installed-extensions';
+import { ProductData } from './products';
+import { ScanStatus } from './scans';
+import { WafStatus } from './waf';
+
+declare module '*.scss';
+declare module '*.png';
+
+declare global {
+ interface Window {
+ jetpackProtectInitialState?: {
+ apiRoot: string;
+ apiNonce: string;
+ registrationNonce: string;
+ credentials: [ Record< string, unknown > ];
+ status: ScanStatus;
+ scanHistory: ScanStatus;
+ installedPlugins: {
+ [ key: string ]: PluginData;
+ };
+ installedThemes: {
+ [ key: string ]: ThemeData;
+ };
+ wpVersion: string;
+ adminUrl: string;
+ siteSuffix: string;
+ blogID: number;
+ jetpackScan: ProductData;
+ hasPlan: boolean;
+ onboardingProgress: string[];
+ waf: WafStatus;
+ };
+ }
+}
diff --git a/projects/plugins/protect/src/js/types/installed-extensions.ts b/projects/plugins/protect/src/js/types/installed-extensions.ts
new file mode 100644
index 0000000000000..127ec470ff221
--- /dev/null
+++ b/projects/plugins/protect/src/js/types/installed-extensions.ts
@@ -0,0 +1,91 @@
+/**
+ * Site's installed plugin data.
+ *
+ * @see https://developer.wordpress.org/reference/functions/get_plugin_data
+ */
+export type PluginData = {
+ /** Name of the plugin. Should be unique. */
+ Name: string;
+
+ /** Plugin URI. */
+ PluginURI?: string;
+
+ /** Plugin version. */
+ Version?: string;
+
+ /** Plugin description. */
+ Description?: string;
+
+ /** Plugin author’s name. */
+ Author?: string;
+
+ /** Plugin author’s website address (if set). */
+ AuthorURI?: string;
+
+ /** Plugin textdomain. */
+ TextDomain?: string;
+
+ /** Plugin’s relative directory path to .mo files. */
+ DomainPath?: string;
+
+ /** Whether the plugin can only be activated network-wide. */
+ Network?: boolean;
+
+ /** Minimum required version of WordPress. */
+ RequiresWP?: string;
+
+ /** Minimum required version of PHP. */
+ RequiresPHP?: string;
+
+ /** ID of the plugin for update purposes, should be a URI. */
+ UpdateURI?: string;
+
+ /** Comma separated list of dot org plugin slugs. */
+ RequiresPlugins?: string;
+
+ /** Title of the plugin and link to the plugin’s site (if set). */
+ Title?: string;
+
+ /** Plugin author’s name. */
+ AuthorName?: string;
+};
+
+/**
+ * Site's installed theme data.
+ *
+ * @see https://developer.wordpress.org/reference/functions/wp_get_themes
+ */
+export type ThemeData = {
+ /** The name of the theme. */
+ Name: string;
+
+ /** The URI of the theme’s webpage. */
+ ThemeURI: string;
+
+ /** The description of the theme. */
+ Description: string;
+
+ /** The theme’s author. */
+ Author: string;
+
+ /** The website of the theme author. */
+ AuthorURI: string;
+
+ /** The version of the theme. */
+ Version: string;
+
+ /** (Optional — used in a child theme) The folder name of the parent theme. */
+ Template?: string;
+
+ /** If the theme is published. */
+ Status: string;
+
+ /** Tags used to describe the theme. */
+ Tags: string[];
+
+ /** The text domain used in the theme for translation purposes. */
+ TextDomain: string;
+
+ /** Path to the theme translation files. */
+ DomainPath: string;
+};
diff --git a/projects/plugins/protect/src/js/types/products.ts b/projects/plugins/protect/src/js/types/products.ts
new file mode 100644
index 0000000000000..0a8d1cfa333df
--- /dev/null
+++ b/projects/plugins/protect/src/js/types/products.ts
@@ -0,0 +1,89 @@
+/**
+ * My Jetpack Product Data Types
+ *
+ * Borrowed from projects/packages/my-jetpack/global.d.ts
+ */
+type ProductStatus =
+ | 'active'
+ | 'inactive'
+ | 'module_disabled'
+ | 'site_connection_error'
+ | 'plugin_absent'
+ | 'plugin_absent_with_plan'
+ | 'needs_plan'
+ | 'needs_activation'
+ | 'needs_first_site_connection'
+ | 'user_connection_error'
+ | 'can_upgrade';
+
+export type ProductData = {
+ class: string;
+ description: string;
+ disclaimers: Array< string[] >;
+ features: string[];
+ has_free_offering: boolean;
+ has_paid_plan_for_product: boolean;
+ features_by_tier: Array< string >;
+ is_bundle: boolean;
+ is_plugin_active: boolean;
+ is_upgradable: boolean;
+ is_upgradable_by_bundle: string[];
+ long_description: string;
+ manage_url: string;
+ name: string;
+ plugin_slug: string;
+ post_activation_url: string;
+ post_checkout_url?: string;
+ pricing_for_ui?: {
+ available: boolean;
+ wpcom_product_slug: string;
+ wpcom_free_product_slug?: string;
+ product_term: string;
+ currency_code: string;
+ full_price: number;
+ discount_price: number;
+ coupon_discount: number;
+ is_introductory_offer: boolean;
+ introductory_offer?: {
+ cost_per_interval: number;
+ interval_count: number;
+ interval_unit: string;
+ should_prorate_when_offer_ends: boolean;
+ transition_after_renewal_count: number;
+ usage_limit?: number;
+ };
+ tiers?: {
+ [ key: string ]: {
+ available: boolean;
+ currencyCode: string;
+ discountPrice: number;
+ fullPrice: number;
+ introductoryOffer?: {
+ costPerInterval: number;
+ intervalCount: number;
+ intervalUnit: string;
+ shouldProrateWhenOfferEnds: boolean;
+ transitionAfterRenewalCount: number;
+ usageLimit?: number;
+ };
+ isIntroductoryOffer: boolean;
+ productTerm: string;
+ wpcomProductSlug: string;
+ quantity: number;
+ };
+ };
+ };
+ purchase_url?: string;
+ requires_user_connection: boolean;
+ slug: string;
+ standalone_plugin_info: {
+ has_standalone_plugin: boolean;
+ is_standalone_installed: boolean;
+ is_standalone_active: boolean;
+ };
+ status: ProductStatus;
+ supported_products: string[];
+ tiers: string[];
+ title: string;
+ wpcom_product_slug: string;
+};
diff --git a/projects/plugins/protect/src/js/types/scans.ts b/projects/plugins/protect/src/js/types/scans.ts
new file mode 100644
index 0000000000000..665062afbfebd
--- /dev/null
+++ b/projects/plugins/protect/src/js/types/scans.ts
@@ -0,0 +1,78 @@
+import { Threat } from './threats';
+
+export type ExtensionStatus = {
+ /** The name of the extension. */
+ name: string;
+
+ /** The slug of the extension. */
+ slug: string;
+
+ /** The version of the extension. */
+ version: string;
+
+ /** The threats found in the extension. */
+ threats: Threat[];
+
+ /** The type of extension. */
+ type: 'plugins' | 'themes';
+
+ /** Whether the extension was checked in the latest scan. */
+ checked: boolean;
+};
+
+export type ScanStatus = {
+ /** The current status of the scanner. */
+ status: 'unavailable' | 'provisioning' | 'idle' | 'scanning' | 'scheduled';
+
+ /** The current scan progress, only available from the Scan API. */
+ current_progress: number | null;
+
+ /** The data source for the scan status. */
+ dataSource: 'protect_report' | 'scan_api';
+
+ /** Whether the site currently has extensions not checked in the latest scan. */
+ hasUncheckedItems: boolean;
+
+ /** The time the last scan was checked, in YYYY-MM-DD HH:MM:SS format. */
+ lastChecked: string | null;
+
+ /** The number of plugin threats found in the latest status. */
+ numPluginsThreats: number;
+
+ /** The number of theme threats found in the latest status. */
+ numThemesThreats: number;
+
+ /** The total number of threats found in the latest status. */
+ numThreats: number;
+
+ /** Whether there was an error in the scan results. */
+ error: boolean | null;
+
+ /** The error code. */
+ errorCode: string | null;
+
+ /** The error message. */
+ errorMessage: string | null;
+
+ /** WordPress Core Status */
+ core: {
+ checked: boolean;
+ name: string;
+ slug: string;
+ threats: Threat[];
+ type: 'core';
+ version: string;
+ } | null;
+
+ /** Plugins Status */
+ plugins: ExtensionStatus[];
+
+ /** Themes Status */
+ themes: ExtensionStatus[];
+
+ /** File Threats */
+ files: Threat[];
+
+ /** Database Threats */
+ database: Threat[];
+};
diff --git a/projects/plugins/protect/src/js/types/threats.ts b/projects/plugins/protect/src/js/types/threats.ts
new file mode 100644
index 0000000000000..757503972fa0c
--- /dev/null
+++ b/projects/plugins/protect/src/js/types/threats.ts
@@ -0,0 +1,59 @@
+export type ThreatStatus = 'fixed' | 'ignored' | 'current';
+
+export type ThreatFixType = 'replace' | 'delete' | 'update' | string;
+
+export type Threat = {
+ /** The threat's unique ID. */
+ id: number;
+
+ /** The threat's signature. */
+ signature: string;
+
+ /** The threat's title. */
+ title: string;
+
+ /** The threat's description. */
+ description: string;
+
+ /** The threat's current status. */
+ status: ThreatStatus;
+
+ /** The threat's severity level (0-10). */
+ severity: number;
+
+ /** The date the threat was first detected on the site, in YYYY-MM-DDTHH:MM:SS.000Z format. */
+ firstDetected: string;
+
+ /** The version the threat is fixed in. */
+ fixedIn?: string | null;
+
+ /** The date the threat was fixed, in YYYY-MM-DDTHH:MM:SS.000Z format. */
+ fixedOn?: string | null;
+
+ /** The fixable details. */
+ fixable:
+ | {
+ fixer: ThreatFixType;
+ target?: string | null;
+ extensionStatus?: string | null;
+ }
+ | false;
+
+ /** The threat's source. */
+ source?: string;
+
+ /** The threat's context. */
+ context?: Record< string, unknown > | null;
+
+ /** The name of the affected file. */
+ filename: string | null;
+
+ /** The rows affected by the database threat. */
+ rows?: unknown;
+
+ /** The table name of the database threat. */
+ table?: string;
+
+ /** The diff showing the threat's modified file contents. */
+ diff?: string;
+};
diff --git a/projects/plugins/protect/src/js/types/waf.ts b/projects/plugins/protect/src/js/types/waf.ts
new file mode 100644
index 0000000000000..9693e65002c5d
--- /dev/null
+++ b/projects/plugins/protect/src/js/types/waf.ts
@@ -0,0 +1,68 @@
+export type WafStatus = {
+ /** The current WAF configuration. */
+ config: WafConfig;
+
+ /** The current user's IP address. */
+ currentIp: string;
+
+ /** Whether to show the "upgrade" badge in the firewall UI. */
+ displayUpgradeBadge: boolean;
+
+ /** Global statistics. */
+ globalStats: {
+ totalVulnerabilities: string;
+ };
+
+ /** Whether the "waf" module is enabled. */
+ isEnabled: boolean;
+
+ /** Whether the current user viewed the firewall UI. */
+ isSeen: boolean;
+
+ /** Stats. */
+ stats: boolean;
+
+ /** Whether the current user has viewed the upgrade message in the firewall UI. */
+ upgradeIsSeen: boolean;
+
+ /** Whether the WAF can run in the current environment. */
+ wafSupported: boolean;
+};
+
+export type WafConfig = {
+ /** True if any version of automatic rules is currently installed on the site */
+ automaticRulesAvailable: boolean;
+
+ /** File path to the bootstrap.php file, i.e. "/var/www/html/wp-content/jetpack-waf/bootstrap.php" */
+ bootstrapPath: string;
+
+ /** Whether brute force protection is enabled. */
+ bruteForceProtection: boolean;
+
+ /** Whether automatic rules are enabled. */
+ jetpackWafAutomaticRules: boolean;
+
+ /** The contents of the IP allow list. */
+ jetpackWafIpAllowList: string;
+
+ /** Whether the IP allow list is enabled. */
+ jetpackWafIpAllowListEnabled: boolean;
+
+ /** The contents of the IP block list. */
+ jetpackWafIpBlockList: boolean;
+
+ /** Whether the IP block list is enabled. */
+ jetpackWafIpBlockListEnabled: boolean;
+
+ /** Whether the user has consented to sharing basic data with Jetpack. */
+ jetpackWafShareData: string;
+
+ /** Whether the user has consented to sharing debug data with Jetpack. */
+ jetpackWafShareDebugData: boolean;
+
+ /** True if the firewall ran in standalone mode for the current request. */
+ standaloneMode: boolean;
+
+ /** @deprecated Whether all IP lists are enabled. */
+ jetpackWafIpList: boolean;
+};
diff --git a/projects/plugins/protect/tsconfig.json b/projects/plugins/protect/tsconfig.json
index 527e1f5fd0401..bc49ef3cebb58 100644
--- a/projects/plugins/protect/tsconfig.json
+++ b/projects/plugins/protect/tsconfig.json
@@ -5,6 +5,6 @@
"sourceMap": true,
"outDir": "./build/",
"target": "esnext",
- "typeRoots": [ "./node_modules/@types/", "./src/js/*" ]
+ "typeRoots": [ "./node_modules/@types/", "./src/js/types" ]
}
}