├── .babelrc.json
├── .eslintrc.js
├── .github
└── workflows
│ ├── lint.yml
│ ├── semgrep.yml
│ └── test.yml
├── .gitignore
├── .nvmrc
├── CHANGELOG.MD
├── CONTRIBUTING.md
├── README.md
├── assets
├── analytics-welcome.svg
├── details-arrows.png
├── global-caching.svg
├── hero-bg-clouds.png
├── icon-bolt.svg
├── icon-lock.svg
├── icon-pin.svg
├── icon-shield.svg
├── icons-seee324dde5.png
├── icons_2x-s6333fe7591.png
├── insight.svg
├── layers-2x.png
├── layers.png
├── logo-reverse.svg
├── logo-symbol.svg
├── logo.svg
├── modal-two-factor-auth.png
├── modal-two-factor-auth_2x.png
├── overview-welcome-yjs.svg
├── overview-welcome.svg
├── plan-changed-success.svg
├── request-submitted-success.svg
├── security.svg
├── select2-cf-white.png
├── select2-cf.png
├── select2x2-cf-white.png
├── select2x2-cf.png
├── spinner.gif
├── throbber.gif
├── vertical-range.png
├── web-optimization.svg
├── yjs-background.jpg
├── yjs-background_2x.jpg
└── yjs-logo.svg
├── config.json.sample
├── fonts
├── FontAwesome.otf
├── cloudflare-font.eot
├── cloudflare-font.svg
├── cloudflare-font.ttf
├── cloudflare-font.woff
├── fontawesome-cloudflare.eot
├── fontawesome-cloudflare.svg
├── fontawesome-cloudflare.ttf
├── fontawesome-cloudflare.woff
├── fontawesome-webfont.eot
├── fontawesome-webfont.svg
├── fontawesome-webfont.ttf
├── fontawesome-webfont.woff
├── opensans-300.eot
├── opensans-300.ttf
├── opensans-300.woff
├── opensans-300.woff2
├── opensans-300i.eot
├── opensans-300i.ttf
├── opensans-300i.woff
├── opensans-300i.woff2
├── opensans-400.eot
├── opensans-400.ttf
├── opensans-400.woff
├── opensans-400.woff2
├── opensans-400i.eot
├── opensans-400i.ttf
├── opensans-400i.woff
├── opensans-400i.woff2
├── opensans-600.eot
├── opensans-600.ttf
├── opensans-600.woff
├── opensans-600.woff2
├── opensans-700.eot
├── opensans-700.ttf
├── opensans-700.woff
└── opensans-700.woff2
├── lang
├── de.js
├── en.js
├── es.js
├── fr.js
├── it.js
├── nl.js
└── pt.js
├── package.json
├── prettier.config.js
├── src
├── actions
│ ├── activeZone.js
│ ├── app.js
│ ├── config.js
│ ├── intl.js
│ ├── notifications.js
│ ├── pluginSettings.js
│ ├── user.js
│ ├── zoneDnsRecords.js
│ ├── zoneEntitlements.js
│ ├── zoneProvision.js
│ ├── zonePurgeCache.js
│ ├── zoneSettings.js
│ └── zones.js
├── components
│ ├── AppNavigationLiNode
│ │ └── AppNavigationLiNode.js
│ ├── BenefitsFeature
│ │ └── BenefitsFeature.js
│ ├── C3Wrapper
│ │ └── C3Wrapper.js
│ ├── CustomCardControl
│ │ └── CustomCardControl.js
│ ├── FeatureManager
│ │ └── FeatureManager.js
│ ├── FormattedMarkdown
│ │ └── FormattedMarkdown.js
│ ├── GradientBanner
│ │ └── GradientBanner.js
│ ├── MarketingFeature
│ │ └── MarketingFeature.js
│ ├── RenderCardsDynamically
│ │ └── RenderCardsDynamically.js
│ └── TimeSeriesChart
│ │ └── TimeSeriesChart.js
├── constants
│ ├── ActionTypes.js
│ ├── Plans.js
│ ├── Schemas.js
│ └── UrlPaths.js
├── containers
│ ├── ActivationCheckCard
│ │ └── ActivationCheckCard.js
│ ├── ActiveZoneSelector
│ │ └── ActiveZoneSelector.js
│ ├── AdvanceDDoSCard
│ │ └── AdvanceDDoSCard.js
│ ├── AlwaysOnlineCard
│ │ └── AlwaysOnlineCard.js
│ ├── AnalyticsPage
│ │ └── AnaltyicsPage.js
│ ├── App
│ │ └── App.js
│ ├── AppNavigation
│ │ └── AppNavigation.js
│ ├── ApplyDefaultSettingsCard
│ │ └── ApplyDefaultSettingsCard.js
│ ├── AutomaticHTTPSRewritesCard
│ │ └── AutomaticHTTPSRewritesCard.js
│ ├── AutomaticPlatformOptimization
│ │ └── AutomaticPlatformOptimizationCard.js
│ ├── BenefitsCollection
│ │ └── BenefitsCollection.js
│ ├── BrowserCacheTTLCard
│ │ └── BrowserCacheTTLCard.js
│ ├── BrowserIntegrityCheckCard
│ │ └── BrowserIntegrityCheckCard.js
│ ├── CacheLevelCard
│ │ └── CacheLevelCard.js
│ ├── ChallengePassageCard
│ │ └── ChallengePassageCard.js
│ ├── DNSManagementPage
│ │ └── DNSManagementPage.js
│ ├── DNSRecordEditor
│ │ └── DNSRecordEditor.js
│ ├── DevelopmentModeCard
│ │ └── DevelopmentModeCard.js
│ ├── GlobalNotifications
│ │ └── GlobalNotifications.js
│ ├── Header
│ │ └── Header.js
│ ├── HomePage
│ │ └── HomePage.js
│ ├── IPV6Card
│ │ └── IPV6Card.js
│ ├── ImageOptimizationCard
│ │ └── ImageOptimizationCard.js
│ ├── IpRewriteCard
│ │ └── IpRewriteCard.js
│ ├── LoginPage
│ │ └── LoginPage.js
│ ├── MarketingFeatureCollection
│ │ └── MarketingFeatureCollection.js
│ ├── MoreSettingsPage
│ │ └── MoreSettingsPage.js
│ ├── PluginSpecificCacheCard
│ │ └── PluginSpecificCacheCard.js
│ ├── PluginSpecificCacheTagCard
│ │ └── PluginSpecificCachetTagCard.js
│ ├── PurgeCacheCard
│ │ └── PurgeCacheCard.js
│ ├── SSLCard
│ │ └── SSLCard.js
│ ├── SecurityLevelCard
│ │ └── SecurityLevelCard.js
│ ├── SignUpPage
│ │ └── SignUpPage.js
│ ├── SplashPage
│ │ └── SplashPage.js
│ ├── UnderAttackButton
│ │ └── UnderAttackButton.js
│ ├── WAFCard
│ │ └── WAFCard.js
│ ├── WaitForSettings
│ │ └── WaitForSettings.js
│ └── ZoneProvisionContainer
│ │ └── ZoneProvisionContainer.js
├── index.js
├── reducers
│ ├── activeZone.js
│ ├── app.js
│ ├── config.js
│ ├── index.js
│ ├── intl.js
│ ├── notifications.js
│ ├── pluginSettings.js
│ ├── user.js
│ ├── zoneDnsRecords.js
│ ├── zoneEntitlements.js
│ ├── zonePurgeCache.js
│ ├── zoneSettings.js
│ └── zones.js
├── routes.js
├── selectors
│ ├── activeZone.js
│ ├── config.js
│ ├── intl.js
│ ├── pluginSettings.js
│ ├── zoneSettings.js
│ └── zones.js
├── store
│ └── configureStore.js
├── test
│ ├── _helpers
│ │ └── createMockStore.js
│ ├── components
│ │ ├── BenefitsFeatureTest.js
│ │ ├── FeatureManagerTest.js
│ │ ├── GradientBannerTest.js
│ │ ├── MarketingFeatureTest.js
│ │ └── __snapshots__
│ │ │ ├── BenefitsFeatureTest.js.snap
│ │ │ ├── FeatureManagerTest.js.snap
│ │ │ ├── GradientBannerTest.js.snap
│ │ │ └── MarketingFeatureTest.js.snap
│ ├── containers
│ │ ├── SplashPageTest.js
│ │ └── __snapshots__
│ │ │ └── SplashPageTest.js.snap
│ ├── reducers
│ │ ├── __snapshots__
│ │ │ └── configTest.js.snap
│ │ ├── activeZoneTest.js
│ │ ├── configTest.js
│ │ ├── pluginSettingsTest.js
│ │ └── zonePurgeCacheTest.js
│ ├── selectors
│ │ └── configTest.js
│ └── utils
│ │ ├── PluginAPITest.js
│ │ └── deduplicateZonesTest.js
└── utils
│ ├── Auth
│ └── Auth.js
│ ├── CFClientV4API
│ └── CFClientV4API.js
│ ├── CFHostAPI
│ └── CFHostAPI.js
│ ├── ImportCards.js
│ ├── PluginAPI
│ └── PluginAPI.js
│ └── utils.js
├── stylesheets
├── cf.core.css
└── components.css
├── webpack.config.js
└── yarn.lock
/.babelrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@babel/eslint-parser',
3 | parserOptions: {
4 | ecmaVersion: 6,
5 | sourceType: 'module',
6 | ecmaFeatures: {
7 | globalReturn: false,
8 | impliedStrict: true,
9 | jsx: true,
10 | experimentalObjectRestSpread: true
11 | },
12 | babelOptions: {
13 | configFile: './.babelrc.json'
14 | }
15 | },
16 | globals: {
17 | __webpack_public_path__: true,
18 | bootstrap: true,
19 | describe: true,
20 | it: true,
21 | beforeEach: true,
22 | afterEach: true,
23 | before: true,
24 | after: true,
25 | test: true,
26 | expect: true
27 | },
28 | env: {
29 | browser: true,
30 | node: true,
31 | es6: true
32 | },
33 | plugins: ['prettier', 'react', 'import', 'json', 'cflint', 'compat', 'mocha'],
34 | extends: [
35 | 'plugin:react/recommended',
36 | 'plugin:import/errors',
37 | 'plugin:import/warnings'
38 | ],
39 | rules: {
40 | 'mocha/no-exclusive-tests': 2,
41 | 'block-scoped-var': 2,
42 | 'default-case': 2,
43 | 'guard-for-in': 2,
44 | 'no-else-return': 2,
45 | 'no-floating-decimal': 2,
46 | 'no-self-compare': 2,
47 | 'no-void': 2,
48 | radix: 2,
49 | 'wrap-iife': ['error', 'inside'],
50 | 'no-catch-shadow': 2,
51 | 'handle-callback-err': 2,
52 | camelcase: 0,
53 | 'no-unused-vars': [2, { vars: 'all', args: 'after-used' }],
54 | 'no-undef': [2, { typeof: false }],
55 | 'react/prop-types': [2, { skipUndeclared: true }],
56 | 'import/no-absolute-path': 2,
57 | 'import/no-deprecated': 1,
58 | 'import/no-mutable-exports': 1,
59 | 'import/no-extraneous-dependencies': 0,
60 | 'import/no-unresolved': 0,
61 | 'import/first': 1,
62 | 'prettier/prettier': [2, { trailingComma: 'none', singleQuote: true }],
63 | 'compat/compat': 0,
64 | 'no-use-before-define': ['error', { functions: false, classes: true }]
65 | },
66 | settings: {
67 | 'import/resolver': {
68 | webpack: {
69 | config: './webpack.config.js'
70 | }
71 | },
72 | react: {
73 | version: 'detect'
74 | }
75 | }
76 | };
77 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 | on: [push, pull_request]
3 | jobs:
4 | lint:
5 | name: lint
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v2
9 | - uses: actions/setup-node@v2
10 | with:
11 | node-version: '14'
12 | - run: yarn install
13 | - run: yarn run build:production
14 | - run: yarn run lint
15 |
--------------------------------------------------------------------------------
/.github/workflows/semgrep.yml:
--------------------------------------------------------------------------------
1 | on:
2 | pull_request: {}
3 | workflow_dispatch: {}
4 | push:
5 | branches:
6 | - main
7 | - master
8 | schedule:
9 | - cron: '0 0 * * *'
10 | name: Semgrep config
11 | jobs:
12 | semgrep:
13 | name: semgrep/ci
14 | runs-on: ubuntu-latest
15 | env:
16 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
17 | SEMGREP_URL: https://cloudflare.semgrep.dev
18 | SEMGREP_APP_URL: https://cloudflare.semgrep.dev
19 | SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version
20 | container:
21 | image: returntocorp/semgrep
22 | steps:
23 | - uses: actions/checkout@v4
24 | - run: semgrep ci
25 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 | on: [push, pull_request]
3 | jobs:
4 | test:
5 | name: test
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v2
9 | - uses: actions/setup-node@v2
10 | with:
11 | node-version: '14'
12 | - run: yarn install
13 | - run: yarn run build:production
14 | - run: yarn run test
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .env
3 | compiled.js
4 | compiled.js.map
5 | *.DS_Store
6 | coverage/
7 | .jest/
8 | .vscode/
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v16
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Cloudflare Plugins
2 |
3 | 👍🎉 First off, thanks for taking the time to contribute! 🎉👍
4 |
5 | ## How To Contribute
6 |
7 | We welcome community contribution to this repository. To help add functionality or address issues, please take the following steps:
8 |
9 | * Fork the repository from the master branch.
10 | * Create a new branch for your features / fixes.
11 | * Make the changes you wish to see.
12 | * Add tests for all changes.
13 | * Create a pull request with details of what changes have been made, explanation of new behaviour, and link to issue that is addressed.
14 | * Addressing (with @...) one or more of the maintainers in the description of the pull request
15 | * Ensure documentation contains the correct information.
16 | * Pull requests will be reviewed and hopefully merged into a release.
17 |
18 | ## Before Contributing
19 |
20 | Cloudflare has multiple plugins using shared codebases.
21 |
22 | [WordPress](https://github.com/cloudflare/Cloudflare-WordPress), [CPanel](https://github.com/cloudflare/CloudFlare-CPanel), [Magento](https://github.com/cloudflare/CloudFlare-Magento) are the main repositories of the plugins. Every plugin has a config.js file which allows them to control the frontend of the plugin.
23 |
24 | Below are Cloudflare maintained repositories the plugins depend on.
25 |
26 | * [cloudflare-frontend](https://github.com/cloudflare/CloudFlare-FrontEnd) is a generic frontend used in plugins. You can add/remove cards simply by editing [config](https://github.com/cloudflare/CloudFlare-FrontEnd/blob/master/config.js) file.
27 | * [cf-ui](https://github.com/cloudflare/cf-ui) is a Cloudflare UI Framework where cloudflare-frontend is using.
28 | * [cloudflare-plugin-backend](https://github.com/cloudflare/cloudflare-plugin-backend) is a generic backend plugins use.
29 | * [cf-ip-rewrite](https://github.com/cloudflare/cf-ip-rewrite) allows to rewrite Cloudflare IP's in Application level.
30 | * [mod_cloudflare](https://github.com/cloudflare/mod_cloudflare) allows Apache to rewrite Cloudflare IP's with user IP's. It is not used in plugins itself but it maybe be a better alternative then `cf-ip-rewrite`.
31 |
32 | ## Frontend Updates
33 |
34 | Each plugin may use different Frontend [versions]((https://github.com/cloudflare/CloudFlare-FrontEnd/releases)). When publishing a Frontend release we copy the following files to other plugins;
35 |
36 | * `assets/`
37 | * `fonts/`
38 | * `lang/`
39 | * `stylesheets/`
40 | * `compiled.js` which is created when `gulp compress` command is called within Frontend repository.
41 |
42 | ## Translations
43 |
44 | The plugins use a common language file which is located [here](https://github.com/cloudflare/CloudFlare-FrontEnd/tree/master/lang). English translation is always up to date where as other translations are not. If you have any issues or questions regarding with translations feel free to open an [issue](https://github.com/cloudflare/CloudFlare-FrontEnd/issues).
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Build using [Yarn](https://yarnpkg.com/en/docs/install)
2 | 1. `yarn install`
3 | 2. `yarn run build`
4 |
5 | # Development Tasks
6 | ```
7 | $ yarn run build
8 | $ OUTPUT_PATH=custom-path.js yarn run build
9 | $ yarn run build:production
10 | $ yarn run lint
11 | $ yarn run format
12 | $ yarn run test
13 | ```
14 |
15 | # Production
16 | For production run `yarn run build:production` to get a minified version of compiled.js
17 |
18 | # Building Your Own Backend
19 | This repository serves as the front end for all of our 3rd party integrations.
20 | It is intended to be backend agnostic with the intention of making it as easy as
21 | possible to port it to new backends. If you would like to build a custom backend
22 | just follow these steps:
23 |
24 | 1. Implement RestProxyCallback()
25 | ```
26 | /*
27 | * A callback for cf-util-http to proxy all calls to our backend
28 | *
29 | * @param {Object} [opts]
30 | * @param {String} [opts.method] - GET/POST/PUT/PATCH/DELETE
31 | * @param {String} [opts.url]
32 | * @param {Object} [opts.parameters]
33 | * @param {Object} [opts.headers]
34 | * @param {Object} [opts.body]
35 | * @param {Function} [opts.onSuccess]
36 | * @param {Function} [opts.onError]
37 | */
38 | function RestProxyCallback(opts) {}
39 | ```
40 | This method is called on every request before it is sent. It should route all
41 | absolute URLs to the endpoint for your backend. Requests with
42 | relative URLs for things like localization (./lang/*.js) and
43 | config (./config.json) should remain unchanged.
44 |
45 | 2. Build your backend data store
46 | Your backend needs to store the following information about each user:
47 | * Cloudflare [Client V4 API](https://api.cloudflare.com/) Key
48 | * Cloudflare Client V4 API Email
49 | * Cloudflare [Host API](https://www.cloudflare.com/docs/host-api.html) Key
50 |
51 | 3. In `index.html` create a variable in local storage called `cfEmail` which contains
52 | Cloudflare Client V4 API Email of the current user.
53 |
54 | 4. Build an API Client for the Cloudflare V4 API which adds the necessary headers
55 | to each request.
56 |
57 | 5. Build an API Client for the Cloudflare Host API which adds the Host Key to all requests.
58 |
59 | ## JSON response for endpoint /config
60 |
61 | ```
62 | {
63 | "debug": false,
64 | "featureManagerIsFullZoneProvisioningEnabled": false,
65 | "isDNSPageEnabled": true,
66 | "isSubdomainCheckEnabled": true,
67 | "homePageCards": [
68 | "ApplyDefaultSettingsCard",
69 | "AutomaticHTTPSRewritesCard",
70 | "IpRewriteCard",
71 | "PluginSpecificCacheCard",
72 | "PluginSpecificCacheTagCard"
73 | ],
74 | "moreSettingsCards": {
75 | "container.moresettings.speed": [
76 | "AlwaysOnlineCard",
77 | "BrowserCacheTTLCard",
78 | "CacheLevelCard",
79 | "DevelopmentModeCard",
80 | "IPV6Card",
81 | "ImageOptimizationCard",
82 | "PurgeCacheCard",
83 | ],
84 | "container.moresettings.security": [
85 | "AdvanceDDoSCard",
86 | "BrowserIntegrityCheckCard",
87 | "ChallengePassageCard",
88 | "SecurityLevelCard",
89 | "SSLCard",
90 | "WAFCard"
91 | ]
92 | },
93 | "locale": "en",
94 | "integrationName": "frontend",
95 | "useHostAPILogin": true,
96 | "version": "2.8.1"
97 | }
98 | ```
99 |
--------------------------------------------------------------------------------
/assets/analytics-welcome.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/details-arrows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/details-arrows.png
--------------------------------------------------------------------------------
/assets/global-caching.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/hero-bg-clouds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/hero-bg-clouds.png
--------------------------------------------------------------------------------
/assets/icon-bolt.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icon-lock.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icon-pin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icon-shield.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/icons-seee324dde5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/icons-seee324dde5.png
--------------------------------------------------------------------------------
/assets/icons_2x-s6333fe7591.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/icons_2x-s6333fe7591.png
--------------------------------------------------------------------------------
/assets/layers-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/layers-2x.png
--------------------------------------------------------------------------------
/assets/layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/layers.png
--------------------------------------------------------------------------------
/assets/modal-two-factor-auth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/modal-two-factor-auth.png
--------------------------------------------------------------------------------
/assets/modal-two-factor-auth_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/modal-two-factor-auth_2x.png
--------------------------------------------------------------------------------
/assets/plan-changed-success.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/request-submitted-success.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/security.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/select2-cf-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/select2-cf-white.png
--------------------------------------------------------------------------------
/assets/select2-cf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/select2-cf.png
--------------------------------------------------------------------------------
/assets/select2x2-cf-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/select2x2-cf-white.png
--------------------------------------------------------------------------------
/assets/select2x2-cf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/select2x2-cf.png
--------------------------------------------------------------------------------
/assets/spinner.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/spinner.gif
--------------------------------------------------------------------------------
/assets/throbber.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/throbber.gif
--------------------------------------------------------------------------------
/assets/vertical-range.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/vertical-range.png
--------------------------------------------------------------------------------
/assets/web-optimization.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/yjs-background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/yjs-background.jpg
--------------------------------------------------------------------------------
/assets/yjs-background_2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/assets/yjs-background_2x.jpg
--------------------------------------------------------------------------------
/config.json.sample:
--------------------------------------------------------------------------------
1 | {
2 | "debug": false,
3 | "featureManagerIsFullZoneProvisioningEnabled": true,
4 | "locale": "en"
5 | }
--------------------------------------------------------------------------------
/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/fonts/cloudflare-font.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/cloudflare-font.eot
--------------------------------------------------------------------------------
/fonts/cloudflare-font.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/cloudflare-font.ttf
--------------------------------------------------------------------------------
/fonts/cloudflare-font.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/cloudflare-font.woff
--------------------------------------------------------------------------------
/fonts/fontawesome-cloudflare.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/fontawesome-cloudflare.eot
--------------------------------------------------------------------------------
/fonts/fontawesome-cloudflare.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/fontawesome-cloudflare.ttf
--------------------------------------------------------------------------------
/fonts/fontawesome-cloudflare.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/fontawesome-cloudflare.woff
--------------------------------------------------------------------------------
/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/fonts/opensans-300.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-300.eot
--------------------------------------------------------------------------------
/fonts/opensans-300.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-300.ttf
--------------------------------------------------------------------------------
/fonts/opensans-300.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-300.woff
--------------------------------------------------------------------------------
/fonts/opensans-300.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-300.woff2
--------------------------------------------------------------------------------
/fonts/opensans-300i.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-300i.eot
--------------------------------------------------------------------------------
/fonts/opensans-300i.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-300i.ttf
--------------------------------------------------------------------------------
/fonts/opensans-300i.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-300i.woff
--------------------------------------------------------------------------------
/fonts/opensans-300i.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-300i.woff2
--------------------------------------------------------------------------------
/fonts/opensans-400.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-400.eot
--------------------------------------------------------------------------------
/fonts/opensans-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-400.ttf
--------------------------------------------------------------------------------
/fonts/opensans-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-400.woff
--------------------------------------------------------------------------------
/fonts/opensans-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-400.woff2
--------------------------------------------------------------------------------
/fonts/opensans-400i.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-400i.eot
--------------------------------------------------------------------------------
/fonts/opensans-400i.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-400i.ttf
--------------------------------------------------------------------------------
/fonts/opensans-400i.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-400i.woff
--------------------------------------------------------------------------------
/fonts/opensans-400i.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-400i.woff2
--------------------------------------------------------------------------------
/fonts/opensans-600.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-600.eot
--------------------------------------------------------------------------------
/fonts/opensans-600.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-600.ttf
--------------------------------------------------------------------------------
/fonts/opensans-600.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-600.woff
--------------------------------------------------------------------------------
/fonts/opensans-600.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-600.woff2
--------------------------------------------------------------------------------
/fonts/opensans-700.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-700.eot
--------------------------------------------------------------------------------
/fonts/opensans-700.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-700.ttf
--------------------------------------------------------------------------------
/fonts/opensans-700.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-700.woff
--------------------------------------------------------------------------------
/fonts/opensans-700.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/cloudflare-plugin-frontend/5abb17f31044680e6a0568726370e89efe68c68b/fonts/opensans-700.woff2
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloudflare-plugin-frontend",
3 | "version": "3.2.4",
4 | "description": "A React/Redux frontend for Cloudflare plugins",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "build": "NODE_ENV=development webpack --progress",
8 | "build:production": "NODE_ENV=production webpack --progress",
9 | "clean": "rm -rf .jest && rm -rf node_modules",
10 | "cover": "jest --coverage",
11 | "format": "eslint src/ --fix --ext js --ext json",
12 | "lint": "eslint src/ --ext js --ext json",
13 | "test": "jest",
14 | "update-snapshot": "jest --updateSnapshot"
15 | },
16 | "jest": {
17 | "cacheDirectory": ".jest",
18 | "collectCoverageFrom": [
19 | "src/**/*.{js,jsx}"
20 | ],
21 | "testMatch": [
22 | "**/test/**/*.js"
23 | ],
24 | "testPathIgnorePatterns": [
25 | "/node_modules/|_helpers"
26 | ]
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "ssh://git@github.com:cloudflare/cloudflare-plugin-frontend.git"
31 | },
32 | "keywords": [
33 | "cloudflare"
34 | ],
35 | "author": "Cloudflare",
36 | "license": "BSD-3-Clause",
37 | "devDependencies": {
38 | "@babel/core": "^7.14.0",
39 | "@babel/eslint-parser": "^7.13.14",
40 | "@babel/preset-env": "^7.14.1",
41 | "@babel/preset-react": "^7.13.13",
42 | "babel-jest": "^26.6.3",
43 | "babel-loader": "^8.2.2",
44 | "babel-polyfill": "^6.26.0",
45 | "c3": "^0.4.10",
46 | "cf-component-button": "3.0.0",
47 | "cf-component-card": "^1.1.0",
48 | "cf-component-checkbox": "^3.0.0",
49 | "cf-component-dropdown": "^2.0.0",
50 | "cf-component-flex": "^2.0.0",
51 | "cf-component-form": "^3.0.3",
52 | "cf-component-heading": "^2.0.0",
53 | "cf-component-input": "^3.0.0",
54 | "cf-component-layout": "^1.2.1",
55 | "cf-component-link": "^4.0.0",
56 | "cf-component-list": "^2.0.0",
57 | "cf-component-loading": "^2.0.0",
58 | "cf-component-modal": "5.3.0",
59 | "cf-component-notifications": "^2.0.0",
60 | "cf-component-radio": "^2.0.0",
61 | "cf-component-select": "^2.5.0",
62 | "cf-component-table": "^2.1.0",
63 | "cf-component-tabs": "^5.2.0",
64 | "cf-component-text": "^2.0.0",
65 | "cf-component-textarea": "^3.0.1",
66 | "cf-component-toggle": "^2.0.1",
67 | "cf-util-http": "^2.0.1",
68 | "d3-format": "^1.0.2",
69 | "eslint": "^7.25.0",
70 | "eslint-import-resolver-webpack": "^0.13.0",
71 | "eslint-loader": "^4",
72 | "eslint-plugin-cflint": "^1.0.0",
73 | "eslint-plugin-compat": "^3",
74 | "eslint-plugin-import": "^2.3.0",
75 | "eslint-plugin-json": "^1.2.0",
76 | "eslint-plugin-mocha": "^8",
77 | "eslint-plugin-prettier": "^3",
78 | "eslint-plugin-react": "^7.1.0",
79 | "history": "3.2.1",
80 | "intl": "^1.1.0",
81 | "jest": "^26",
82 | "lodash": "^3.10.1",
83 | "markdown-it": "^8.3.1",
84 | "normalizr": "^2.0.0",
85 | "prettier": "^1.2.2",
86 | "react": "^15.5.4",
87 | "react-dom": "^15.5.4",
88 | "react-gateway": "^2.0.4",
89 | "react-intl": "^2.3.0",
90 | "react-redux": "^5.0.4",
91 | "react-router": "3.0.5",
92 | "react-router-redux": "4.0.7",
93 | "react-test-renderer": "^15.4.2",
94 | "redux": "^3.0.4",
95 | "redux-logger": "^2.6.1",
96 | "redux-mock-store": "^1.2.3",
97 | "redux-thunk": "^1.0.0",
98 | "webpack": "^4"
99 | },
100 | "dependencies": {
101 | "@cloudflare/component-icon": "^5.15.2",
102 | "cf-component-box": "^2.2.1",
103 | "cf-style-provider": "^1.5.0",
104 | "prop-types": "^15.5.8",
105 | "webpack-cli": "^4.6.0"
106 | },
107 | "browserslist": "> 1%"
108 | }
109 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'none',
3 | tabWidth: 2,
4 | semi: true,
5 | singleQuote: true
6 | };
7 |
--------------------------------------------------------------------------------
/src/actions/activeZone.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 | import { asyncDNSRecordFetchList } from './zoneDnsRecords';
3 | import { asyncZoneEntitlements } from './zoneEntitlements';
4 | import { asyncZoneFetchSettings } from './zoneSettings';
5 | import { asyncPluginFetchSettings } from './pluginSettings';
6 |
7 | export function zoneSetActiveZone(zone) {
8 | return {
9 | type: ActionTypes.ZONES_SET_ACTIVE_ZONE,
10 | zone
11 | };
12 | }
13 |
14 | export function asyncZoneSetActiveZone(zone) {
15 | return dispatch => {
16 | dispatch(zoneSetActiveZone(zone));
17 | if (typeof zone.id !== 'undefined') {
18 | dispatch(asyncDNSRecordFetchList(zone.id));
19 | dispatch(asyncPluginFetchSettings(zone.id));
20 | dispatch(asyncZoneFetchSettings(zone.id));
21 | dispatch(asyncZoneEntitlements(zone.id));
22 | }
23 | };
24 | }
25 |
26 | export function zoneSetActiveZoneIfEmpty(zone) {
27 | return (dispatch, getState) => {
28 | if (getState().activeZone.name === '') {
29 | dispatch(asyncZoneSetActiveZone(zone));
30 | }
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/actions/app.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 |
3 | export function applicationInit() {
4 | return {
5 | type: ActionTypes.APPLICATION_INIT
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/src/actions/config.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 | import { asyncIntlFetchTranslations } from './intl';
3 | import { notificationAddError } from './notifications';
4 | import { isLoggedIn, getEmail } from '../utils/Auth/Auth';
5 | import { configGet } from '../utils/PluginAPI/PluginAPI';
6 | import { asyncUserLoginSuccess } from '../actions/user';
7 | import { ABSOLUTE_URL_BASE_KEY } from '../reducers/config';
8 |
9 | export function configFetch() {
10 | return {
11 | type: ActionTypes.CONFIG_FETCH
12 | };
13 | }
14 |
15 | export function configFetchSuccess() {
16 | return {
17 | type: ActionTypes.CONFIG_FETCH_SUCCESS
18 | };
19 | }
20 |
21 | export function configFetchError() {
22 | return {
23 | type: ActionTypes.CONFIG_FETCH_ERROR
24 | };
25 | }
26 |
27 | export function asyncConfigInit() {
28 | return dispatch => {
29 | /*
30 | * 1. Fetch config.js
31 | * 2. Fetch userConfig.js (which may not exist)
32 | * 3. Fetch translations with the language from the config.
33 | */
34 | dispatch(asyncConfigFetch());
35 | if (typeof window.absoluteUrlBase !== 'undefined') {
36 | /*
37 | * Some integrations don't work with relative paths because the URL doesn't match
38 | * the actual file path, this function allows integrations to configure a base absolute
39 | * url path to be used in components/Image. absoluteBaseUrl should be defined globally
40 | * on the page where the SPA is loaded.
41 | */
42 | dispatch(
43 | configUpdateByKey(ABSOLUTE_URL_BASE_KEY, window.absoluteUrlBase)
44 | );
45 | }
46 | //log user in if their email is in local storage
47 | if (isLoggedIn()) {
48 | dispatch(asyncUserLoginSuccess(getEmail()));
49 | }
50 | };
51 | }
52 |
53 | export function asyncConfigFetch() {
54 | return dispatch => {
55 | dispatch(configFetch());
56 | configGet(function(error, response) {
57 | if (response) {
58 | dispatch(configFetchSuccess());
59 | try {
60 | let userConfig = JSON.parse(response.text).result;
61 | Object.keys(userConfig).map(function(key) {
62 | dispatch(configUpdateByKey(key, userConfig[key]));
63 | });
64 | } catch (e) {
65 | dispatch(notificationAddError(`/config - ${e.message}`));
66 | }
67 | dispatch(asyncIntlFetchTranslations());
68 | } else {
69 | dispatch(configFetchError());
70 | }
71 | });
72 | };
73 | }
74 |
75 | export function configUpdateByKey(key, value) {
76 | return {
77 | type: ActionTypes.CONFIG_UPDATE_BY_KEY,
78 | key,
79 | value
80 | };
81 | }
82 |
--------------------------------------------------------------------------------
/src/actions/intl.js:
--------------------------------------------------------------------------------
1 | import http from 'cf-util-http';
2 |
3 | import * as ActionTypes from '../constants/ActionTypes';
4 | import { applicationInit } from './app';
5 | import { notificationAddError } from './notifications';
6 | import { getConfigValue } from '../selectors/config.js';
7 | import { getLocale } from '../selectors/intl.js';
8 |
9 | export function intlFetchTranslations() {
10 | return {
11 | type: ActionTypes.INTL_FETCH_TRANSLATIONS
12 | };
13 | }
14 |
15 | export function intlFetchTranslationsSuccess(locale, translations) {
16 | return {
17 | type: ActionTypes.INTL_FETCH_TRANSLATIONS_SUCCESS,
18 | locale,
19 | translations
20 | };
21 | }
22 |
23 | export function intlFetchTranslationsError(error) {
24 | return {
25 | type: ActionTypes.INTL_FETCH_TRANSLATIONS_ERROR,
26 | error
27 | };
28 | }
29 |
30 | export function asyncIntlFetchTranslations() {
31 | return (dispatch, getState) => {
32 | dispatch(intlFetchTranslations());
33 | let locale = getConfigValue(getState().config, 'locale');
34 | let currentLocale = getLocale(getState());
35 | if (typeof locale != 'undefined' && locale != currentLocale) {
36 | let opts = {};
37 | opts.headers = { Accept: 'application/javascript' };
38 | http.get('./lang/' + locale + '.js', opts, function(error, response) {
39 | if (response) {
40 | let translations = JSON.parse(response.text);
41 | dispatch(intlFetchTranslationsSuccess(locale, translations));
42 | dispatch(applicationInit());
43 | } else {
44 | dispatch(notificationAddError(error));
45 | }
46 | });
47 | }
48 | };
49 | }
50 |
--------------------------------------------------------------------------------
/src/actions/notifications.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 | import { getZoneSettingsValueForZoneId } from '../selectors/zoneSettings';
3 | import _ from 'lodash';
4 |
5 | export function notificationAdd(
6 | level,
7 | message,
8 | localized = false,
9 | persistant = false,
10 | delay = 5000
11 | ) {
12 | return {
13 | type: ActionTypes.NOTIFICATION_ADD,
14 | level,
15 | message,
16 | localized,
17 | persistant,
18 | delay
19 | };
20 | }
21 |
22 | export function notificationAddSuccess(
23 | message,
24 | localized = false,
25 | persistant = false,
26 | delay = 5000
27 | ) {
28 | return notificationAdd('success', message, localized, persistant, delay);
29 | }
30 |
31 | export function notificationAddInfo(
32 | message,
33 | localized = false,
34 | persistant = false,
35 | delay = 5000
36 | ) {
37 | return notificationAdd('info', message, localized, persistant, delay);
38 | }
39 |
40 | export function notificationAddWarning(
41 | message,
42 | localized = false,
43 | persistant = false,
44 | delay = 5000
45 | ) {
46 | return notificationAdd('warning', message, localized, persistant, delay);
47 | }
48 |
49 | export function notificationAddError(
50 | message,
51 | localized = false,
52 | persistant = false,
53 | delay = 5000
54 | ) {
55 | return notificationAdd('error', message, localized, persistant, delay);
56 | }
57 |
58 | export function notificationRemove(key) {
59 | return {
60 | type: ActionTypes.NOTIFICATION_REMOVE,
61 | key
62 | };
63 | }
64 |
65 | export function notificationAddClientAPIError(errorAction, errorMessage) {
66 | return dispatch => {
67 | dispatch(errorAction);
68 | if (typeof errorMessage === 'string') {
69 | dispatch(notificationAddError(errorMessage));
70 | } else {
71 | errorMessage.body.errors.forEach(function(error) {
72 | dispatch(notificationAddError(error.message));
73 | });
74 | }
75 | };
76 | }
77 |
78 | export function notificationAddHostAPIError(errorAction, errorMessage) {
79 | return dispatch => {
80 | dispatch(errorAction);
81 | if (typeof errorMessage === 'string') {
82 | dispatch(notificationAddError(errorMessage));
83 | } else {
84 | dispatch(notificationAddError(errorMessage.body.msg));
85 | }
86 | };
87 | }
88 |
89 | export function notificationHandleDevelopmentMode(activeZoneId) {
90 | return (dispatch, getState) => {
91 | let notifications = getState().notifications;
92 | let developmentModeValue = getZoneSettingsValueForZoneId(
93 | activeZoneId,
94 | 'development_mode',
95 | getState()
96 | );
97 |
98 | var notificationKey = null;
99 | _.forEach(notifications, function(notification) {
100 | if (
101 | notification['level'] === 'warning' &&
102 | notification['message'] === 'warning.developmentmode'
103 | ) {
104 | notificationKey = notification['key'];
105 | }
106 | });
107 |
108 | if (developmentModeValue === 'on' && notificationKey === null) {
109 | dispatch(notificationAddWarning('warning.developmentmode', true, true));
110 | }
111 |
112 | if (developmentModeValue === 'off' && notificationKey !== null) {
113 | dispatch(notificationRemove(notificationKey));
114 | }
115 | };
116 | }
117 |
--------------------------------------------------------------------------------
/src/actions/pluginSettings.js:
--------------------------------------------------------------------------------
1 | import {
2 | pluginSettingListGet,
3 | pluginSettingPatch
4 | } from '../utils/PluginAPI/PluginAPI';
5 | import {
6 | notificationAddSuccess,
7 | notificationAddClientAPIError
8 | } from './notifications';
9 | import * as ActionTypes from '../constants/ActionTypes';
10 |
11 | export function pluginFetchSettings() {
12 | return {
13 | type: ActionTypes.PLUGIN_SETTINGS_FETCH
14 | };
15 | }
16 |
17 | export function pluginFetchSettingsSuccess(zoneId, setting) {
18 | return {
19 | type: ActionTypes.PLUGIN_SETTINGS_FETCH_SUCCESS,
20 | zoneId,
21 | setting
22 | };
23 | }
24 |
25 | export function pluginFetchSettingsError() {
26 | return {
27 | type: ActionTypes.PLUGIN_SETTINGS_FETCH_ERROR
28 | };
29 | }
30 |
31 | export function pluginUpdateSetting(zoneId, setting) {
32 | return {
33 | type: ActionTypes.PLUGIN_SETTING_UPDATE,
34 | zoneId,
35 | setting
36 | };
37 | }
38 |
39 | export function pluginUpdateSettingSuccess(zoneId, setting) {
40 | return {
41 | type: ActionTypes.PLUGIN_SETTING_UPDATE_SUCCESS,
42 | zoneId,
43 | setting
44 | };
45 | }
46 |
47 | export function pluginUpdateSettingError(zoneId, setting) {
48 | return {
49 | type: ActionTypes.PLUGIN_SETTING_UPDATE_ERROR,
50 | zoneId,
51 | setting
52 | };
53 | }
54 |
55 | export function asyncPluginFetchSettings(zoneId) {
56 | return dispatch => {
57 | dispatch(pluginFetchSettings());
58 | pluginSettingListGet({ zoneId: zoneId }, function(error, response) {
59 | if (response) {
60 | dispatch(pluginFetchSettingsSuccess(zoneId, response.body.result));
61 | } else {
62 | dispatch(
63 | notificationAddClientAPIError(pluginFetchSettingsError(), error)
64 | );
65 | }
66 | });
67 | };
68 | }
69 |
70 | export function asyncPluginUpdateSetting(settingName, zoneId, value) {
71 | return (dispatch, getState) => {
72 | let oldSetting = getState().pluginSettings.entities[zoneId][settingName];
73 |
74 | dispatch(pluginUpdateSetting(zoneId, { id: settingName, value: value }));
75 | pluginSettingPatch(zoneId, settingName, value, function(error, response) {
76 | if (response) {
77 | dispatch(pluginUpdateSettingSuccess(zoneId, response.body.result));
78 |
79 | if (settingName == 'default_settings') {
80 | dispatch(
81 | notificationAddSuccess(
82 | 'container.applydefaultsettingscard.success',
83 | true
84 | )
85 | );
86 | }
87 | } else {
88 | dispatch(
89 | notificationAddClientAPIError(
90 | pluginUpdateSettingError(zoneId, oldSetting),
91 | error
92 | )
93 | );
94 | }
95 | });
96 | };
97 | }
98 |
--------------------------------------------------------------------------------
/src/actions/user.js:
--------------------------------------------------------------------------------
1 | import { push } from 'react-router-redux';
2 | import { userAuth, userCreate } from '../utils/CFHostAPI/CFHostAPI';
3 | import { pluginAccountPost } from '../utils/PluginAPI/PluginAPI';
4 | import {
5 | notificationAddHostAPIError,
6 | notificationAddClientAPIError
7 | } from './notifications';
8 | import * as ActionTypes from '../constants/ActionTypes';
9 | import * as UrlPaths from '../constants/UrlPaths';
10 | import { getConfigValue } from '../selectors/config';
11 |
12 | import { asyncFetchZones } from './zones';
13 |
14 | export function userLogin() {
15 | return {
16 | type: ActionTypes.USER_LOGIN
17 | };
18 | }
19 |
20 | /*
21 | * always call asyncUserLoginSuccess instead of userLoginSuccess
22 | * this is how we trigger GETs that need to occur after a successful login.
23 | * The user can also be logged in from localStorage automatically when the app loads
24 | * which is why this logic doens't live in asyncLogin().
25 | */
26 | export function userLoginSuccess(email) {
27 | return {
28 | type: ActionTypes.USER_LOGIN_SUCCESS,
29 | email
30 | };
31 | }
32 |
33 | export function asyncUserLoginSuccess(email) {
34 | return (dispatch, getState) => {
35 | dispatch(userLoginSuccess(email));
36 | dispatch(asyncFetchZones());
37 | let route = UrlPaths.HOME_PAGE;
38 | if (getConfigValue(getState().config, 'integrationName') === 'cpanel') {
39 | route = UrlPaths.DOMAINS_OVERVIEW_PAGE;
40 | }
41 | dispatch(push(route));
42 | };
43 | }
44 |
45 | export function userLoginError(error) {
46 | return {
47 | type: ActionTypes.USER_LOGIN_ERROR,
48 | error
49 | };
50 | }
51 |
52 | export function asyncLogin(email, password) {
53 | return dispatch => {
54 | dispatch(userLogin());
55 | userAuth({ cloudflare_email: email, cloudflare_pass: password }, function(
56 | error,
57 | response
58 | ) {
59 | if (response) {
60 | dispatch(
61 | asyncUserLoginSuccess(response.body.response.cloudflare_email)
62 | );
63 | } else {
64 | dispatch(notificationAddHostAPIError(userLoginError(), error));
65 | }
66 | });
67 | };
68 | }
69 |
70 | export function asyncAPILogin(email, apiKey) {
71 | return dispatch => {
72 | dispatch(userLogin());
73 | pluginAccountPost(email, apiKey, function(error, response) {
74 | if (response) {
75 | dispatch(asyncUserLoginSuccess(email));
76 | } else {
77 | dispatch(userLoginError());
78 | dispatch(notificationAddClientAPIError(userLoginError(), error));
79 | }
80 | });
81 | };
82 | }
83 |
84 | export function userLogout() {
85 | return {
86 | type: ActionTypes.USER_LOGOUT
87 | };
88 | }
89 |
90 | export function userSignup() {
91 | return {
92 | type: ActionTypes.USER_SIGNUP
93 | };
94 | }
95 |
96 | export function userSignupSuccess() {
97 | return {
98 | type: ActionTypes.USER_SIGNUP_SUCCESS
99 | };
100 | }
101 |
102 | export function userSignupError() {
103 | return {
104 | type: ActionTypes.USER_SIGNUP_ERROR
105 | };
106 | }
107 |
108 | export function asyncUserSignup(email, password) {
109 | return dispatch => {
110 | dispatch(userSignup());
111 | userCreate({ cloudflare_email: email, cloudflare_pass: password }, function(
112 | error,
113 | response
114 | ) {
115 | if (response) {
116 | dispatch(userSignupSuccess());
117 | dispatch(asyncLogin(email, password));
118 | } else {
119 | dispatch(notificationAddHostAPIError(userSignupError(), error));
120 | }
121 | });
122 | };
123 | }
124 |
--------------------------------------------------------------------------------
/src/actions/zoneDnsRecords.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 | import {
3 | zoneDNSRecordGetAll,
4 | zoneDNSRecordPostNew,
5 | zoneDNSRecordPatch
6 | } from '../utils/CFClientV4API/CFClientV4API';
7 | import { notificationAddClientAPIError } from './notifications';
8 |
9 | export function dnsRecordClearAll(zoneId) {
10 | return {
11 | type: ActionTypes.DNS_RECORD_CLEAR_ALL,
12 | zoneId
13 | };
14 | }
15 |
16 | export function dnsRecordCreate(name) {
17 | return {
18 | type: ActionTypes.DNS_RECORD_CREATE,
19 | name
20 | };
21 | }
22 |
23 | export function dnsRecordCreateSuccess(zoneId, dnsRecord) {
24 | return {
25 | type: ActionTypes.DNS_RECORD_CREATE_SUCCESS,
26 | zoneId,
27 | dnsRecord
28 | };
29 | }
30 |
31 | export function dnsRecordCreateError() {
32 | return {
33 | type: ActionTypes.DNS_RECORD_CREATE_ERROR
34 | };
35 | }
36 |
37 | export function asyncDNSRecordCreate(zoneId, type, name, content) {
38 | return dispatch => {
39 | dispatch(dnsRecordCreate(name));
40 | zoneDNSRecordPostNew(
41 | { zoneId: zoneId, type: type, name: name, content: content },
42 | function(error, response) {
43 | if (response) {
44 | dispatch(dnsRecordCreateSuccess(zoneId, response.body.result));
45 | //CloudFlare defaults new records with proxied = false.
46 | dispatch(asyncDNSRecordUpdate(zoneId, response.body.result, true));
47 | } else {
48 | dispatch(
49 | notificationAddClientAPIError(dnsRecordCreateError(), error)
50 | );
51 | }
52 | }
53 | );
54 | };
55 | }
56 |
57 | export function dnsRecordFetchList() {
58 | return {
59 | type: ActionTypes.DNS_RECORD_FETCH_LIST
60 | };
61 | }
62 |
63 | export function dnsRecordFetchListSuccess(zoneId, dnsRecords) {
64 | return {
65 | type: ActionTypes.DNS_RECORD_FETCH_LIST_SUCCESS,
66 | zoneId,
67 | dnsRecords
68 | };
69 | }
70 |
71 | export function dnsRecordFetchListError() {
72 | return {
73 | type: ActionTypes.DNS_RECORD_FETCH_LIST_ERROR
74 | };
75 | }
76 |
77 | export function asyncDNSRecordFetchList(zoneId) {
78 | return dispatch => {
79 | dispatch(dnsRecordFetchList());
80 | zoneDNSRecordGetAll(zoneId, function(error, response) {
81 | if (response) {
82 | dispatch(dnsRecordFetchListSuccess(zoneId, response.body.result));
83 | } else {
84 | dispatch(
85 | notificationAddClientAPIError(dnsRecordFetchListError(), error)
86 | );
87 | }
88 | });
89 | };
90 | }
91 |
92 | export function dnsRecordUpdate(name) {
93 | return {
94 | type: ActionTypes.DNS_RECORD_UPDATE,
95 | name
96 | };
97 | }
98 |
99 | export function dnsRecordUpdateSuccess(zoneId, dnsRecord) {
100 | return {
101 | type: ActionTypes.DNS_RECORD_UPDATE_SUCCESS,
102 | zoneId,
103 | dnsRecord
104 | };
105 | }
106 |
107 | export function dnsRecordUpdateError() {
108 | return {
109 | type: ActionTypes.DNS_RECORD_UPDATE_ERROR
110 | };
111 | }
112 |
113 | export function asyncDNSRecordUpdate(zoneId, dnsRecord, proxied) {
114 | return dispatch => {
115 | dispatch(dnsRecordUpdate(dnsRecord.name));
116 | zoneDNSRecordPatch(
117 | { zoneId: zoneId, dnsRecordId: dnsRecord.id, proxied: proxied },
118 | function(error, response) {
119 | if (response) {
120 | dispatch(dnsRecordUpdateSuccess(zoneId, response.body.result));
121 | } else {
122 | dispatch(
123 | notificationAddClientAPIError(dnsRecordUpdateError(), error)
124 | );
125 | }
126 | }
127 | );
128 | };
129 | }
130 |
--------------------------------------------------------------------------------
/src/actions/zoneEntitlements.js:
--------------------------------------------------------------------------------
1 | import { zoneGetEntitlements } from '../utils/CFClientV4API/CFClientV4API';
2 | import { notificationAddClientAPIError } from './notifications';
3 | import * as ActionTypes from '../constants/ActionTypes';
4 |
5 | export function zoneEntitlements() {
6 | return {
7 | type: ActionTypes.ZONE_ENTITLEMENTS
8 | };
9 | }
10 |
11 | export function zoneEntitlementsSuccess(zoneId, zoneEntitlements) {
12 | return {
13 | type: ActionTypes.ZONE_ENTITLEMENTS_SUCCESS,
14 | zoneId,
15 | zoneEntitlements
16 | };
17 | }
18 |
19 | export function zoneEntitlementsError() {
20 | return {
21 | type: ActionTypes.ZONE_ENTITLEMENTS_ERROR
22 | };
23 | }
24 |
25 | export function asyncZoneEntitlements(zoneId) {
26 | return dispatch => {
27 | dispatch(zoneEntitlements());
28 | zoneGetEntitlements(zoneId, function(error, response) {
29 | if (response) {
30 | dispatch(zoneEntitlementsSuccess(zoneId, response.body.result));
31 | } else {
32 | dispatch(notificationAddClientAPIError(zoneEntitlementsError(), error));
33 | }
34 | });
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/src/actions/zonePurgeCache.js:
--------------------------------------------------------------------------------
1 | import { zonePurgeCache as v4ZonePurgeCache } from '../utils/CFClientV4API/CFClientV4API';
2 | import {
3 | notificationAddClientAPIError,
4 | notificationAddSuccess
5 | } from './notifications';
6 | import * as ActionTypes from '../constants/ActionTypes';
7 |
8 | export function zonePurgeCache() {
9 | return {
10 | type: ActionTypes.ZONE_PURGE_CACHE
11 | };
12 | }
13 |
14 | export function zonePurgeCacheSuccess() {
15 | return {
16 | type: ActionTypes.ZONE_PURGE_CACHE_SUCCESS
17 | };
18 | }
19 |
20 | export function zonePurgeCacheError() {
21 | return {
22 | type: ActionTypes.ZONE_PURGE_CACHE_ERROR
23 | };
24 | }
25 |
26 | export function asyncZonePurgeCacheIndividualFiles(zoneId, files) {
27 | return dispatch => {
28 | dispatch(zonePurgeCache());
29 |
30 | // Get an unstructured string like " \nhttp://example.com\n\n \n http://example.com/hey \n "
31 | // Return ["http://example.com", "http://example.com/hey"]
32 | var formatedFiles = files.replace(/^\s+|\s+$/g, '').split(/\s+/);
33 |
34 | v4ZonePurgeCache({ zoneId: zoneId, files: formatedFiles }, function(
35 | error,
36 | response
37 | ) {
38 | if (response) {
39 | dispatch(zonePurgeCacheSuccess());
40 | dispatch(
41 | notificationAddSuccess('container.purgeCacheCard.success', true)
42 | );
43 | } else {
44 | dispatch(notificationAddClientAPIError(zonePurgeCacheError(), error));
45 | }
46 | });
47 | };
48 | }
49 |
50 | export function asyncZonePurgeCacheEverything(zoneId) {
51 | return dispatch => {
52 | dispatch(zonePurgeCache());
53 | v4ZonePurgeCache({ zoneId: zoneId, purge_everything: true }, function(
54 | error,
55 | response
56 | ) {
57 | if (response) {
58 | dispatch(zonePurgeCacheSuccess());
59 | dispatch(
60 | notificationAddSuccess('container.purgeCacheByURLCard.success', true)
61 | );
62 | } else {
63 | dispatch(notificationAddClientAPIError(zonePurgeCacheError(), error));
64 | }
65 | });
66 | };
67 | }
68 |
--------------------------------------------------------------------------------
/src/actions/zoneSettings.js:
--------------------------------------------------------------------------------
1 | import {
2 | zoneGetSettings,
3 | zonePatchSetting
4 | } from '../utils/CFClientV4API/CFClientV4API';
5 | import {
6 | notificationAddClientAPIError,
7 | notificationHandleDevelopmentMode
8 | } from './notifications';
9 | import * as ActionTypes from '../constants/ActionTypes';
10 |
11 | export function zoneFetchSettings() {
12 | return {
13 | type: ActionTypes.ZONE_FETCH_SETTINGS
14 | };
15 | }
16 |
17 | export function zoneFetchSettingsSuccess(zoneId, zoneSettings) {
18 | return {
19 | type: ActionTypes.ZONE_FETCH_SETTINGS_SUCCESS,
20 | zoneId,
21 | zoneSettings
22 | };
23 | }
24 |
25 | export function zoneFetchSettingsError() {
26 | return {
27 | type: ActionTypes.ZONE_FETCH_SETTINGS_ERROR
28 | };
29 | }
30 |
31 | export function asyncZoneFetchSettings(zoneId) {
32 | return dispatch => {
33 | dispatch(zoneFetchSettings());
34 | zoneGetSettings(zoneId, function(error, response) {
35 | if (response) {
36 | dispatch(zoneFetchSettingsSuccess(zoneId, response.body.result));
37 |
38 | // Lastly check if development mode value and add/remove notification
39 | dispatch(notificationHandleDevelopmentMode(zoneId));
40 | } else {
41 | dispatch(
42 | notificationAddClientAPIError(zoneFetchSettingsError(), error)
43 | );
44 | }
45 | });
46 | };
47 | }
48 |
49 | export function zoneUpdateSetting(zoneId, setting) {
50 | return {
51 | type: ActionTypes.ZONE_UPDATE_SETTING,
52 | zoneId,
53 | setting
54 | };
55 | }
56 |
57 | export function zoneUpdateSettingSuccess(zoneId, setting) {
58 | return {
59 | type: ActionTypes.ZONE_UPDATE_SETTING_SUCCESS,
60 | zoneId,
61 | setting
62 | };
63 | }
64 |
65 | export function zoneUpdateSettingError(zoneId, setting) {
66 | return {
67 | type: ActionTypes.ZONE_UPDATE_SETTING_ERROR,
68 | zoneId,
69 | setting
70 | };
71 | }
72 |
73 | export function asyncZoneUpdateSetting(settingName, zoneId, value) {
74 | return (dispatch, getState) => {
75 | let oldSetting = getState().zoneSettings.entities[zoneId][settingName];
76 |
77 | dispatch(zoneUpdateSetting(zoneId, { id: settingName, value: value }));
78 | zonePatchSetting(settingName, zoneId, value, function(error, response) {
79 | if (response) {
80 | dispatch(zoneUpdateSettingSuccess(zoneId, response.body.result));
81 |
82 | // Lastly check if development mode value and add/remove notification
83 | dispatch(notificationHandleDevelopmentMode(zoneId));
84 | } else {
85 | dispatch(
86 | notificationAddClientAPIError(
87 | zoneUpdateSettingError(zoneId, oldSetting),
88 | error
89 | )
90 | );
91 | }
92 | });
93 | };
94 | }
95 |
--------------------------------------------------------------------------------
/src/actions/zones.js:
--------------------------------------------------------------------------------
1 | import {
2 | zoneGetAll,
3 | zoneDeleteZone
4 | } from '../utils/CFClientV4API/CFClientV4API';
5 | import { notificationAddClientAPIError } from './notifications';
6 | import * as ActionTypes from '../constants/ActionTypes';
7 | import { zoneSetActiveZoneIfEmpty } from './activeZone';
8 | import { dnsRecordClearAll } from './zoneDnsRecords';
9 |
10 | export function zoneDelete() {
11 | return {
12 | type: ActionTypes.ZONES_DELETE_ZONE
13 | };
14 | }
15 |
16 | export function zoneDeleteSuccess() {
17 | return {
18 | type: ActionTypes.ZONES_DELETE_ZONE_SUCCESS
19 | };
20 | }
21 |
22 | export function zoneDeleteError(error) {
23 | return {
24 | type: ActionTypes.ZONES_DELETE_ZONE_ERROR,
25 | error
26 | };
27 | }
28 |
29 | export function asyncZoneDelete(zoneId) {
30 | return dispatch => {
31 | dispatch(zoneDelete(zoneId));
32 |
33 | zoneDeleteZone(zoneId, function(error, response) {
34 | if (response) {
35 | dispatch(zoneDeleteSuccess());
36 | dispatch(dnsRecordClearAll(zoneId));
37 | //after we provision a cname refresh the zone list
38 | dispatch(asyncFetchZones());
39 | } else {
40 | dispatch(notificationAddClientAPIError(zoneDeleteError(), error));
41 | }
42 | });
43 | };
44 | }
45 |
46 | export function zoneFetch() {
47 | return {
48 | type: ActionTypes.ZONES_FETCH
49 | };
50 | }
51 |
52 | export function zoneFetchSuccess(zoneList) {
53 | return {
54 | type: ActionTypes.ZONES_FETCH_SUCCESS,
55 | zoneList
56 | };
57 | }
58 |
59 | export function zoneFetchError(error) {
60 | return {
61 | type: ActionTypes.ZONES_FETCH_ERROR,
62 | error
63 | };
64 | }
65 |
66 | export function asyncFetchZones() {
67 | return dispatch => {
68 | dispatch(zoneFetch());
69 |
70 | zoneGetAll(function(error, response) {
71 | if (response) {
72 | dispatch(zoneFetchSuccess(response.body.result));
73 | const activeZone = response.body.result.find(
74 | zone => zone.status === 'active'
75 | );
76 | if (activeZone) {
77 | dispatch(zoneSetActiveZoneIfEmpty(activeZone));
78 | } else if (response.body.result[0]) {
79 | dispatch(zoneSetActiveZoneIfEmpty(response.body.result[0]));
80 | }
81 | } else {
82 | dispatch(notificationAddClientAPIError(zoneFetchError(), error));
83 | }
84 | });
85 | };
86 | }
87 |
--------------------------------------------------------------------------------
/src/components/AppNavigationLiNode/AppNavigationLiNode.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { FormattedMessage } from 'react-intl';
4 |
5 | import Link from 'cf-component-link';
6 |
7 | export default class AppNavigationLiNode extends Component {
8 | render() {
9 | return (
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | }
30 | }
31 |
32 | AppNavigationLiNode.propTypes = {
33 | onClick: PropTypes.func.isRequired,
34 | title: PropTypes.string.isRequired,
35 | children: PropTypes.node
36 | };
37 |
--------------------------------------------------------------------------------
/src/components/BenefitsFeature/BenefitsFeature.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export default class BenefitsFeature extends Component {
5 | render() {
6 | /*
7 | * These styles are stolen from the marketing site and aren't in our CSS
8 | */
9 | let divStyles = {
10 | padding: '30px 15px 30px 15px'
11 | };
12 | let iconStyles = {
13 | display: 'block',
14 | width: '75px',
15 | height: 'auto',
16 | maxWidth: '100px',
17 | margin: '0 auto'
18 | };
19 | let largeLinkStyles = {
20 | padding: '20px 0 0 0',
21 | textAlign: 'center',
22 | fontSize: '16px',
23 | color: '#333333',
24 | width: '100%',
25 | display: 'block'
26 | };
27 | let columnPStyles = {
28 | padding: '10px 0 0 0',
29 | fontSize: '12px',
30 | textAlign: 'center',
31 | color: '#9A9D9E'
32 | };
33 |
34 | return (
35 |
36 |

37 |
38 | {this.props.title}
39 |
40 |
{this.props.description}
41 |
42 | );
43 | }
44 | }
45 |
46 | BenefitsFeature.propTypes = {
47 | imgSrc: PropTypes.string.isRequired,
48 | title: PropTypes.string.isRequired,
49 | description: PropTypes.string.isRequired
50 | };
51 |
--------------------------------------------------------------------------------
/src/components/C3Wrapper/C3Wrapper.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import c3 from 'c3';
4 |
5 | export default class C3Wrapper extends Component {
6 | updateC3(props) {
7 | props.config.bindto = this._container;
8 | if (this._chart) {
9 | this._chart.destroy();
10 | }
11 | this._chart = c3.generate(props.config);
12 | }
13 |
14 | componentDidMount() {
15 | this.updateC3(this.props);
16 | }
17 |
18 | componentWillReceiveProps(props) {
19 | this.updateC3(props);
20 | }
21 |
22 | componentWillUnmount() {
23 | this._chart.destroy();
24 | }
25 |
26 | render() {
27 | return (this._container = chart)} />;
28 | }
29 | }
30 |
31 | C3Wrapper.propTypes = {
32 | config: PropTypes.object.isRequired
33 | };
34 |
--------------------------------------------------------------------------------
/src/components/CustomCardControl/CustomCardControl.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { connect } from 'react-redux';
4 | import { FormattedMessage, injectIntl } from 'react-intl';
5 | import { CardControl } from 'cf-component-card';
6 | import { Button } from 'cf-component-button';
7 | import {
8 | planNeedsUpgrade,
9 | getLocalizedPlanId,
10 | FREE_PLAN,
11 | getPlanUpdateParam
12 | } from '../../constants/Plans.js';
13 | import { getConfigValue } from '../../selectors/config.js';
14 | import { openWindow720x720 } from '../../utils/utils.js';
15 |
16 | class CustomCardControl extends Component {
17 | render() {
18 | let {
19 | activeZone,
20 | purchaseSubscription,
21 | purchaseSubscriptionPath
22 | } = this.props;
23 |
24 | if (purchaseSubscription && purchaseSubscriptionPath) {
25 | let upgradeLink = `https://dash.cloudflare.com/${activeZone.account.id}/${activeZone.name}${purchaseSubscriptionPath}`;
26 | return (
27 |
28 | {purchaseSubscription ? (
29 |
35 | ) : (
36 | this.props.children
37 | )}
38 |
39 | );
40 | }
41 |
42 | var currentPlan = this.props.hasOwnProperty('currentPlan')
43 | ? this.props.currentPlan
44 | : FREE_PLAN;
45 | var minimumPlan = this.props.hasOwnProperty('minimumPlan')
46 | ? this.props.minimumPlan
47 | : FREE_PLAN;
48 | var needToUpgrade = planNeedsUpgrade(currentPlan, minimumPlan);
49 | var localizedPlanId = getLocalizedPlanId(minimumPlan);
50 |
51 | // Upgrade Plan Page can get the following parameters
52 | // /a/upgrade-plan?pt=[f|p|b|]
53 | let upgradeLink = `https://dash.cloudflare.com?to=/:account/${activeZone.name}/update-plan`;
54 | upgradeLink += '&pt=' + getPlanUpdateParam(minimumPlan);
55 |
56 | return (
57 |
58 | {needToUpgrade ? (
59 |
66 | ) : (
67 | this.props.children
68 | )}
69 |
70 | );
71 | }
72 | }
73 |
74 | CustomCardControl.propTypes = {
75 | name: PropTypes.string,
76 | indentifier: PropTypes.string.isRequired,
77 | integrationName: PropTypes.string,
78 | activeZone: PropTypes.object.isRequired,
79 | currentPlan: PropTypes.string,
80 | minimumPlan: PropTypes.string,
81 | purchaseSubscription: PropTypes.bool,
82 | purchaseSubscriptionPath: PropTypes.string,
83 | children: PropTypes.node
84 | };
85 |
86 | function mapStateToProps(state) {
87 | return {
88 | integrationName: getConfigValue(state.config, 'integrationName'),
89 | activeZone: state.activeZone
90 | };
91 | }
92 | export default injectIntl(connect(mapStateToProps)(CustomCardControl));
93 |
--------------------------------------------------------------------------------
/src/components/FeatureManager/FeatureManager.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export default class FeatureManager extends Component {
5 | render() {
6 | return (
7 |
8 | {this.props.isEnabled && this.props.children}
9 | {!this.props.isEnabled && this.props.error && this.props.error}
10 |
11 | );
12 | }
13 | }
14 |
15 | FeatureManager.propTypes = {
16 | isEnabled: PropTypes.bool.isRequired,
17 | error: PropTypes.string,
18 | children: PropTypes.node
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/FormattedMarkdown/FormattedMarkdown.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { connect } from 'react-redux';
4 | import { injectIntl } from 'react-intl';
5 | import MarkdownIt from 'markdown-it';
6 |
7 | import { formatMessageForIntegration } from '../../utils/utils';
8 | import { getConfigValue } from '../../selectors/config.js';
9 |
10 | // To support different translations for differents integrations add
11 | // "mycomponent.myfeature.integrationName1"
12 | // "mycomponent.myfeature.integrationName2"
13 | // ...
14 | // and the default translation "mycomponent.myfeature"
15 | //
16 | // Afterwards call
17 | //
18 | class FormattedMarkdown extends Component {
19 | render() {
20 | let { integrationName, formattedMessage } = this.props;
21 |
22 | if (!formattedMessage) {
23 | formattedMessage = formatMessageForIntegration(
24 | this.props.intl,
25 | this.props.text,
26 | integrationName
27 | );
28 | }
29 |
30 | var md = new MarkdownIt();
31 |
32 | return (
33 |
38 | );
39 | }
40 | }
41 |
42 | FormattedMarkdown.propTypes = {
43 | text: PropTypes.string,
44 | formattedMessage: PropTypes.string,
45 | intl: PropTypes.object,
46 | integrationName: PropTypes.string
47 | };
48 |
49 | function mapStateToProps(state) {
50 | return {
51 | integrationName: getConfigValue(state.config, 'integrationName')
52 | };
53 | }
54 | export default injectIntl(connect(mapStateToProps)(FormattedMarkdown));
55 |
--------------------------------------------------------------------------------
/src/components/GradientBanner/GradientBanner.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createComponent } from 'cf-style-container';
3 |
4 | const gradientStyles = () => ({
5 | width: '100%',
6 | height: '150px',
7 | display: 'flex',
8 | justifyContent: 'center',
9 | background: 'linear-gradient(left, #8176B5, #76C4E2)',
10 | backgroundRepeat: 'no-repeat',
11 | backgroundSize: '100% 250px',
12 | backgroundColor: '#e6e6e6'
13 | });
14 |
15 | const GradientBanner = ({ className, children }) => {
16 | return React.createElement('div', { className }, children);
17 | };
18 |
19 | export default createComponent(gradientStyles, GradientBanner);
20 |
--------------------------------------------------------------------------------
/src/components/MarketingFeature/MarketingFeature.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export default class MarketingFeature extends Component {
5 | render() {
6 | /*
7 | * These styles are stolen from the marketing site and aren't in our CSS
8 | */
9 | let divStyles = {
10 | padding: '30px 15px 30px 15px'
11 | };
12 | let iconStyles = {
13 | display: 'block',
14 | width: '40px',
15 | height: 'auto',
16 | maxWidth: '100px',
17 | margin: '0 auto'
18 | };
19 | let largeLinkStyles = {
20 | padding: '20px 0 0 0',
21 | textAlign: 'center',
22 | fontSize: '16px',
23 | color: '#2f7bbf',
24 | width: '100%',
25 | display: 'block'
26 | };
27 | let columnPStyles = {
28 | padding: '10px 0 0 0',
29 | fontSize: '12px',
30 | textAlign: 'center'
31 | };
32 |
33 | return (
34 |
35 |

36 |
37 | {this.props.title}
38 |
39 |
{this.props.description}
40 |
41 | );
42 | }
43 | }
44 |
45 | MarketingFeature.propTypes = {
46 | imgSrc: PropTypes.string.isRequired,
47 | title: PropTypes.string.isRequired,
48 | description: PropTypes.string.isRequired
49 | };
50 |
--------------------------------------------------------------------------------
/src/components/RenderCardsDynamically/RenderCardsDynamically.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import _ from 'lodash';
3 | import { cardMapper } from '../../utils/ImportCards';
4 |
5 | export function renderCards(cards) {
6 | // While rendering config.*isEnabled settings are not being checked
7 | return _.map(cards, function(cardName, i) {
8 | return React.createElement(cardMapper[cardName], { key: i });
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/TimeSeriesChart/TimeSeriesChart.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import C3Wrapper from '../C3Wrapper/C3Wrapper';
4 |
5 | export default class TimeSeriesChart extends Component {
6 | /**
7 | * let xAxisValues = [{
8 | * 'label': 'x',
9 | * 'values': [1,2,3,4,5]
10 | * },
11 | * {
12 | * 'label': 'cached',
13 | * 'values: [1,2,3,4,5]
14 | * }];
15 | */
16 | render() {
17 | let { xAxisValues, yAxisLabel } = this.props;
18 |
19 | let columns = [];
20 |
21 | //First row of columms is the x axis key
22 | //The first element in the remaining rows column has to be the label
23 | for (let i = 0; i < xAxisValues.length; i++) {
24 | let label = xAxisValues[i].label;
25 | let values = xAxisValues[i].values;
26 |
27 | if (label && values) {
28 | columns.push([label].concat(values));
29 | }
30 | }
31 |
32 | //for some reason this only renders correctly if we put the xformat in data AND axis
33 | let xformat = '%m/%d';
34 |
35 | let config = {
36 | data: {
37 | x: 'x',
38 | xFormat: xformat,
39 | columns: columns
40 | },
41 | axis: {
42 | x: {
43 | type: 'timeseries',
44 | tick: {
45 | format: xformat
46 | }
47 | },
48 | y: {
49 | label: yAxisLabel
50 | }
51 | }
52 | };
53 |
54 | return (
55 |
56 |
57 |
58 | );
59 | }
60 | }
61 |
62 | TimeSeriesChart.propTypes = {
63 | xAxisValues: PropTypes.array,
64 | yAxisLabel: PropTypes.string
65 | };
66 |
--------------------------------------------------------------------------------
/src/constants/Plans.js:
--------------------------------------------------------------------------------
1 | const LOCALIZED_PRO_PLAN_ID = 'constants.plans.pro';
2 | const LOCALIZED_BIZ_PLAN_ID = 'constants.plans.biz';
3 | const LOCALIZED_ENT_PLAN_ID = 'constants.plans.ent';
4 |
5 | export const FREE_PLAN = 'free';
6 | export const PRO_PLAN = 'pro';
7 | export const BIZ_PLAN = 'business';
8 | export const ENT_PLAN = 'enterprise';
9 |
10 | export function planNeedsUpgrade(currentPlan, minimumPlan) {
11 | var planList = {};
12 | planList[FREE_PLAN] = 0;
13 | planList[PRO_PLAN] = 1;
14 | planList[BIZ_PLAN] = 2;
15 | planList[ENT_PLAN] = 3;
16 |
17 | return planList[currentPlan] < planList[minimumPlan];
18 | }
19 |
20 | export function getPlanUpdateParam(minimumPlan) {
21 | switch (minimumPlan) {
22 | case FREE_PLAN:
23 | return 'f';
24 | case PRO_PLAN:
25 | return 'p';
26 | case BIZ_PLAN:
27 | return 'b';
28 | // Note that we dont support ENT plan
29 | default:
30 | // By default return a pro plan
31 | return 'p';
32 | }
33 | }
34 |
35 | export function getLocalizedPlanId(planName) {
36 | var localizedPlanName = planName;
37 | switch (planName) {
38 | case PRO_PLAN:
39 | localizedPlanName = LOCALIZED_PRO_PLAN_ID;
40 | break;
41 | case BIZ_PLAN:
42 | localizedPlanName = LOCALIZED_BIZ_PLAN_ID;
43 | break;
44 | case ENT_PLAN:
45 | localizedPlanName = LOCALIZED_ENT_PLAN_ID;
46 | break;
47 | default:
48 | // This should never happen
49 | break;
50 | }
51 |
52 | return localizedPlanName;
53 | }
54 |
--------------------------------------------------------------------------------
/src/constants/Schemas.js:
--------------------------------------------------------------------------------
1 | import { normalize, Schema, arrayOf } from 'normalizr';
2 |
3 | const zoneSchema = new Schema('zones', { idAttribute: 'name' });
4 | const zoneEntitlementsSchema = new Schema('entitlements', {
5 | idAttribute: 'id'
6 | });
7 |
8 | export function normalizeZoneByIdGetAll(zoneId, result) {
9 | var zoneSchemaById = new Schema(zoneId, { idAttribute: 'id' });
10 | return normalize(result, arrayOf(zoneSchemaById));
11 | }
12 |
13 | export function normalizeZoneGetAll(result) {
14 | return normalize(result, arrayOf(zoneSchema));
15 | }
16 |
17 | export function normalizeZoneEntitlements(result) {
18 | return normalize(result, arrayOf(zoneEntitlementsSchema));
19 | }
20 |
--------------------------------------------------------------------------------
/src/constants/UrlPaths.js:
--------------------------------------------------------------------------------
1 | export const ANALYTICS_PAGE = '/analytics';
2 | export const CLOUDFLARE_API_KB_ARTICLE_PAGE =
3 | 'https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-CloudFlare-API-key-';
4 | export const CLOUDFLARE_FORGOT_PASSWORD_PAGE =
5 | 'https://dash.cloudflare.com/forgot-password';
6 | export const CLOUDFLARE_SIGNUP_PAGE = 'https://dash.cloudflare.com/sign-up';
7 | export const CLOUDFLARE_DASHBOARD_PAGE = 'https://dash.cloudflare.com/overview';
8 | export const DOMAINS_OVERVIEW_PAGE = '/zones';
9 | export const HOME_PAGE = '/home';
10 | export const MORE_SETTINGS_PAGE = '/more-settings';
11 | export const LOGIN_PAGE = '/login';
12 | export const SIGN_UP_PAGE = '/sign-up';
13 | export const SUPPORT_PAGE = 'https://support.cloudflare.com/hc/en-us/';
14 | export const TERMS_AND_CONDITIONS_PAGE = 'https://www.cloudflare.com/terms';
15 | export const PRIVACY_POLICY_PAGE = 'https://www.cloudflare.com/security-policy';
16 | export const CLOUDFLARE_ACCOUNT_PAGE = 'https://dash.cloudflare.com/profile';
17 | export const CLOUDFLARE_ADD_SITE_PAGE =
18 | 'https://dash.cloudflare.com/?to=/:account/add-site';
19 |
--------------------------------------------------------------------------------
/src/containers/ActivationCheckCard/ActivationCheckCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 |
5 | import { asyncZoneActivationCheck } from '../../actions/zoneProvision';
6 | import { Card, CardSection, CardContent, CardControl } from 'cf-component-card';
7 | import { Button } from 'cf-component-button';
8 | import { List, ListItem } from 'cf-component-list';
9 |
10 | class ActivationCheckCard extends Component {
11 | handleButtonClick() {
12 | let { activeZone, dispatch } = this.props;
13 | dispatch(asyncZoneActivationCheck(activeZone.id));
14 | }
15 |
16 | render() {
17 | const { formatMessage } = this.props.intl;
18 | let { zone } = this.props;
19 |
20 | return (
21 |
22 |
23 |
24 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 | {zone.name_servers.map(nameserver => (
40 | {nameserver}
41 | ))}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
54 |
55 |
56 |
57 |
58 | );
59 | }
60 | }
61 |
62 | function mapStateToProps(state) {
63 | return {
64 | activeZone: state.activeZone,
65 | zone: state.zones.entities.zones[state.activeZone.name]
66 | };
67 | }
68 | export default injectIntl(connect(mapStateToProps)(ActivationCheckCard));
69 |
--------------------------------------------------------------------------------
/src/containers/ActiveZoneSelector/ActiveZoneSelector.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { injectIntl } from 'react-intl';
4 | import _ from 'lodash';
5 |
6 | import Select from 'cf-component-select';
7 | import { notificationAddWarning } from '../../actions/notifications';
8 | import { getConfigValue } from '../../selectors/config';
9 | import { asyncZoneSetActiveZone } from '../../actions/activeZone';
10 | import { isSubdomain } from '../../utils/utils';
11 |
12 | class ActiveZoneSelector extends Component {
13 | constructor(props) {
14 | super(props);
15 | this.state = {
16 | subdomainChecked: false
17 | };
18 | }
19 |
20 | handleChange(event) {
21 | let { dispatch, zoneList } = this.props;
22 |
23 | _.values(zoneList).forEach(zone => {
24 | if (zone.name === event.value) {
25 | dispatch(asyncZoneSetActiveZone(zone));
26 | }
27 | });
28 | }
29 |
30 | componentWillReceiveProps() {
31 | let { activeZoneName, config, dispatch } = this.props;
32 |
33 | // If the current active zone is a subdomain show a notification
34 | // regarding that the changes are made to the original zone.
35 | var shouldUseSubdomain = getConfigValue(config, 'isSubdomainCheckEnabled');
36 | if (
37 | shouldUseSubdomain &&
38 | !this.state.subdomainChecked &&
39 | isSubdomain(activeZoneName)
40 | ) {
41 | this.setState({ subdomainChecked: true });
42 | dispatch(notificationAddWarning('warning.usingSubdomain', true, true));
43 | }
44 | }
45 |
46 | render() {
47 | let { activeZoneName, zoneList } = this.props;
48 | let zones = _.values(zoneList).map(zone => {
49 | return { value: zone.name, label: zone.name };
50 | });
51 |
52 | let isSingleZone = zones.length < 2;
53 |
54 | //vertically center the active zone selector
55 | let activeZoneSelectorStyles = {
56 | position: 'relative',
57 | top: '30px', // Hard code .header-main height/2
58 | transform: 'translateY(-50%)'
59 | };
60 |
61 | return (
62 |
69 | {isSingleZone ? (
70 | activeZoneName
71 | ) : (
72 |
77 | )}
78 |
79 | );
80 | }
81 | }
82 |
83 | function mapStateToProps(state) {
84 | return {
85 | activeZoneName: state.activeZone.name,
86 | zoneList: state.zones.entities.zones,
87 | config: state.config
88 | };
89 | }
90 |
91 | export default injectIntl(connect(mapStateToProps)(ActiveZoneSelector));
92 |
--------------------------------------------------------------------------------
/src/containers/AdvanceDDoSCard/AdvanceDDoSCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import { Card, CardSection, CardContent, CardDrawers } from 'cf-component-card';
5 |
6 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
7 | import CustomCardControl from '../../components/CustomCardControl/CustomCardControl';
8 | import { BIZ_PLAN, planNeedsUpgrade } from '../../constants/Plans.js';
9 |
10 | const SETTING_NAME = 'advanceddos';
11 | const MINIMUM_PLAN = BIZ_PLAN;
12 |
13 | class AdvanceDDoSCard extends Component {
14 | constructor(props) {
15 | super(props);
16 | this.state = {
17 | activeDrawer: null
18 | };
19 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
20 | }
21 |
22 | handleDrawerClick(id) {
23 | this.setState({
24 | activeDrawer: id === this.state.activeDrawer ? null : id
25 | });
26 | }
27 |
28 | render() {
29 | let { activeZone, zones } = this.props;
30 | let zone = zones[activeZone.name];
31 | const { formatMessage } = this.props.intl;
32 |
33 | if (!planNeedsUpgrade(zone.plan.legacy_id, MINIMUM_PLAN)) {
34 | return null;
35 | }
36 |
37 | return (
38 |
39 |
40 |
43 |
44 |
45 |
50 |
51 |
52 |
53 |
62 | )
63 | }
64 | ]}
65 | />
66 |
67 | );
68 | }
69 | }
70 |
71 | function mapStateToProps(state) {
72 | return {
73 | activeZone: state.activeZone,
74 | zones: state.zones.entities.zones
75 | };
76 | }
77 | export default injectIntl(connect(mapStateToProps)(AdvanceDDoSCard));
78 |
--------------------------------------------------------------------------------
/src/containers/AlwaysOnlineCard/AlwaysOnlineCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import Toggle from 'cf-component-toggle';
5 | import {
6 | Card,
7 | CardSection,
8 | CardContent,
9 | CardControl,
10 | CardDrawers
11 | } from 'cf-component-card';
12 |
13 | import { asyncZoneUpdateSetting } from '../../actions/zoneSettings';
14 | import { getLastModifiedDate } from '../../utils/utils';
15 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
16 | import {
17 | getZoneSettingsValueForZoneId,
18 | getZoneSettingsModifiedDateForZoneId
19 | } from '../../selectors/zoneSettings';
20 |
21 | const SETTING_NAME = 'always_online';
22 |
23 | class AlwaysOnlineCard extends Component {
24 | constructor(props) {
25 | super(props);
26 | this.state = {
27 | activeDrawer: null
28 | };
29 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
30 | }
31 |
32 | handleDrawerClick(id) {
33 | this.setState({
34 | activeDrawer: id === this.state.activeDrawer ? null : id
35 | });
36 | }
37 |
38 | handleChange(value) {
39 | let { activeZoneId, dispatch } = this.props;
40 | value = value === true ? 'on' : 'off';
41 | dispatch(asyncZoneUpdateSetting(SETTING_NAME, activeZoneId, value));
42 | }
43 |
44 | render() {
45 | const { formatMessage } = this.props.intl;
46 | let { modifiedDate } = this.props;
47 |
48 | return (
49 |
50 |
51 |
52 |
56 |
57 |
58 |
59 |
60 |
61 |
66 |
67 |
68 |
77 | )
78 | }
79 | ]}
80 | />
81 |
82 |
83 | );
84 | }
85 | }
86 |
87 | function mapStateToProps(state) {
88 | return {
89 | activeZoneId: state.activeZone.id,
90 | alwaysOnlineValue: getZoneSettingsValueForZoneId(
91 | state.activeZone.id,
92 | SETTING_NAME,
93 | state
94 | ),
95 | modifiedDate: getZoneSettingsModifiedDateForZoneId(
96 | state.activeZone.id,
97 | SETTING_NAME,
98 | state
99 | )
100 | };
101 | }
102 | export default injectIntl(connect(mapStateToProps)(AlwaysOnlineCard));
103 |
--------------------------------------------------------------------------------
/src/containers/AnalyticsPage/AnaltyicsPage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import { Card, CardSection, CardContent, CardControl } from 'cf-component-card';
5 | import { Button } from 'cf-component-button';
6 | import { Heading } from 'cf-component-heading';
7 |
8 | class AnaltyicsPage extends Component {
9 | render() {
10 | let { activeZone } = this.props;
11 | const accountId = activeZone.account.id;
12 | const analyticsDeepLink = `https://dash.cloudflare.com/${accountId}/${activeZone.name}/analytics`;
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 | }
26 | >
27 |
28 |
29 |
30 |
31 |
32 |
44 |
45 |
46 |
47 |
48 | );
49 | }
50 | }
51 |
52 | function mapStateToProps(state) {
53 | return {
54 | activeZone: state.activeZone
55 | };
56 | }
57 |
58 | export default injectIntl(connect(mapStateToProps)(AnaltyicsPage));
59 |
--------------------------------------------------------------------------------
/src/containers/App/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl, IntlProvider } from 'react-intl';
4 | import { GatewayDest, GatewayProvider } from 'react-gateway';
5 |
6 | import { LayoutContainer, LayoutRow, LayoutColumn } from 'cf-component-layout';
7 |
8 | import AppNavigation from '../../containers/AppNavigation/AppNavigation';
9 | import { isLoggedIn } from '../../utils/Auth/Auth';
10 | import { asyncConfigInit } from '../../actions/config';
11 | import GlobalNotifications from '../../containers/GlobalNotifications/GlobalNotifications';
12 | import Header from '../../containers/Header/Header';
13 | import GradientBanner from '../../components/GradientBanner/GradientBanner';
14 |
15 | import { StyleProvider } from 'cf-style-provider';
16 |
17 | //Safari Intl Polyfill
18 | if (!global.Intl) {
19 | require('intl');
20 | }
21 | class AppContainer extends Component {
22 | render() {
23 | return (
24 |
28 |
29 |
30 |
31 |
32 | {isLoggedIn() ? null : }
33 |
34 |
38 | {isLoggedIn() ? (
39 |
40 |
41 |
42 |
43 |
44 | ) : null}
45 |
46 | {isLoggedIn() ? (
47 |
48 |
49 |
50 | {this.props.children}
51 |
52 |
53 |
54 | ) : (
55 |
{this.props.children}
56 | )}
57 |
58 |
59 |
60 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 | }
77 | }
78 |
79 | function mapStateToProps(state) {
80 | return { state: state };
81 | }
82 |
83 | //
must be instantiated before injectIntl() is used so we wrap AppContainer in AppWrapper
84 | const App = injectIntl(connect(mapStateToProps)(AppContainer));
85 |
86 | class AppWrapper extends React.Component {
87 | componentWillMount() {
88 | let { dispatch } = this.props;
89 | dispatch(asyncConfigInit());
90 | }
91 |
92 | render() {
93 | if (this.props.state.app.isInitialized) {
94 | return (
95 |
99 |
100 | {this.props.children}
101 |
102 |
103 | );
104 | }
105 |
106 | return ;
107 | }
108 | }
109 |
110 | export default connect(mapStateToProps)(AppWrapper);
111 |
--------------------------------------------------------------------------------
/src/containers/ApplyDefaultSettingsCard/ApplyDefaultSettingsCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import {
5 | Card,
6 | CardSection,
7 | CardContent,
8 | CardControl,
9 | CardDrawers
10 | } from 'cf-component-card';
11 | import { Button } from 'cf-component-button';
12 | import Loading from 'cf-component-loading';
13 |
14 | import {
15 | getPluginSettingsIsFetching,
16 | getPluginSettingsModifiedDateForZoneId
17 | } from '../../selectors/pluginSettings';
18 | import { asyncPluginUpdateSetting } from '../../actions/pluginSettings';
19 | import { getConfigValue } from '../../selectors/config.js';
20 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
21 | import {
22 | getLastModifiedDate,
23 | formatMessageForIntegration
24 | } from '../../utils/utils';
25 |
26 | const SETTING_NAME = 'default_settings';
27 | const VALUE = true;
28 |
29 | class ApplyDefaultSettingsCard extends Component {
30 | constructor(props) {
31 | super(props);
32 | this.state = {
33 | activeDrawer: null
34 | };
35 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
36 | }
37 |
38 | handleDrawerClick(id) {
39 | this.setState({
40 | activeDrawer: id === this.state.activeDrawer ? null : id
41 | });
42 | }
43 |
44 | onButtonClick() {
45 | let { activeZoneId, dispatch } = this.props;
46 | dispatch(asyncPluginUpdateSetting(SETTING_NAME, activeZoneId, VALUE));
47 | }
48 |
49 | render() {
50 | const { formatMessage } = this.props.intl;
51 | let { modifiedDate, integrationName } = this.props;
52 |
53 | return (
54 |
55 |
56 |
57 |
65 |
66 |
67 |
68 |
69 |
70 | {this.props.isFetching === SETTING_NAME ? (
71 |
72 | ) : (
73 |
76 | )}
77 |
78 |
79 |
88 | )
89 | }
90 | ]}
91 | />
92 |
93 |
94 | );
95 | }
96 | }
97 |
98 | function mapStateToProps(state) {
99 | return {
100 | activeZoneId: state.activeZone.id,
101 | modifiedDate: getPluginSettingsModifiedDateForZoneId(
102 | state.activeZone.id,
103 | SETTING_NAME,
104 | state
105 | ),
106 | isFetching: getPluginSettingsIsFetching(state),
107 | integrationName: getConfigValue(state.config, 'integrationName')
108 | };
109 | }
110 | export default injectIntl(connect(mapStateToProps)(ApplyDefaultSettingsCard));
111 |
--------------------------------------------------------------------------------
/src/containers/AutomaticHTTPSRewritesCard/AutomaticHTTPSRewritesCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import Toggle from 'cf-component-toggle';
5 | import {
6 | Card,
7 | CardSection,
8 | CardContent,
9 | CardControl,
10 | CardDrawers
11 | } from 'cf-component-card';
12 |
13 | import { asyncZoneUpdateSetting } from '../../actions/zoneSettings';
14 | import { getLastModifiedDate } from '../../utils/utils';
15 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
16 | import {
17 | getZoneSettingsValueForZoneId,
18 | getZoneSettingsModifiedDateForZoneId
19 | } from '../../selectors/zoneSettings';
20 |
21 | const SETTING_NAME = 'automatic_https_rewrites';
22 |
23 | class AutomaticHTTPSRewritesCard extends Component {
24 | constructor(props) {
25 | super(props);
26 | this.state = {
27 | activeDrawer: null
28 | };
29 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
30 | }
31 |
32 | handleDrawerClick(id) {
33 | this.setState({
34 | activeDrawer: id === this.state.activeDrawer ? null : id
35 | });
36 | }
37 |
38 | handleChange(value) {
39 | let { activeZoneId, dispatch } = this.props;
40 | value = value === true ? 'on' : 'off';
41 | dispatch(asyncZoneUpdateSetting(SETTING_NAME, activeZoneId, value));
42 | }
43 |
44 | render() {
45 | const { formatMessage } = this.props.intl;
46 | let { modifiedDate } = this.props;
47 |
48 | return (
49 |
50 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
68 |
69 |
70 |
79 | )
80 | }
81 | ]}
82 | />
83 |
84 |
85 | );
86 | }
87 | }
88 |
89 | function mapStateToProps(state) {
90 | return {
91 | activeZoneId: state.activeZone.id,
92 | rewriteValue: getZoneSettingsValueForZoneId(
93 | state.activeZone.id,
94 | SETTING_NAME,
95 | state
96 | ),
97 | modifiedDate: getZoneSettingsModifiedDateForZoneId(
98 | state.activeZone.id,
99 | SETTING_NAME,
100 | state
101 | )
102 | };
103 | }
104 | export default injectIntl(connect(mapStateToProps)(AutomaticHTTPSRewritesCard));
105 |
--------------------------------------------------------------------------------
/src/containers/BenefitsCollection/BenefitsCollection.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 |
4 | import { LayoutRow, LayoutColumn } from 'cf-component-layout';
5 | import { Flex } from 'cf-component-flex';
6 | import { injectIntl } from 'react-intl';
7 |
8 | import BenefitsFeature from '../../components/BenefitsFeature/BenefitsFeature';
9 | import { getAbsoluteUrl } from '../../selectors/config';
10 |
11 | class BenefitsCollection extends Component {
12 | render() {
13 | const { config } = this.props;
14 | let { formatMessage } = this.props.intl;
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
30 |
31 |
32 |
41 |
42 |
43 |
52 |
53 |
54 |
63 |
64 |
65 |
66 |
67 | );
68 | }
69 | }
70 |
71 | function mapStateToProps(state) {
72 | return { config: state.config };
73 | }
74 |
75 | export default injectIntl(connect(mapStateToProps)(BenefitsCollection));
76 |
--------------------------------------------------------------------------------
/src/containers/BrowserIntegrityCheckCard/BrowserIntegrityCheckCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import Toggle from 'cf-component-toggle';
5 | import {
6 | Card,
7 | CardSection,
8 | CardContent,
9 | CardControl,
10 | CardDrawers
11 | } from 'cf-component-card';
12 |
13 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
14 | import { asyncZoneUpdateSetting } from '../../actions/zoneSettings';
15 | import { getLastModifiedDate } from '../../utils/utils';
16 | import {
17 | getZoneSettingsValueForZoneId,
18 | getZoneSettingsModifiedDateForZoneId
19 | } from '../../selectors/zoneSettings';
20 |
21 | const SETTING_NAME = 'browser_check';
22 |
23 | class BrowserIntegrityCheckCard extends Component {
24 | constructor(props) {
25 | super(props);
26 | this.state = {
27 | activeDrawer: null
28 | };
29 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
30 | }
31 |
32 | handleDrawerClick(id) {
33 | this.setState({
34 | activeDrawer: id === this.state.activeDrawer ? null : id
35 | });
36 | }
37 |
38 | handleChange(value) {
39 | let { activeZoneId, dispatch } = this.props;
40 | value = value === true ? 'on' : 'off';
41 | dispatch(asyncZoneUpdateSetting(SETTING_NAME, activeZoneId, value));
42 | }
43 |
44 | render() {
45 | const { formatMessage } = this.props.intl;
46 | let { modifiedDate } = this.props;
47 |
48 | return (
49 |
50 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
68 |
69 |
70 |
79 | )
80 | }
81 | ]}
82 | />
83 |
84 |
85 | );
86 | }
87 | }
88 |
89 | function mapStateToProps(state) {
90 | return {
91 | activeZoneId: state.activeZone.id,
92 | browserIntegrityCheckValue: getZoneSettingsValueForZoneId(
93 | state.activeZone.id,
94 | SETTING_NAME,
95 | state
96 | ),
97 | modifiedDate: getZoneSettingsModifiedDateForZoneId(
98 | state.activeZone.id,
99 | SETTING_NAME,
100 | state
101 | )
102 | };
103 | }
104 | export default injectIntl(connect(mapStateToProps)(BrowserIntegrityCheckCard));
105 |
--------------------------------------------------------------------------------
/src/containers/CacheLevelCard/CacheLevelCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import {
5 | Card,
6 | CardSection,
7 | CardContent,
8 | CardControl,
9 | CardDrawers
10 | } from 'cf-component-card';
11 | import { RadioGroup } from 'cf-component-radio';
12 |
13 | import { asyncZoneUpdateSetting } from '../../actions/zoneSettings';
14 | import { getLastModifiedDate } from '../../utils/utils';
15 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
16 | import {
17 | getZoneSettingsValueForZoneId,
18 | getZoneSettingsModifiedDateForZoneId
19 | } from '../../selectors/zoneSettings';
20 |
21 | const SETTING_NAME = 'cache_level';
22 |
23 | class CacheLevelCard extends Component {
24 | constructor(props) {
25 | super(props);
26 | this.state = {
27 | activeDrawer: null
28 | };
29 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
30 | }
31 |
32 | handleDrawerClick(id) {
33 | this.setState({
34 | activeDrawer: id === this.state.activeDrawer ? null : id
35 | });
36 | }
37 |
38 | handleRadioChange(value) {
39 | let { activeZoneId, dispatch } = this.props;
40 | dispatch(asyncZoneUpdateSetting(SETTING_NAME, activeZoneId, value));
41 | }
42 |
43 | render() {
44 | const { formatMessage } = this.props.intl;
45 | let { modifiedDate } = this.props;
46 |
47 | return (
48 |
49 |
50 |
51 |
55 |
56 |
57 |
58 |
59 |
60 |
87 |
88 |
89 |
98 | )
99 | }
100 | ]}
101 | />
102 |
103 |
104 | );
105 | }
106 | }
107 |
108 | function mapStateToProps(state) {
109 | return {
110 | activeZoneId: state.activeZone.id,
111 | cacheLevelValue: getZoneSettingsValueForZoneId(
112 | state.activeZone.id,
113 | SETTING_NAME,
114 | state
115 | ),
116 | modifiedDate: getZoneSettingsModifiedDateForZoneId(
117 | state.activeZone.id,
118 | SETTING_NAME,
119 | state
120 | )
121 | };
122 | }
123 | export default injectIntl(connect(mapStateToProps)(CacheLevelCard));
124 |
--------------------------------------------------------------------------------
/src/containers/DNSRecordEditor/DNSRecordEditor.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import {
5 | Table,
6 | TableHead,
7 | TableBody,
8 | TableRow,
9 | TableHeadCell,
10 | TableCell
11 | } from 'cf-component-table';
12 | import Toggle from 'cf-component-toggle';
13 | import _ from 'lodash';
14 |
15 | import Loading from 'cf-component-loading';
16 | import {
17 | asyncDNSRecordCreate,
18 | asyncDNSRecordUpdate
19 | } from '../../actions/zoneDnsRecords';
20 |
21 | class DNSRecordEditor extends Component {
22 | handleToggle(value, dnsRecord) {
23 | let { dispatch } = this.props;
24 | if (dnsRecord.id) {
25 | dispatch(asyncDNSRecordUpdate(dnsRecord.zone_id, dnsRecord, value));
26 | } else {
27 | dispatch(
28 | asyncDNSRecordCreate(
29 | dnsRecord.zone_id,
30 | dnsRecord.type,
31 | dnsRecord.name,
32 | dnsRecord.content
33 | )
34 | );
35 | }
36 | }
37 |
38 | render() {
39 | return (
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | {_.sortBy(_.values(this.props.dnsRecords), function(dnsRecord) {
63 | return dnsRecord.name;
64 | }).map(dnsRecord => (
65 |
66 | {dnsRecord.type}
67 | {dnsRecord.name}
68 | {dnsRecord.content}
69 | {dnsRecord.ttl}
70 |
71 | {this.props.updateIsFetching === dnsRecord.name ? (
72 |
73 | ) : (
74 | this.handleToggle(e, dnsRecord)}
79 | />
80 | )}
81 |
82 |
83 | ))}
84 |
85 |
86 |
87 | );
88 | }
89 | }
90 | function mapStateToProps(state) {
91 | return {
92 | activeZoneId: state.activeZone.id,
93 | dnsRecords: state.zoneDnsRecords.entities[state.activeZone.id],
94 | updateIsFetching: state.zoneDnsRecords.updateIsFetching
95 | };
96 | }
97 | export default injectIntl(connect(mapStateToProps)(DNSRecordEditor));
98 |
--------------------------------------------------------------------------------
/src/containers/DevelopmentModeCard/DevelopmentModeCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import {
5 | Card,
6 | CardSection,
7 | CardContent,
8 | CardControl,
9 | CardDrawers
10 | } from 'cf-component-card';
11 | import Toggle from 'cf-component-toggle';
12 |
13 | import { asyncZoneUpdateSetting } from '../../actions/zoneSettings';
14 | import { getLastModifiedDate } from '../../utils/utils';
15 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
16 | import {
17 | getZoneSettingsValueForZoneId,
18 | getZoneSettingsModifiedDateForZoneId
19 | } from '../../selectors/zoneSettings';
20 |
21 | const SETTING_NAME = 'development_mode';
22 |
23 | class DevelopmentModeCard extends Component {
24 | constructor(props) {
25 | super(props);
26 | this.state = {
27 | activeDrawer: null
28 | };
29 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
30 | }
31 |
32 | handleDrawerClick(id) {
33 | this.setState({
34 | activeDrawer: id === this.state.activeDrawer ? null : id
35 | });
36 | }
37 |
38 | handleChange(value) {
39 | let { activeZoneId, dispatch } = this.props;
40 | value = value === true ? 'on' : 'off';
41 | dispatch(asyncZoneUpdateSetting(SETTING_NAME, activeZoneId, value));
42 | }
43 |
44 | render() {
45 | const { formatMessage } = this.props.intl;
46 | let { modifiedDate } = this.props;
47 |
48 | return (
49 |
50 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
68 |
69 |
70 |
79 | )
80 | }
81 | ]}
82 | />
83 |
84 |
85 | );
86 | }
87 | }
88 |
89 | function mapStateToProps(state) {
90 | return {
91 | activeZoneId: state.activeZone.id,
92 | developmentModeValue: getZoneSettingsValueForZoneId(
93 | state.activeZone.id,
94 | SETTING_NAME,
95 | state
96 | ),
97 | modifiedDate: getZoneSettingsModifiedDateForZoneId(
98 | state.activeZone.id,
99 | SETTING_NAME,
100 | state
101 | )
102 | };
103 | }
104 | export default injectIntl(connect(mapStateToProps)(DevelopmentModeCard));
105 |
--------------------------------------------------------------------------------
/src/containers/GlobalNotifications/GlobalNotifications.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { injectIntl } from 'react-intl';
4 | import {
5 | NotificationGlobalContainer,
6 | NotificationList,
7 | Notification
8 | } from 'cf-component-notifications';
9 | import * as NotificationActionCreators from '../../actions/notifications';
10 |
11 | class GlobalNotifications extends Component {
12 | handleClose(id) {
13 | let { dispatch } = this.props;
14 | dispatch(NotificationActionCreators.notificationRemove(id));
15 | }
16 |
17 | render() {
18 | let { notifications } = this.props;
19 | const { formatMessage } = this.props.intl;
20 |
21 | const newNotifications = notifications.map(n => {
22 | return (
23 |
31 | );
32 | });
33 |
34 | return (
35 |
36 | {newNotifications}
37 |
38 | );
39 | }
40 | }
41 |
42 | function mapStateToProps(state) {
43 | return {
44 | notifications: state.notifications
45 | };
46 | }
47 |
48 | export default injectIntl(connect(mapStateToProps)(GlobalNotifications));
49 |
--------------------------------------------------------------------------------
/src/containers/Header/Header.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { injectIntl } from 'react-intl';
4 |
5 | import ActiveZoneSelector from '../../containers/ActiveZoneSelector/ActiveZoneSelector';
6 | import { isLoggedIn } from '../../utils/Auth/Auth';
7 | import { getAbsoluteUrl } from '../../selectors/config';
8 | import UnderAttackButton from '../../containers/UnderAttackButton/UnderAttackButton';
9 | import { LayoutColumn } from 'cf-component-layout';
10 |
11 | class Header extends Component {
12 | render() {
13 | let { config, activeZone, zoneSettings } = this.props;
14 |
15 | var logoStyle = {
16 | width: '140px',
17 | height: '40px',
18 | position: 'relative',
19 | top: '30px', // Hard code .header-main height/2
20 | transform: 'translateY(-50%)'
21 | };
22 |
23 | var columnStyle = {
24 | position: 'relative'
25 | };
26 |
27 | return (
28 |
47 | );
48 | }
49 | }
50 |
51 | function mapStateToProps(state) {
52 | return {
53 | config: state.config,
54 | activeZone: state.activeZone,
55 | zoneSettings: state.zoneSettings
56 | };
57 | }
58 |
59 | export default injectIntl(connect(mapStateToProps)(Header));
60 |
--------------------------------------------------------------------------------
/src/containers/HomePage/HomePage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 |
5 | import { Heading } from 'cf-component-heading';
6 |
7 | import { renderCards } from '../../components/RenderCardsDynamically/RenderCardsDynamically';
8 | import WaitForSettings from '../../containers/WaitForSettings/WaitForSettings';
9 |
10 | class HomePage extends Component {
11 | render() {
12 | let { config } = this.props;
13 |
14 | return (
15 |
16 |
17 |
18 |
19 | {renderCards(config.homePageCards)}
20 |
21 | );
22 | }
23 | }
24 |
25 | function mapStateToProps(state) {
26 | return {
27 | config: state.config.config
28 | };
29 | }
30 | export default injectIntl(connect(mapStateToProps)(HomePage));
31 |
--------------------------------------------------------------------------------
/src/containers/IPV6Card/IPV6Card.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import Toggle from 'cf-component-toggle';
5 | import {
6 | Card,
7 | CardSection,
8 | CardContent,
9 | CardControl,
10 | CardDrawers
11 | } from 'cf-component-card';
12 |
13 | import { asyncZoneUpdateSetting } from '../../actions/zoneSettings';
14 | import { getLastModifiedDate } from '../../utils/utils';
15 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
16 | import {
17 | getZoneSettingsValueForZoneId,
18 | getZoneSettingsModifiedDateForZoneId
19 | } from '../../selectors/zoneSettings';
20 |
21 | const SETTING_NAME = 'ipv6';
22 |
23 | class IPV6Card extends Component {
24 | constructor(props) {
25 | super(props);
26 | this.state = {
27 | activeDrawer: null
28 | };
29 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
30 | }
31 |
32 | handleDrawerClick(id) {
33 | this.setState({
34 | activeDrawer: id === this.state.activeDrawer ? null : id
35 | });
36 | }
37 |
38 | handleChange(value) {
39 | let { activeZoneId, dispatch } = this.props;
40 | value = value === true ? 'on' : 'off';
41 | dispatch(asyncZoneUpdateSetting(SETTING_NAME, activeZoneId, value));
42 | }
43 |
44 | render() {
45 | const { formatMessage } = this.props.intl;
46 | let { modifiedDate } = this.props;
47 |
48 | return (
49 |
50 |
51 |
52 |
56 |
57 |
58 |
59 |
60 |
61 |
66 |
67 |
68 |
77 | )
78 | }
79 | ]}
80 | />
81 |
82 |
83 | );
84 | }
85 | }
86 |
87 | function mapStateToProps(state) {
88 | return {
89 | activeZoneId: state.activeZone.id,
90 | ipv6Value: getZoneSettingsValueForZoneId(
91 | state.activeZone.id,
92 | SETTING_NAME,
93 | state
94 | ),
95 | modifiedDate: getZoneSettingsModifiedDateForZoneId(
96 | state.activeZone.id,
97 | SETTING_NAME,
98 | state
99 | )
100 | };
101 | }
102 | export default injectIntl(connect(mapStateToProps)(IPV6Card));
103 |
--------------------------------------------------------------------------------
/src/containers/IpRewriteCard/IpRewriteCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import { Card, CardSection, CardContent, CardControl } from 'cf-component-card';
5 | import Toggle from 'cf-component-toggle';
6 |
7 | import { asyncPluginUpdateSetting } from '../../actions/pluginSettings';
8 | import {
9 | getPluginSettingsValueForZoneId,
10 | getPluginSettingsModifiedDateForZoneId
11 | } from '../../selectors/pluginSettings';
12 | import { getLastModifiedDate } from '../../utils/utils';
13 |
14 | const SETTING_NAME = 'ip_rewrite';
15 |
16 | class IpRewriteCard extends Component {
17 | handleChange(value) {
18 | let { activeZoneId, dispatch } = this.props;
19 | value = value === true ? 'on' : 'off';
20 | dispatch(asyncPluginUpdateSetting(SETTING_NAME, activeZoneId, value));
21 | }
22 |
23 | render() {
24 | const { formatMessage } = this.props.intl;
25 | let { modifiedDate } = this.props;
26 |
27 | return (
28 |
29 |
30 |
31 |
35 |
36 |
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
49 | );
50 | }
51 | }
52 |
53 | function mapStateToProps(state) {
54 | return {
55 | activeZoneId: state.activeZone.id,
56 | ipRewriteValue: getPluginSettingsValueForZoneId(
57 | state.activeZone.id,
58 | SETTING_NAME,
59 | state
60 | ),
61 | modifiedDate: getPluginSettingsModifiedDateForZoneId(
62 | state.activeZone.id,
63 | SETTING_NAME,
64 | state
65 | )
66 | };
67 | }
68 | export default injectIntl(connect(mapStateToProps)(IpRewriteCard));
69 |
--------------------------------------------------------------------------------
/src/containers/MarketingFeatureCollection/MarketingFeatureCollection.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 |
4 | import { LayoutRow, LayoutColumn } from 'cf-component-layout';
5 | import { Flex } from 'cf-component-flex';
6 | import { injectIntl } from 'react-intl';
7 |
8 | import MarketingFeature from '../../components/MarketingFeature/MarketingFeature';
9 | import { getAbsoluteUrl } from '../../selectors/config';
10 |
11 | class MarketingFeatureCollection extends Component {
12 | render() {
13 | const { config } = this.props;
14 | let { formatMessage } = this.props.intl;
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
31 |
32 |
33 |
42 |
43 |
44 |
53 |
54 |
55 |
64 |
65 |
66 |
67 |
68 |
69 | );
70 | }
71 | }
72 |
73 | function mapStateToProps(state) {
74 | return { config: state.config };
75 | }
76 |
77 | export default injectIntl(connect(mapStateToProps)(MarketingFeatureCollection));
78 |
--------------------------------------------------------------------------------
/src/containers/MoreSettingsPage/MoreSettingsPage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import _ from 'lodash';
5 |
6 | import { Heading } from 'cf-component-heading';
7 |
8 | import { renderCards } from '../../components/RenderCardsDynamically/RenderCardsDynamically';
9 | import WaitForSettings from '../../containers/WaitForSettings/WaitForSettings';
10 |
11 | class MoreSettingsPage extends Component {
12 | renderContent() {
13 | let { config } = this.props;
14 | var count = 0;
15 |
16 | return _.map(config.moreSettingsCards, function(value, key) {
17 | var categoryTitle = key;
18 | return (
19 |
20 |
21 |
22 |
23 | {renderCards(value)}
24 |
25 | );
26 | });
27 | }
28 |
29 | render() {
30 | return (
31 |
32 | {this.renderContent()}
33 |
34 | );
35 | }
36 | }
37 |
38 | function mapStateToProps(state) {
39 | return {
40 | config: state.config.config
41 | };
42 | }
43 | export default injectIntl(connect(mapStateToProps)(MoreSettingsPage));
44 |
--------------------------------------------------------------------------------
/src/containers/PluginSpecificCacheTagCard/PluginSpecificCachetTagCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import { Card, CardSection, CardContent, CardDrawers } from 'cf-component-card';
5 | import Toggle from 'cf-component-toggle';
6 |
7 | import { asyncPluginUpdateSetting } from '../../actions/pluginSettings';
8 | import CustomCardControl from '../../components/CustomCardControl/CustomCardControl';
9 | import { ENT_PLAN } from '../../constants/Plans.js';
10 | import { getZonePlanLegacyId } from '../../selectors/zones';
11 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
12 | import {
13 | getPluginSettingsValueForZoneId,
14 | getPluginSettingsModifiedDateForZoneId
15 | } from '../../selectors/pluginSettings';
16 | import { getLastModifiedDate } from '../../utils/utils';
17 |
18 | const SETTING_NAME = 'plugin_specific_cache_tag';
19 |
20 | class PluginSpecificCacheTagCard extends Component {
21 | constructor(props) {
22 | super(props);
23 | this.state = {
24 | activeDrawer: null
25 | };
26 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
27 | }
28 |
29 | handleDrawerClick(id) {
30 | this.setState({
31 | activeDrawer: id === this.state.activeDrawer ? null : id
32 | });
33 | }
34 |
35 | handleChange(value) {
36 | let { activeZoneId, dispatch } = this.props;
37 | value = value === true ? 'on' : 'off';
38 | dispatch(asyncPluginUpdateSetting(SETTING_NAME, activeZoneId, value));
39 | }
40 |
41 | render() {
42 | const { formatMessage } = this.props.intl;
43 | let { modifiedDate } = this.props;
44 |
45 | return (
46 |
47 |
48 |
49 |
55 |
56 |
60 |
61 |
62 |
67 |
72 |
73 |
74 |
83 | )
84 | }
85 | ]}
86 | />
87 |
88 |
89 | );
90 | }
91 | }
92 |
93 | function mapStateToProps(state) {
94 | return {
95 | activeZoneId: state.activeZone.id,
96 | cacheTagCardValue: getPluginSettingsValueForZoneId(
97 | state.activeZone.id,
98 | SETTING_NAME,
99 | state
100 | ),
101 | modifiedDate: getPluginSettingsModifiedDateForZoneId(
102 | state.activeZone.id,
103 | SETTING_NAME,
104 | state
105 | ),
106 | integrationName: state.config.config.integrationName,
107 | activeZonePlan: getZonePlanLegacyId(state.activeZone.name, state.zones)
108 | };
109 | }
110 | export default injectIntl(connect(mapStateToProps)(PluginSpecificCacheTagCard));
111 |
--------------------------------------------------------------------------------
/src/containers/SSLCard/SSLCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import {
5 | Card,
6 | CardSection,
7 | CardContent,
8 | CardControl,
9 | CardDrawers
10 | } from 'cf-component-card';
11 | import Select from 'cf-component-select';
12 |
13 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
14 | import { asyncZoneUpdateSetting } from '../../actions/zoneSettings';
15 | import { getLastModifiedDate } from '../../utils/utils';
16 | import {
17 | getZoneSettingsValueForZoneId,
18 | getZoneSettingsModifiedDateForZoneId
19 | } from '../../selectors/zoneSettings';
20 |
21 | const SETTING_NAME = 'ssl';
22 |
23 | class SSLCard extends Component {
24 | constructor(props) {
25 | super(props);
26 | this.state = {
27 | activeDrawer: null
28 | };
29 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
30 | }
31 |
32 | handleDrawerClick(id) {
33 | this.setState({
34 | activeDrawer: id === this.state.activeDrawer ? null : id
35 | });
36 | }
37 |
38 | handleChange(option) {
39 | let { dispatch } = this.props;
40 | let { value } = option;
41 |
42 | dispatch(asyncZoneUpdateSetting('ssl', this.props.activeZoneId, value));
43 | }
44 |
45 | render() {
46 | const { formatMessage } = this.props.intl;
47 | let { modifiedDate } = this.props;
48 |
49 | return (
50 |
51 |
52 |
53 |
57 |
58 |
59 |
60 |
61 |
62 |
91 |
92 |
93 |
102 | )
103 | }
104 | ]}
105 | />
106 |
107 |
108 | );
109 | }
110 | }
111 |
112 | function mapStateToProps(state) {
113 | return {
114 | activeZoneId: state.activeZone.id,
115 | sslValue: getZoneSettingsValueForZoneId(
116 | state.activeZone.id,
117 | SETTING_NAME,
118 | state
119 | ),
120 | modifiedDate: getZoneSettingsModifiedDateForZoneId(
121 | state.activeZone.id,
122 | SETTING_NAME,
123 | state
124 | )
125 | };
126 | }
127 | export default injectIntl(connect(mapStateToProps)(SSLCard));
128 |
--------------------------------------------------------------------------------
/src/containers/UnderAttackButton/UnderAttackButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import { Button } from 'cf-component-button';
5 |
6 | import { asyncZoneUpdateSetting } from '../../actions/zoneSettings';
7 | import { getZoneSettingsValueForZoneId } from '../../selectors/zoneSettings';
8 |
9 | const SETTING_NAME = 'security_level';
10 |
11 | class UnderAttackButton extends Component {
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | value: this.props.securityLevelValue
16 | };
17 | }
18 |
19 | handleChange(value) {
20 | let { dispatch } = this.props;
21 | this.setState({ value: value });
22 | dispatch(
23 | asyncZoneUpdateSetting(SETTING_NAME, this.props.activeZoneId, value)
24 | );
25 | }
26 |
27 | render() {
28 | let { value } = this.state;
29 | let buttonText =
30 | value === 'under_attack'
31 | ? 'container.underAttackButton.turnOn'
32 | : 'container.underAttackButton.turnOff';
33 | let buttonValue =
34 | value === 'under_attack' ? 'essentially_off' : 'under_attack';
35 | let buttonType = value === 'under_attack' ? 'warning' : 'primary';
36 |
37 | let underAttackStyles = {
38 | fontSize: '75%',
39 | textAlign: 'right',
40 | position: 'relative',
41 | top: '30px', // Hard code .header-main height/2
42 | left: '25%',
43 | width: '73%',
44 | transform: 'translateY(-50%)'
45 | };
46 |
47 | return (
48 |
49 |
50 |
51 |
52 |
53 |
61 |
62 | );
63 | }
64 | }
65 |
66 | function mapStateToProps(state) {
67 | return {
68 | activeZoneId: state.activeZone.id,
69 | securityLevelValue: getZoneSettingsValueForZoneId(
70 | state.activeZone.id,
71 | SETTING_NAME,
72 | state
73 | )
74 | };
75 | }
76 | export default injectIntl(connect(mapStateToProps)(UnderAttackButton));
77 |
--------------------------------------------------------------------------------
/src/containers/WAFCard/WAFCard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { FormattedMessage, injectIntl } from 'react-intl';
4 | import { Card, CardSection, CardContent, CardDrawers } from 'cf-component-card';
5 | import Toggle from 'cf-component-toggle';
6 |
7 | import FormattedMarkdown from '../../components/FormattedMarkdown/FormattedMarkdown';
8 | import { getLastModifiedDate } from '../../utils/utils';
9 | import {
10 | getZoneSettingsValueForZoneId,
11 | getZoneSettingsModifiedDateForZoneId
12 | } from '../../selectors/zoneSettings';
13 | import CustomCardControl from '../../components/CustomCardControl/CustomCardControl';
14 | import { asyncZoneUpdateSetting } from '../../actions/zoneSettings';
15 | import { PRO_PLAN } from '../../constants/Plans.js';
16 |
17 | const SETTING_NAME = 'waf';
18 | const MINIMUM_PLAN = PRO_PLAN;
19 |
20 | class WAFCard extends Component {
21 | constructor(props) {
22 | super(props);
23 | this.state = {
24 | activeDrawer: null
25 | };
26 | this.handleDrawerClick = this.handleDrawerClick.bind(this);
27 | }
28 |
29 | handleDrawerClick(id) {
30 | this.setState({
31 | activeDrawer: id === this.state.activeDrawer ? null : id
32 | });
33 | }
34 |
35 | handleChange(value) {
36 | let { activeZoneId, dispatch } = this.props;
37 | value = value === true ? 'on' : 'off';
38 | dispatch(asyncZoneUpdateSetting(SETTING_NAME, activeZoneId, value));
39 | }
40 |
41 | render() {
42 | let { activeZone, zones, modifiedDate } = this.props;
43 | let zone = zones[activeZone.name];
44 |
45 | const { formatMessage } = this.props.intl;
46 | return (
47 |
48 |
49 |
50 |
54 |
55 |
56 |
61 |
66 |
67 |
68 |
76 | }
77 | ]}
78 | />
79 |
80 |
81 | );
82 | }
83 | }
84 |
85 | function mapStateToProps(state) {
86 | return {
87 | activeZoneId: state.activeZone.id,
88 | WAFValue: getZoneSettingsValueForZoneId(
89 | state.activeZone.id,
90 | SETTING_NAME,
91 | state
92 | ),
93 | modifiedDate: getZoneSettingsModifiedDateForZoneId(
94 | state.activeZone.id,
95 | SETTING_NAME,
96 | state
97 | ),
98 | activeZone: state.activeZone,
99 | zones: state.zones.entities.zones
100 | };
101 | }
102 | export default injectIntl(connect(mapStateToProps)(WAFCard));
103 |
--------------------------------------------------------------------------------
/src/containers/WaitForSettings/WaitForSettings.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { connect } from 'react-redux';
4 | import { isActiveZoneOnCloudflare } from '../../selectors/activeZone';
5 | import Loading from 'cf-component-loading';
6 | import Text from 'cf-component-text';
7 | import { getPluginSettingsForZoneId } from '../../selectors/pluginSettings';
8 | import { FormattedMessage, injectIntl } from 'react-intl';
9 | import { getAllZoneSettingsForZoneId } from '../../selectors/zoneSettings';
10 | import { isDNSPageEnabled } from '../../selectors/config';
11 | import { push } from 'react-router-redux';
12 | import { Link } from 'react-router';
13 | import {
14 | CLOUDFLARE_ADD_SITE_PAGE,
15 | DOMAINS_OVERVIEW_PAGE
16 | } from '../../constants/UrlPaths.js';
17 |
18 | class WaitForSettings extends Component {
19 | handleClick(path) {
20 | let { dispatch } = this.props;
21 | dispatch(push(path));
22 | }
23 |
24 | render() {
25 | let {
26 | activeZone,
27 | zoneSettings,
28 | zonePluginSettings,
29 | settings,
30 | pluginSettings,
31 | config
32 | } = this.props;
33 |
34 | let isSettingsLoaded = true;
35 | let isPluginSettingsLoaded = true;
36 |
37 | if (settings) {
38 | isSettingsLoaded = getAllZoneSettingsForZoneId(
39 | activeZone.id,
40 | zoneSettings
41 | );
42 | }
43 |
44 | if (pluginSettings) {
45 | isPluginSettingsLoaded = getPluginSettingsForZoneId(
46 | activeZone.id,
47 | zonePluginSettings
48 | );
49 | }
50 |
51 | let isZoneOnCloudflare = isActiveZoneOnCloudflare(activeZone);
52 |
53 | let isEverythingLoaded = isSettingsLoaded && isPluginSettingsLoaded;
54 |
55 | var link = (
56 |
57 | Cloudflare
58 |
59 | );
60 | if (isDNSPageEnabled(config)) {
61 | link = (
62 | this.handleClick(DOMAINS_OVERVIEW_PAGE)}>
63 |
64 |
65 | );
66 | }
67 |
68 | return (
69 |
70 | {!isEverythingLoaded && isZoneOnCloudflare && (
71 |
72 |
73 |
74 | )}
75 | {!isEverythingLoaded && !isZoneOnCloudflare && (
76 |
77 |
81 |
82 | )}
83 | {isEverythingLoaded && isZoneOnCloudflare && this.props.children}
84 |
85 | );
86 | }
87 | }
88 |
89 | WaitForSettings.propTypes = {
90 | settings: PropTypes.bool,
91 | pluginSettings: PropTypes.bool,
92 | analytics: PropTypes.bool,
93 | dispatch: PropTypes.func,
94 | activeZone: PropTypes.object,
95 | zoneSettings: PropTypes.object,
96 | zonePluginSettings: PropTypes.object,
97 | config: PropTypes.object,
98 | intl: PropTypes.object,
99 | children: PropTypes.node
100 | };
101 |
102 | function mapStateToProps(state) {
103 | return {
104 | activeZone: state.activeZone,
105 | zoneSettings: state.zoneSettings,
106 | zonePluginSettings: state.pluginSettings,
107 | config: state.config
108 | };
109 | }
110 | export default injectIntl(connect(mapStateToProps)(WaitForSettings));
111 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Provider } from 'react-redux';
4 | import { Router } from 'react-router';
5 | import configureStore from './store/configureStore';
6 | import routes from './routes';
7 | import createHistory from 'history/lib/createHashHistory';
8 | import http from 'cf-util-http';
9 | import { syncHistoryWithStore } from 'react-router-redux';
10 |
11 | const store = configureStore(createHistory());
12 | const history = syncHistoryWithStore(createHistory(), store);
13 |
14 | /*
15 | * Register our RestProxyCallback to send all cf-util-http calls to
16 | * our backend instead of their actual endpoint.
17 | */
18 | http.beforeSend(window.RestProxyCallback);
19 |
20 | ReactDOM.render(
21 |
22 | {routes}
23 | ,
24 | document.getElementById('root')
25 | );
26 |
--------------------------------------------------------------------------------
/src/reducers/activeZone.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 |
3 | const initialState = {
4 | id: '',
5 | name: ''
6 | };
7 |
8 | export function activeZoneReducer(state = initialState, action) {
9 | switch (action.type) {
10 | case ActionTypes.ZONES_SET_ACTIVE_ZONE:
11 | return Object.assign({}, state, {
12 | id: action.zone.id,
13 | name: action.zone.name,
14 | account: action.zone.account
15 | });
16 | default:
17 | return state;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/reducers/app.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 |
3 | const initialState = {
4 | isInitialized: false
5 | };
6 |
7 | export function appReducer(state = initialState, action) {
8 | switch (action.type) {
9 | case ActionTypes.APPLICATION_INIT:
10 | return Object.assign({}, state, {
11 | isInitialized: true
12 | });
13 | default:
14 | return state;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/reducers/config.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 |
3 | const initialState = {
4 | config: {},
5 | isFetching: false
6 | };
7 |
8 | export const ABSOLUTE_URL_BASE_KEY = 'absoluteUrlBase';
9 |
10 | export function configReducer(state = initialState, action) {
11 | switch (action.type) {
12 | case ActionTypes.CONFIG_FETCH:
13 | return Object.assign({}, state, {
14 | isFetching: true
15 | });
16 | case ActionTypes.CONFIG_FETCH_SUCCESS:
17 | return Object.assign({}, state, {
18 | isFetching: false
19 | });
20 | case ActionTypes.CONFIG_FETCH_ERROR:
21 | return Object.assign({}, state, {
22 | isFetching: false
23 | });
24 | case ActionTypes.CONFIG_UPDATE_BY_KEY:
25 | return Object.assign({}, state, {
26 | config: Object.assign({}, state.config, {
27 | [action.key]: action.value
28 | })
29 | });
30 | default:
31 | return state;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 | import { activeZoneReducer } from './activeZone';
4 | import { appReducer } from './app';
5 | import { configReducer } from './config';
6 | import { dnsRecordsReducer } from './zoneDnsRecords.js';
7 | import { intlReducer } from './intl';
8 | import { notificationsReducer } from './notifications';
9 | import { userReducer } from './user';
10 | import { zonePurgeCacheReducer } from './zonePurgeCache';
11 | import { zoneEntitlementsReducer } from './zoneEntitlements';
12 | import { zoneSettingsReducer } from './zoneSettings';
13 | import { zonesReducer } from './zones';
14 | import { pluginSettingsReducer } from './pluginSettings';
15 |
16 | const rootReducer = combineReducers({
17 | activeZone: activeZoneReducer,
18 | app: appReducer,
19 | config: configReducer,
20 | intl: intlReducer,
21 | user: userReducer,
22 | notifications: notificationsReducer,
23 | routing: routerReducer,
24 | zones: zonesReducer,
25 | zoneDnsRecords: dnsRecordsReducer,
26 | zonePurgeCache: zonePurgeCacheReducer,
27 | zoneSettings: zoneSettingsReducer,
28 | pluginSettings: pluginSettingsReducer,
29 | zoneEntitlements: zoneEntitlementsReducer
30 | });
31 |
32 | export default rootReducer;
33 |
--------------------------------------------------------------------------------
/src/reducers/intl.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 |
3 | const initialState = {
4 | locale: '',
5 | translations: {},
6 | isFetching: false
7 | };
8 |
9 | export function intlReducer(state = initialState, action) {
10 | switch (action.type) {
11 | case ActionTypes.INTL_FETCH_TRANSLATIONS:
12 | return Object.assign({}, state, {
13 | isFetching: true
14 | });
15 | case ActionTypes.INTL_FETCH_TRANSLATIONS_SUCCESS:
16 | return Object.assign({}, state, {
17 | locale: action.locale,
18 | translations: action.translations,
19 | isFetching: false
20 | });
21 | case ActionTypes.INTL_FETCH_TRANSLATIONS_ERROR:
22 | return Object.assign({}, state, {
23 | isFetching: false
24 | });
25 | default:
26 | return state;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/reducers/notifications.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 |
3 | const initialState = [];
4 |
5 | export function notificationsReducer(state = initialState, action) {
6 | switch (action.type) {
7 | case ActionTypes.NOTIFICATION_ADD:
8 | return [
9 | {
10 | key: Date.now(),
11 | level: action.level,
12 | message: action.message,
13 | localized: action.localized,
14 | persistant: action.persistant,
15 | delay: action.delay
16 | },
17 | ...state
18 | ];
19 | case ActionTypes.NOTIFICATION_REMOVE:
20 | return state.filter(notification => notification.key !== action.key);
21 | default:
22 | return state;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/reducers/pluginSettings.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import * as ActionTypes from '../constants/ActionTypes';
3 | import { normalizeZoneByIdGetAll } from '../constants/Schemas';
4 |
5 | const initialState = {
6 | entities: {},
7 | result: [],
8 | isFetching: ''
9 | };
10 |
11 | export function pluginSettingsReducer(state = initialState, action) {
12 | switch (action.type) {
13 | case ActionTypes.PLUGIN_SETTINGS_FETCH:
14 | return Object.assign({}, state, {
15 | isFetching: 'FETCH ALL PLUGIN SETTINGS'
16 | });
17 | case ActionTypes.PLUGIN_SETTINGS_FETCH_SUCCESS:
18 | let normalizedPluginSettings = normalizeZoneByIdGetAll(
19 | action.zoneId,
20 | action.setting
21 | );
22 |
23 | return Object.assign({}, state, {
24 | entities: _.merge(state.entities, normalizedPluginSettings.entities),
25 | result: _.merge(state.result, normalizedPluginSettings.result),
26 | isFetching: ''
27 | });
28 | case ActionTypes.PLUGIN_SETTINGS_FETCH_ERROR:
29 | return Object.assign({}, state, {
30 | isFetching: ''
31 | });
32 | case ActionTypes.PLUGIN_SETTING_UPDATE:
33 | return Object.assign({}, state, {
34 | entities: pluginPatchSetting(action.zoneId, action.setting, state),
35 | isFetching: action.setting.id
36 | });
37 | case ActionTypes.PLUGIN_SETTING_UPDATE_SUCCESS:
38 | return Object.assign({}, state, {
39 | entities: pluginPatchSetting(action.zoneId, action.setting, state),
40 | isFetching: ''
41 | });
42 | case ActionTypes.PLUGIN_SETTING_UPDATE_ERROR:
43 | return Object.assign({}, state, {
44 | entities: pluginPatchSetting(action.zoneId, action.setting, state),
45 | isFetching: ''
46 | });
47 | default:
48 | return state;
49 | }
50 | }
51 |
52 | function pluginPatchSetting(zoneId, setting, state) {
53 | let patchedEntities = Object.assign({}, state.entities);
54 | patchedEntities[zoneId][setting.id] = setting;
55 | return patchedEntities;
56 | }
57 |
--------------------------------------------------------------------------------
/src/reducers/user.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 | import { setEmail } from '../utils/Auth/Auth';
3 |
4 | const initialState = {
5 | isLoggedIn: false,
6 | isFetching: false
7 | };
8 |
9 | export function userReducer(state = initialState, action) {
10 | switch (action.type) {
11 | case ActionTypes.USER_LOGIN:
12 | return Object.assign({}, state, {
13 | isFetching: true
14 | });
15 | case ActionTypes.USER_LOGIN_SUCCESS:
16 | //routes.js needs cfEmail in localStorage to detect logged in state
17 | setEmail(action.email);
18 | return Object.assign({}, state, {
19 | isLoggedIn: true,
20 | isFetching: false
21 | });
22 | case ActionTypes.USER_LOGIN_ERROR:
23 | return Object.assign({}, state, {
24 | isFetching: false
25 | });
26 | case ActionTypes.USER_SIGNUP:
27 | return Object.assign({}, state, {
28 | isFetching: true
29 | });
30 | case ActionTypes.USER_SIGNUP_SUCCESS:
31 | return Object.assign({}, state, {
32 | isFetching: false
33 | });
34 | case ActionTypes.USER_SIGNUP_ERROR:
35 | return Object.assign({}, state, {
36 | isFetching: false
37 | });
38 | case ActionTypes.USER_LOGOUT:
39 | setEmail('');
40 | return Object.assign({}, state, {
41 | isLoggedIn: false
42 | });
43 | default:
44 | return state;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/reducers/zoneDnsRecords.js:
--------------------------------------------------------------------------------
1 | import { normalize, Schema, arrayOf } from 'normalizr';
2 | import _ from 'lodash';
3 | import * as ActionTypes from '../constants/ActionTypes';
4 |
5 | const initialState = {
6 | entities: {},
7 | result: [],
8 | isFetching: false,
9 | updateIsFetching: ''
10 | };
11 |
12 | export function dnsRecordsReducer(state = initialState, action) {
13 | switch (action.type) {
14 | case ActionTypes.DNS_RECORD_CLEAR_ALL:
15 | let dnsRecordEntities = state.entities;
16 | dnsRecordEntities[action.zoneId] = {};
17 |
18 | return Object.assign({}, state, {
19 | entities: dnsRecordEntities
20 | });
21 | case ActionTypes.DNS_RECORD_CREATE:
22 | return Object.assign({}, state, {
23 | updateIsFetching: action.name
24 | });
25 | case ActionTypes.DNS_RECORD_CREATE_SUCCESS:
26 | return Object.assign({}, state, {
27 | entities: patchDnsRecord(
28 | action.zoneId,
29 | state.entities,
30 | action.dnsRecord
31 | ),
32 | updateIsFetching: ''
33 | });
34 | case ActionTypes.DNS_RECORD_CREATE_ERROR:
35 | return Object.assign({}, state, {
36 | updateIsFetching: ''
37 | });
38 | case ActionTypes.DNS_RECORD_FETCH_LIST:
39 | return Object.assign({}, state, {
40 | isFetching: true
41 | });
42 | case ActionTypes.DNS_RECORD_FETCH_LIST_SUCCESS:
43 | let dnsRecordSchema = new Schema(action.zoneId, { idAttribute: 'name' });
44 | let normalizedDnsRecords = normalize(
45 | action.dnsRecords,
46 | arrayOf(dnsRecordSchema)
47 | );
48 |
49 | return Object.assign({}, state, {
50 | entities: _.merge(state.entities, normalizedDnsRecords.entities),
51 | result: _.merge(state.result, normalizedDnsRecords.result),
52 | isFetching: false
53 | });
54 | case ActionTypes.DNS_RECORD_FETCH_LIST_ERROR:
55 | return Object.assign({}, state, {
56 | isFetching: false
57 | });
58 | case ActionTypes.DNS_RECORD_UPDATE:
59 | return Object.assign({}, state, {
60 | updateIsFetching: action.name
61 | });
62 | case ActionTypes.DNS_RECORD_UPDATE_SUCCESS:
63 | return Object.assign({}, state, {
64 | entities: patchDnsRecord(
65 | action.zoneId,
66 | state.entities,
67 | action.dnsRecord
68 | ),
69 | updateIsFetching: ''
70 | });
71 | case ActionTypes.DNS_RECORD_UPDATE_ERROR:
72 | return Object.assign({}, state, {
73 | updateIsFetching: ''
74 | });
75 | default:
76 | return state;
77 | }
78 | }
79 |
80 | function patchDnsRecord(zoneId, dnsRecordList, dnsRecord) {
81 | dnsRecordList[zoneId][dnsRecord.name] = dnsRecord;
82 | return dnsRecordList;
83 | }
84 |
--------------------------------------------------------------------------------
/src/reducers/zoneEntitlements.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 | import { normalizeZoneEntitlements } from '../constants/Schemas';
3 |
4 | const initialState = {
5 | entities: {},
6 | isFetching: false
7 | };
8 |
9 | export function zoneEntitlementsReducer(state = initialState, action) {
10 | switch (action.type) {
11 | case ActionTypes.ZONE_ENTITLEMENTS:
12 | return Object.assign({}, state, {
13 | isFetching: true
14 | });
15 | case ActionTypes.ZONE_ENTITLEMENTS_SUCCESS:
16 | let normalizedZoneEntitlements = normalizeZoneEntitlements(
17 | action.zoneEntitlements
18 | );
19 | let newEntities = Object.assign({}, state.entities);
20 | newEntities[action.zoneId] =
21 | normalizedZoneEntitlements.entities.entitlements;
22 | return Object.assign({}, state, {
23 | entities: newEntities,
24 | isFetching: false
25 | });
26 | case ActionTypes.ZONE_ENTITLEMENTS_ERROR:
27 | return Object.assign({}, state, {
28 | isFetching: false
29 | });
30 | default:
31 | return state;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/reducers/zonePurgeCache.js:
--------------------------------------------------------------------------------
1 | import * as ActionTypes from '../constants/ActionTypes';
2 |
3 | const initialState = {
4 | isFetching: false
5 | };
6 |
7 | export function zonePurgeCacheReducer(state = initialState, action) {
8 | switch (action.type) {
9 | case ActionTypes.ZONE_PURGE_CACHE:
10 | return Object.assign({}, state, {
11 | isFetching: true
12 | });
13 | case ActionTypes.ZONE_PURGE_CACHE_SUCCESS:
14 | return Object.assign({}, state, {
15 | isFetching: false
16 | });
17 | case ActionTypes.ZONE_PURGE_CACHE_ERROR:
18 | return Object.assign({}, state, {
19 | isFetching: false
20 | });
21 | default:
22 | return state;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/reducers/zoneSettings.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import * as ActionTypes from '../constants/ActionTypes';
3 | import { normalizeZoneByIdGetAll } from '../constants/Schemas';
4 |
5 | const initialState = {
6 | entities: {},
7 | result: [],
8 | isFetching: ''
9 | };
10 |
11 | export function zoneSettingsReducer(state = initialState, action) {
12 | switch (action.type) {
13 | case ActionTypes.ZONE_FETCH_SETTINGS:
14 | return Object.assign({}, state, {
15 | isFetching: 'fetchAllSettings'
16 | });
17 | case ActionTypes.ZONE_FETCH_SETTINGS_SUCCESS:
18 | let normalizedZoneSettings = normalizeZoneByIdGetAll(
19 | action.zoneId,
20 | action.zoneSettings
21 | );
22 |
23 | return Object.assign({}, state, {
24 | entities: _.merge(state.entities, normalizedZoneSettings.entities),
25 | result: _.merge(state.result, normalizedZoneSettings.result),
26 | isFetching: ''
27 | });
28 | case ActionTypes.ZONE_FETCH_SETTINGS_ERROR:
29 | return Object.assign({}, state, {
30 | isFetching: ''
31 | });
32 | case ActionTypes.ZONE_UPDATE_SETTING:
33 | return Object.assign({}, state, {
34 | entities: zonePatchSetting(action.zoneId, action.setting, state),
35 | isFetching: action.setting.id
36 | });
37 | case ActionTypes.ZONE_UPDATE_SETTING_SUCCESS:
38 | return Object.assign({}, state, {
39 | entities: zonePatchSetting(action.zoneId, action.setting, state),
40 | isFetching: ''
41 | });
42 | case ActionTypes.ZONE_UPDATE_SETTING_ERROR:
43 | return Object.assign({}, state, {
44 | entities: zonePatchSetting(action.zoneId, action.setting, state),
45 | isFetching: ''
46 | });
47 | default:
48 | return state;
49 | }
50 | }
51 |
52 | function zonePatchSetting(zoneId, setting, state) {
53 | let patchedEntities = Object.assign({}, state.entities);
54 | patchedEntities[zoneId][setting.id] = setting;
55 | return patchedEntities;
56 | }
57 |
--------------------------------------------------------------------------------
/src/reducers/zones.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import * as ActionTypes from '../constants/ActionTypes';
3 | import { normalizeZoneGetAll } from '../constants/Schemas';
4 |
5 | const initialState = {
6 | entities: {},
7 | result: {},
8 | zoneDeleteIsFetching: false,
9 | zoneFetchIsFetching: false,
10 | zoneProvisionCnameIsFetching: false,
11 | zoneProvisionFullIsFetching: false
12 | };
13 |
14 | export function zonesReducer(state = initialState, action) {
15 | switch (action.type) {
16 | case ActionTypes.ZONES_DELETE_ZONE:
17 | return Object.assign({}, state, {
18 | zoneDeleteIsFetching: true
19 | });
20 | case ActionTypes.ZONES_DELETE_ZONE_SUCCESS:
21 | return Object.assign({}, state, {
22 | zoneDeleteIsFetching: false
23 | });
24 | case ActionTypes.ZONES_DELETE_ZONE_ERROR:
25 | return Object.assign({}, state, {
26 | zoneDeleteIsFetching: false
27 | });
28 | case ActionTypes.ZONES_FETCH:
29 | return Object.assign({}, state, {
30 | zoneFetchIsFetching: true
31 | });
32 | case ActionTypes.ZONES_FETCH_SUCCESS:
33 | let normalizedZoneList = normalizeZoneGetAll(action.zoneList);
34 |
35 | return Object.assign({}, state, {
36 | entities: _.merge(state.entities, normalizedZoneList.entities),
37 | result: _.merge(state.result, normalizedZoneList.result),
38 | zoneFetchIsFetching: false
39 | });
40 | case ActionTypes.ZONES_FETCH_ERROR:
41 | return Object.assign({}, state, {
42 | zoneFetchIsFetching: false
43 | });
44 | case ActionTypes.ZONES_PROVISION_CNAME:
45 | return Object.assign({}, state, {
46 | zoneProvisionCnameIsFetching: true
47 | });
48 | case ActionTypes.ZONES_PROVISION_CNAME_SUCCESS:
49 | return Object.assign({}, state, {
50 | zoneProvisionCnameIsFetching: false
51 | });
52 | case ActionTypes.ZONES_PROVISION_CNAME_ERROR:
53 | return Object.assign({}, state, {
54 | zoneProvisionCnameIsFetching: false
55 | });
56 | case ActionTypes.ZONES_PROVISION_FULL:
57 | return Object.assign({}, state, {
58 | zoneProvisionFullIsFetching: true
59 | });
60 | case ActionTypes.ZONES_PROVISION_FULL_SUCCESS:
61 | return Object.assign({}, state, {
62 | zoneProvisionFullIsFetching: false
63 | });
64 | case ActionTypes.ZONES_PROVISION_FULL_ERROR:
65 | return Object.assign({}, state, {
66 | zoneProvisionFullIsFetching: false
67 | });
68 | default:
69 | return state;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, IndexRoute } from 'react-router';
3 | import * as UrlPaths from './constants/UrlPaths';
4 | import { isLoggedIn } from './utils/Auth/Auth';
5 |
6 | /* containers */
7 | import AnalyticsPage from './containers/AnalyticsPage/AnaltyicsPage';
8 | import App from './containers/App/App';
9 | import DNSManagementPage from './containers/DNSManagementPage/DNSManagementPage';
10 | import LoginPage from './containers/LoginPage/LoginPage';
11 | import SplashPage from './containers/SplashPage/SplashPage';
12 | import SignUpPage from './containers/SignUpPage/SignUpPage';
13 | import HomePage from './containers/HomePage/HomePage';
14 | import MoreSettingsPage from './containers/MoreSettingsPage/MoreSettingsPage';
15 |
16 | function requireAuth(nextState, replaceState) {
17 | if (!isLoggedIn()) {
18 | replaceState(
19 | { nextPathname: nextState.location.pathname },
20 | UrlPaths.LOGIN_PAGE
21 | );
22 | }
23 | }
24 |
25 | export default (
26 |
27 |
28 |
29 |
30 |
35 |
40 |
45 |
50 |
51 | );
52 |
--------------------------------------------------------------------------------
/src/selectors/activeZone.js:
--------------------------------------------------------------------------------
1 | export function isActiveZoneOnCloudflare(activeZone) {
2 | return activeZone.id !== undefined;
3 | }
4 |
--------------------------------------------------------------------------------
/src/selectors/config.js:
--------------------------------------------------------------------------------
1 | import { ABSOLUTE_URL_BASE_KEY } from '../reducers/config';
2 |
3 | export function getAbsoluteUrl(config, url) {
4 | let baseUrl =
5 | typeof config.config[ABSOLUTE_URL_BASE_KEY] !== 'undefined'
6 | ? config.config[ABSOLUTE_URL_BASE_KEY]
7 | : '';
8 | return baseUrl + url;
9 | }
10 |
11 | export function getConfigValue(config, key) {
12 | return config.config[key];
13 | }
14 |
15 | export function isDNSPageEnabled(config) {
16 | return getConfigValue(config, 'isDNSPageEnabled') === true;
17 | }
18 |
--------------------------------------------------------------------------------
/src/selectors/intl.js:
--------------------------------------------------------------------------------
1 | export function getLocale(state) {
2 | return state.intl.locale;
3 | }
4 |
--------------------------------------------------------------------------------
/src/selectors/pluginSettings.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export function getPluginSettingsForZoneId(zoneId, state) {
4 | return _.get(state, ['entities', zoneId], false);
5 | }
6 |
7 | export function getPluginSettingsIsFetching(state) {
8 | return _.get(state, ['pluginSettings', 'isFetching']);
9 | }
10 |
11 | export function getPluginSettingsValueForZoneId(zoneId, settingId, state) {
12 | // return false as default value
13 | return _.get(
14 | state,
15 | ['pluginSettings', 'entities', zoneId, settingId, 'value'],
16 | false
17 | );
18 | }
19 |
20 | export function getPluginSettingsModifiedDateForZoneId(
21 | zoneId,
22 | settingId,
23 | state
24 | ) {
25 | // return '' as default value
26 | return _.get(
27 | state,
28 | ['pluginSettings', 'entities', zoneId, settingId, 'modified_on'],
29 | ''
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/src/selectors/zoneSettings.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export function getZoneSettingsValueForZoneId(zoneId, settingId, state) {
4 | // return false as default value
5 | return _.get(
6 | state,
7 | ['zoneSettings', 'entities', zoneId, settingId, 'value'],
8 | false
9 | );
10 | }
11 |
12 | export function getZoneSettingsModifiedDateForZoneId(zoneId, settingId, state) {
13 | // return '' as default value
14 | return _.get(
15 | state,
16 | ['zoneSettings', 'entities', zoneId, settingId, 'modified_on'],
17 | ''
18 | );
19 | }
20 |
21 | export function getAllZoneSettingsForZoneId(zoneId, state) {
22 | return _.get(state, ['entities', zoneId], false);
23 | }
24 |
--------------------------------------------------------------------------------
/src/selectors/zones.js:
--------------------------------------------------------------------------------
1 | export function getZonePlanLegacyId(zoneName, zones) {
2 | return zones.entities.zones[zoneName].plan.legacy_id;
3 | }
4 |
--------------------------------------------------------------------------------
/src/store/configureStore.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import thunkMiddleware from 'redux-thunk';
3 | import createLogger from 'redux-logger';
4 | import rootReducer from '../reducers';
5 | import { routerMiddleware } from 'react-router-redux';
6 |
7 | export default function configureStore(history, initialState) {
8 | const router = routerMiddleware(history);
9 |
10 | const logger = createLogger({ collapsed: true });
11 |
12 | const createStoreWithMiddleware = applyMiddleware(
13 | thunkMiddleware,
14 | logger,
15 | router
16 | )(createStore);
17 | const store = createStoreWithMiddleware(rootReducer, initialState);
18 |
19 | return store;
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/_helpers/createMockStore.js:
--------------------------------------------------------------------------------
1 | import configureStore from 'redux-mock-store';
2 | import thunk from 'redux-thunk';
3 | const middlewares = [thunk];
4 |
5 | export default state => configureStore(middlewares)(state);
6 |
--------------------------------------------------------------------------------
/src/test/components/BenefitsFeatureTest.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import BenefitsFeature from '../../components/BenefitsFeature/BenefitsFeature';
3 | import renderer from 'react-test-renderer';
4 |
5 | it('renders enabled correctly', () => {
6 | const component = renderer
7 | .create(
8 |
13 | )
14 | .toJSON();
15 | expect(component).toMatchSnapshot();
16 | });
17 |
--------------------------------------------------------------------------------
/src/test/components/FeatureManagerTest.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import FeatureManager from '../../components/FeatureManager/FeatureManager';
3 | import renderer from 'react-test-renderer';
4 |
5 | it('renders enabled correctly', () => {
6 | const component = renderer
7 | .create(
8 |
9 | children
10 |
11 | )
12 | .toJSON();
13 | expect(component).toMatchSnapshot();
14 | });
15 |
16 | it('renders disabled correctly', () => {
17 | const component = renderer
18 | .create(
19 |
20 | children
21 |
22 | )
23 | .toJSON();
24 | expect(component).toMatchSnapshot();
25 | });
26 |
--------------------------------------------------------------------------------
/src/test/components/GradientBannerTest.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import GradientBanner from '../../components/GradientBanner/GradientBanner';
3 | import renderer from 'react-test-renderer';
4 | import { StyleProvider } from 'cf-style-provider';
5 |
6 | it('renders enabled correctly', () => {
7 | const component = renderer
8 | .create(
9 |
15 |
20 |
21 | )
22 | .toJSON();
23 | expect(component).toMatchSnapshot();
24 | });
25 |
--------------------------------------------------------------------------------
/src/test/components/MarketingFeatureTest.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import MarketingFeature from '../../components/MarketingFeature/MarketingFeature';
3 | import renderer from 'react-test-renderer';
4 |
5 | it('renders enabled correctly', () => {
6 | const component = renderer
7 | .create(
8 |
13 | )
14 | .toJSON();
15 | expect(component).toMatchSnapshot();
16 | });
17 |
--------------------------------------------------------------------------------
/src/test/components/__snapshots__/BenefitsFeatureTest.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders enabled correctly 1`] = `
4 |
11 |

23 |
36 | title
37 |
38 |
48 | description
49 |
50 |
51 | `;
52 |
--------------------------------------------------------------------------------
/src/test/components/__snapshots__/FeatureManagerTest.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders disabled correctly 1`] = `
4 |
5 | error
6 |
7 | `;
8 |
9 | exports[`renders enabled correctly 1`] = `
10 |
11 | children
12 |
13 | `;
14 |
--------------------------------------------------------------------------------
/src/test/components/__snapshots__/GradientBannerTest.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders enabled correctly 1`] = `
4 |
7 | `;
8 |
--------------------------------------------------------------------------------
/src/test/components/__snapshots__/MarketingFeatureTest.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders enabled correctly 1`] = `
4 |
11 |

23 |
36 | title
37 |
38 |
47 | description
48 |
49 |
50 | `;
51 |
--------------------------------------------------------------------------------
/src/test/containers/SplashPageTest.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SplashPage from '../../containers/SplashPage/SplashPage';
3 |
4 | import renderer from 'react-test-renderer';
5 | import createMockStore from '../_helpers/createMockStore';
6 |
7 | import { Provider } from 'react-redux';
8 | import { StyleProvider } from 'cf-style-provider';
9 | import { IntlProvider } from 'react-intl';
10 |
11 | const messages = {
12 | 'container.splashPage.heading.speedUp':
13 | 'Speed up and Optimize your WordPress Site with Cloudflare',
14 | 'container.splashPage.help.alreadyHaveAccount':
15 | 'Have an account already? Sign in',
16 | 'container.splashPage.help.here': 'here',
17 | 'container.splashPage.button.createFreeAccount': 'Create Your Free Account',
18 | 'component.benefitsFeature.globalCaching.title': 'Global Caching',
19 | 'component.benefitsFeature.globalCaching.description':
20 | "Shorten your visitor's load times with content caching in our 110+ global locations.",
21 | 'component.benefitsFeature.optimization.title': 'Website Optimization',
22 | 'component.benefitsFeature.optimization.description':
23 | "Deliver content to users faster by enabling Cloudflare's content optimizations.",
24 | 'component.benefitsFeature.security.title': 'Security',
25 | 'component.benefitsFeature.security.description':
26 | 'Cloudflare offers protections against vulnerabilities including DDoS protection.',
27 | 'component.benefitsFeature.insights.title': 'Insights',
28 | 'component.benefitsFeature.insights.description':
29 | 'Monitor bandwidth saved, threats blocked, and more with built-in analytics.'
30 | };
31 |
32 | it('renders enabled correctly', () => {
33 | window.localStorage = {};
34 | const component = renderer
35 | .create(
36 |
41 |
42 |
48 |
49 |
50 |
51 |
52 | )
53 | .toJSON();
54 | expect(component).toMatchSnapshot();
55 | });
56 |
--------------------------------------------------------------------------------
/src/test/reducers/__snapshots__/configTest.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Config Reducer should handle CONFIG_FETCH 1`] = `
4 | Object {
5 | "config": Object {},
6 | "isFetching": true,
7 | }
8 | `;
9 |
10 | exports[`Config Reducer should handle CONFIG_FETCH_ERROR 1`] = `
11 | Object {
12 | "config": Object {},
13 | "isFetching": false,
14 | }
15 | `;
16 |
17 | exports[`Config Reducer should handle CONFIG_FETCH_SUCCESS 1`] = `
18 | Object {
19 | "config": Object {},
20 | "isFetching": false,
21 | }
22 | `;
23 |
24 | exports[`Config Reducer should handle CONFIG_UPDATE_BY_KEY 1`] = `
25 | Object {
26 | "config": Object {
27 | "key": "value",
28 | },
29 | "isFetching": false,
30 | }
31 | `;
32 |
33 | exports[`Config Reducer should return the initial state 1`] = `
34 | Object {
35 | "config": Object {},
36 | "isFetching": false,
37 | }
38 | `;
39 |
--------------------------------------------------------------------------------
/src/test/reducers/activeZoneTest.js:
--------------------------------------------------------------------------------
1 | import { activeZoneReducer } from '../..//reducers/activeZone';
2 | import * as ActionTypes from '../..//constants/ActionTypes';
3 |
4 | describe('Active Zone Reducer', () => {
5 | it('should return the initial state', () => {
6 | expect(activeZoneReducer(undefined, {})).toEqual({
7 | id: '',
8 | name: ''
9 | });
10 | });
11 |
12 | it('should set active zone', () => {
13 | expect(
14 | activeZoneReducer(
15 | {},
16 | {
17 | type: ActionTypes.ZONES_SET_ACTIVE_ZONE,
18 | zone: { id: 1, name: 'name' }
19 | }
20 | )
21 | ).toEqual({
22 | id: 1,
23 | name: 'name'
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/src/test/reducers/configTest.js:
--------------------------------------------------------------------------------
1 | import { configReducer } from '../..//reducers/config';
2 | import * as ActionTypes from '../..//constants/ActionTypes';
3 |
4 | describe('Config Reducer', () => {
5 | it('should return the initial state', () => {
6 | expect(configReducer(undefined, {})).toMatchSnapshot();
7 | });
8 |
9 | it('should handle CONFIG_FETCH', () => {
10 | expect(
11 | configReducer(undefined, {
12 | type: ActionTypes.CONFIG_FETCH
13 | })
14 | ).toMatchSnapshot();
15 | });
16 |
17 | it('should handle CONFIG_FETCH_SUCCESS', () => {
18 | expect(
19 | configReducer(undefined, {
20 | type: ActionTypes.CONFIG_FETCH_SUCCESS
21 | })
22 | ).toMatchSnapshot();
23 | });
24 |
25 | it('should handle CONFIG_FETCH_ERROR', () => {
26 | expect(
27 | configReducer(undefined, {
28 | type: ActionTypes.CONFIG_FETCH_ERROR
29 | })
30 | ).toMatchSnapshot();
31 | });
32 |
33 | it('should handle CONFIG_UPDATE_BY_KEY', () => {
34 | expect(
35 | configReducer(undefined, {
36 | type: ActionTypes.CONFIG_UPDATE_BY_KEY,
37 | key: 'key',
38 | value: 'value'
39 | })
40 | ).toMatchSnapshot();
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/src/test/reducers/zonePurgeCacheTest.js:
--------------------------------------------------------------------------------
1 | import { zonePurgeCacheReducer } from '../..//reducers/zonePurgeCache';
2 | import * as ActionTypes from '../..//constants/ActionTypes';
3 |
4 | let initialState = {
5 | isFetching: false
6 | };
7 |
8 | describe('Plugin Settings Reducer', () => {
9 | it('should return the initial state', () => {
10 | expect(zonePurgeCacheReducer(initialState, {})).toEqual(initialState);
11 | });
12 |
13 | it('should handle ZONE_PURGE_CACHE', () => {
14 | expect(
15 | zonePurgeCacheReducer(initialState, {
16 | type: ActionTypes.ZONE_PURGE_CACHE
17 | })
18 | ).toEqual({
19 | isFetching: true
20 | });
21 | });
22 |
23 | it('should handle ZONE_PURGE_CACHE_SUCCESS', () => {
24 | expect(
25 | zonePurgeCacheReducer(initialState, {
26 | type: ActionTypes.ZONE_PURGE_CACHE_SUCCESS
27 | })
28 | ).toEqual({
29 | isFetching: false
30 | });
31 | });
32 |
33 | it('should handle ZONE_PURGE_CACHE_ERROR', () => {
34 | expect(
35 | zonePurgeCacheReducer(initialState, {
36 | type: ActionTypes.ZONE_PURGE_CACHE_ERROR
37 | })
38 | ).toEqual({
39 | isFetching: false
40 | });
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/src/test/selectors/configTest.js:
--------------------------------------------------------------------------------
1 | import { getAbsoluteUrl, getConfigValue } from '../..//selectors/config';
2 | import { ABSOLUTE_URL_BASE_KEY } from '../..//reducers/config.js';
3 |
4 | describe('Config Selector', () => {
5 | it('getAbsoluteUrl should concatenate the absolute URL', () => {
6 | expect(
7 | getAbsoluteUrl(
8 | {
9 | config: {
10 | [ABSOLUTE_URL_BASE_KEY]: 'http://site.com/'
11 | }
12 | },
13 | 'test.html'
14 | )
15 | ).toBe('http://site.com/test.html');
16 | });
17 |
18 | it('getAbsoluteUrl should return url if no absolute url base is present', () => {
19 | expect(
20 | getAbsoluteUrl(
21 | {
22 | config: {}
23 | },
24 | 'test.html'
25 | )
26 | ).toBe('test.html');
27 | });
28 |
29 | it('getConfigValue should return value if key exists', () => {
30 | expect(
31 | getConfigValue(
32 | {
33 | config: {
34 | key: 'value'
35 | }
36 | },
37 | 'key'
38 | )
39 | ).toBe('value');
40 | });
41 |
42 | it('isDNSPageEnabled should return true if key is true', () => {
43 | expect(
44 | getConfigValue(
45 | {
46 | config: {
47 | isDNSPageEnabled: true
48 | }
49 | },
50 | 'isDNSPageEnabled'
51 | )
52 | ).toBeTruthy();
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/src/test/utils/PluginAPITest.js:
--------------------------------------------------------------------------------
1 | import { pluginResponseOk } from '../..//utils/PluginAPI/PluginAPI';
2 |
3 | describe('PluginAPI', () => {
4 | it('pluginResponseOk should return true for valid response', () => {
5 | expect(
6 | pluginResponseOk({
7 | body: {
8 | success: true
9 | }
10 | })
11 | ).toBeTruthy();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/test/utils/deduplicateZonesTest.js:
--------------------------------------------------------------------------------
1 | import { deduplicateOnActiveZones } from '../../utils/utils';
2 | import expect from 'expect';
3 |
4 | describe('deduplicateZones', () => {
5 | it('should not change a list of zones without any duplicate', () => {
6 | expect(
7 | deduplicateOnActiveZones([
8 | { id: '1', name: 'cloudflare.com', status: 'active' },
9 | { id: '2', name: 'blog.cloudflare.com', status: 'active' }
10 | ])
11 | ).toEqual([
12 | { id: '1', name: 'cloudflare.com', status: 'active' },
13 | { id: '2', name: 'blog.cloudflare.com', status: 'active' }
14 | ]);
15 | });
16 |
17 | it('should remove non-active zone in case of duplicates', () => {
18 | expect(
19 | deduplicateOnActiveZones([
20 | { id: '1', name: 'cloudflare.com', status: 'active' },
21 | { id: '2', name: 'blog.cloudflare.com', status: 'active' },
22 | { id: '3', name: 'cloudflare.com', status: 'purged' }
23 | ])
24 | ).toEqual([
25 | { id: '1', name: 'cloudflare.com', status: 'active' },
26 | { id: '2', name: 'blog.cloudflare.com', status: 'active' }
27 | ]);
28 | });
29 |
30 | it('should not remove duplicates when there is no active zone', () => {
31 | expect(
32 | deduplicateOnActiveZones([
33 | { id: '1', name: 'cloudflare.com', status: 'pending' },
34 | { id: '2', name: 'blog.cloudflare.com', status: 'active' },
35 | { id: '3', name: 'cloudflare.com', status: 'purged' }
36 | ])
37 | ).toEqual([
38 | { id: '1', name: 'cloudflare.com', status: 'pending' },
39 | { id: '2', name: 'blog.cloudflare.com', status: 'active' },
40 | { id: '3', name: 'cloudflare.com', status: 'purged' }
41 | ]);
42 | });
43 |
44 | it('should remove multiple duplicates when there is one active zone', () => {
45 | expect(
46 | deduplicateOnActiveZones([
47 | { id: '1', name: 'cloudflare.com', status: 'pending' },
48 | { id: '2', name: 'cloudflare.com', status: 'purged' },
49 | { id: '3', name: 'cloudflare.com', status: 'active' },
50 | { id: '4', name: 'cloudflare.com', status: 'moved' }
51 | ])
52 | ).toEqual([{ id: '3', name: 'cloudflare.com', status: 'active' }]);
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/src/utils/Auth/Auth.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export function isLoggedIn() {
4 | if (_.isEmpty(localStorage.cfEmail)) {
5 | return false;
6 | }
7 | return true;
8 | }
9 |
10 | export function getEmail() {
11 | return localStorage.cfEmail;
12 | }
13 |
14 | export function setEmail(email) {
15 | localStorage.cfEmail = email;
16 | }
17 |
--------------------------------------------------------------------------------
/src/utils/ImportCards.js:
--------------------------------------------------------------------------------
1 | // Cards
2 | import AdvanceDDoSCard from '../containers/AdvanceDDoSCard/AdvanceDDoSCard';
3 | import AlwaysOnlineCard from '../containers/AlwaysOnlineCard/AlwaysOnlineCard';
4 | import ApplyDefaultSettingsCard from '../containers/ApplyDefaultSettingsCard/ApplyDefaultSettingsCard';
5 | import BrowserCacheTTLCard from '../containers/BrowserCacheTTLCard/BrowserCacheTTLCard';
6 | import BrowserIntegrityCheckCard from '../containers/BrowserIntegrityCheckCard/BrowserIntegrityCheckCard';
7 | import CacheLevelCard from '../containers/CacheLevelCard/CacheLevelCard';
8 | import ChallengePassageCard from '../containers/ChallengePassageCard/ChallengePassageCard';
9 | import DevelopmentModeCard from '../containers/DevelopmentModeCard/DevelopmentModeCard';
10 | import IPV6Card from '../containers/IPV6Card/IPV6Card';
11 | import ImageOptimizationCard from '../containers/ImageOptimizationCard/ImageOptimizationCard';
12 | import IpRewriteCard from '../containers/IpRewriteCard/IpRewriteCard';
13 | import PurgeCacheCard from '../containers/PurgeCacheCard/PurgeCacheCard';
14 | import SSLCard from '../containers/SSLCard/SSLCard';
15 | import SecurityLevelCard from '../containers/SecurityLevelCard/SecurityLevelCard';
16 | import WAFCard from '../containers/WAFCard/WAFCard';
17 | import PluginSpecificCacheCard from '../containers/PluginSpecificCacheCard/PluginSpecificCacheCard';
18 | import PluginSpecificCacheTagCard from '../containers/PluginSpecificCacheTagCard/PluginSpecificCachetTagCard.js';
19 | import AutomaticHTTPSRewritesCard from '../containers/AutomaticHTTPSRewritesCard/AutomaticHTTPSRewritesCard';
20 | import AutomaticPlatformOptimizationCard from '../containers/AutomaticPlatformOptimization/AutomaticPlatformOptimizationCard';
21 |
22 | // Pages
23 | import DNSManagementPage from '../containers/DNSManagementPage/DNSManagementPage';
24 |
25 | const cardMapper = {
26 | AdvanceDDoSCard: AdvanceDDoSCard,
27 | AlwaysOnlineCard: AlwaysOnlineCard,
28 | ApplyDefaultSettingsCard: ApplyDefaultSettingsCard,
29 | AutomaticHTTPSRewritesCard: AutomaticHTTPSRewritesCard,
30 | AutomaticPlatformOptimizationCard: AutomaticPlatformOptimizationCard,
31 | BrowserCacheTTLCard: BrowserCacheTTLCard,
32 | BrowserIntegrityCheckCard: BrowserIntegrityCheckCard,
33 | CacheLevelCard: CacheLevelCard,
34 | ChallengePassageCard: ChallengePassageCard,
35 | DNSManagementPage: DNSManagementPage,
36 | DevelopmentModeCard: DevelopmentModeCard,
37 | IPV6Card: IPV6Card,
38 | ImageOptimizationCard: ImageOptimizationCard,
39 | IpRewriteCard: IpRewriteCard,
40 | PluginSpecificCacheCard: PluginSpecificCacheCard,
41 | PluginSpecificCacheTagCard: PluginSpecificCacheTagCard,
42 | PurgeCacheCard: PurgeCacheCard,
43 | SSLCard: SSLCard,
44 | SecurityLevelCard: SecurityLevelCard,
45 | WAFCard: WAFCard
46 | };
47 |
48 | export { cardMapper };
49 |
--------------------------------------------------------------------------------
/src/utils/PluginAPI/PluginAPI.js:
--------------------------------------------------------------------------------
1 | import http from 'cf-util-http';
2 | import {
3 | v4ResponseOk,
4 | v4Callback
5 | } from '../../utils/CFClientV4API/CFClientV4API';
6 |
7 | /*
8 | * This endpoint isn't real but we'll use it to identify REST calls for
9 | * plugin specific functionality like saving a v4 API key/email or toggling
10 | * admin settings. The structure of this API's responses will mimic the client V4 API.
11 | */
12 | const ENDPOINT = 'https://partners.cloudflare/plugins';
13 |
14 | /*
15 | * Indicates api call success
16 | *
17 | * @param {Object} [response]
18 | *
19 | * @returns {Boolean} Successful
20 | */
21 | export function pluginResponseOk(response) {
22 | return v4ResponseOk(response);
23 | }
24 |
25 | export function pluginCallback(callback) {
26 | return v4Callback(callback);
27 | }
28 |
29 | export function pluginAccountPost(email, apiKey, callback) {
30 | let opts = {
31 | body: {
32 | email: email.toString().trim(),
33 | apiKey: apiKey.toString().trim()
34 | }
35 | };
36 | return http.post(ENDPOINT + '/account/', opts, pluginCallback(callback));
37 | }
38 |
39 | export function pluginSettingListGet(zoneId, callback) {
40 | let opts = {};
41 |
42 | return http.get(
43 | ENDPOINT + '/plugin/' + zoneId['zoneId'] + '/settings/',
44 | opts,
45 | pluginCallback(callback)
46 | );
47 | }
48 |
49 | export function pluginSettingPatch(zoneId, settingName, value, callback) {
50 | let opts = {
51 | body: {
52 | value: value
53 | }
54 | };
55 |
56 | return http.patch(
57 | ENDPOINT + '/plugin/' + zoneId + '/settings/' + settingName,
58 | opts,
59 | pluginCallback(callback)
60 | );
61 | }
62 |
63 | export function configGet(callback) {
64 | let opts = {};
65 | return http.get(ENDPOINT + '/config/', opts, pluginCallback(callback));
66 | }
67 |
--------------------------------------------------------------------------------
/src/utils/utils.js:
--------------------------------------------------------------------------------
1 | function beginsWith(needle, haystack) {
2 | return haystack.substr(0, needle.length) == needle;
3 | }
4 |
5 | function endsWith(str, suffix) {
6 | return str.indexOf(suffix, str.length - suffix.length) !== -1;
7 | }
8 |
9 | export function isSubdomain(selectedZoneName) {
10 | var currentDomainName = new URL(document.URL).hostname;
11 |
12 | if (
13 | endsWith(currentDomainName, selectedZoneName) &&
14 | !beginsWith('www.', currentDomainName) &&
15 | selectedZoneName !== currentDomainName &&
16 | currentDomainName &&
17 | selectedZoneName
18 | ) {
19 | return true;
20 | }
21 |
22 | return false;
23 | }
24 |
25 | export function getLastModifiedDate(intl, modfiedDate) {
26 | const { formatMessage, formatRelative } = intl;
27 |
28 | if (!modfiedDate) {
29 | // Once you get the new code try this
30 | return null;
31 | }
32 |
33 | var formattedModefiedDate = formatRelative(new Date(modfiedDate), {
34 | now: Date.now()
35 | });
36 |
37 | var value = { date: formattedModefiedDate };
38 | return formatMessage({ id: 'utils.utils.lastmodifieddate' }, value);
39 | }
40 |
41 | export function humanFileSize(bytes) {
42 | var thresh = 1000;
43 | if (Math.abs(bytes) < thresh) {
44 | return bytes + ' B';
45 | }
46 | var units = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
47 | var u = -1;
48 | do {
49 | bytes /= thresh;
50 | ++u;
51 | } while (Math.abs(bytes) >= thresh && u < units.length - 1);
52 | return bytes.toFixed(1) + ' ' + units[u];
53 | }
54 |
55 | export function openWindow720x720(link) {
56 | window.open(link, '_blank', 'toolbar=0,status=0,width=720,height=700');
57 | }
58 |
59 | export function formatMessageForIntegration(
60 | intl,
61 | translationId,
62 | integrationName
63 | ) {
64 | const { formatMessage } = intl;
65 |
66 | let integrationKey = translationId + '.' + integrationName;
67 | const messageId = !!intl.messages[integrationKey]
68 | ? integrationKey
69 | : translationId;
70 |
71 | return formatMessage({ id: messageId });
72 | }
73 |
74 | // deduplicateOnActiveZones was introduced as a potential fix for CUSTESC-36595. The goal of this function is to
75 | // deduplicate the list of zones returned by the Cloudflare API based on the status of the zone. This way, we hope to
76 | // guarantee that when normalizing the list of zones, only the active zone would show up in case of duplicates. For
77 | // instance, in the mentioned tickets, two zones were potentially returned: a purged as well as an active zone.
78 | export function deduplicateOnActiveZones(zones) {
79 | let filteredZones = [];
80 | const isActive = z => z.status === 'active';
81 | const isSameZone = (z1, z2) => z1.name === z2.name;
82 |
83 | zones.forEach(zone => {
84 | if (isActive(zone)) {
85 | // If the zone is active, we first remove all existing duplicates and then insert the active zone.
86 | filteredZones = filteredZones.filter(fZone => !isSameZone(zone, fZone));
87 | filteredZones.push(zone);
88 | } else {
89 | // If the zone is not active, we only insert it if there is no existing active zone.
90 | const alreadyHasActiveZone = filteredZones.some(
91 | fZone => isSameZone(zone, fZone) && isActive(fZone)
92 | );
93 | if (!alreadyHasActiveZone) {
94 | filteredZones.push(zone);
95 | }
96 | }
97 | });
98 |
99 | return filteredZones;
100 | }
101 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 |
3 | var isDev = process.env.NODE_ENV !== 'production';
4 |
5 | module.exports = {
6 | entry: './src/index.js',
7 | mode: isDev ? 'development' : 'production',
8 | devtool: isDev ? 'cheap-module-source-map' : false,
9 | output: {
10 | path: __dirname,
11 | filename: process.env.OUTPUT_PATH
12 | ? process.env.OUTPUT_PATH
13 | : './compiled.js'
14 | },
15 | watch: isDev,
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | loader: 'babel-loader'
22 | }
23 | ]
24 | }
25 | };
26 |
--------------------------------------------------------------------------------