@mailmodo/cli 0.0.19-beta.pr21.31 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -10,15 +10,9 @@ export default class Deploy extends BaseCommand {
|
|
|
10
10
|
* Fetches current DNS verification status for the deploy flow.
|
|
11
11
|
*
|
|
12
12
|
* @param jsonOutput - When true, spinner uses stderr for stdout-safe JSON runs.
|
|
13
|
-
* @param domain - Sending domain to check. If empty, request will fail and trigger setup flow.
|
|
14
13
|
*/
|
|
15
14
|
private fetchDomainVerifyForDeploy;
|
|
16
15
|
run(): Promise<void>;
|
|
17
|
-
private buildDeployPayload;
|
|
18
|
-
private mapEmailToPayload;
|
|
19
|
-
private buildProjectPayload;
|
|
20
|
-
private confirmDeploy;
|
|
21
|
-
private ensureDomainReady;
|
|
22
16
|
/**
|
|
23
17
|
* Lists emails about to be deployed (skipped when `--json` is set).
|
|
24
18
|
*
|
|
@@ -38,11 +32,9 @@ export default class Deploy extends BaseCommand {
|
|
|
38
32
|
* @returns {Promise<boolean>} true if domain was verified, false if skipped.
|
|
39
33
|
*/
|
|
40
34
|
private runDomainSetup;
|
|
41
|
-
private collectDomainInputs;
|
|
42
|
-
private showDnsRecords;
|
|
43
35
|
/**
|
|
44
36
|
* Calls the domain verification API endpoint and reports pass/fail
|
|
45
|
-
* status for each DNS record (DKIM, DMARC
|
|
37
|
+
* status for each DNS record (SPF, DKIM, DMARC).
|
|
46
38
|
*
|
|
47
39
|
* @returns {Promise<boolean>} true if all records pass.
|
|
48
40
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { confirm, input } from '@inquirer/prompts';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
4
|
-
import { API_ENDPOINTS,
|
|
5
|
-
import {
|
|
4
|
+
import { API_ENDPOINTS, DNS_GUIDE_URL } from '../../lib/constants.js';
|
|
5
|
+
import { saveYaml } from '../../lib/yaml-config.js';
|
|
6
6
|
export default class Deploy extends BaseCommand {
|
|
7
7
|
static description = 'Deploy email sequences and verify sending domain';
|
|
8
8
|
static examples = [
|
|
@@ -16,130 +16,69 @@ export default class Deploy extends BaseCommand {
|
|
|
16
16
|
* Fetches current DNS verification status for the deploy flow.
|
|
17
17
|
*
|
|
18
18
|
* @param jsonOutput - When true, spinner uses stderr for stdout-safe JSON runs.
|
|
19
|
-
* @param domain - Sending domain to check. If empty, request will fail and trigger setup flow.
|
|
20
19
|
*/
|
|
21
|
-
fetchDomainVerifyForDeploy(jsonOutput
|
|
22
|
-
return this.withApiSpinner({ json: jsonOutput, text: ' Checking domain verification...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY
|
|
23
|
-
domain: domain || '',
|
|
24
|
-
}));
|
|
20
|
+
fetchDomainVerifyForDeploy(jsonOutput) {
|
|
21
|
+
return this.withApiSpinner({ json: jsonOutput, text: ' Checking domain verification...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY));
|
|
25
22
|
}
|
|
26
23
|
async run() {
|
|
27
24
|
const { flags } = await this.parse(Deploy);
|
|
28
25
|
await this.ensureAuth();
|
|
29
26
|
const yamlConfig = await this.ensureYaml();
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const domainVerify = await this.fetchDomainVerifyForDeploy(flags.json);
|
|
28
|
+
const domainVerified = domainVerify.ok &&
|
|
29
|
+
domainVerify.data?.spf === 'pass' &&
|
|
30
|
+
domainVerify.data?.dkim === 'pass' &&
|
|
31
|
+
domainVerify.data?.dmarc === 'pass';
|
|
32
|
+
if (!domainVerified) {
|
|
33
|
+
if (!flags.json) {
|
|
34
|
+
this.log(`\n No sending domain verified yet.`);
|
|
35
|
+
this.log(` You need to verify a domain before sending emails.`);
|
|
36
|
+
this.log(` This is a one-time setup. Takes about 5 minutes.\n`);
|
|
37
|
+
}
|
|
38
|
+
if (!flags.yes) {
|
|
39
|
+
const setupNow = await confirm({
|
|
40
|
+
default: true,
|
|
41
|
+
message: 'Set up your sending domain now?',
|
|
42
|
+
});
|
|
43
|
+
if (!setupNow) {
|
|
44
|
+
this.log(`\n Sequences saved but ${chalk.yellow('NOT deployed')}.`);
|
|
45
|
+
this.log(` Emails will not send until your domain is verified.`);
|
|
46
|
+
this.log(` When ready, run: ${chalk.cyan('mailmodo domain')}`);
|
|
47
|
+
this.log(` Then: ${chalk.cyan('mailmodo deploy')}\n`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const completed = await this.runDomainSetup(yamlConfig, flags);
|
|
52
|
+
if (!completed)
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
33
55
|
this.logPreDeploySummary(yamlConfig, flags.json);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
if (!flags.yes) {
|
|
57
|
+
const proceed = await confirm({
|
|
58
|
+
default: true,
|
|
59
|
+
message: `Deploy ${yamlConfig.emails.length} emails?`,
|
|
60
|
+
});
|
|
61
|
+
if (!proceed) {
|
|
62
|
+
this.log('\n Deploy cancelled.\n');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const response = await this.withApiSpinner({ json: flags.json, text: ' Deploying email sequences...' }, () => this.apiClient.post(API_ENDPOINTS.SEQUENCES, {
|
|
67
|
+
emails: yamlConfig.emails,
|
|
68
|
+
project: yamlConfig.project,
|
|
69
|
+
}));
|
|
39
70
|
if (!response.ok) {
|
|
40
71
|
this.handleApiError(response);
|
|
41
72
|
}
|
|
42
73
|
if (flags.json) {
|
|
43
74
|
this.log(JSON.stringify({
|
|
44
|
-
deployed:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
sdkSnippet: response.data.sdkSnippet,
|
|
48
|
-
sequenceId: response.data.sequenceId,
|
|
75
|
+
deployed: true,
|
|
76
|
+
emailsLive: yamlConfig.emails.length,
|
|
77
|
+
sdkSnippet: response.data?.sdkSnippet,
|
|
49
78
|
}, null, 2));
|
|
50
79
|
return;
|
|
51
80
|
}
|
|
52
|
-
this.logDeploySuccessInstructions(
|
|
53
|
-
}
|
|
54
|
-
async buildDeployPayload(yamlConfig) {
|
|
55
|
-
const emailsWithHtml = await Promise.all(yamlConfig.emails.map(async (email) => {
|
|
56
|
-
const html = (await loadTemplate(`${email.id}.html`)) || '';
|
|
57
|
-
return { ...this.mapEmailToPayload(email), html, plainHtml: html };
|
|
58
|
-
}));
|
|
59
|
-
return {
|
|
60
|
-
...this.buildProjectPayload(yamlConfig.project),
|
|
61
|
-
emails: emailsWithHtml,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
mapEmailToPayload(email) {
|
|
65
|
-
return {
|
|
66
|
-
condition: email.condition || null,
|
|
67
|
-
ctaText: '',
|
|
68
|
-
delay: typeof email.delay === 'string'
|
|
69
|
-
? Number.parseInt(email.delay, 10) || 0
|
|
70
|
-
: email.delay,
|
|
71
|
-
goal: email.goal || '',
|
|
72
|
-
id: email.id,
|
|
73
|
-
isReminder: false,
|
|
74
|
-
previewText: email.previewText || '',
|
|
75
|
-
priority: 'medium',
|
|
76
|
-
subject: email.subject,
|
|
77
|
-
trigger: email.trigger,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
buildProjectPayload(project) {
|
|
81
|
-
return {
|
|
82
|
-
brand: {
|
|
83
|
-
colors: [project?.brandColor || DEFAULT_BRAND_COLOR],
|
|
84
|
-
logoUrl: project?.logoUrl || '',
|
|
85
|
-
},
|
|
86
|
-
emailStyle: project?.emailStyle || 'branded',
|
|
87
|
-
monthlyCap: project?.monthlyCap ?? DEFAULT_MONTHLY_CAP,
|
|
88
|
-
product: {
|
|
89
|
-
businessType: project?.type || '',
|
|
90
|
-
description: '',
|
|
91
|
-
pricingModel: '',
|
|
92
|
-
productName: project?.name || '',
|
|
93
|
-
saasModel: '',
|
|
94
|
-
targetUser: '',
|
|
95
|
-
url: project?.url || '',
|
|
96
|
-
},
|
|
97
|
-
senderDetails: {
|
|
98
|
-
address: project?.address || '',
|
|
99
|
-
domain: project?.domain || '',
|
|
100
|
-
fromEmail: project?.fromEmail || '',
|
|
101
|
-
fromName: project?.fromName || '',
|
|
102
|
-
replyTo: project?.replyTo || project?.fromEmail || '',
|
|
103
|
-
},
|
|
104
|
-
webhookUrl: project?.webhookUrl || '',
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
async confirmDeploy(yamlConfig, flags) {
|
|
108
|
-
if (flags.yes)
|
|
109
|
-
return true;
|
|
110
|
-
const proceed = await confirm({
|
|
111
|
-
default: true,
|
|
112
|
-
message: `Deploy ${yamlConfig.emails.length} emails?`,
|
|
113
|
-
});
|
|
114
|
-
if (!proceed) {
|
|
115
|
-
this.log('\n Deploy cancelled.\n');
|
|
116
|
-
}
|
|
117
|
-
return proceed;
|
|
118
|
-
}
|
|
119
|
-
async ensureDomainReady(yamlConfig, flags) {
|
|
120
|
-
const domainVerify = await this.fetchDomainVerifyForDeploy(flags.json, yamlConfig.project?.domain);
|
|
121
|
-
if (domainVerify.ok && domainVerify.data?.domainStatus === 'VERIFIED') {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
if (!flags.json) {
|
|
125
|
-
this.log(`\n No sending domain verified yet.`);
|
|
126
|
-
this.log(` You need to verify a domain before sending emails.`);
|
|
127
|
-
this.log(` This is a one-time setup. Takes about 5 minutes.\n`);
|
|
128
|
-
}
|
|
129
|
-
if (!flags.yes) {
|
|
130
|
-
const setupNow = await confirm({
|
|
131
|
-
default: true,
|
|
132
|
-
message: 'Set up your sending domain now?',
|
|
133
|
-
});
|
|
134
|
-
if (!setupNow) {
|
|
135
|
-
this.log(`\n Sequences saved but ${chalk.yellow('NOT deployed')}.`);
|
|
136
|
-
this.log(` Emails will not send until your domain is verified.`);
|
|
137
|
-
this.log(` When ready, run: ${chalk.cyan('mailmodo domain')}`);
|
|
138
|
-
this.log(` Then: ${chalk.cyan('mailmodo deploy')}\n`);
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return this.runDomainSetup(yamlConfig, flags);
|
|
81
|
+
this.logDeploySuccessInstructions();
|
|
143
82
|
}
|
|
144
83
|
/**
|
|
145
84
|
* Lists emails about to be deployed (skipped when `--json` is set).
|
|
@@ -160,19 +99,19 @@ export default class Deploy extends BaseCommand {
|
|
|
160
99
|
/**
|
|
161
100
|
* Prints the post-deploy success message and SDK install snippet for interactive runs.
|
|
162
101
|
*/
|
|
163
|
-
logDeploySuccessInstructions(
|
|
102
|
+
logDeploySuccessInstructions() {
|
|
164
103
|
this.log(` ${chalk.green('Deployed.')} Emails are live.\n`);
|
|
165
104
|
this.log(` ${'─'.repeat(53)}`);
|
|
166
105
|
this.log(` ${chalk.bold('ADD THIS TO YOUR APP (one-time only):')}`);
|
|
167
106
|
this.log(` ${'─'.repeat(53)}\n`);
|
|
168
|
-
this.log(` ${chalk.cyan(
|
|
107
|
+
this.log(` ${chalk.cyan('npm install @mailmodo/sdk')}\n`);
|
|
169
108
|
this.log(` ${chalk.dim("import { track, identify } from '@mailmodo/sdk'")}\n`);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
this.log('');
|
|
109
|
+
this.log(` ${chalk.dim('// On user signup:')}`);
|
|
110
|
+
this.log(` ${chalk.dim("track('user.signup', { email, first_name, app_url })")}\n`);
|
|
111
|
+
this.log(` ${chalk.dim('// When user creates a project:')}`);
|
|
112
|
+
this.log(` ${chalk.dim('identify(email, { has_created_project: true })')}\n`);
|
|
113
|
+
this.log(` ${chalk.dim('// On trial expiry:')}`);
|
|
114
|
+
this.log(` ${chalk.dim("track('user.trial_expiry', { email, first_name })")}\n`);
|
|
176
115
|
this.log(` Full SDK docs: ${chalk.cyan('mailmodo.com/docs/sdk')}\n`);
|
|
177
116
|
this.log(` ${'─'.repeat(53)}\n`);
|
|
178
117
|
}
|
|
@@ -184,7 +123,31 @@ export default class Deploy extends BaseCommand {
|
|
|
184
123
|
* @returns {Promise<boolean>} true if domain was verified, false if skipped.
|
|
185
124
|
*/
|
|
186
125
|
async runDomainSetup(yamlConfig, flags) {
|
|
187
|
-
|
|
126
|
+
let domain;
|
|
127
|
+
let senderEmail;
|
|
128
|
+
let address;
|
|
129
|
+
if (flags.yes) {
|
|
130
|
+
domain = yamlConfig.project?.domain || '';
|
|
131
|
+
senderEmail = yamlConfig.project?.fromEmail || '';
|
|
132
|
+
address = yamlConfig.project?.address || '';
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.log(`\n ${'─'.repeat(53)}`);
|
|
136
|
+
this.log(` ${chalk.bold('DOMAIN SETUP')}`);
|
|
137
|
+
this.log(` ${'─'.repeat(53)}\n`);
|
|
138
|
+
domain = await input({
|
|
139
|
+
message: 'What domain will you send from?',
|
|
140
|
+
validate: (v) => (v?.trim() ? true : 'Domain is required'),
|
|
141
|
+
});
|
|
142
|
+
senderEmail = await input({
|
|
143
|
+
message: 'Sender email address:',
|
|
144
|
+
validate: (v) => v?.includes('@') ? true : 'Please enter a valid email',
|
|
145
|
+
});
|
|
146
|
+
address = await input({
|
|
147
|
+
message: 'Business address (required by law for email footers):',
|
|
148
|
+
validate: (v) => (v?.trim() ? true : 'Address is required'),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
188
151
|
const domainResponse = await this.withApiSpinner({ json: flags.json, text: ' Configuring domain...' }, () => this.apiClient.post(API_ENDPOINTS.DOMAIN, {
|
|
189
152
|
address,
|
|
190
153
|
domain,
|
|
@@ -197,9 +160,20 @@ export default class Deploy extends BaseCommand {
|
|
|
197
160
|
yamlConfig.project.fromEmail = senderEmail;
|
|
198
161
|
yamlConfig.project.address = address;
|
|
199
162
|
await saveYaml(yamlConfig);
|
|
200
|
-
|
|
163
|
+
const dnsRecords = domainResponse.data?.dnsRecords || [];
|
|
164
|
+
if (!flags.json) {
|
|
165
|
+
this.log(`\n Add these ${dnsRecords.length} DNS records to your domain provider:\n`);
|
|
166
|
+
for (const [i, record] of dnsRecords.entries()) {
|
|
167
|
+
this.log(` ${chalk.bold(`RECORD ${i + 1}`)}`);
|
|
168
|
+
this.log(` Type: ${record.type}`);
|
|
169
|
+
this.log(` Host: ${record.host}`);
|
|
170
|
+
this.log(` Value: ${record.value}\n`);
|
|
171
|
+
}
|
|
172
|
+
this.log(` DNS changes take 5–30 minutes to propagate.`);
|
|
173
|
+
this.log(` Full guide: ${chalk.cyan(DNS_GUIDE_URL)}\n`);
|
|
174
|
+
}
|
|
201
175
|
if (flags.yes) {
|
|
202
|
-
return this.verifyDomain(flags.json
|
|
176
|
+
return this.verifyDomain(flags.json);
|
|
203
177
|
}
|
|
204
178
|
const action = await input({
|
|
205
179
|
default: '',
|
|
@@ -211,65 +185,25 @@ export default class Deploy extends BaseCommand {
|
|
|
211
185
|
this.log(` Then: ${chalk.cyan('mailmodo deploy')}\n`);
|
|
212
186
|
return false;
|
|
213
187
|
}
|
|
214
|
-
return this.verifyDomain(flags.json
|
|
215
|
-
}
|
|
216
|
-
async collectDomainInputs(yamlConfig, flags) {
|
|
217
|
-
if (flags.yes) {
|
|
218
|
-
return {
|
|
219
|
-
address: yamlConfig.project?.address || '',
|
|
220
|
-
domain: yamlConfig.project?.domain || '',
|
|
221
|
-
senderEmail: yamlConfig.project?.fromEmail || '',
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
this.log(`\n ${'─'.repeat(53)}`);
|
|
225
|
-
this.log(` ${chalk.bold('DOMAIN SETUP')}`);
|
|
226
|
-
this.log(` ${'─'.repeat(53)}\n`);
|
|
227
|
-
const domain = await input({
|
|
228
|
-
message: 'What domain will you send from?',
|
|
229
|
-
validate: (v) => (v?.trim() ? true : 'Domain is required'),
|
|
230
|
-
});
|
|
231
|
-
const senderEmail = await input({
|
|
232
|
-
message: 'Sender email address:',
|
|
233
|
-
validate: (v) => (v?.includes('@') ? true : 'Please enter a valid email'),
|
|
234
|
-
});
|
|
235
|
-
const address = await input({
|
|
236
|
-
message: 'Business address (required by law for email footers):',
|
|
237
|
-
validate: (v) => (v?.trim() ? true : 'Address is required'),
|
|
238
|
-
});
|
|
239
|
-
return { address, domain, senderEmail };
|
|
240
|
-
}
|
|
241
|
-
showDnsRecords(dnsRecords, jsonOutput) {
|
|
242
|
-
if (jsonOutput)
|
|
243
|
-
return;
|
|
244
|
-
this.log(`\n Add these ${dnsRecords.length} DNS records to your domain provider:\n`);
|
|
245
|
-
for (const [i, record] of dnsRecords.entries()) {
|
|
246
|
-
this.log(` ${chalk.bold(`RECORD ${i + 1}`)}`);
|
|
247
|
-
this.log(` Type: ${record.type}`);
|
|
248
|
-
this.log(` Host: ${record.host}`);
|
|
249
|
-
this.log(` Value: ${record.value}\n`);
|
|
250
|
-
}
|
|
251
|
-
this.log(` DNS changes take 5–30 minutes to propagate.`);
|
|
252
|
-
this.log(` Full guide: ${chalk.cyan(DNS_GUIDE_URL)}\n`);
|
|
188
|
+
return this.verifyDomain(flags.json);
|
|
253
189
|
}
|
|
254
190
|
/**
|
|
255
191
|
* Calls the domain verification API endpoint and reports pass/fail
|
|
256
|
-
* status for each DNS record (DKIM, DMARC
|
|
192
|
+
* status for each DNS record (SPF, DKIM, DMARC).
|
|
257
193
|
*
|
|
258
194
|
* @returns {Promise<boolean>} true if all records pass.
|
|
259
195
|
*/
|
|
260
|
-
async verifyDomain(jsonOutput
|
|
261
|
-
const verify = await this.withApiSpinner({ json: jsonOutput, text: ' Checking DNS...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY
|
|
262
|
-
domain,
|
|
263
|
-
}));
|
|
196
|
+
async verifyDomain(jsonOutput) {
|
|
197
|
+
const verify = await this.withApiSpinner({ json: jsonOutput, text: ' Checking DNS...' }, () => this.apiClient.get(API_ENDPOINTS.DOMAIN_VERIFY));
|
|
264
198
|
if (!verify.ok) {
|
|
265
199
|
this.handleApiError(verify);
|
|
266
200
|
}
|
|
267
|
-
const { dkim, dmarc,
|
|
268
|
-
const allPassed =
|
|
201
|
+
const { dkim, dmarc, spf } = verify.data;
|
|
202
|
+
const allPassed = spf === 'pass' && dkim === 'pass' && dmarc === 'pass';
|
|
269
203
|
if (!jsonOutput) {
|
|
270
|
-
this.log(`
|
|
271
|
-
this.log(`
|
|
272
|
-
this.log(`
|
|
204
|
+
this.log(` SPF ${spf === 'pass' ? chalk.green('✓') : chalk.red('✗')}`);
|
|
205
|
+
this.log(` DKIM ${dkim === 'pass' ? chalk.green('✓') : chalk.red('✗')}`);
|
|
206
|
+
this.log(` DMARC ${dmarc === 'pass' ? chalk.green('✓') : chalk.red('✗')}`);
|
|
273
207
|
if (allPassed) {
|
|
274
208
|
this.log(`\n ${chalk.green('Domain verified.')} Continuing deploy...\n`);
|
|
275
209
|
}
|
|
@@ -84,14 +84,11 @@ export default class Preview extends BaseCommand {
|
|
|
84
84
|
app_url: yamlConfig.project?.url || 'https://yourapp.com', // eslint-disable-line camelcase
|
|
85
85
|
product_name: yamlConfig.project?.name || 'YourApp', // eslint-disable-line camelcase
|
|
86
86
|
};
|
|
87
|
-
const templateHtml = await loadTemplate(`${email.id}.html`);
|
|
88
87
|
if (flags.send) {
|
|
89
|
-
|
|
90
|
-
? renderTemplate(templateHtml, sampleData)
|
|
91
|
-
: '';
|
|
92
|
-
await this.sendTestEmail(email, rendered, yamlConfig.project?.domain, flags.send, flags.json);
|
|
88
|
+
await this.sendTestEmail(templateId, flags.send, flags.json);
|
|
93
89
|
return;
|
|
94
90
|
}
|
|
91
|
+
const templateHtml = await loadTemplate(`${email.id}.html`);
|
|
95
92
|
if (flags.text) {
|
|
96
93
|
await this.renderText(email, templateHtml, sampleData, flags.json);
|
|
97
94
|
return;
|
|
@@ -126,27 +123,21 @@ export default class Preview extends BaseCommand {
|
|
|
126
123
|
* Calls the API to send a test email to the specified address.
|
|
127
124
|
* Before domain verification, tests send via the mailmodo.com domain.
|
|
128
125
|
*/
|
|
129
|
-
async sendTestEmail(
|
|
126
|
+
async sendTestEmail(templateId, toAddress, jsonOutput) {
|
|
130
127
|
await this.ensureAuth();
|
|
131
|
-
const response = await this.withApiSpinner({ json: jsonOutput, text: ' Sending test email...' }, () => this.apiClient.post(`${API_ENDPOINTS.PREVIEW}/send`, {
|
|
132
|
-
domain,
|
|
133
|
-
html,
|
|
134
|
-
subject: email.subject,
|
|
128
|
+
const response = await this.withApiSpinner({ json: jsonOutput, text: ' Sending test email...' }, () => this.apiClient.post(`${API_ENDPOINTS.PREVIEW}/${templateId}/send`, {
|
|
135
129
|
to: toAddress,
|
|
136
130
|
}));
|
|
137
131
|
if (!response.ok) {
|
|
138
132
|
this.handleApiError(response);
|
|
139
133
|
}
|
|
140
|
-
const { note, sentTo, sentVia, status } = response.data;
|
|
141
134
|
if (jsonOutput) {
|
|
142
|
-
this.log(JSON.stringify({
|
|
135
|
+
this.log(JSON.stringify({ templateId, sentTo: toAddress, status: 'sent' }, null, 2));
|
|
143
136
|
return;
|
|
144
137
|
}
|
|
145
|
-
this.log(`\n
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
this.log('');
|
|
138
|
+
this.log(`\n Sending test to ${chalk.cyan(toAddress)}...`);
|
|
139
|
+
this.log(` ${chalk.dim('Note: If domain is not yet verified, test sends via mailmodo.com domain.')}`);
|
|
140
|
+
this.log(` ${chalk.green('✓')} Sent.\n`);
|
|
150
141
|
}
|
|
151
142
|
/**
|
|
152
143
|
* Starts a local HTTP server on PREVIEW_PORT to serve the rendered email
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -17,8 +17,6 @@ export declare const API_ENDPOINTS: Readonly<{
|
|
|
17
17
|
LOGS: "/logs";
|
|
18
18
|
PREVIEW: "/preview";
|
|
19
19
|
SEQUENCES: "/sequences";
|
|
20
|
-
SEQUENCES_DEPLOY: "/sequences/deploy";
|
|
21
|
-
SEQUENCES_VALIDATE: "/sequences/validate";
|
|
22
20
|
}>;
|
|
23
21
|
export declare const LOGIN_URL = "https://app-vertex-debug.azurewebsites.net/login.html";
|
|
24
22
|
export declare const DOCS_URL = "https://mailmodo.com/docs/cli";
|
package/dist/lib/constants.js
CHANGED
|
@@ -23,8 +23,6 @@ export const API_ENDPOINTS = Object.freeze({
|
|
|
23
23
|
LOGS: '/logs',
|
|
24
24
|
PREVIEW: '/preview',
|
|
25
25
|
SEQUENCES: '/sequences',
|
|
26
|
-
SEQUENCES_DEPLOY: '/sequences/deploy',
|
|
27
|
-
SEQUENCES_VALIDATE: '/sequences/validate',
|
|
28
26
|
});
|
|
29
27
|
const DEV_LOGIN_URL = 'https://app-vertex-debug.azurewebsites.net/login.html';
|
|
30
28
|
// const PRODUCTION_LOGIN_URL = 'https://mailmodo.com/cli';
|
package/oclif.manifest.json
CHANGED
|
@@ -114,6 +114,45 @@
|
|
|
114
114
|
"index.js"
|
|
115
115
|
]
|
|
116
116
|
},
|
|
117
|
+
"deploy": {
|
|
118
|
+
"aliases": [],
|
|
119
|
+
"args": {},
|
|
120
|
+
"description": "Deploy email sequences and verify sending domain",
|
|
121
|
+
"examples": [
|
|
122
|
+
"<%= config.bin %> deploy",
|
|
123
|
+
"<%= config.bin %> deploy --yes"
|
|
124
|
+
],
|
|
125
|
+
"flags": {
|
|
126
|
+
"json": {
|
|
127
|
+
"description": "Output as JSON",
|
|
128
|
+
"name": "json",
|
|
129
|
+
"allowNo": false,
|
|
130
|
+
"type": "boolean"
|
|
131
|
+
},
|
|
132
|
+
"yes": {
|
|
133
|
+
"char": "y",
|
|
134
|
+
"description": "Skip confirmation prompts",
|
|
135
|
+
"name": "yes",
|
|
136
|
+
"allowNo": false,
|
|
137
|
+
"type": "boolean"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"hasDynamicHelp": false,
|
|
141
|
+
"hiddenAliases": [],
|
|
142
|
+
"id": "deploy",
|
|
143
|
+
"pluginAlias": "@mailmodo/cli",
|
|
144
|
+
"pluginName": "@mailmodo/cli",
|
|
145
|
+
"pluginType": "core",
|
|
146
|
+
"strict": true,
|
|
147
|
+
"enableJsonFlag": false,
|
|
148
|
+
"isESM": true,
|
|
149
|
+
"relativePath": [
|
|
150
|
+
"dist",
|
|
151
|
+
"commands",
|
|
152
|
+
"deploy",
|
|
153
|
+
"index.js"
|
|
154
|
+
]
|
|
155
|
+
},
|
|
117
156
|
"domain": {
|
|
118
157
|
"aliases": [],
|
|
119
158
|
"args": {},
|
|
@@ -218,45 +257,6 @@
|
|
|
218
257
|
"index.js"
|
|
219
258
|
]
|
|
220
259
|
},
|
|
221
|
-
"deploy": {
|
|
222
|
-
"aliases": [],
|
|
223
|
-
"args": {},
|
|
224
|
-
"description": "Deploy email sequences and verify sending domain",
|
|
225
|
-
"examples": [
|
|
226
|
-
"<%= config.bin %> deploy",
|
|
227
|
-
"<%= config.bin %> deploy --yes"
|
|
228
|
-
],
|
|
229
|
-
"flags": {
|
|
230
|
-
"json": {
|
|
231
|
-
"description": "Output as JSON",
|
|
232
|
-
"name": "json",
|
|
233
|
-
"allowNo": false,
|
|
234
|
-
"type": "boolean"
|
|
235
|
-
},
|
|
236
|
-
"yes": {
|
|
237
|
-
"char": "y",
|
|
238
|
-
"description": "Skip confirmation prompts",
|
|
239
|
-
"name": "yes",
|
|
240
|
-
"allowNo": false,
|
|
241
|
-
"type": "boolean"
|
|
242
|
-
}
|
|
243
|
-
},
|
|
244
|
-
"hasDynamicHelp": false,
|
|
245
|
-
"hiddenAliases": [],
|
|
246
|
-
"id": "deploy",
|
|
247
|
-
"pluginAlias": "@mailmodo/cli",
|
|
248
|
-
"pluginName": "@mailmodo/cli",
|
|
249
|
-
"pluginType": "core",
|
|
250
|
-
"strict": true,
|
|
251
|
-
"enableJsonFlag": false,
|
|
252
|
-
"isESM": true,
|
|
253
|
-
"relativePath": [
|
|
254
|
-
"dist",
|
|
255
|
-
"commands",
|
|
256
|
-
"deploy",
|
|
257
|
-
"index.js"
|
|
258
|
-
]
|
|
259
|
-
},
|
|
260
260
|
"emails": {
|
|
261
261
|
"aliases": [],
|
|
262
262
|
"args": {},
|
|
@@ -618,5 +618,5 @@
|
|
|
618
618
|
]
|
|
619
619
|
}
|
|
620
620
|
},
|
|
621
|
-
"version": "0.0.19
|
|
621
|
+
"version": "0.0.19"
|
|
622
622
|
}
|