Website: add fleetPremiumTrialLicenseKey user migration script. (#30125)

Changes:
- Added a script that sets a `fleetPremiumTrialLicenseKey` and
`fleetPremiumTrialLicenseKeyExpiresAt` values on all User records.
- Added a new email template to inform users that they have a new Fleet
premium trial available.
- Added the announcement banner to the /login, /register, and /try-fleet
pages.
This commit is contained in:
Eric 2025-06-19 16:09:26 -05:00 committed by GitHub
parent 12fb016708
commit 0f5cc22d61
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 154 additions and 21 deletions

View file

@ -135,6 +135,13 @@ module.exports = {
emailAddress: 'sage@example.com',
};
break;
case 'email-fleet-premium-trial':
// layout = 'layout-nurture-email';
fakeData = {
firstName: 'Sage',
emailAddress: 'sage@example.com',
};
break;
case 'email-deal-registration':
layout = 'layout-email';
fakeData = {

View file

@ -59,7 +59,29 @@ html, body {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 500ms;
}
[purpose='announcement-banner'] {
br {
display: none;
@media (max-width: 375px) {
display: block;
}
}
display: flex;
width: 100%;
padding: 12px 8px;
justify-content: center;
align-items: center;
background: #0587FF;
color: var(--color-brand-white, #FFF);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: normal;
[parasails-component='animated-arrow-button'] {
padding: 0;
}
}
[purpose='continue-banner'] {
z-index: 198;
position: fixed;

View file

@ -34,26 +34,6 @@
line-height: 150%;
}
[purpose='announcement-banner'] {
br {
display: none;
}
display: flex;
width: 100%;
padding: 12px 8px;
justify-content: center;
align-items: center;
background: #0587FF;
color: var(--color-brand-white, #FFF);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: normal;
[parasails-component='animated-arrow-button'] {
padding: 0;
}
}
[purpose='hero-container'] {
background: #FFFFFF;
overflow: hidden;

View file

@ -0,0 +1,85 @@
module.exports = {
friendlyName: 'Migrate users with no trial key',
description: 'Generate qualifying users a new Fleet premium license key and inform them via email.',
inputs: {
dry: { type: 'boolean', description: 'Whether to make this a dry run. (No database changes will be made. Emails will not be sent.)' },
},
fn: async function ({dry}) {
sails.log('Running custom shell script... (`sails run migrate-users-with-no-trial-key`)');
let thirtyDaysFromNowAt = Date.now() + (1000 * 60 * 60 * 24 * 30);
// Get an array of all Users with
let idsOfUsersWithSubscriptions = await Subscription.find().select('user');
let numberOfUsersWhoWouldHaveBeenUpdated = 0;
await User.stream({
lastSubmittedGetStartedQuestionnaireStep: {
'in': [ // Only update users who have made it to the 'Is-it-any-good' step of the start questionnaire.
'is-it-any-good',
'what-did-you-think',
'deploy-fleet-in-your-environment',
'managed-cloud-for-growing-deployments',
'self-hosted-deploy',
'whats-left-to-get-you-set-up'
]
},
psychologicalStage: {'!=': '2 - Aware'},// Skip users who have told us that they decided not to use Fleet.
stageThreeNurtureEmailSentAt: {'!=': 1},// Do not update users who have unsubscribed from marketing emails.
}).eachRecord(async (thisUser)=>{
// Stop running if this user has purchased a fleet premium license.
if(idsOfUsersWithSubscriptions.includes(thisUser.id)){
sails.log.verbose(`Skipping ${thisUser.emailAddress} because they already have a premium subscription.`);
return;
}
if(dry) {
sails.log.verbose(`Would have generated a new trial license key for ${thisUser.emailAddress} and informed them via email`);
numberOfUsersWhoWouldHaveBeenUpdated++;
} else {
// Generate a new trial license key for the user.
let trialLicenseKeyForThisUser = await sails.helpers.createLicenseKey.with({
numberOfHosts: 10,
organization: thisUser.organization ? thisUser.organization : 'Fleet Premium trial',
expiresAt: thirtyDaysFromNowAt,
});
// Save the new license key to this user's db record
await User.updateOne({id: thisUser.id}).set({
fleetPremiumTrialLicenseKey: trialLicenseKeyForThisUser,
fleetPremiumTrialLicenseKeyExpiresAt: thirtyDaysFromNowAt,
});
// Send an email informing the user that their new Fleet premium trial is available.
await sails.helpers.sendTemplateEmail.with({
template: 'email-fleet-premium-trial',
layout: false,
templateData: {
emailAddress: thisUser.emailAddress
},
to: thisUser.emailAddress,
subject: 'Whoops',
from: sails.config.custom.contactFormEmailAddress,
fromName: 'Mike McNeil',
ensureAck: true,
});
}
});
if(dry){
sails.log(`Dry run: would have generated trial licenses for and updated ${numberOfUsersWhoWouldHaveBeenUpdated} user records.`);
}
}
};

View file

@ -0,0 +1,30 @@
<div style="width: 100%; max-width: 600px; font-family: 'Inter','Helvetica','arial', sans-serif; box-sizing: border-box; padding: 0; margin: auto; border-radius: 8px; border: 1px solid #E2E4EA; background: #FFF;">
<div style="color: #192147; font-size: 16px; box-sizing: border-box; padding: 32px; width: 100%; max-width: 600px; margin-left: auto; margin-right: auto;">
<div style="background: transparent; text-align: left;">
<div style="background: transparent; text-align: left;">
<a href="https://fleetdm.com"><img style="display: inline-block; width: 162px; height: 92px; width: auto;" alt="Logo" src="https://fleetdm.com/images/logo-email-162x92@2x.png"/></a>
</div>
</div>
<div>
<div style="padding: 0px 0px 0px 0px">
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 24px; margin-top: 24px">Hi there,</p>
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 48px; margin-top: 0px">In the process of migrating the license key database for the Fleet website, we hit a snag and figured "why not just let everybody have a new 30 day trial?"</p>
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 48px; margin-top: 0px">
You can visit <a style="color: #6A67FE; text-decoration: none; word-break: break-word;" href="https://fleetdm.com/try-fleet" target="_blank">https://fleetdm.com/try-fleet</a> if you'd like to try it out (your license key will be available when you login).</p>
<p style=" font-size: 16px; line-height: 24px; margin-bottom: 48px; margin-top: 0px">Enjoy!<br> Mike, CEO</p>
</div>
<hr style="color: #E2E4EA;"/>
<div style="display: inline-flex; padding-top: 32px;">
<a href="https://fleetdm.com/slack" target="_blank"><img style="height: 20px; width: 20px; margin-right: 24px;" alt="Join the osquery Slack community" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-slack-dark-20x20@2x.png')%>"></a>
<a href="https://github.com/fleetdm/fleet" target="_blank"><img style="height: 20px; width: 20px; margin-right: 24px;" alt="View Fleet's GitHub repo" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-github-dark-24x24@2x.png')%>"></a>
<a href="https://fleetdm.com/slack" target="_blank"><img style="height: 20px; width: 20px; margin-right: 24px;" alt="Conenct with Fleet on LinkedIn" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-linkedin-24x24@2x.png')%>"></a>
<a href="https://twitter.com/fleetctl" target="_blank"><img style="height: 20px; width: 24px; margin-right: 24px;" alt="Follow Fleet on Twitter" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-x-24x24@2x.png')%>"></a>
<a href="https://www.youtube.com/channel/UCZyoqZ4exJvoibmTKJrQ-dQ" target="_blank"><img style="width: 20px; margin-top: 3px; margin-right: 24px;" alt="Watch Fleet's Youtube videos" src="<%= url.resolve(sails.config.custom.baseUrl,'images/logo-youtube-29x20@2x.png')%>"></a>
</div>
<div style="text-align: left; padding-top: 15px; font-size: 12px; color: #3E4771;">
<p>© <%= (new Date()).getFullYear() %> Fleet Inc.<br> All trademarks are the property of their respective owners.</p>
</div>
</div>
<a style="font-size: 12px; color: #3E4771;" href="<%= url.resolve(sails.config.custom.baseUrl, '/unsubscribe?emailAddress='+encodeURIComponent(emailAddress)) %>">Unsubscribe</a>
</div>
</div>

View file

@ -1,4 +1,7 @@
<div id="login" v-cloak>
<div purpose="announcement-banner">
<animated-arrow-button arrow-color="white" text-color="white" href="/funding-announcement" target="_blank">🎉 Fleet raises $27M for<br> open device management </animated-arrow-button>
</div>
<div purpose="page-container" class="container">
<div purpose="page-heading">
<h1>Welcome to Fleet</h1>

View file

@ -1,4 +1,7 @@
<div id="signup" v-cloak>
<div purpose="announcement-banner">
<animated-arrow-button arrow-color="white" text-color="white" href="/funding-announcement" target="_blank">🎉 Fleet raises $27M for<br> open device management </animated-arrow-button>
</div>
<div purpose="page-container" class="container">
<div purpose="page-heading">
<h1>Welcome to Fleet</h1>

View file

@ -1,4 +1,7 @@
<div id="fleetctl-preview" v-cloak>
<div purpose="announcement-banner">
<animated-arrow-button arrow-color="white" text-color="white" href="/funding-announcement" target="_blank">🎉 Fleet raises $27M for<br> open device management </animated-arrow-button>
</div>
<div purpose="page-container" class="d-flex flex-column container">
<div purpose="page-title">
<h1>Try Fleet</h1>