@medicus.ai/medicus-report-pdf-generator 1.3.13 → 1.3.15
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/.vscode/settings.json +3 -3
- package/HANDOVER.md +146 -0
- package/README.md +26 -26
- package/app/i18n.config.js +20 -20
- package/app/i18n_wellbeing.config.js +20 -20
- package/app/services/localeService.js +28 -28
- package/assets/Mediclinic/css/nasco_report.css +6297 -6297
- package/assets/Mediclinic/css/nasco_report_rtl.css +851 -851
- package/assets/Mediclinic/css/style.css +33 -33
- package/assets/Mediclinic/js/js.js +112 -112
- package/assets/Mediclinic/js/raphael.js +8437 -8437
- package/assets/arabic.css +275 -275
- package/assets/charts.min.js +7 -7
- package/assets/corporate_report/css/report.css +582 -582
- package/assets/corporate_report/css/report_rtl.css +144 -144
- package/assets/corporate_report/js/js.js +14 -14
- package/assets/data/data.json +124 -124
- package/assets/data/data2.json +675 -675
- package/assets/data/pdf_data_with_history.json +1255 -1255
- package/assets/doctor-note-insight-icon.svg +11 -11
- package/assets/imc/css/style.css +61 -61
- package/assets/jquery-1.4.min.js +151 -151
- package/assets/jquery-2.1.0.min.js +3 -3
- package/assets/js.js +201 -201
- package/assets/medicus_pdf/arabic.css +444 -444
- package/assets/medicus_pdf/styles.css +2427 -2427
- package/assets/print.css +4 -4
- package/assets/qr-code-style.css +68 -68
- package/assets/qrcode.min.js +3 -3
- package/assets/raphael-min.js +10 -10
- package/assets/sanusx/css/sanusx_report.css +707 -707
- package/assets/sizing.js +2 -2
- package/assets/styles.css +1545 -1545
- package/assets/styles.min.css +1358 -1358
- package/assets/translation.js +30 -30
- package/assets/wellbeing/css/nasco_report.css +3472 -3472
- package/assets/wellbeing/css/nasco_report_rtl.css +679 -679
- package/assets/wellbeing/js/js.js +111 -111
- package/assets/wellbeing/js/raphael.js +8437 -8437
- package/base64/cairo-font.js +2 -2
- package/config/Mediclinic.json +37 -37
- package/config/Najeeb.ai.json +35 -35
- package/config/Pha.json +64 -64
- package/config/bionext.json +37 -37
- package/config/default.json +35 -35
- package/config/diagnostikare.json +34 -34
- package/config/maisonsante.json +39 -39
- package/config/nasco.json +35 -35
- package/config/sanitas.json +35 -35
- package/config/sanusx.json +34 -34
- package/config/test.txt +4 -4
- package/data converter.js +53 -53
- package/docs/01-architecture-overview.md +149 -0
- package/docs/02-configuration-and-whitelabelling.md +111 -0
- package/docs/03-templating-and-rendering.md +71 -0
- package/docs/04-report-pipelines.md +128 -0
- package/docs/05-questions-and-data-model.md +67 -0
- package/docs/06-internationalization.md +59 -0
- package/docs/07-email-notifications.md +59 -0
- package/docs/08-known-issues-and-technical-debt.md +41 -0
- package/docs/README.md +22 -0
- package/downloadfile.js +6 -6
- package/example.txt +1188 -1188
- package/index.js +481 -480
- package/lib/big_integral_questionnaire.js +370 -298
- package/lib/corporate_report_generator.js +399 -399
- package/lib/pdf_generator.min.js +525 -525
- package/lib/sanusx_report_generator.js +533 -533
- package/lib/sendEmail.js +343 -343
- package/lib/template.js +2227 -2227
- package/lib/wellbeing_report_generator.js +2164 -2134
- package/locales/ar-AE.json +169 -169
- package/locales/ar-SA.json +169 -169
- package/locales/ar.json +397 -397
- package/locales/de.json +169 -169
- package/locales/en.json +229 -229
- package/locales/fr.json +168 -168
- package/locales/pt.json +165 -165
- package/locales/wellbeing/ar-AE.json +149 -149
- package/locales/wellbeing/ar-SA.json +257 -257
- package/locales/wellbeing/de.json +301 -301
- package/locales/wellbeing/en.json +423 -423
- package/locales/wellbeing/es.json +133 -133
- package/locales/wellbeing/fr.json +121 -121
- package/locales/wellbeing/it-IT.json +121 -121
- package/locales/wellbeing/pt.json +121 -121
- package/locales/wellbeing/tr.json +121 -121
- package/locales/wellbeing/zh-CN.json +121 -121
- package/locales/zh-CN.json +165 -165
- package/package.json +47 -47
- package/preview-big-integral.js +75 -75
- package/run.js +15 -15
- package/templates/Mediclinic/blocks/first-section.html +205 -205
- package/templates/Mediclinic/blocks/footer.html +7 -7
- package/templates/Mediclinic/blocks/header-template.html +5 -5
- package/templates/Mediclinic/blocks/header.html +20 -20
- package/templates/Mediclinic/blocks/patient-table.html +38 -38
- package/templates/Mediclinic/blocks/pdf-header.html +9 -9
- package/templates/Mediclinic/blocks/tips.html +8 -8
- package/templates/Mediclinic/ltr_no_pages.html +37 -37
- package/templates/Mediclinic/rtl_no_pages.html +42 -42
- package/templates/Mediclinic/wellbeing_template.html +41 -41
- package/templates/Pha/blocks/first-section.html +207 -207
- package/templates/Pha/blocks/footer.html +7 -7
- package/templates/Pha/blocks/header-template.html +5 -5
- package/templates/Pha/blocks/header.html +20 -20
- package/templates/Pha/blocks/patient-table.html +38 -38
- package/templates/Pha/blocks/pdf-header.html +8 -8
- package/templates/Pha/blocks/tips.html +8 -8
- package/templates/Pha/ltr_no_pages.html +37 -37
- package/templates/Pha/rtl_no_pages.html +42 -42
- package/templates/Pha/wellbeing_template.html +41 -41
- package/templates/base.html +23 -23
- package/templates/blocks/ar-first-page-header.html +68 -68
- package/templates/blocks/ar-footer.html +54 -54
- package/templates/blocks/ar-header.html +67 -67
- package/templates/blocks/biomarker-compact-ar.html +27 -27
- package/templates/blocks/biomarker-compact.html +27 -27
- package/templates/blocks/biomarker-details.html +41 -41
- package/templates/blocks/biomarker-insight.html +22 -22
- package/templates/blocks/biomarker-min.html +9 -9
- package/templates/blocks/doctor-note.html +11 -11
- package/templates/blocks/footer.html +35 -35
- package/templates/blocks/header-first-page.html +46 -46
- package/templates/blocks/header.html +49 -49
- package/templates/blocks/panel-compact.html +38 -38
- package/templates/blocks/panel-details.html +16 -16
- package/templates/blocks/report-summary.html +16 -16
- package/templates/blocks/section-title.html +4 -4
- package/templates/blocks/signature.html +16 -16
- package/templates/blocks/summary-insight.html +15 -15
- package/templates/blocks/summary.html +9 -9
- package/templates/corporate_report/cover_page.html +23 -23
- package/templates/corporate_report/footer.html +9 -9
- package/templates/corporate_report/ltr_no_pages.html +20 -20
- package/templates/corporate_report/page.html +24 -24
- package/templates/corporate_report/participant_analytics.html +5 -5
- package/templates/corporate_report/rtl_no_pages.html +20 -20
- package/templates/empty.html +10 -10
- package/templates/first_page_head.html +157 -157
- package/templates/imc/first-header-template.html +53 -53
- package/templates/ltr.html +1368 -1368
- package/templates/ltr_no_pages.html +89 -89
- package/templates/maisonsante/blocks/first-section.html +151 -207
- package/templates/maisonsante/blocks/footer.html +7 -7
- package/templates/maisonsante/blocks/header-template.html +5 -5
- package/templates/maisonsante/blocks/header.html +20 -20
- package/templates/maisonsante/blocks/patient-table.html +38 -38
- package/templates/maisonsante/blocks/pdf-header.html +8 -8
- package/templates/maisonsante/blocks/tips.html +8 -8
- package/templates/maisonsante/ltr_no_pages.html +38 -38
- package/templates/maisonsante/rtl_no_pages.html +43 -43
- package/templates/maisonsante/wellbeing_template.html +41 -41
- package/templates/no_pages.html +1224 -1224
- package/templates/popup/popup-template.html +129 -129
- package/templates/rtl_no_pages.html +92 -92
- package/templates/sanusx/blocks/footer.html +6 -6
- package/templates/sanusx/blocks/header.html +20 -20
- package/templates/sanusx/blocks/personal-details.html +123 -123
- package/templates/sanusx/blocks/predictions-section.html +57 -57
- package/templates/sanusx/blocks/recommendations-section.html +6 -6
- package/templates/sanusx/blocks/super-power-section.html +29 -29
- package/templates/sanusx/blocks/tips.html +8 -8
- package/templates/sanusx/ltr_no_pages.html +26 -26
- package/templates/sanusx/rtl_no_pages.html +36 -36
- package/templates/template.html +1377 -1377
- package/templates/wellbeing/blocks/first-section.html +106 -106
- package/templates/wellbeing/blocks/footer.html +7 -7
- package/templates/wellbeing/blocks/header.html +37 -37
- package/templates/wellbeing/blocks/tips.html +8 -8
- package/templates/wellbeing/ltr_no_pages.html +35 -35
- package/templates/wellbeing/rtl_no_pages.html +36 -36
- package/templates/wellbeing/wellbeing_template.html +43 -43
- package/test.js +74 -74
- package/testing-reports/mediclinic/arabic-data.js +0 -439
- package/testing-reports/mediclinic/doctor-data.js +0 -523
- package/testing-reports/mediclinic/with-wellbeing.js +0 -415
- package/testing-reports/pha/notes-example.js +0 -397
package/index.js
CHANGED
|
@@ -1,480 +1,481 @@
|
|
|
1
|
-
const { generateHTMLStaging, generatePDF, generatePatientQR } = require("./lib/pdf_generator.min");
|
|
2
|
-
const {
|
|
3
|
-
generateHTMLWellbeingReport,
|
|
4
|
-
generatePDFWellbeingReport,
|
|
5
|
-
generateHTMLWellbeingReportWithSmartReport,
|
|
6
|
-
generatePDFReport
|
|
7
|
-
} = require("./lib/wellbeing_report_generator");
|
|
8
|
-
const { generateSanusXReport, generateHTMLSanusXReport } = require("./lib/sanusx_report_generator");
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const os = require('os');
|
|
11
|
-
const crypto = require('crypto');
|
|
12
|
-
const { sendNascoEmail, sendEmailNotification } = require('./lib/sendEmail')
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const { generatePDFCorporateReport, generateHTMLCorporateReport } = require('./lib/corporate_report_generator')
|
|
15
|
-
|
|
16
|
-
// Best-effort fire-and-forget cleanup for per-call temp files. Never throws.
|
|
17
|
-
const safeUnlink = (filePath) => {
|
|
18
|
-
if (!filePath) return Promise.resolve();
|
|
19
|
-
return fs.promises.unlink(filePath).catch(() => { });
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
module.exports = {
|
|
23
|
-
|
|
24
|
-
generateHTMLStaging,
|
|
25
|
-
|
|
26
|
-
generateMedicusPDF: async (base64Object, isDebugging, isDownloadable, onlyHTML) => {
|
|
27
|
-
let LOGS = '';
|
|
28
|
-
let LOGS_FILE_PATH = __dirname + '/output/LOGS.txt';
|
|
29
|
-
let PDF_FILE_PATH = __dirname + '/output/sample.pdf';
|
|
30
|
-
if (isDebugging) {
|
|
31
|
-
if (fs.existsSync(LOGS_FILE_PATH)) {
|
|
32
|
-
fs.unlinkSync(LOGS_FILE_PATH);
|
|
33
|
-
}
|
|
34
|
-
if (fs.existsSync(PDF_FILE_PATH)) {
|
|
35
|
-
fs.unlinkSync(PDF_FILE_PATH);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// 1: save incoming base64
|
|
39
|
-
if (isDebugging) {
|
|
40
|
-
console.log('1: save incoming base64');
|
|
41
|
-
LOGS += '1: save incoming base64' + '\n' + '=============' + '\n';
|
|
42
|
-
// LOGS += base64Object + '\n\n\n';
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// 2: save decoded json string
|
|
47
|
-
const json = Buffer.from(base64Object, 'base64').toString('utf8');
|
|
48
|
-
if (isDebugging) {
|
|
49
|
-
console.log('2: save decoded json string');
|
|
50
|
-
LOGS += '2: save decoded json string' + '\n' + '=============' + '\n';
|
|
51
|
-
LOGS += json + '\n\n\n';
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// 3: save decoded json
|
|
56
|
-
const decodedJSON = JSON.parse(json);
|
|
57
|
-
if (isDebugging) {
|
|
58
|
-
console.log('3: save decoded json string')
|
|
59
|
-
LOGS += '3: save decoded json' + '\n' + '=============' + '\n';
|
|
60
|
-
LOGS += decodedJSON + '\n\n\n';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
let html = await generateHTMLStaging(decodedJSON, isDebugging);
|
|
65
|
-
let fileBuffer, base64data = ''
|
|
66
|
-
if (!onlyHTML) {
|
|
67
|
-
// 4: save PDF buffer
|
|
68
|
-
const startDate = Date.now();
|
|
69
|
-
fileBuffer = await generatePDF(html);
|
|
70
|
-
console.log(`Took ${Date.now() - startDate} ms.`);
|
|
71
|
-
if (isDebugging) {
|
|
72
|
-
console.log('4: save PDF buffer')
|
|
73
|
-
LOGS += '4: save PDF buffer' + '\n' + '=============' + '\n';
|
|
74
|
-
LOGS += fileBuffer + '\n\n\n';
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
fs.writeFileSync(LOGS_FILE_PATH, LOGS, 'utf8');
|
|
78
|
-
fs.writeFileSync(PDF_FILE_PATH, fileBuffer, 'utf8');
|
|
79
|
-
|
|
80
|
-
// 5: save encoded base64
|
|
81
|
-
base64data = Buffer.from(fileBuffer, 'utf8').toString('base64');
|
|
82
|
-
if (isDebugging) {
|
|
83
|
-
console.log('5: save encoded base64');
|
|
84
|
-
LOGS += '5: save encoded base64' + '\n' + '=============' + '\n';
|
|
85
|
-
LOGS += base64data + '\n\n\n';
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (onlyHTML) {
|
|
89
|
-
return html;
|
|
90
|
-
} else {
|
|
91
|
-
if (isDownloadable) {
|
|
92
|
-
return fileBuffer;
|
|
93
|
-
} else {
|
|
94
|
-
return base64data;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
generateNascoPDF: async (data, isDebugging, isDownloadable, shouldSendEmail) => {
|
|
101
|
-
let reportData = JSON.parse(data)
|
|
102
|
-
let base64Object = reportData.data
|
|
103
|
-
let LOGS = '';
|
|
104
|
-
let LOGS_FILE_PATH = __dirname + '/output/LOGS.txt';
|
|
105
|
-
let PDF_FILE_PATH = __dirname + '/output/nasco-sample.pdf';
|
|
106
|
-
|
|
107
|
-
let mailConfig = {
|
|
108
|
-
host: reportData.host,
|
|
109
|
-
port: reportData.port,
|
|
110
|
-
authUser: reportData.authUser,
|
|
111
|
-
authPass: reportData.authPass,
|
|
112
|
-
sendFromEmail: reportData.sendFromEmail,
|
|
113
|
-
secure: reportData.secure
|
|
114
|
-
}
|
|
115
|
-
if (isDebugging) {
|
|
116
|
-
if (fs.existsSync(LOGS_FILE_PATH)) {
|
|
117
|
-
fs.unlinkSync(LOGS_FILE_PATH);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (fs.existsSync(PDF_FILE_PATH)) {
|
|
121
|
-
fs.unlinkSync(PDF_FILE_PATH);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 1: save decoded json string
|
|
126
|
-
if (isDebugging) {
|
|
127
|
-
console.log('2: save json string');
|
|
128
|
-
LOGS += '2: save json string' + '\n' + '=============' + '\n';
|
|
129
|
-
LOGS += base64Object + '\n\n\n';
|
|
130
|
-
}
|
|
131
|
-
// 2: save decoded json string
|
|
132
|
-
const json = Buffer.from(base64Object, 'base64').toString('utf8');
|
|
133
|
-
if (isDebugging) {
|
|
134
|
-
console.log('2: save decoded json string');
|
|
135
|
-
LOGS += '2: save decoded json string' + '\n' + '=============' + '\n';
|
|
136
|
-
LOGS += json + '\n\n\n';
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// 3: save decoded json
|
|
140
|
-
const decodedJSON = JSON.parse(json);
|
|
141
|
-
if (isDebugging) {
|
|
142
|
-
console.log('3: save decoded json string')
|
|
143
|
-
LOGS += '3: save decoded json' + '\n' + '=============' + '\n';
|
|
144
|
-
LOGS += decodedJSON + '\n\n\n';
|
|
145
|
-
}
|
|
146
|
-
let html = await generateHTMLWellbeingReport(decodedJSON, isDebugging, reportData.client, reportData.language);
|
|
147
|
-
|
|
148
|
-
// 3: save PDF buffer
|
|
149
|
-
let fileBuffer = await generatePDFWellbeingReport(html);
|
|
150
|
-
if (isDebugging) {
|
|
151
|
-
console.log('3: save PDF buffer')
|
|
152
|
-
LOGS += '3: save PDF buffer' + '\n' + '=============' + '\n';
|
|
153
|
-
LOGS += fileBuffer + '\n\n\n';
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
console.log("PDF_FILE_PATH", PDF_FILE_PATH)
|
|
157
|
-
fs.writeFileSync(PDF_FILE_PATH, fileBuffer, 'utf8');
|
|
158
|
-
|
|
159
|
-
// 4: save encoded base64
|
|
160
|
-
const base64data = Buffer.from(fileBuffer, 'utf8').toString('base64');
|
|
161
|
-
if (isDebugging) {
|
|
162
|
-
console.log('4: save encoded base64');
|
|
163
|
-
LOGS += '4: save encoded base64' + '\n' + '=============' + '\n';
|
|
164
|
-
LOGS += base64data + '\n\n\n';
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// 5: save logs to file
|
|
169
|
-
fs.writeFileSync(LOGS_FILE_PATH, LOGS, 'utf8');
|
|
170
|
-
if (isDebugging) {
|
|
171
|
-
console.log('5: save logs to file');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (isDownloadable) {
|
|
175
|
-
return fileBuffer;
|
|
176
|
-
} else {
|
|
177
|
-
if (shouldSendEmail)
|
|
178
|
-
return await sendNascoEmail(decodedJSON, PDF_FILE_PATH, mailConfig, reportData.client)
|
|
179
|
-
else
|
|
180
|
-
return base64data;
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Generate full PDF report with encryption
|
|
186
|
-
* @param {string} data - JSON string containing report data with base64 encoded content
|
|
187
|
-
* @param {boolean} isDebugging - Enable debug mode (saves logs and keeps output files)
|
|
188
|
-
* @param {boolean} isDownloadable - If true, returns PDF buffer; otherwise returns base64 string
|
|
189
|
-
* @param {boolean} shouldSendEmail - If true, sends email with PDF attachment and returns email result
|
|
190
|
-
* @param {Array<string>} [selectedLabs=[]] - Array of selected lab names for header logos
|
|
191
|
-
* @param {boolean} [showHeaderLogo=true] - Whether to display logo in PDF header
|
|
192
|
-
* @returns {Promise<Buffer|string|object>} Returns encrypted PDF buffer if isDownloadable, base64 string if not sending email, or email result if shouldSendEmail
|
|
193
|
-
*/
|
|
194
|
-
generateFullPdf: async (data, isDebugging, isDownloadable, shouldSendEmail, selectedLabs = [], showHeaderLogo = true) => {
|
|
195
|
-
let reportData = JSON.parse(data);
|
|
196
|
-
let base64Object = reportData.data;
|
|
197
|
-
|
|
198
|
-
// Per-call unique identifiers so concurrent callers never share a path.
|
|
199
|
-
// Even when two requests overlap, each one owns its own input/output
|
|
200
|
-
// files and email attachment, eliminating cross-user PDF swapping.
|
|
201
|
-
const callId = crypto.randomUUID();
|
|
202
|
-
const tmpDir = os.tmpdir();
|
|
203
|
-
const tmpInputPath = path.join(tmpDir, `wb-${callId}-in.pdf`);
|
|
204
|
-
const tmpEncryptedPath = path.join(tmpDir, `wb-${callId}-enc.pdf`);
|
|
205
|
-
let tmpEmailAttachmentPath = null;
|
|
206
|
-
|
|
207
|
-
let LOGS = '';
|
|
208
|
-
const LOGS_FILE_PATH = isDebugging
|
|
209
|
-
? path.join(__dirname, 'output', `LOGS-${callId}.txt`)
|
|
210
|
-
: null;
|
|
211
|
-
|
|
212
|
-
const mailConfig = {
|
|
213
|
-
host: reportData.host,
|
|
214
|
-
port: reportData.port,
|
|
215
|
-
authUser: reportData.authUser,
|
|
216
|
-
authPass: reportData.authPass,
|
|
217
|
-
sendFromEmail: reportData.sendFromEmail,
|
|
218
|
-
secure: reportData.secure,
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
if (isDebugging) {
|
|
222
|
-
console.log('2: save json string');
|
|
223
|
-
LOGS += '2: save json string\n===============\n' + base64Object + '\n\n\n';
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const json = Buffer.from(base64Object, 'base64').toString('utf8');
|
|
227
|
-
|
|
228
|
-
if (isDebugging) {
|
|
229
|
-
console.log('2: save decoded json string');
|
|
230
|
-
LOGS += '2: save decoded json string\n===============\n' + json + '\n\n\n';
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const decodedJSON = JSON.parse(json);
|
|
234
|
-
|
|
235
|
-
if (isDebugging) {
|
|
236
|
-
console.log('3: save decoded json string');
|
|
237
|
-
LOGS += '3: save decoded json\n===============\n' + JSON.stringify(decodedJSON, null, 2) + '\n\n\n';
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const html = await generateHTMLWellbeingReportWithSmartReport(
|
|
241
|
-
decodedJSON,
|
|
242
|
-
isDebugging,
|
|
243
|
-
reportData.client,
|
|
244
|
-
reportData.language,
|
|
245
|
-
selectedLabs || [],
|
|
246
|
-
showHeaderLogo || true
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
const fileBuffer = await generatePDFReport({ ...html, client: reportData.client, footer: decodedJSON?.footer });
|
|
250
|
-
|
|
251
|
-
if (isDebugging) {
|
|
252
|
-
console.log('3: save PDF buffer');
|
|
253
|
-
LOGS += '3: save PDF buffer\n===============\n' + '[binary content omitted]' + '\n\n\n';
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
// Write the unencrypted PDF to a per-call temp file.
|
|
258
|
-
// qpdf needs a real input path; using a uuid-suffixed file in
|
|
259
|
-
// os.tmpdir() guarantees no concurrent caller can overwrite it.
|
|
260
|
-
await fs.promises.writeFile(tmpInputPath, fileBuffer);
|
|
261
|
-
|
|
262
|
-
const qpdf = await import('node-qpdf2');
|
|
263
|
-
|
|
264
|
-
// Encrypt to a per-call output file. We can't use qpdf's stdout
|
|
265
|
-
// mode (output omitted) because node-qpdf2's spawn helper joins
|
|
266
|
-
// stdout chunks as strings, which corrupts binary PDF bytes.
|
|
267
|
-
await qpdf.encrypt({
|
|
268
|
-
input: tmpInputPath,
|
|
269
|
-
output: tmpEncryptedPath,
|
|
270
|
-
keyLength: 256,
|
|
271
|
-
password: reportData.pdfPassword,
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
const encryptedBuffer = await fs.promises.readFile(tmpEncryptedPath);
|
|
275
|
-
|
|
276
|
-
const base64data = encryptedBuffer.toString('base64');
|
|
277
|
-
if (isDebugging) {
|
|
278
|
-
console.log('4: save encoded base64');
|
|
279
|
-
LOGS += '4: save encoded base64\n===============\n' + base64data + '\n\n\n';
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (isDebugging && LOGS_FILE_PATH) {
|
|
283
|
-
await fs.promises.writeFile(LOGS_FILE_PATH, LOGS, 'utf8');
|
|
284
|
-
console.log('5: save logs to file');
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (isDownloadable) {
|
|
288
|
-
return encryptedBuffer;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (shouldSendEmail) {
|
|
292
|
-
// Write the encrypted buffer to a dedicated per-call file just
|
|
293
|
-
// for the email attachment. sendNascoEmail expects a path
|
|
294
|
-
// (nodemailer reads it from disk), so we materialize it once,
|
|
295
|
-
// pass it in, then unlink in the finally block.
|
|
296
|
-
tmpEmailAttachmentPath = path.join(tmpDir, `wb-${callId}-mail.pdf`);
|
|
297
|
-
await fs.promises.writeFile(tmpEmailAttachmentPath, encryptedBuffer);
|
|
298
|
-
return await sendNascoEmail(
|
|
299
|
-
decodedJSON.wellbeing,
|
|
300
|
-
tmpEmailAttachmentPath,
|
|
301
|
-
mailConfig,
|
|
302
|
-
reportData.client
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return base64data;
|
|
307
|
-
} finally {
|
|
308
|
-
await Promise.all([
|
|
309
|
-
safeUnlink(tmpInputPath),
|
|
310
|
-
safeUnlink(tmpEncryptedPath),
|
|
311
|
-
safeUnlink(tmpEmailAttachmentPath),
|
|
312
|
-
]);
|
|
313
|
-
}
|
|
314
|
-
},
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
let
|
|
319
|
-
let
|
|
320
|
-
let
|
|
321
|
-
let
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
LOGS +=
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
LOGS += json + '\n\n
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
LOGS +=
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
LOGS +=
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
LOGS +=
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
let
|
|
416
|
-
let
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
LOGS += json + '\n\n
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
LOGS +=
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
LOGS +=
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
LOGS +=
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
}
|
|
1
|
+
const { generateHTMLStaging, generatePDF, generatePatientQR } = require("./lib/pdf_generator.min");
|
|
2
|
+
const {
|
|
3
|
+
generateHTMLWellbeingReport,
|
|
4
|
+
generatePDFWellbeingReport,
|
|
5
|
+
generateHTMLWellbeingReportWithSmartReport,
|
|
6
|
+
generatePDFReport
|
|
7
|
+
} = require("./lib/wellbeing_report_generator");
|
|
8
|
+
const { generateSanusXReport, generateHTMLSanusXReport } = require("./lib/sanusx_report_generator");
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const crypto = require('crypto');
|
|
12
|
+
const { sendNascoEmail, sendEmailNotification } = require('./lib/sendEmail')
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { generatePDFCorporateReport, generateHTMLCorporateReport } = require('./lib/corporate_report_generator')
|
|
15
|
+
|
|
16
|
+
// Best-effort fire-and-forget cleanup for per-call temp files. Never throws.
|
|
17
|
+
const safeUnlink = (filePath) => {
|
|
18
|
+
if (!filePath) return Promise.resolve();
|
|
19
|
+
return fs.promises.unlink(filePath).catch(() => { });
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
|
|
24
|
+
generateHTMLStaging,
|
|
25
|
+
|
|
26
|
+
generateMedicusPDF: async (base64Object, isDebugging, isDownloadable, onlyHTML) => {
|
|
27
|
+
let LOGS = '';
|
|
28
|
+
let LOGS_FILE_PATH = __dirname + '/output/LOGS.txt';
|
|
29
|
+
let PDF_FILE_PATH = __dirname + '/output/sample.pdf';
|
|
30
|
+
if (isDebugging) {
|
|
31
|
+
if (fs.existsSync(LOGS_FILE_PATH)) {
|
|
32
|
+
fs.unlinkSync(LOGS_FILE_PATH);
|
|
33
|
+
}
|
|
34
|
+
if (fs.existsSync(PDF_FILE_PATH)) {
|
|
35
|
+
fs.unlinkSync(PDF_FILE_PATH);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// 1: save incoming base64
|
|
39
|
+
if (isDebugging) {
|
|
40
|
+
console.log('1: save incoming base64');
|
|
41
|
+
LOGS += '1: save incoming base64' + '\n' + '=============' + '\n';
|
|
42
|
+
// LOGS += base64Object + '\n\n\n';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// 2: save decoded json string
|
|
47
|
+
const json = Buffer.from(base64Object, 'base64').toString('utf8');
|
|
48
|
+
if (isDebugging) {
|
|
49
|
+
console.log('2: save decoded json string');
|
|
50
|
+
LOGS += '2: save decoded json string' + '\n' + '=============' + '\n';
|
|
51
|
+
LOGS += json + '\n\n\n';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
// 3: save decoded json
|
|
56
|
+
const decodedJSON = JSON.parse(json);
|
|
57
|
+
if (isDebugging) {
|
|
58
|
+
console.log('3: save decoded json string')
|
|
59
|
+
LOGS += '3: save decoded json' + '\n' + '=============' + '\n';
|
|
60
|
+
LOGS += decodedJSON + '\n\n\n';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
let html = await generateHTMLStaging(decodedJSON, isDebugging);
|
|
65
|
+
let fileBuffer, base64data = ''
|
|
66
|
+
if (!onlyHTML) {
|
|
67
|
+
// 4: save PDF buffer
|
|
68
|
+
const startDate = Date.now();
|
|
69
|
+
fileBuffer = await generatePDF(html);
|
|
70
|
+
console.log(`Took ${Date.now() - startDate} ms.`);
|
|
71
|
+
if (isDebugging) {
|
|
72
|
+
console.log('4: save PDF buffer')
|
|
73
|
+
LOGS += '4: save PDF buffer' + '\n' + '=============' + '\n';
|
|
74
|
+
LOGS += fileBuffer + '\n\n\n';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fs.writeFileSync(LOGS_FILE_PATH, LOGS, 'utf8');
|
|
78
|
+
fs.writeFileSync(PDF_FILE_PATH, fileBuffer, 'utf8');
|
|
79
|
+
|
|
80
|
+
// 5: save encoded base64
|
|
81
|
+
base64data = Buffer.from(fileBuffer, 'utf8').toString('base64');
|
|
82
|
+
if (isDebugging) {
|
|
83
|
+
console.log('5: save encoded base64');
|
|
84
|
+
LOGS += '5: save encoded base64' + '\n' + '=============' + '\n';
|
|
85
|
+
LOGS += base64data + '\n\n\n';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (onlyHTML) {
|
|
89
|
+
return html;
|
|
90
|
+
} else {
|
|
91
|
+
if (isDownloadable) {
|
|
92
|
+
return fileBuffer;
|
|
93
|
+
} else {
|
|
94
|
+
return base64data;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
generateNascoPDF: async (data, isDebugging, isDownloadable, shouldSendEmail) => {
|
|
101
|
+
let reportData = JSON.parse(data)
|
|
102
|
+
let base64Object = reportData.data
|
|
103
|
+
let LOGS = '';
|
|
104
|
+
let LOGS_FILE_PATH = __dirname + '/output/LOGS.txt';
|
|
105
|
+
let PDF_FILE_PATH = __dirname + '/output/nasco-sample.pdf';
|
|
106
|
+
|
|
107
|
+
let mailConfig = {
|
|
108
|
+
host: reportData.host,
|
|
109
|
+
port: reportData.port,
|
|
110
|
+
authUser: reportData.authUser,
|
|
111
|
+
authPass: reportData.authPass,
|
|
112
|
+
sendFromEmail: reportData.sendFromEmail,
|
|
113
|
+
secure: reportData.secure
|
|
114
|
+
}
|
|
115
|
+
if (isDebugging) {
|
|
116
|
+
if (fs.existsSync(LOGS_FILE_PATH)) {
|
|
117
|
+
fs.unlinkSync(LOGS_FILE_PATH);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(PDF_FILE_PATH)) {
|
|
121
|
+
fs.unlinkSync(PDF_FILE_PATH);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 1: save decoded json string
|
|
126
|
+
if (isDebugging) {
|
|
127
|
+
console.log('2: save json string');
|
|
128
|
+
LOGS += '2: save json string' + '\n' + '=============' + '\n';
|
|
129
|
+
LOGS += base64Object + '\n\n\n';
|
|
130
|
+
}
|
|
131
|
+
// 2: save decoded json string
|
|
132
|
+
const json = Buffer.from(base64Object, 'base64').toString('utf8');
|
|
133
|
+
if (isDebugging) {
|
|
134
|
+
console.log('2: save decoded json string');
|
|
135
|
+
LOGS += '2: save decoded json string' + '\n' + '=============' + '\n';
|
|
136
|
+
LOGS += json + '\n\n\n';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 3: save decoded json
|
|
140
|
+
const decodedJSON = JSON.parse(json);
|
|
141
|
+
if (isDebugging) {
|
|
142
|
+
console.log('3: save decoded json string')
|
|
143
|
+
LOGS += '3: save decoded json' + '\n' + '=============' + '\n';
|
|
144
|
+
LOGS += decodedJSON + '\n\n\n';
|
|
145
|
+
}
|
|
146
|
+
let html = await generateHTMLWellbeingReport(decodedJSON, isDebugging, reportData.client, reportData.language);
|
|
147
|
+
|
|
148
|
+
// 3: save PDF buffer
|
|
149
|
+
let fileBuffer = await generatePDFWellbeingReport(html);
|
|
150
|
+
if (isDebugging) {
|
|
151
|
+
console.log('3: save PDF buffer')
|
|
152
|
+
LOGS += '3: save PDF buffer' + '\n' + '=============' + '\n';
|
|
153
|
+
LOGS += fileBuffer + '\n\n\n';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log("PDF_FILE_PATH", PDF_FILE_PATH)
|
|
157
|
+
fs.writeFileSync(PDF_FILE_PATH, fileBuffer, 'utf8');
|
|
158
|
+
|
|
159
|
+
// 4: save encoded base64
|
|
160
|
+
const base64data = Buffer.from(fileBuffer, 'utf8').toString('base64');
|
|
161
|
+
if (isDebugging) {
|
|
162
|
+
console.log('4: save encoded base64');
|
|
163
|
+
LOGS += '4: save encoded base64' + '\n' + '=============' + '\n';
|
|
164
|
+
LOGS += base64data + '\n\n\n';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
// 5: save logs to file
|
|
169
|
+
fs.writeFileSync(LOGS_FILE_PATH, LOGS, 'utf8');
|
|
170
|
+
if (isDebugging) {
|
|
171
|
+
console.log('5: save logs to file');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (isDownloadable) {
|
|
175
|
+
return fileBuffer;
|
|
176
|
+
} else {
|
|
177
|
+
if (shouldSendEmail)
|
|
178
|
+
return await sendNascoEmail(decodedJSON, PDF_FILE_PATH, mailConfig, reportData.client)
|
|
179
|
+
else
|
|
180
|
+
return base64data;
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Generate full PDF report with encryption
|
|
186
|
+
* @param {string} data - JSON string containing report data with base64 encoded content
|
|
187
|
+
* @param {boolean} isDebugging - Enable debug mode (saves logs and keeps output files)
|
|
188
|
+
* @param {boolean} isDownloadable - If true, returns PDF buffer; otherwise returns base64 string
|
|
189
|
+
* @param {boolean} shouldSendEmail - If true, sends email with PDF attachment and returns email result
|
|
190
|
+
* @param {Array<string>} [selectedLabs=[]] - Array of selected lab names for header logos
|
|
191
|
+
* @param {boolean} [showHeaderLogo=true] - Whether to display logo in PDF header
|
|
192
|
+
* @returns {Promise<Buffer|string|object>} Returns encrypted PDF buffer if isDownloadable, base64 string if not sending email, or email result if shouldSendEmail
|
|
193
|
+
*/
|
|
194
|
+
generateFullPdf: async (data, isDebugging, isDownloadable, shouldSendEmail, selectedLabs = [], showHeaderLogo = true) => {
|
|
195
|
+
let reportData = JSON.parse(data);
|
|
196
|
+
let base64Object = reportData.data;
|
|
197
|
+
|
|
198
|
+
// Per-call unique identifiers so concurrent callers never share a path.
|
|
199
|
+
// Even when two requests overlap, each one owns its own input/output
|
|
200
|
+
// files and email attachment, eliminating cross-user PDF swapping.
|
|
201
|
+
const callId = crypto.randomUUID();
|
|
202
|
+
const tmpDir = os.tmpdir();
|
|
203
|
+
const tmpInputPath = path.join(tmpDir, `wb-${callId}-in.pdf`);
|
|
204
|
+
const tmpEncryptedPath = path.join(tmpDir, `wb-${callId}-enc.pdf`);
|
|
205
|
+
let tmpEmailAttachmentPath = null;
|
|
206
|
+
|
|
207
|
+
let LOGS = '';
|
|
208
|
+
const LOGS_FILE_PATH = isDebugging
|
|
209
|
+
? path.join(__dirname, 'output', `LOGS-${callId}.txt`)
|
|
210
|
+
: null;
|
|
211
|
+
|
|
212
|
+
const mailConfig = {
|
|
213
|
+
host: reportData.host,
|
|
214
|
+
port: reportData.port,
|
|
215
|
+
authUser: reportData.authUser,
|
|
216
|
+
authPass: reportData.authPass,
|
|
217
|
+
sendFromEmail: reportData.sendFromEmail,
|
|
218
|
+
secure: reportData.secure,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
if (isDebugging) {
|
|
222
|
+
console.log('2: save json string');
|
|
223
|
+
LOGS += '2: save json string\n===============\n' + base64Object + '\n\n\n';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const json = Buffer.from(base64Object, 'base64').toString('utf8');
|
|
227
|
+
|
|
228
|
+
if (isDebugging) {
|
|
229
|
+
console.log('2: save decoded json string');
|
|
230
|
+
LOGS += '2: save decoded json string\n===============\n' + json + '\n\n\n';
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const decodedJSON = JSON.parse(json);
|
|
234
|
+
|
|
235
|
+
if (isDebugging) {
|
|
236
|
+
console.log('3: save decoded json string');
|
|
237
|
+
LOGS += '3: save decoded json\n===============\n' + JSON.stringify(decodedJSON, null, 2) + '\n\n\n';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const html = await generateHTMLWellbeingReportWithSmartReport(
|
|
241
|
+
decodedJSON,
|
|
242
|
+
isDebugging,
|
|
243
|
+
reportData.client,
|
|
244
|
+
reportData.language,
|
|
245
|
+
selectedLabs || [],
|
|
246
|
+
showHeaderLogo || true
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const fileBuffer = await generatePDFReport({ ...html, client: reportData.client, footer: decodedJSON?.footer });
|
|
250
|
+
|
|
251
|
+
if (isDebugging) {
|
|
252
|
+
console.log('3: save PDF buffer');
|
|
253
|
+
LOGS += '3: save PDF buffer\n===============\n' + '[binary content omitted]' + '\n\n\n';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
// Write the unencrypted PDF to a per-call temp file.
|
|
258
|
+
// qpdf needs a real input path; using a uuid-suffixed file in
|
|
259
|
+
// os.tmpdir() guarantees no concurrent caller can overwrite it.
|
|
260
|
+
await fs.promises.writeFile(tmpInputPath, fileBuffer);
|
|
261
|
+
|
|
262
|
+
const qpdf = await import('node-qpdf2');
|
|
263
|
+
|
|
264
|
+
// Encrypt to a per-call output file. We can't use qpdf's stdout
|
|
265
|
+
// mode (output omitted) because node-qpdf2's spawn helper joins
|
|
266
|
+
// stdout chunks as strings, which corrupts binary PDF bytes.
|
|
267
|
+
await qpdf.encrypt({
|
|
268
|
+
input: tmpInputPath,
|
|
269
|
+
output: tmpEncryptedPath,
|
|
270
|
+
keyLength: 256,
|
|
271
|
+
password: reportData.pdfPassword,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const encryptedBuffer = await fs.promises.readFile(tmpEncryptedPath);
|
|
275
|
+
|
|
276
|
+
const base64data = encryptedBuffer.toString('base64');
|
|
277
|
+
if (isDebugging) {
|
|
278
|
+
console.log('4: save encoded base64');
|
|
279
|
+
LOGS += '4: save encoded base64\n===============\n' + base64data + '\n\n\n';
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (isDebugging && LOGS_FILE_PATH) {
|
|
283
|
+
await fs.promises.writeFile(LOGS_FILE_PATH, LOGS, 'utf8');
|
|
284
|
+
console.log('5: save logs to file');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (isDownloadable) {
|
|
288
|
+
return encryptedBuffer;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (shouldSendEmail) {
|
|
292
|
+
// Write the encrypted buffer to a dedicated per-call file just
|
|
293
|
+
// for the email attachment. sendNascoEmail expects a path
|
|
294
|
+
// (nodemailer reads it from disk), so we materialize it once,
|
|
295
|
+
// pass it in, then unlink in the finally block.
|
|
296
|
+
tmpEmailAttachmentPath = path.join(tmpDir, `wb-${callId}-mail.pdf`);
|
|
297
|
+
await fs.promises.writeFile(tmpEmailAttachmentPath, encryptedBuffer);
|
|
298
|
+
return await sendNascoEmail(
|
|
299
|
+
decodedJSON.wellbeing,
|
|
300
|
+
tmpEmailAttachmentPath,
|
|
301
|
+
mailConfig,
|
|
302
|
+
reportData.client
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return base64data;
|
|
307
|
+
} finally {
|
|
308
|
+
await Promise.all([
|
|
309
|
+
safeUnlink(tmpInputPath),
|
|
310
|
+
safeUnlink(tmpEncryptedPath),
|
|
311
|
+
safeUnlink(tmpEmailAttachmentPath),
|
|
312
|
+
]);
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
//not used
|
|
317
|
+
generateSanuxPDF: async (data, isDebugging, isDownloadable, shouldSendEmail) => {
|
|
318
|
+
let reportData = JSON.parse(data)
|
|
319
|
+
let base64Object = reportData.data
|
|
320
|
+
let LOGS = '';
|
|
321
|
+
let LOGS_FILE_PATH = __dirname + '/output/LOGS.txt';
|
|
322
|
+
let PDF_FILE_PATH = __dirname + '/output/sanusx-sample.pdf';
|
|
323
|
+
|
|
324
|
+
let mailConfig = {
|
|
325
|
+
host: reportData.host,
|
|
326
|
+
port: reportData.port,
|
|
327
|
+
authUser: reportData.authUser,
|
|
328
|
+
authPass: reportData.authPass,
|
|
329
|
+
sendFromEmail: reportData.sendFromEmail,
|
|
330
|
+
secure: reportData.secure
|
|
331
|
+
}
|
|
332
|
+
if (isDebugging) {
|
|
333
|
+
if (fs.existsSync(LOGS_FILE_PATH)) {
|
|
334
|
+
fs.unlinkSync(LOGS_FILE_PATH);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (fs.existsSync(PDF_FILE_PATH)) {
|
|
338
|
+
fs.unlinkSync(PDF_FILE_PATH);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 1: save decoded json string
|
|
343
|
+
if (isDebugging) {
|
|
344
|
+
console.log('2: save json string');
|
|
345
|
+
LOGS += '2: save json string' + '\n' + '=============' + '\n';
|
|
346
|
+
LOGS += base64Object + '\n\n\n';
|
|
347
|
+
}
|
|
348
|
+
// 2: save decoded json string
|
|
349
|
+
const json = Buffer.from(base64Object, 'base64').toString('utf8');
|
|
350
|
+
if (isDebugging) {
|
|
351
|
+
console.log('2: save decoded json string');
|
|
352
|
+
LOGS += '2: save decoded json string' + '\n' + '=============' + '\n';
|
|
353
|
+
LOGS += json + '\n\n\n';
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 3: save decoded json
|
|
357
|
+
const decodedJSON = JSON.parse(json);
|
|
358
|
+
if (isDebugging) {
|
|
359
|
+
console.log('3: save decoded json string')
|
|
360
|
+
LOGS += '3: save decoded json' + '\n' + '=============' + '\n';
|
|
361
|
+
LOGS += decodedJSON + '\n\n\n';
|
|
362
|
+
}
|
|
363
|
+
let html = await generateHTMLSanusXReport(decodedJSON, isDebugging, reportData.client, reportData.language);
|
|
364
|
+
|
|
365
|
+
// 3: save PDF buffer
|
|
366
|
+
let fileBuffer = await generateSanusXReport(html);
|
|
367
|
+
if (isDebugging) {
|
|
368
|
+
console.log('3: save PDF buffer')
|
|
369
|
+
LOGS += '3: save PDF buffer' + '\n' + '=============' + '\n';
|
|
370
|
+
LOGS += fileBuffer + '\n\n\n';
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
console.log("PDF_FILE_PATH", PDF_FILE_PATH)
|
|
374
|
+
fs.writeFileSync(PDF_FILE_PATH, fileBuffer, 'utf8');
|
|
375
|
+
|
|
376
|
+
// 4: save encoded base64
|
|
377
|
+
const base64data = Buffer.from(fileBuffer, 'utf8').toString('base64');
|
|
378
|
+
if (isDebugging) {
|
|
379
|
+
console.log('4: save encoded base64');
|
|
380
|
+
LOGS += '4: save encoded base64' + '\n' + '=============' + '\n';
|
|
381
|
+
LOGS += base64data + '\n\n\n';
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
// 5: save logs to file
|
|
386
|
+
fs.writeFileSync(LOGS_FILE_PATH, LOGS, 'utf8');
|
|
387
|
+
if (isDebugging) {
|
|
388
|
+
console.log('5: save logs to file');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (isDownloadable) {
|
|
392
|
+
return fileBuffer;
|
|
393
|
+
} else {
|
|
394
|
+
if (shouldSendEmail)
|
|
395
|
+
return await sendNascoEmail(decodedJSON, PDF_FILE_PATH, mailConfig, reportData.client)
|
|
396
|
+
else
|
|
397
|
+
return base64data;
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
//not used
|
|
401
|
+
sendEmail: async (json) => {
|
|
402
|
+
const decodedJSON = JSON.parse(json);
|
|
403
|
+
const email = await sendEmailNotification(decodedJSON)
|
|
404
|
+
return email
|
|
405
|
+
},
|
|
406
|
+
//not used
|
|
407
|
+
generateQrCode: async (json) => {
|
|
408
|
+
let fileBuffer = await generatePatientQR(json)
|
|
409
|
+
const base64data = Buffer.from(fileBuffer, 'utf8').toString('base64');
|
|
410
|
+
return base64data
|
|
411
|
+
},
|
|
412
|
+
//not used
|
|
413
|
+
generateCorporateReportPDF: async (json, isDebugging, isDownloadable) => {
|
|
414
|
+
|
|
415
|
+
let LOGS = '';
|
|
416
|
+
let LOGS_FILE_PATH = __dirname + '/output/LOGS.txt';
|
|
417
|
+
let PDF_FILE_PATH = __dirname + '/output/corporate-report-sample.pdf';
|
|
418
|
+
|
|
419
|
+
if (isDebugging) {
|
|
420
|
+
if (fs.existsSync(LOGS_FILE_PATH)) {
|
|
421
|
+
fs.unlinkSync(LOGS_FILE_PATH);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (fs.existsSync(PDF_FILE_PATH)) {
|
|
425
|
+
fs.unlinkSync(PDF_FILE_PATH);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// 1: save decoded json string
|
|
430
|
+
if (isDebugging) {
|
|
431
|
+
console.log('2: save json string');
|
|
432
|
+
LOGS += '2: save json string' + '\n' + '=============' + '\n';
|
|
433
|
+
LOGS += json + '\n\n\n';
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
// 2: save decoded json
|
|
438
|
+
const decodedJSON = JSON.parse(json);
|
|
439
|
+
if (isDebugging) {
|
|
440
|
+
console.log('2: save decoded json string')
|
|
441
|
+
LOGS += '2: save decoded json' + '\n' + '=============' + '\n';
|
|
442
|
+
LOGS += decodedJSON + '\n\n\n';
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
let html = await generateHTMLCorporateReport(decodedJSON, isDebugging);
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
// 3: save PDF buffer
|
|
450
|
+
let fileBuffer = await generatePDFCorporateReport(html);
|
|
451
|
+
if (isDebugging) {
|
|
452
|
+
console.log('3: save PDF buffer')
|
|
453
|
+
LOGS += '3: save PDF buffer' + '\n' + '=============' + '\n';
|
|
454
|
+
LOGS += fileBuffer + '\n\n\n';
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
fs.writeFileSync(PDF_FILE_PATH, fileBuffer, 'utf8');
|
|
459
|
+
|
|
460
|
+
// 4: save encoded base64
|
|
461
|
+
const base64data = Buffer.from(fileBuffer, 'utf8').toString('base64');
|
|
462
|
+
if (isDebugging) {
|
|
463
|
+
console.log('4: save encoded base64');
|
|
464
|
+
LOGS += '4: save encoded base64' + '\n' + '=============' + '\n';
|
|
465
|
+
LOGS += base64data + '\n\n\n';
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
// 5: save logs to file
|
|
470
|
+
fs.writeFileSync(LOGS_FILE_PATH, LOGS, 'utf8');
|
|
471
|
+
if (isDebugging) {
|
|
472
|
+
console.log('5: save logs to file');
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (isDownloadable) {
|
|
476
|
+
return fileBuffer;
|
|
477
|
+
} else {
|
|
478
|
+
return base64data;
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
}
|