From d92d9980ce4f6833292818cb1a9213c7db0c781e Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 5 Dec 2022 14:11:46 -0600 Subject: [PATCH] Website: Build pricing page from yaml file (#8549) * Create pricing-features-table.yml * update built-static-content to add the pricing table configuration to builtStaticContent * Update pricing page to use builtStaticContent.pricingTable * Update view-pricing.js * update pricing table, lint fix Co-authored-by: Mike McNeil --- handbook/product/pricing-features-table.yml | 131 +++ website/api/controllers/view-pricing.js | 13 +- website/assets/styles/pages/pricing.less | 8 +- website/scripts/build-static-content.js | 36 + website/views/pages/pricing.ejs | 1083 +------------------ 5 files changed, 230 insertions(+), 1041 deletions(-) create mode 100644 handbook/product/pricing-features-table.yml diff --git a/handbook/product/pricing-features-table.yml b/handbook/product/pricing-features-table.yml new file mode 100644 index 000000000000..da1701df6e7a --- /dev/null +++ b/handbook/product/pricing-features-table.yml @@ -0,0 +1,131 @@ +- categoryName: Support + features: + - name: Public issue tracker (GitHub) + tier: Free + comingSoon: false + - name: Community Slack channel + tier: Free + comingSoon: false + - name: Unlimited email support (confidential) + tier: Premium + comingSoon: false + - name: Phone and video call support + tier: Premium + comingSoon: false +- categoryName: Inventory management + features: + - name: Secure REST API + tier: Free + comingSoon: false + - name: Command line tool (CLI) + tier: Free + comingSoon: false + - name: Realtime device inventory dashboard + tier: Free + comingSoon: false + - name: Browse installed software packages + tier: Free + comingSoon: false + - name: Search devices by IP, serial, hostname, UUID + tier: Free + comingSoon: false + - name: Target and configure specific groups of devices + tier: Premium + comingSoon: false + - name: Aggregate insights for groups of devices + tier: Premium + comingSoon: false +- categoryName: Collaboration + features: + - name: Shareable device health reports + tier: Free + comingSoon: false + - name: Versionable queries and config (GitOps) + tier: Free + comingSoon: false + - name: Human-to-device mapping + tier: Free + comingSoon: false + - name: Scope transparency + tier: Free + comingSoon: false + - name: Multiple teams + tier: Premium + comingSoon: false +- categoryName: Security and compliance + features: + - name: Single sign on (SSO, SAML) + tier: Free + comingSoon: false + - name: Audit queries and user activities + tier: Free + comingSoon: false + - name: Grant API-only access + tier: Free + comingSoon: false + - name: Role-based access control + tier: Free + comingSoon: false + - name: Just-in-time provisioning + tier: Premium + comingSoon: false +- categoryName: Monitoring + features: + - name: Schedule and automate custom queries + tier: Free + comingSoon: false + - name: Detect vulnerable software + tier: Free + comingSoon: false + - name: Query performance monitoring + tier: Free + comingSoon: false + - name: Standard query and policy library + tier: Free + comingSoon: false + - name: Detect and surface issues with devices + tier: Free + comingSoon: false + - name: Policy and vulnerability automations (webhook, Zendesk, JIRA, ServiceNow*) + tier: Free + comingSoon: false + - name: Vulnerability scores (EPSS and CVSS) + tier: Premium + comingSoon: false + - name: CISA known exploited vulnerabilities + tier: Premium + comingSoon: false + - name: End-user self-service + tier: Premium + comingSoon: false +- categoryName: Data outputs + features: + - name: Flexible log destinations (AWS Kinesis, Lambda, GCP, Kafka) + tier: Free + comingSoon: false + - name: File carving (AWS S3) + tier: Free + comingSoon: false +- categoryName: Deployment + features: + - name: Self-managed + tier: Free + comingSoon: false + - name: Deployment tools (Helm, Terraform) + tier: Free + comingSoon: false + - name: Configure osquery startup flags remotely + tier: Free + comingSoon: true + - name: Autoupdate osquery agents + tier: Free + comingSoon: false + - name: Self-managed autoupdate registry + tier: Premium + comingSoon: false + - name: Manage osquery extensions remotely + tier: Premium + comingSoon: false + + + diff --git a/website/api/controllers/view-pricing.js b/website/api/controllers/view-pricing.js index 2c0bf511262d..1f65d22a1187 100644 --- a/website/api/controllers/view-pricing.js +++ b/website/api/controllers/view-pricing.js @@ -11,15 +11,24 @@ module.exports = { success: { viewTemplatePath: 'pages/pricing' - } + }, + + badConfig: { + responseType: 'badConfig' + }, }, fn: async function () { + if(!_.isObject(sails.config.builtStaticContent) || !_.isArray(sails.config.builtStaticContent.pricingTable)) { + throw {badConfig: 'builtStaticContent.pricingTable'}; + } + let pricingTable = sails.config.builtStaticContent.pricingTable; + // Respond with view. - return {}; + return { pricingTable }; } diff --git a/website/assets/styles/pages/pricing.less b/website/assets/styles/pages/pricing.less index b90ff274c937..286654706e72 100644 --- a/website/assets/styles/pages/pricing.less +++ b/website/assets/styles/pages/pricing.less @@ -8,10 +8,7 @@ .btn { color: #fff; } - .faq-question { - font-weight: @bold; - } - .faq-list { + [purpose='faq-list'] { padding-left: 20px; li { @@ -20,6 +17,9 @@ } } + [purpose='pricing-table-category']:not(:first-of-type) { + margin-top: 48px; + } @media (max-width: 767px) { p {font-size: 16px;} diff --git a/website/scripts/build-static-content.js b/website/scripts/build-static-content.js index f4627d1e4037..09ea978579a8 100644 --- a/website/scripts/build-static-content.js +++ b/website/scripts/build-static-content.js @@ -638,6 +638,42 @@ module.exports = { builtStaticContent.compiledPagePartialsAppPath = APP_PATH_TO_COMPILED_PAGE_PARTIALS; }, + async()=>{ + // Validate the pricing table yaml and add it to builtStaticContent.pricingTable. + let RELATIVE_PATH_TO_PRICING_TABLE_YML_IN_FLEET_REPO = 'handbook/product/pricing-features-table.yml';// TODO: Is there a better home for this file? + let yaml = await sails.helpers.fs.read(path.join(topLvlRepoPath, RELATIVE_PATH_TO_PRICING_TABLE_YML_IN_FLEET_REPO)).intercept('doesNotExist', (err)=>new Error(`Could not find pricing table features YAML file at "${RELATIVE_PATH_TO_PRICING_TABLE_YML_IN_FLEET_REPO}". Was it accidentally moved? Raw error: `+err.message)); + let pricingTableCategories = YAML.parse(yaml, {prettyErrors: true}); + + for(let category of pricingTableCategories){ + if(!category.categoryName){ // Throw an error if a category is missing a categoryName. + throw new Error('Could not build pricing table config from pricing-features-table.yml, a category in the pricing table configuration is missing a categoryName. To resolve, make sure every category in the pricing table YAML file has a categoryName'); + } + if(!category.features){// Throw an error if a category is missing `features`. + throw new Error('Could not build pricing table config from pricing-features-table.yml, the "'+category.categoryName+'" category in the yaml file is missing features. To resolve, add an array of features to this category.'); + } + if(!_.isArray(category.features)){ // Throw an error if a category's `features`` is not an array. + throw new Error('Could not build pricing table config from pricing-features-table.yml, The value of the "'+category.categoryName+'" category is invalid, to resolve, change the features for this category to be an array of objects.'); + } + // Validate all features in a category. + for(let feature of category.features){ + if(!feature.name) { // Throw an error if a feature is missing a `name`. + throw new Error('Could not build pricing table config from pricing-features-table.yml. A feature in the "'+category.categoryName+'" category is missing a "name". To resolve, add a "name" to this feature '+feature); + } + if(!feature.tier) { // Throw an error if a feature is missing a `tier`. + throw new Error('Could not build pricing table config from pricing-features-table.yml. The "'+feature.name+'" feature is missing a "tier". To resolve, add a "tier" (either "Free" or "Premium") to this feature.'); + } else if(!_.contains(['Free', 'Premium'], feature.tier)){ // Throw an error if a feature's `tier` is not either "Free" or "Premium". + throw new Error('Could not build pricing table config from pricing-features-table.yml. The "'+feature.name+'" feature has an invalid "tier". to resolve, change the value of this features "tier" (currently set to '+feature.tier+') to be either "Free" or "Premium".'); + } + if(feature.comingSoon === undefined) { // Throw an error if a feature is missing a `comingSoon` value + throw new Error('Could not build pricing table config from pricing-features-table.yml. The "'+feature.name+'" feature is missing a "comingSoon" value (boolean). To resolve, add a comingSoon value to this feature.'); + } else if(typeof feature.comingSoon !== 'boolean'){ // Throw an error if the `comingSoon` value is not a boolean. + throw new Error('Could not build pricing table config from pricing-features-table.yml. The "'+feature.name+'" feature has an invalid "comingSoon" value (currently set to '+feature.comingSoon+'). To resolve, change the value of "comingSoon" for this feature to be either "true" or "false".'); + } + } + } + builtStaticContent.pricingTable = pricingTableCategories; + }, + ]); // ██████╗ ███████╗██████╗ ██╗ █████╗ ██████╗███████╗ ███████╗ █████╗ ██╗██╗ ███████╗██████╗ ██████╗ diff --git a/website/views/pages/pricing.ejs b/website/views/pages/pricing.ejs index 19d2b3324028..202615e16ad9 100644 --- a/website/views/pages/pricing.ejs +++ b/website/views/pages/pricing.ejs @@ -1,10 +1,7 @@ -
+
-
-
+
+

Choose your plan

Telemetry that scales with your organization. @@ -29,11 +26,9 @@ A flexible solution to get accurate, actionable data from your servers and workstations.

- See Fleet in Sandbox + + See Fleet in Sandbox +
@@ -55,10 +50,7 @@ An enterprise plan that delivers rich endpoint data with dedicated support to organizations at scale.

- + Get your license key
@@ -66,1022 +58,58 @@ - +

Does your organization have a large server fleet? Talk to an expert.

-
-
-
-
-
+
+
+
+
+
+
-

Support

+

+ {{category.categoryName}} +

-
+

Fleet Free

-
+

Fleet Premium

-
-
- a bullet point ellipse -

Public issue tracker (GitHub)

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Community Slack channel

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Unlimited email support (confidential)

-
-
-
- a check mark -
-
-
-
- a bullet point ellipse -

Phone and video call support

-
-
-
- a check mark -
-
-
-
-

- Inventory management -

-
-
-
-
-
+
- a bullet point ellipse -

Secure REST API

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Command line tool (CLI)

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Realtime device inventory dashboard

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Browse installed software packages

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse + a bullet point ellipse

- Search devices by IP, serial, hostname, UUID + {{feature.name}} {{feature.comingSoon ? '*' : ''}}

- a check mark + a check mark
- a check mark + a check mark
-
-
- a bullet point ellipse -

- Target and configure specific groups of devices -

-
-
-
- a check mark -
-
-
-
- a bullet point ellipse -

- Aggregate insights for groups of devices -

-
-
-
- a check mark -
-
-
-
-

Collaboration

-
-
-
-
-
-
- a bullet point ellipse -

Shareable device health reports

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Versionable queries and config (GitOps)

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Human-to-device mapping

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Scope transparency

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Multiple teams

-
-
-
- a check mark -
-
-
-
-

- Security and compliance -

-
-
-
-
-
-
- a bullet point ellipse -

Single sign on (SSO, SAML)

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Audit queries and user activities

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

- Grant API-only access -

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Role-based access control

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Just-in-time provisioning

-
-
-
- a check mark -
-
-
-
-

Monitoring

-
-
-
-
-
-
- a bullet point ellipse -

Schedule and automate custom queries

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

- Detect vulnerable software -

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Query performance monitoring

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Standard query and policy library

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

- Detect and surface issues with devices -

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

- Policy and vulnerability automations -
- (webhook, Zendesk, JIRA, ServiceNow*) -

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Vulnerability scores (EPSS and CVSS)

-
-
-
- a check mark -
-
-
-
- a bullet point ellipse -

CISA known exploited vulnerabilities

-
-
-
- a check mark -
-
-
-
- a bullet point ellipse -

End-user self-service

-
-
-
- a check mark -
-
-
-
-

Data outputs

-
-
-
-
-
-
- a bullet point ellipse -

- Flexible log destinations -
- (AWS Kinesis, Lambda, GCP, Kafka) -

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

File carving (AWS S3)

-
-
- a check mark -
-
- a check mark -
-
-
-
-

Deployment

-
-
-
-
-
-
- a bullet point ellipse -

Self-managed

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

- Deployment tools (Helm, Terraform) -

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Configure osquery startup flags remotely*

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Autoupdate osquery agents

-
-
- a check mark -
-
- a check mark -
-
-
-
- a bullet point ellipse -

Self-managed autoupdate registry

-
-
-
- a check mark -
-
-
-
- a bullet point ellipse -

- Manage osquery extensions remotely* -

-
-
-
- a check mark -
-
-

- * Coming soon -

+

+ * Coming soon +

- +

FAQ

-

+

Is Fleet MIT licensed?

@@ -1090,11 +118,11 @@

-

+

What is your commitment to open source stewardship?

-
    +
    1. When a feature is free and open source we won't move that feature to a paid tier. Features might be removed from the open source codebase in other cases, for example when combining features from multiple tiers into one new feature.
    2. The majority of new capabilities added to Fleet will benefit all users, not just customers.
    3. We won't introduce features into the open source codebase with a fixed delay; if a feature is planned to land in both it will be released simultaneously in both.
    4. @@ -1108,7 +136,7 @@
-

+

How do I contact Fleet for support?

@@ -1117,7 +145,7 @@

-

+

What if we choose not to renew?

@@ -1126,7 +154,7 @@

-

+

Can we buy a licence to access premium features with reduced support for a reduced cost?

@@ -1135,7 +163,7 @@

-

+

Do you offer pricing for ephemeral hosts which may scale up or down?

@@ -1144,7 +172,7 @@

-

+

When run locally, what resources does the Fleet app typically consume on an individual instance, and when run in HA, at high volume? And how is latency on an individual instance vs clustered deployment?

@@ -1153,7 +181,7 @@

-

+

Where's the data stored?

@@ -1162,7 +190,7 @@

-

+

Can I fork Fleet's source code and build upon it myself to create my own features?

@@ -1171,7 +199,7 @@

-

+

Can I buy support or services separate from Fleet Premium?

@@ -1181,7 +209,6 @@
-
@@ -1196,33 +223,19 @@
- -
-
-

- Still got questions? -

-
-
+
+

Still got questions?

+ -
+ +<%- /* Expose locals as `window.SAILS_LOCALS` :: */ exposeLocalsToBrowser() %>