@technomoron/mail-magic-client 1.0.23
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.
- package/CHANGES +4 -0
- package/README.md +102 -0
- package/dist/cjs/mail-magic-client.d.ts +82 -0
- package/dist/cjs/mail-magic-client.js +281 -0
- package/dist/cli-env.js +55 -0
- package/dist/cli-helpers.js +405 -0
- package/dist/cli.js +307 -0
- package/dist/esm/mail-magic-client.js +276 -0
- package/dist/mail-magic-client.js +281 -0
- package/dist/preprocess.js +310 -0
- package/package.json +69 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const readline_1 = __importDefault(require("readline"));
|
|
9
|
+
const commander_1 = require("commander");
|
|
10
|
+
const cli_env_1 = require("./cli-env");
|
|
11
|
+
const cli_helpers_1 = require("./cli-helpers");
|
|
12
|
+
const mail_magic_client_1 = __importDefault(require("./mail-magic-client"));
|
|
13
|
+
const preprocess_1 = require("./preprocess");
|
|
14
|
+
const program = new commander_1.Command();
|
|
15
|
+
const envDefaults = (0, cli_env_1.loadCliEnv)();
|
|
16
|
+
const defaultToken = (0, cli_env_1.resolveToken)(envDefaults);
|
|
17
|
+
const apiDefault = envDefaults.api || 'http://localhost:3000';
|
|
18
|
+
program.option('-a, --api <api>', 'Base API endpoint', apiDefault);
|
|
19
|
+
if (defaultToken) {
|
|
20
|
+
program.option('-t, --token <token>', 'Authentication token in the format "username:token"', defaultToken);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
program.option('-t, --token <token>', 'Authentication token in the format "username:token"');
|
|
24
|
+
}
|
|
25
|
+
program
|
|
26
|
+
.option('-f, --file <file>', 'Path to the file containing the template data (Nunjucks with MJML)')
|
|
27
|
+
.option('-s, --sender <sender>', 'Sender email address')
|
|
28
|
+
.option('-r, --rcpt <rcpt>', 'Recipient email addresses (comma-separated)')
|
|
29
|
+
.option('-n, --name <name>', 'Template name')
|
|
30
|
+
.option('-b, --subject <subject>', 'Email subject')
|
|
31
|
+
.option('-l, --locale <locale>', 'Locale')
|
|
32
|
+
.option('-d, --domain <domain>', 'Domain', envDefaults.domain)
|
|
33
|
+
.option('-p, --part <true|false>', 'Part')
|
|
34
|
+
.option('-v, --vars <vars>', 'Template parameters (JSON string)');
|
|
35
|
+
const readStdin = async () => {
|
|
36
|
+
if (process.stdin.isTTY) {
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const rl = readline_1.default.createInterface({
|
|
41
|
+
input: process.stdin,
|
|
42
|
+
output: process.stdout,
|
|
43
|
+
terminal: false
|
|
44
|
+
});
|
|
45
|
+
let data = '';
|
|
46
|
+
rl.on('line', (line) => {
|
|
47
|
+
data += line + '\n';
|
|
48
|
+
});
|
|
49
|
+
rl.on('close', () => {
|
|
50
|
+
resolve(data.trim());
|
|
51
|
+
});
|
|
52
|
+
rl.on('error', (err) => {
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const getTemplateData = async () => {
|
|
58
|
+
if (program.opts().file) {
|
|
59
|
+
const filePath = program.opts().file;
|
|
60
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
61
|
+
throw new Error(`File not found: ${filePath}`);
|
|
62
|
+
}
|
|
63
|
+
return fs_1.default.readFileSync(filePath, 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
return await readStdin();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
program
|
|
70
|
+
.command('template')
|
|
71
|
+
.description('Store a template on the server')
|
|
72
|
+
.action(async () => {
|
|
73
|
+
const client = new mail_magic_client_1.default(program.opts().api, program.opts().token);
|
|
74
|
+
try {
|
|
75
|
+
const template = await getTemplateData();
|
|
76
|
+
const templateData = {
|
|
77
|
+
template,
|
|
78
|
+
sender: program.opts().sender,
|
|
79
|
+
name: program.opts().name,
|
|
80
|
+
subject: program.opts().subject,
|
|
81
|
+
locale: program.opts().locale,
|
|
82
|
+
domain: program.opts().domain,
|
|
83
|
+
part: !!program.opts().part
|
|
84
|
+
};
|
|
85
|
+
const result = await client.storeTemplate(templateData);
|
|
86
|
+
console.log('Template updated');
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof Error) {
|
|
90
|
+
console.error('Error:', error.message);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
console.error('An unknown error occurred.');
|
|
94
|
+
}
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
program
|
|
99
|
+
.command('send')
|
|
100
|
+
.description('Send a template to recipients')
|
|
101
|
+
.action(async () => {
|
|
102
|
+
const client = new mail_magic_client_1.default(program.opts().api, program.opts().token);
|
|
103
|
+
try {
|
|
104
|
+
const template = await getTemplateData();
|
|
105
|
+
const vars = program.opts().vars ? JSON.parse(program.opts().vars) : '{}';
|
|
106
|
+
const templateData = {
|
|
107
|
+
name: program.opts().name,
|
|
108
|
+
rcpt: program.opts().rcpt,
|
|
109
|
+
domain: program.opts().domain,
|
|
110
|
+
locale: program.opts().locale,
|
|
111
|
+
vars
|
|
112
|
+
};
|
|
113
|
+
const result = await client.sendTemplate(templateData);
|
|
114
|
+
console.log('Template sent');
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (error instanceof Error) {
|
|
118
|
+
console.error('Error:', error.message);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
console.error('An unknown error occurred.');
|
|
122
|
+
}
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
program
|
|
127
|
+
.command('version')
|
|
128
|
+
.description('Show current client version')
|
|
129
|
+
.action(async (cmdOptions) => {
|
|
130
|
+
console.log('1.0.19');
|
|
131
|
+
});
|
|
132
|
+
program
|
|
133
|
+
.command('compile')
|
|
134
|
+
.description('Compile templates by resolving inheritance and processing with FFE')
|
|
135
|
+
.option('-i, --input <input>', 'Input directory', './templates')
|
|
136
|
+
.option('-o, --output <output>', 'Output directory', './templates-dist')
|
|
137
|
+
.option('-c, --css <css>', 'Path to Foundation for Emails CSS', './templates/foundation-emails.css')
|
|
138
|
+
.option('-t, --template <template>', 'Process a specific template only')
|
|
139
|
+
.action(async (cmdOptions) => {
|
|
140
|
+
try {
|
|
141
|
+
await (0, preprocess_1.do_the_template_thing)({
|
|
142
|
+
src_dir: cmdOptions.input,
|
|
143
|
+
dist_dir: cmdOptions.output,
|
|
144
|
+
css_path: cmdOptions.css,
|
|
145
|
+
tplname: cmdOptions.template // Pass undefined if not specified
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (error instanceof Error) {
|
|
150
|
+
console.error('Error:', error.message);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
console.error('An unknown error occurred.');
|
|
154
|
+
}
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
program
|
|
159
|
+
.command('push')
|
|
160
|
+
.description('Compile a template with partials and store it on the server')
|
|
161
|
+
.option('-i, --input <input>', 'Input directory', './templates')
|
|
162
|
+
.option('-c, --css <css>', 'Path to Foundation for Emails CSS', './templates/foundation-emails.css')
|
|
163
|
+
.option('-t, --template <template>', 'Template path relative to input (without .njk)')
|
|
164
|
+
.option('-n, --name <name>', 'Template name (defaults to template basename)')
|
|
165
|
+
.option('-s, --sender <sender>', 'Sender email address')
|
|
166
|
+
.option('-b, --subject <subject>', 'Email subject')
|
|
167
|
+
.option('-l, --locale <locale>', 'Locale')
|
|
168
|
+
.option('-d, --domain <domain>', 'Domain')
|
|
169
|
+
.option('--dry-run', 'Show what would be uploaded without sending anything')
|
|
170
|
+
.action(async (cmdOptions) => {
|
|
171
|
+
try {
|
|
172
|
+
const summary = await (0, cli_helpers_1.pushTemplate)({
|
|
173
|
+
api: program.opts().api,
|
|
174
|
+
token: program.opts().token,
|
|
175
|
+
domain: cmdOptions.domain,
|
|
176
|
+
template: cmdOptions.template,
|
|
177
|
+
name: cmdOptions.name,
|
|
178
|
+
locale: cmdOptions.locale,
|
|
179
|
+
sender: cmdOptions.sender,
|
|
180
|
+
subject: cmdOptions.subject,
|
|
181
|
+
input: cmdOptions.input,
|
|
182
|
+
css: cmdOptions.css,
|
|
183
|
+
dryRun: !!cmdOptions.dryRun
|
|
184
|
+
});
|
|
185
|
+
if (cmdOptions.dryRun) {
|
|
186
|
+
console.log(`Dry run - template: ${summary.domain} ${summary.locale || ''} ${summary.name}`);
|
|
187
|
+
console.log(`Source: ${summary.filePath}`);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
console.log('Template compiled and uploaded');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (error instanceof Error) {
|
|
195
|
+
console.error('Error:', error.message);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
console.error('An unknown error occurred.');
|
|
199
|
+
}
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
program
|
|
204
|
+
.command('push-dir')
|
|
205
|
+
.description('Upload templates and assets from a config-style directory')
|
|
206
|
+
.option('-i, --input <input>', 'Config directory (contains init-data.json)', './data')
|
|
207
|
+
.option('-c, --css <css>', 'Path to Foundation for Emails CSS (optional)')
|
|
208
|
+
.option('--dry-run', 'Show what would be uploaded without sending anything')
|
|
209
|
+
.option('--skip-assets', 'Skip asset uploads')
|
|
210
|
+
.option('--skip-tx', 'Skip transactional templates')
|
|
211
|
+
.option('--skip-forms', 'Skip form templates')
|
|
212
|
+
.option('-d, --domain <domain>', 'Domain to upload (overrides global)')
|
|
213
|
+
.action(async (cmdOptions) => {
|
|
214
|
+
try {
|
|
215
|
+
const summary = await (0, cli_helpers_1.pushTemplateDir)({
|
|
216
|
+
api: program.opts().api,
|
|
217
|
+
token: program.opts().token,
|
|
218
|
+
input: cmdOptions.input,
|
|
219
|
+
domain: cmdOptions.domain || program.opts().domain,
|
|
220
|
+
css: cmdOptions.css,
|
|
221
|
+
includeAssets: !cmdOptions.skipAssets,
|
|
222
|
+
includeTx: !cmdOptions.skipTx,
|
|
223
|
+
includeForms: !cmdOptions.skipForms,
|
|
224
|
+
dryRun: !!cmdOptions.dryRun
|
|
225
|
+
});
|
|
226
|
+
if (cmdOptions.dryRun) {
|
|
227
|
+
console.log('Dry run - planned uploads:');
|
|
228
|
+
for (const action of summary.actions) {
|
|
229
|
+
if (action.kind === 'tx-template') {
|
|
230
|
+
console.log(`tx-template: ${action.domain} ${action.locale || ''} ${action.template}`);
|
|
231
|
+
}
|
|
232
|
+
else if (action.kind === 'form-template') {
|
|
233
|
+
console.log(`form-template: ${action.domain} ${action.locale || ''} ${action.template}`);
|
|
234
|
+
}
|
|
235
|
+
else if (action.kind === 'domain-assets') {
|
|
236
|
+
const files = action.files?.join(', ') || '';
|
|
237
|
+
console.log(`domain-assets: ${action.domain} ${action.path || '.'} ${files}`);
|
|
238
|
+
}
|
|
239
|
+
else if (action.kind === 'template-assets') {
|
|
240
|
+
const files = action.files?.join(', ') || '';
|
|
241
|
+
console.log(`template-assets: ${action.domain} ${action.locale || ''} ${action.template} ${action.path || '.'} ${files}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
console.log(`Summary: ${summary.templates} tx template(s), ${summary.forms} form template(s), ${summary.assetBatches} asset batch(es)`);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
console.log('Templates and assets uploaded');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
if (error instanceof Error) {
|
|
252
|
+
console.error('Error:', error.message);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
console.error('An unknown error occurred.');
|
|
256
|
+
}
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
program
|
|
261
|
+
.command('assets')
|
|
262
|
+
.description('Upload asset files to the server')
|
|
263
|
+
.option('-f, --file <file...>', 'Asset file path(s)')
|
|
264
|
+
.option('--template-type <type>', 'Template type (tx or form)')
|
|
265
|
+
.option('--template <template>', 'Template name/idname')
|
|
266
|
+
.option('--path <path>', 'Destination subdirectory under assets or template')
|
|
267
|
+
.option('-l, --locale <locale>', 'Locale')
|
|
268
|
+
.option('--dry-run', 'Show what would be uploaded without sending anything')
|
|
269
|
+
.action(async (cmdOptions) => {
|
|
270
|
+
const client = new mail_magic_client_1.default(program.opts().api, program.opts().token);
|
|
271
|
+
try {
|
|
272
|
+
const files = cmdOptions.file;
|
|
273
|
+
if (!files || files.length === 0) {
|
|
274
|
+
throw new Error('At least one --file is required');
|
|
275
|
+
}
|
|
276
|
+
if (cmdOptions.dryRun) {
|
|
277
|
+
for (const file of files) {
|
|
278
|
+
if (!fs_1.default.existsSync(file)) {
|
|
279
|
+
throw new Error(`File not found: ${file}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
console.log('Dry run - assets:');
|
|
283
|
+
console.log(`domain=${program.opts().domain} templateType=${cmdOptions.templateType || ''} template=${cmdOptions.template || ''} locale=${cmdOptions.locale || ''} path=${cmdOptions.path || ''}`);
|
|
284
|
+
console.log(`files=${files.join(', ')}`);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
await client.uploadAssets({
|
|
288
|
+
domain: program.opts().domain,
|
|
289
|
+
files,
|
|
290
|
+
templateType: cmdOptions.templateType,
|
|
291
|
+
template: cmdOptions.template,
|
|
292
|
+
locale: cmdOptions.locale,
|
|
293
|
+
path: cmdOptions.path
|
|
294
|
+
});
|
|
295
|
+
console.log('Assets uploaded');
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
if (error instanceof Error) {
|
|
299
|
+
console.error('Error:', error.message);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
console.error('An unknown error occurred.');
|
|
303
|
+
}
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import emailAddresses from 'email-addresses';
|
|
4
|
+
import nunjucks from 'nunjucks';
|
|
5
|
+
class templateClient {
|
|
6
|
+
constructor(baseURL, apiKey) {
|
|
7
|
+
this.baseURL = baseURL;
|
|
8
|
+
this.apiKey = apiKey;
|
|
9
|
+
if (!apiKey || !baseURL) {
|
|
10
|
+
throw new Error('Apikey/api-url required');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async request(method, command, body) {
|
|
14
|
+
const url = `${this.baseURL}${command}`;
|
|
15
|
+
const options = {
|
|
16
|
+
method,
|
|
17
|
+
headers: {
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
Authorization: `Bearer apikey-${this.apiKey}`
|
|
20
|
+
},
|
|
21
|
+
body: body ? JSON.stringify(body) : '{}'
|
|
22
|
+
};
|
|
23
|
+
// console.log(JSON.stringify({ options, url }));
|
|
24
|
+
const response = await fetch(url, options);
|
|
25
|
+
const j = await response.json();
|
|
26
|
+
if (response.ok) {
|
|
27
|
+
return j;
|
|
28
|
+
}
|
|
29
|
+
// console.log(JSON.stringify(j, undefined, 2));
|
|
30
|
+
if (j && j.message) {
|
|
31
|
+
throw new Error(`FETCH FAILED: ${response.status} ${j.message}`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
throw new Error(`FETCH FAILED: ${response.status} ${response.statusText}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async get(command) {
|
|
38
|
+
return this.request('GET', command);
|
|
39
|
+
}
|
|
40
|
+
async post(command, body) {
|
|
41
|
+
return this.request('POST', command, body);
|
|
42
|
+
}
|
|
43
|
+
async put(command, body) {
|
|
44
|
+
return this.request('PUT', command, body);
|
|
45
|
+
}
|
|
46
|
+
async delete(command, body) {
|
|
47
|
+
return this.request('DELETE', command, body);
|
|
48
|
+
}
|
|
49
|
+
validateEmails(list) {
|
|
50
|
+
const valid = [], invalid = [];
|
|
51
|
+
const emails = list
|
|
52
|
+
.split(',')
|
|
53
|
+
.map((email) => email.trim())
|
|
54
|
+
.filter((email) => email !== '');
|
|
55
|
+
emails.forEach((email) => {
|
|
56
|
+
const parsed = emailAddresses.parseOneAddress(email);
|
|
57
|
+
if (parsed && parsed.address) {
|
|
58
|
+
valid.push(parsed.address);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
invalid.push(email);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return { valid, invalid };
|
|
65
|
+
}
|
|
66
|
+
validateTemplate(template) {
|
|
67
|
+
try {
|
|
68
|
+
const env = new nunjucks.Environment(new nunjucks.FileSystemLoader(['./templates']));
|
|
69
|
+
const t = env.renderString(template, {});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof Error) {
|
|
73
|
+
throw new Error(`Template validation failed: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
throw new Error('Template validation failed with an unknown error');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
validateSender(sender) {
|
|
81
|
+
const exp = /^[^<>]+<[^<>]+@[^<>]+\.[^<>]+>$/;
|
|
82
|
+
if (!exp.test(sender)) {
|
|
83
|
+
throw new Error('Invalid sender format. Expected "Name <email@example.com>"');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
createAttachmentPayload(attachments) {
|
|
87
|
+
const formData = new FormData();
|
|
88
|
+
const usedFields = [];
|
|
89
|
+
for (const attachment of attachments) {
|
|
90
|
+
if (!attachment?.path) {
|
|
91
|
+
throw new Error('Attachment path is required');
|
|
92
|
+
}
|
|
93
|
+
const raw = fs.readFileSync(attachment.path);
|
|
94
|
+
const filename = attachment.filename || path.basename(attachment.path);
|
|
95
|
+
const blob = new Blob([raw], attachment.contentType ? { type: attachment.contentType } : undefined);
|
|
96
|
+
const field = attachment.field || 'attachment';
|
|
97
|
+
formData.append(field, blob, filename);
|
|
98
|
+
usedFields.push(field);
|
|
99
|
+
}
|
|
100
|
+
return { formData, usedFields };
|
|
101
|
+
}
|
|
102
|
+
appendFields(formData, fields) {
|
|
103
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
104
|
+
if (value === undefined || value === null) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (typeof value === 'string') {
|
|
108
|
+
formData.append(key, value);
|
|
109
|
+
}
|
|
110
|
+
else if (typeof value === 'number' || typeof value === 'boolean') {
|
|
111
|
+
formData.append(key, String(value));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
formData.append(key, JSON.stringify(value));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async postFormData(command, formData) {
|
|
119
|
+
const url = `${this.baseURL}${command}`;
|
|
120
|
+
const response = await fetch(url, {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: {
|
|
123
|
+
Authorization: `Bearer apikey-${this.apiKey}`
|
|
124
|
+
},
|
|
125
|
+
body: formData
|
|
126
|
+
});
|
|
127
|
+
const j = await response.json();
|
|
128
|
+
if (response.ok) {
|
|
129
|
+
return j;
|
|
130
|
+
}
|
|
131
|
+
if (j && j.message) {
|
|
132
|
+
throw new Error(`FETCH FAILED: ${response.status} ${j.message}`);
|
|
133
|
+
}
|
|
134
|
+
throw new Error(`FETCH FAILED: ${response.status} ${response.statusText}`);
|
|
135
|
+
}
|
|
136
|
+
async storeTemplate(td) {
|
|
137
|
+
if (!td.template) {
|
|
138
|
+
throw new Error('No template data provided');
|
|
139
|
+
}
|
|
140
|
+
this.validateTemplate(td.template);
|
|
141
|
+
if (td.sender) {
|
|
142
|
+
this.validateSender(td.sender);
|
|
143
|
+
}
|
|
144
|
+
return this.storeTxTemplate(td);
|
|
145
|
+
}
|
|
146
|
+
async sendTemplate(std) {
|
|
147
|
+
if (!std.name || !std.rcpt) {
|
|
148
|
+
throw new Error('Invalid request body; name/rcpt required');
|
|
149
|
+
}
|
|
150
|
+
return this.sendTxMessage(std);
|
|
151
|
+
}
|
|
152
|
+
async storeTxTemplate(td) {
|
|
153
|
+
if (!td.template) {
|
|
154
|
+
throw new Error('No template data provided');
|
|
155
|
+
}
|
|
156
|
+
this.validateTemplate(td.template);
|
|
157
|
+
if (td.sender) {
|
|
158
|
+
this.validateSender(td.sender);
|
|
159
|
+
}
|
|
160
|
+
return this.post('/api/v1/tx/template', td);
|
|
161
|
+
}
|
|
162
|
+
async sendTxMessage(std) {
|
|
163
|
+
if (!std.name || !std.rcpt) {
|
|
164
|
+
throw new Error('Invalid request body; name/rcpt required');
|
|
165
|
+
}
|
|
166
|
+
const { valid, invalid } = this.validateEmails(std.rcpt);
|
|
167
|
+
if (invalid.length > 0) {
|
|
168
|
+
throw new Error('Invalid email address(es): ' + invalid.join(','));
|
|
169
|
+
}
|
|
170
|
+
// this.validateTemplate(template);
|
|
171
|
+
const body = {
|
|
172
|
+
name: std.name,
|
|
173
|
+
rcpt: std.rcpt,
|
|
174
|
+
domain: std.domain || '',
|
|
175
|
+
locale: std.locale || '',
|
|
176
|
+
vars: std.vars || {},
|
|
177
|
+
replyTo: std.replyTo,
|
|
178
|
+
headers: std.headers
|
|
179
|
+
};
|
|
180
|
+
// console.log(JSON.stringify(body, undefined, 2));
|
|
181
|
+
if (std.attachments && std.attachments.length > 0) {
|
|
182
|
+
if (std.headers) {
|
|
183
|
+
throw new Error('Headers are not supported with attachment uploads');
|
|
184
|
+
}
|
|
185
|
+
const { formData } = this.createAttachmentPayload(std.attachments);
|
|
186
|
+
this.appendFields(formData, {
|
|
187
|
+
name: std.name,
|
|
188
|
+
rcpt: std.rcpt,
|
|
189
|
+
domain: std.domain || '',
|
|
190
|
+
locale: std.locale || '',
|
|
191
|
+
vars: JSON.stringify(std.vars || {}),
|
|
192
|
+
replyTo: std.replyTo
|
|
193
|
+
});
|
|
194
|
+
return this.postFormData('/api/v1/tx/message', formData);
|
|
195
|
+
}
|
|
196
|
+
return this.post('/api/v1/tx/message', body);
|
|
197
|
+
}
|
|
198
|
+
async storeFormTemplate(data) {
|
|
199
|
+
if (!data.template) {
|
|
200
|
+
throw new Error('No template data provided');
|
|
201
|
+
}
|
|
202
|
+
if (!data.idname) {
|
|
203
|
+
throw new Error('Missing form identifier');
|
|
204
|
+
}
|
|
205
|
+
if (!data.sender) {
|
|
206
|
+
throw new Error('Missing sender address');
|
|
207
|
+
}
|
|
208
|
+
if (!data.recipient) {
|
|
209
|
+
throw new Error('Missing recipient address');
|
|
210
|
+
}
|
|
211
|
+
this.validateTemplate(data.template);
|
|
212
|
+
this.validateSender(data.sender);
|
|
213
|
+
return this.post('/api/v1/form/template', data);
|
|
214
|
+
}
|
|
215
|
+
async sendFormMessage(data) {
|
|
216
|
+
if (!data.formid) {
|
|
217
|
+
throw new Error('Invalid request body; formid required');
|
|
218
|
+
}
|
|
219
|
+
const fields = data.fields || {};
|
|
220
|
+
const baseFields = {
|
|
221
|
+
formid: data.formid,
|
|
222
|
+
secret: data.secret,
|
|
223
|
+
recipient: data.recipient,
|
|
224
|
+
domain: data.domain,
|
|
225
|
+
locale: data.locale,
|
|
226
|
+
vars: data.vars || {},
|
|
227
|
+
replyTo: data.replyTo,
|
|
228
|
+
...fields
|
|
229
|
+
};
|
|
230
|
+
if (data.attachments && data.attachments.length > 0) {
|
|
231
|
+
const { formData } = this.createAttachmentPayload(data.attachments);
|
|
232
|
+
this.appendFields(formData, {
|
|
233
|
+
formid: data.formid,
|
|
234
|
+
secret: data.secret,
|
|
235
|
+
recipient: data.recipient,
|
|
236
|
+
domain: data.domain,
|
|
237
|
+
locale: data.locale,
|
|
238
|
+
vars: JSON.stringify(data.vars || {}),
|
|
239
|
+
replyTo: data.replyTo
|
|
240
|
+
});
|
|
241
|
+
this.appendFields(formData, fields);
|
|
242
|
+
return this.postFormData('/api/v1/form/message', formData);
|
|
243
|
+
}
|
|
244
|
+
return this.post('/api/v1/form/message', baseFields);
|
|
245
|
+
}
|
|
246
|
+
async uploadAssets(data) {
|
|
247
|
+
if (!data.domain) {
|
|
248
|
+
throw new Error('domain is required');
|
|
249
|
+
}
|
|
250
|
+
if (!data.files || data.files.length === 0) {
|
|
251
|
+
throw new Error('At least one asset file is required');
|
|
252
|
+
}
|
|
253
|
+
if (data.templateType && !data.template) {
|
|
254
|
+
throw new Error('template is required when templateType is provided');
|
|
255
|
+
}
|
|
256
|
+
if (data.template && !data.templateType) {
|
|
257
|
+
throw new Error('templateType is required when template is provided');
|
|
258
|
+
}
|
|
259
|
+
const attachments = data.files.map((input) => {
|
|
260
|
+
if (typeof input === 'string') {
|
|
261
|
+
return { path: input, field: 'asset' };
|
|
262
|
+
}
|
|
263
|
+
return { ...input, field: input.field || 'asset' };
|
|
264
|
+
});
|
|
265
|
+
const { formData } = this.createAttachmentPayload(attachments);
|
|
266
|
+
this.appendFields(formData, {
|
|
267
|
+
domain: data.domain,
|
|
268
|
+
templateType: data.templateType,
|
|
269
|
+
template: data.template,
|
|
270
|
+
locale: data.locale,
|
|
271
|
+
path: data.path
|
|
272
|
+
});
|
|
273
|
+
return this.postFormData('/api/v1/assets', formData);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
export default templateClient;
|