anote-server-libs 0.11.0 → 0.11.1
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/package.json +1 -1
- package/services/utils.js +92 -0
- package/services/utils.ts +89 -1
package/package.json
CHANGED
package/services/utils.js
CHANGED
|
@@ -11,6 +11,12 @@ exports.idempotent = idempotent;
|
|
|
11
11
|
exports.sendSelfPostableMessage = sendSelfPostableMessage;
|
|
12
12
|
exports.fpEuros = fpEuros;
|
|
13
13
|
exports.digitize = digitize;
|
|
14
|
+
exports.readEmailTemplates = readEmailTemplates;
|
|
15
|
+
exports.replacePlaceholders = replacePlaceholders;
|
|
16
|
+
exports.getHtmlReplaced = getHtmlReplaced;
|
|
17
|
+
exports.getEmailTitle = getEmailTitle;
|
|
18
|
+
exports.getEmailTranslation = getEmailTranslation;
|
|
19
|
+
const fs = require("fs");
|
|
14
20
|
function atob(str) {
|
|
15
21
|
return Buffer.from(str, 'base64').toString('binary');
|
|
16
22
|
}
|
|
@@ -195,3 +201,89 @@ function digitize(value, opts) {
|
|
|
195
201
|
}
|
|
196
202
|
return (opts && opts.currency) ? (parts[0] + '.00') : parts[0];
|
|
197
203
|
}
|
|
204
|
+
function loadEmailTemplates(path) {
|
|
205
|
+
if (!path || !fs.existsSync(path))
|
|
206
|
+
return undefined;
|
|
207
|
+
const loadedEmailTemplates = {};
|
|
208
|
+
const fileNames = fs.readdirSync(path);
|
|
209
|
+
fileNames.forEach(fileName => {
|
|
210
|
+
if (!fileName.startsWith('template-') || !fileName.endsWith('.html'))
|
|
211
|
+
return;
|
|
212
|
+
const templateIdentifier = fileName.slice('template-'.length, -'.html'.length);
|
|
213
|
+
loadedEmailTemplates[templateIdentifier] = fs.readFileSync(path + '/' + fileName).toString('utf8');
|
|
214
|
+
});
|
|
215
|
+
return loadedEmailTemplates;
|
|
216
|
+
}
|
|
217
|
+
function readEmailTemplates(path, config) {
|
|
218
|
+
const { product, name } = config.app;
|
|
219
|
+
if (!product || !name)
|
|
220
|
+
return loadEmailTemplates(path);
|
|
221
|
+
const mainPath = path + '/' + product;
|
|
222
|
+
const overridePath = product !== name ? __dirname + `/${product}/${name}` : undefined;
|
|
223
|
+
const defaultTemplates = loadEmailTemplates(mainPath);
|
|
224
|
+
const overrideTemplates = overridePath ? loadEmailTemplates(overridePath) : {};
|
|
225
|
+
return { ...defaultTemplates, ...overrideTemplates };
|
|
226
|
+
}
|
|
227
|
+
function replacePlaceholders(data, keys, depth) {
|
|
228
|
+
if (!keys || depth >= keys.length)
|
|
229
|
+
return undefined;
|
|
230
|
+
if (depth === keys.length - 1)
|
|
231
|
+
return (data || {})[keys[depth]];
|
|
232
|
+
return replacePlaceholders((data || {})[keys[depth]], keys, depth + 1);
|
|
233
|
+
}
|
|
234
|
+
function getHtml(template, translationKeyValue) {
|
|
235
|
+
if (!template)
|
|
236
|
+
return undefined;
|
|
237
|
+
const pattern = new RegExp('{{\\s*([a-zA-Z._0-9]+)\\s*}}', 'g');
|
|
238
|
+
const replacer = (_, key) => replacePlaceholders(translationKeyValue, key.split('.'), 0) ?? '';
|
|
239
|
+
template = template.replace(pattern, replacer);
|
|
240
|
+
template = template.replace(pattern, replacer);
|
|
241
|
+
template = template.replace(new RegExp('\\[\\[\\s*([a-zA-Z._0-9:]+)\\s*\\]\\]', 'g'), (_, key) => {
|
|
242
|
+
const keyParts = key.split(':');
|
|
243
|
+
return (replacePlaceholders(translationKeyValue, keyParts[1].split('.'), 0) || [])
|
|
244
|
+
.map((subContext) => getHtml(keyParts[0], subContext)).join('');
|
|
245
|
+
});
|
|
246
|
+
template = template.replace(new RegExp('<<\\s*([a-zA-Z._0-9:]+)\\s*>>', 'g'), (_, key) => {
|
|
247
|
+
const keyParts = key.split(':');
|
|
248
|
+
if (replacePlaceholders(translationKeyValue, keyParts[1].split('.'), 0))
|
|
249
|
+
return getHtml(keyParts[0], translationKeyValue);
|
|
250
|
+
return '';
|
|
251
|
+
});
|
|
252
|
+
return template;
|
|
253
|
+
}
|
|
254
|
+
function getHtmlReplaced(config, templates, key, lang, defaultTemplateTranslations = {}, envSpecificTranslations, extraData, newPage) {
|
|
255
|
+
const name = config.app.name;
|
|
256
|
+
const defaultTranslations = defaultTemplateTranslations?.[lang] || defaultTemplateTranslations?.[0] || {};
|
|
257
|
+
const envSpecificTrans = envSpecificTranslations?.[name]?.[key]?.[lang]
|
|
258
|
+
|| envSpecificTranslations?.[name]?.[key]?.[0]
|
|
259
|
+
|| {};
|
|
260
|
+
const translationKeyValue = {
|
|
261
|
+
...defaultTranslations,
|
|
262
|
+
...envSpecificTrans,
|
|
263
|
+
...extraData,
|
|
264
|
+
domain: config.frontend,
|
|
265
|
+
...config.app.emailConfig
|
|
266
|
+
};
|
|
267
|
+
let html = getHtml(templates[key], translationKeyValue);
|
|
268
|
+
if (newPage) {
|
|
269
|
+
html = html.replace('</head>', `<style>
|
|
270
|
+
@media print {
|
|
271
|
+
.new-page {
|
|
272
|
+
page-break-before: always;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
</style>
|
|
276
|
+
</head>`);
|
|
277
|
+
html = html.replace('</body>', `<p class="new-page">Provided by Satoris</p>
|
|
278
|
+
</body>`);
|
|
279
|
+
}
|
|
280
|
+
return html;
|
|
281
|
+
}
|
|
282
|
+
function getEmailTitle(key, lang, titles, overrideTitles, data) {
|
|
283
|
+
const pattern = new RegExp('{{\\s*([a-zA-Z._0-9]+)\\s*}}', 'g');
|
|
284
|
+
const title = overrideTitles?.[lang]?.[key] ?? titles[lang]?.[key];
|
|
285
|
+
return title?.replace(pattern, (_, placeholderKey) => data?.[placeholderKey]);
|
|
286
|
+
}
|
|
287
|
+
function getEmailTranslation(key, lang, translationKeyValue, defaultLang = 0) {
|
|
288
|
+
return translationKeyValue?.[lang]?.[key] ?? translationKeyValue?.[defaultLang]?.[key];
|
|
289
|
+
}
|
package/services/utils.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
1
2
|
import {NextFunction, Request, Response} from 'express';
|
|
2
3
|
import {Logger} from 'winston';
|
|
3
4
|
import {BaseModelRepository} from '../models/repository/BaseModelRepository';
|
|
@@ -178,4 +179,91 @@ export function digitize(value: number | string, opts?: {[key: string]: any}): s
|
|
|
178
179
|
return parts[0] + '.' + decimals;
|
|
179
180
|
}
|
|
180
181
|
return (opts && opts.currency)? (parts[0] + '.00') : parts[0];
|
|
181
|
-
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function loadEmailTemplates(path: string): Record<string, string> | undefined {
|
|
185
|
+
if(!path || !fs.existsSync(path)) return undefined;
|
|
186
|
+
const loadedEmailTemplates: {[id: string]: string} = {};
|
|
187
|
+
const fileNames = fs.readdirSync(path);
|
|
188
|
+
fileNames.forEach(fileName => {
|
|
189
|
+
if(!fileName.startsWith('template-') || !fileName.endsWith('.html')) return;
|
|
190
|
+
const templateIdentifier = fileName.slice('template-'.length, -'.html'.length);
|
|
191
|
+
loadedEmailTemplates[templateIdentifier] = fs.readFileSync(path + '/' + fileName).toString('utf8');
|
|
192
|
+
});
|
|
193
|
+
return loadedEmailTemplates;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function readEmailTemplates(path: string, config: Record<string, any>): {[id: string]: string} {
|
|
197
|
+
const { product, name } = config.app;
|
|
198
|
+
if (!product || !name) return loadEmailTemplates(path);
|
|
199
|
+
const mainPath = path + '/' + product;
|
|
200
|
+
const overridePath = product !== name ? __dirname + `/${product}/${name}` : undefined;
|
|
201
|
+
const defaultTemplates = loadEmailTemplates(mainPath);
|
|
202
|
+
const overrideTemplates = overridePath ? loadEmailTemplates(overridePath) : {};
|
|
203
|
+
return {...defaultTemplates, ...overrideTemplates};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function replacePlaceholders(data: Record<string, any>, keys: string[], depth: number): any {
|
|
207
|
+
if(!keys || depth >= keys.length) return undefined;
|
|
208
|
+
if(depth === keys.length - 1) return (data || {})[keys[depth]];
|
|
209
|
+
return replacePlaceholders((data || {})[keys[depth]], keys, depth + 1);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function getHtml(template: string, translationKeyValue: Record<string, any>): string | undefined {
|
|
213
|
+
if(!template) return undefined;
|
|
214
|
+
const pattern = new RegExp('{{\\s*([a-zA-Z._0-9]+)\\s*}}', 'g');
|
|
215
|
+
const replacer = (_: string, key: string) => replacePlaceholders(translationKeyValue, key.split('.'), 0) ?? '';
|
|
216
|
+
template = template.replace(pattern, replacer);
|
|
217
|
+
template = template.replace(pattern, replacer);
|
|
218
|
+
template = template.replace(new RegExp('\\[\\[\\s*([a-zA-Z._0-9:]+)\\s*\\]\\]', 'g'), (_, key) => {
|
|
219
|
+
const keyParts = key.split(':');
|
|
220
|
+
return (replacePlaceholders(translationKeyValue, keyParts[1].split('.'), 0) || [])
|
|
221
|
+
.map((subContext: any) => getHtml(keyParts[0], subContext)).join('');
|
|
222
|
+
});
|
|
223
|
+
template = template.replace(new RegExp('<<\\s*([a-zA-Z._0-9:]+)\\s*>>', 'g'), (_, key) => {
|
|
224
|
+
const keyParts = key.split(':');
|
|
225
|
+
if(replacePlaceholders(translationKeyValue, keyParts[1].split('.'), 0))
|
|
226
|
+
return getHtml(keyParts[0], translationKeyValue);
|
|
227
|
+
return '';
|
|
228
|
+
});
|
|
229
|
+
return template;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function getHtmlReplaced(config: Record<string, any>, templates: Record<string, string>, key: string, lang: number, defaultTemplateTranslations: Record<number, any> = {}, envSpecificTranslations?: Record<string, any>, extraData?: Record<string, any>, newPage?: boolean): string {
|
|
233
|
+
const name = config.app.name;
|
|
234
|
+
const defaultTranslations = defaultTemplateTranslations?.[lang] || defaultTemplateTranslations?.[0] || {};
|
|
235
|
+
const envSpecificTrans = envSpecificTranslations?.[name]?.[key]?.[lang]
|
|
236
|
+
|| envSpecificTranslations?.[name]?.[key]?.[0]
|
|
237
|
+
|| {};
|
|
238
|
+
const translationKeyValue = {
|
|
239
|
+
...defaultTranslations,
|
|
240
|
+
...envSpecificTrans,
|
|
241
|
+
...extraData,
|
|
242
|
+
domain: config.frontend,
|
|
243
|
+
...config.app.emailConfig
|
|
244
|
+
};
|
|
245
|
+
let html = getHtml(templates[key], translationKeyValue);
|
|
246
|
+
if(newPage) {
|
|
247
|
+
html = html.replace('</head>', `<style>
|
|
248
|
+
@media print {
|
|
249
|
+
.new-page {
|
|
250
|
+
page-break-before: always;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
</style>
|
|
254
|
+
</head>`);
|
|
255
|
+
html = html.replace('</body>', `<p class="new-page">Provided by Satoris</p>
|
|
256
|
+
</body>`);
|
|
257
|
+
}
|
|
258
|
+
return html;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function getEmailTitle(key: string, lang: number, titles: Record<number, any>, overrideTitles?: Record<number, any>, data?: Record<string, string>): string | undefined {
|
|
262
|
+
const pattern = new RegExp('{{\\s*([a-zA-Z._0-9]+)\\s*}}', 'g');
|
|
263
|
+
const title = overrideTitles?.[lang]?.[key] ?? titles[lang]?.[key];
|
|
264
|
+
return title?.replace(pattern, (_: string, placeholderKey: string) => data?.[placeholderKey]);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function getEmailTranslation(key: string, lang: number, translationKeyValue: Record<number, any>, defaultLang: number = 0): string | undefined {
|
|
268
|
+
return translationKeyValue?.[lang]?.[key] ?? translationKeyValue?.[defaultLang]?.[key];
|
|
269
|
+
}
|