@hyperbytes/wappler-imap-manager 1.0.4 → 1.0.6
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 +7 -3
- package/server_connect/modules/imapgetattachments.hjson +11 -11
- package/server_connect/modules/imapgetattachments.js +32 -7
- package/server_connect/modules/imapmailcontent.js +1 -0
- package/server_connect/modules/imapsaveasdraft-copy.js +184 -0
- package/server_connect/modules/imapsendmail.hjson +5 -3
- package/server_connect/modules/imapsendmail.js +12 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperbytes/wappler-imap-manager",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "IMAP eMail Management for Wappler",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -10,13 +10,17 @@
|
|
|
10
10
|
"wappler-extension",
|
|
11
11
|
"server-connect",
|
|
12
12
|
"IMAP",
|
|
13
|
-
"email"
|
|
13
|
+
"email",
|
|
14
|
+
"wappler",
|
|
15
|
+
"node"
|
|
14
16
|
],
|
|
15
17
|
"dependencies": {
|
|
16
18
|
"imap": "^0.8.0",
|
|
17
19
|
"mailparser": "^3.7.2",
|
|
18
20
|
"imap-simple": "^5.1.0",
|
|
19
21
|
"mailtrap": "^4.1.0",
|
|
20
|
-
"mailcomposer": "^1.0.0"
|
|
22
|
+
"mailcomposer": "^1.0.0",
|
|
23
|
+
"mime-types": "^3.0.1",
|
|
24
|
+
"mime-db": "^1.54.0"
|
|
21
25
|
}
|
|
22
26
|
}
|
|
@@ -10,23 +10,23 @@ groupIcon : 'fas fa-envelope comp-general',
|
|
|
10
10
|
usedModules : {
|
|
11
11
|
node: {
|
|
12
12
|
'imap' : '^0.8.19',
|
|
13
|
-
'mailparser' : '^3.7.2'
|
|
13
|
+
'mailparser' : '^3.7.2',
|
|
14
|
+
'mime-lookup' : '^0.0.2',
|
|
15
|
+
'mime-db' : '^1.54.0'
|
|
14
16
|
}
|
|
15
17
|
},
|
|
16
18
|
dataScheme:[
|
|
17
|
-
|
|
18
|
-
{name: '$value', type:'text'}
|
|
19
|
-
]},
|
|
20
|
-
{name: 'message', type:'text'},
|
|
19
|
+
{name: 'status', type:'number'},
|
|
21
20
|
{name: 'url', type:'text'},
|
|
22
21
|
{name: 'path', type:'text'},
|
|
23
|
-
{name: '
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
{name: 'uploads', type:'file', sub:[
|
|
23
|
+
{name: 'name', type:'text'},
|
|
24
|
+
{name: 'path', type:'text'},
|
|
25
|
+
{name: 'url', type:'text'},
|
|
26
|
+
{name: 'type', type:'text'},
|
|
27
|
+
{name: 'size', type:'number'}
|
|
26
28
|
]},
|
|
27
|
-
|
|
28
|
-
{name: '$value', type:'text'}
|
|
29
|
-
]}
|
|
29
|
+
|
|
30
30
|
],
|
|
31
31
|
|
|
32
32
|
|
|
@@ -3,6 +3,9 @@ const fs = require('fs');
|
|
|
3
3
|
const { simpleParser } = require('mailparser');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { toSystemPath } = require("../../../lib/core/path");
|
|
6
|
+
const mime = require('mime-types');
|
|
7
|
+
const mimeDb = require('mime-db');
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
exports.imapgetattachments = async function (options, name) {
|
|
8
11
|
const IMAP_USER = process.env.IMAP_USER;
|
|
@@ -15,8 +18,8 @@ exports.imapgetattachments = async function (options, name) {
|
|
|
15
18
|
const mailbox = this.parseRequired(options.mailbox, '*', "No mailbox specified");
|
|
16
19
|
let sentPath = this.parseRequired(options.path, '*', 'No path Specified');
|
|
17
20
|
const returnPath = sentPath.split('/').slice(2).join('/');
|
|
18
|
-
savePath = toSystemPath(sentPath);
|
|
19
|
-
|
|
21
|
+
//savePath = toSystemPath(sentPath);
|
|
22
|
+
savePath = sentPath;
|
|
20
23
|
// Ensure the directory exists or create it
|
|
21
24
|
if (!fs.existsSync(savePath)) {
|
|
22
25
|
fs.mkdirSync(savePath, { recursive: true });
|
|
@@ -68,17 +71,39 @@ exports.imapgetattachments = async function (options, name) {
|
|
|
68
71
|
console.log(`Saved attachment: ${filePath}`);
|
|
69
72
|
});
|
|
70
73
|
const savedFilesExt = [];
|
|
71
|
-
|
|
74
|
+
|
|
72
75
|
parsed.attachments.forEach(att => {
|
|
73
76
|
const filePath = path.join(savePath, att.filename);
|
|
77
|
+
// Replace backslashes with forward slashes
|
|
78
|
+
let correctedPath = filePath.replace(/\\/g, "/");
|
|
79
|
+
// Remove the "public" prefix
|
|
80
|
+
correctedPathURL = correctedPath.replace(/^\/?public\//, "/");
|
|
81
|
+
|
|
82
|
+
|
|
74
83
|
fs.writeFileSync(filePath, att.content);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
let mimetype = mime.lookup(att.filename)
|
|
85
|
+
const fs2 = require('fs');
|
|
86
|
+
|
|
87
|
+
//const filePath = 'example.txt';
|
|
88
|
+
const stats = fs2.statSync(filePath);
|
|
89
|
+
console.log(`File size: ${stats.size} bytes`);
|
|
90
|
+
savedFilesExt.push({
|
|
91
|
+
name: att.filename,
|
|
92
|
+
path: correctedPath,
|
|
93
|
+
url: correctedPathURL,
|
|
94
|
+
type: mimetype,
|
|
95
|
+
size: stats.size
|
|
96
|
+
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
console.log(`Saved attachment Extended: ${filePath}`);
|
|
78
100
|
});
|
|
79
101
|
|
|
102
|
+
// Output JSON
|
|
103
|
+
console.log(JSON.stringify(savedFilesExt, null, 2));
|
|
104
|
+
|
|
80
105
|
imap.end();
|
|
81
|
-
resolve({
|
|
106
|
+
resolve({ status: 200, url: returnPath, path: sentPath, uploads: savedFilesExt });
|
|
82
107
|
|
|
83
108
|
} catch (parseError) {
|
|
84
109
|
imap.end();
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
const Imap = require('imap-simple');
|
|
2
|
+
const nodemailer = require('nodemailer');
|
|
3
|
+
const mailcomposer = require('mailcomposer');
|
|
4
|
+
const { simpleParser } = require('mailparser'); // Used for debugging email content
|
|
5
|
+
|
|
6
|
+
exports.imapsaveasdraft = async function (options) {
|
|
7
|
+
console.log('Starting IMAP draft save process...');
|
|
8
|
+
|
|
9
|
+
// Parse IMAP configuration
|
|
10
|
+
const IMAP_HOST = this.parseOptional(options.imap_host, '*', process.env.IMAP_HOST);
|
|
11
|
+
const IMAP_PASSWORD = this.parseOptional(options.imap_password, '*', process.env.IMAP_PASSWORD);
|
|
12
|
+
const IMAP_USER = this.parseOptional(options.imap_user, '*', process.env.IMAP_USER);
|
|
13
|
+
const IMAP_PORT = this.parseOptional(options.imap_port, '*', process.env.IMAP_PORT);
|
|
14
|
+
const imap_tlsstring = this.parseOptional(options.imap_tls, '*', process.env.IMAP_TLS).toLowerCase();
|
|
15
|
+
const IMAP_TLS = (imap_tlsstring === 'true');
|
|
16
|
+
|
|
17
|
+
// Validate essential IMAP configuration
|
|
18
|
+
if (!IMAP_HOST || !IMAP_USER || !IMAP_PASSWORD) {
|
|
19
|
+
console.error('IMAP host, user, or password not configured.');
|
|
20
|
+
return {
|
|
21
|
+
status: 401,
|
|
22
|
+
message: 'IMAP configuration incomplete.',
|
|
23
|
+
error: 'IMAP host, user, or password not configured.',
|
|
24
|
+
uid: '',
|
|
25
|
+
folder: this.parseOptional(options.mailbox, '*', 'Drafts')
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Parse email details
|
|
30
|
+
const to = this.parseOptional(options.to, "*", "");
|
|
31
|
+
console.log('To: ' + to);
|
|
32
|
+
const mailfrom = this.parseOptional(options.from, "*", "");
|
|
33
|
+
console.log('From: ' + mailfrom);
|
|
34
|
+
const cc = this.parseOptional(options.cc, "*", "");
|
|
35
|
+
console.log('CC: ' + cc);
|
|
36
|
+
const bcc = this.parseOptional(options.bcc, "*", "");
|
|
37
|
+
console.log('BCC: ' + bcc);
|
|
38
|
+
const subject = this.parseOptional(options.mailsubject, "*", "default subject");
|
|
39
|
+
console.log('Subject: ' + subject);
|
|
40
|
+
const body = this.parseOptional(options.body, "*", ""); // Assuming this is HTML body
|
|
41
|
+
console.log('Body: ' + body);
|
|
42
|
+
const attachments = this.parseOptional(options.attachments, '*', []);
|
|
43
|
+
console.log('Attachments: ' + attachments);
|
|
44
|
+
let folder = this.parseOptional(options.mailbox, '*', 'Drafts'); // Default to 'Drafts'
|
|
45
|
+
console.log('Folder: ' + folder);
|
|
46
|
+
|
|
47
|
+
// Check if a UID is provided – meaning the draft is to be updated
|
|
48
|
+
const providedUid = this.parseOptional(options.uid, "*", "");
|
|
49
|
+
console.log('UID: ' + providedUid);
|
|
50
|
+
console.log(`Attempting to save draft to folder: ${folder}`);
|
|
51
|
+
|
|
52
|
+
const imapConfig = {
|
|
53
|
+
imap: {
|
|
54
|
+
user: IMAP_USER,
|
|
55
|
+
password: IMAP_PASSWORD,
|
|
56
|
+
host: IMAP_HOST,
|
|
57
|
+
port: IMAP_PORT,
|
|
58
|
+
tls: IMAP_TLS,
|
|
59
|
+
tlsOptions: { rejectUnauthorized: false },
|
|
60
|
+
authTimeout: 5000
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
let connection;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
// 1. Construct the email message using mailcomposer
|
|
68
|
+
const mailOptions = {
|
|
69
|
+
from: mailfrom,
|
|
70
|
+
to: to,
|
|
71
|
+
cc: cc,
|
|
72
|
+
bcc: bcc,
|
|
73
|
+
subject: subject,
|
|
74
|
+
html: body, // Use 'text' if it's plain text
|
|
75
|
+
attachments: attachments
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const mail = mailcomposer(mailOptions);
|
|
79
|
+
|
|
80
|
+
const rawEmail = await new Promise((resolve, reject) => {
|
|
81
|
+
mail.build((err, message) => {
|
|
82
|
+
if (err) {
|
|
83
|
+
console.error('Error building email:', err);
|
|
84
|
+
return {
|
|
85
|
+
status: 400,
|
|
86
|
+
message: 'Error building email.',
|
|
87
|
+
uid: '',
|
|
88
|
+
folder: folder
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
resolve(message);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
console.log('Raw email constructed.');
|
|
96
|
+
|
|
97
|
+
// Debug: Parse the raw email to verify its content
|
|
98
|
+
const parsedEmail = await simpleParser(rawEmail);
|
|
99
|
+
console.log('Parsed Email Content:', {
|
|
100
|
+
subject: parsedEmail.subject,
|
|
101
|
+
text: parsedEmail.text,
|
|
102
|
+
html: parsedEmail.html,
|
|
103
|
+
headers: parsedEmail.headers
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// 2. Connect to IMAP server
|
|
107
|
+
console.log(`Connecting to IMAP server ${IMAP_HOST}:${IMAP_PORT}...`);
|
|
108
|
+
connection = await Imap.connect(imapConfig);
|
|
109
|
+
console.log('Successfully connected to IMAP server.');
|
|
110
|
+
|
|
111
|
+
// 3. Open the target mailbox
|
|
112
|
+
await connection.openBox(folder);
|
|
113
|
+
console.log(`Mailbox "${folder}" opened.`);
|
|
114
|
+
// Retrieve UID validity of the destination mailbox (if available)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// 4. If a UID is provided, update the existing draft; otherwise, append a new one.
|
|
118
|
+
if (providedUid) {
|
|
119
|
+
console.log(`UID provided (${providedUid}). Updating existing draft...`);
|
|
120
|
+
// Attempt to delete the existing draft for that UID
|
|
121
|
+
try {
|
|
122
|
+
await connection.deleteMessage(providedUid);
|
|
123
|
+
console.log(`Existing draft with UID ${providedUid} deleted.`);
|
|
124
|
+
} catch (delErr) {
|
|
125
|
+
console.warn(`Could not delete existing draft with UID ${providedUid}: ${delErr}`);
|
|
126
|
+
// You might decide to abort here if deletion is mandatory
|
|
127
|
+
}
|
|
128
|
+
// Append the new draft (note: the server will normally assign a new UID,
|
|
129
|
+
// but we are returning the provided UID per the requirement).
|
|
130
|
+
console.log(`Appending updated draft to folder: ${folder}...`);
|
|
131
|
+
const updateAppendResult = await connection.append(rawEmail, {
|
|
132
|
+
mailbox: folder,
|
|
133
|
+
flags: ['\\Draft']
|
|
134
|
+
});
|
|
135
|
+
console.log('Updated draft appended.', updateAppendResult);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
status: 200,
|
|
139
|
+
message: 'Draft updated successfully.',
|
|
140
|
+
uid: providedUid,
|
|
141
|
+
folder: folder
|
|
142
|
+
};
|
|
143
|
+
} else {
|
|
144
|
+
console.log(`Appending email as a draft to folder: ${folder}...`);
|
|
145
|
+
const appendResult = await connection.append(rawEmail, {
|
|
146
|
+
mailbox: folder,
|
|
147
|
+
flags: ['\\Draft']
|
|
148
|
+
});
|
|
149
|
+
console.log('Email appended to drafts folder.', appendResult);
|
|
150
|
+
|
|
151
|
+
// Retrieve UID of the saved message
|
|
152
|
+
const searchResult = await connection.search(['ALL'], { bodies: ['HEADER.FIELDS (MESSAGE-ID)'] });
|
|
153
|
+
const uid = searchResult.length > 0 ? searchResult[searchResult.length - 1].attributes.uid : '';
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
status: 200,
|
|
157
|
+
message: 'Draft saved successfully.',
|
|
158
|
+
uid: uid,
|
|
159
|
+
folder: folder
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.error('IMAP draft save failed:', err);
|
|
165
|
+
return {
|
|
166
|
+
status: 400,
|
|
167
|
+
message: 'Failed to save draft.',
|
|
168
|
+
error: err.toString(),
|
|
169
|
+
uid: '',
|
|
170
|
+
folder: folder
|
|
171
|
+
};
|
|
172
|
+
} finally {
|
|
173
|
+
if (connection) {
|
|
174
|
+
try {
|
|
175
|
+
await connection.end();
|
|
176
|
+
console.log('IMAP connection closed.');
|
|
177
|
+
} catch (endErr) {
|
|
178
|
+
console.error('Error ending IMAP connection:', endErr);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
node: {
|
|
12
12
|
'imap' : '^0.8.19',
|
|
13
13
|
'mailparser' : '^3.7.2',
|
|
14
|
-
'nodemailer' : '^6.10.1'
|
|
14
|
+
'nodemailer' : '^6.10.1',
|
|
15
|
+
'mime-lookup' : '^0.0.2',
|
|
16
|
+
'mime-db' : '^1.54.0'
|
|
15
17
|
}
|
|
16
18
|
},
|
|
17
19
|
dataScheme: [
|
|
@@ -164,11 +166,11 @@
|
|
|
164
166
|
},
|
|
165
167
|
{ name: 'attachments',
|
|
166
168
|
optionName: 'attachments',
|
|
167
|
-
title: '
|
|
169
|
+
title: 'eMail attachments sent from client',
|
|
168
170
|
type: 'text',
|
|
169
171
|
defaultValue: "",
|
|
170
172
|
serverDataBindings: true,
|
|
171
|
-
help: '
|
|
173
|
+
help: 'eMail attachments sent from client via upload action'
|
|
172
174
|
},
|
|
173
175
|
{ name: 'save_to_send',
|
|
174
176
|
optionName: 'save_to_send',
|
|
@@ -6,7 +6,13 @@ const path = require("path");
|
|
|
6
6
|
const { toSystemPath } = require("../../../lib/core/path");
|
|
7
7
|
|
|
8
8
|
exports.imapsendmail = async function (options) {
|
|
9
|
-
const upload = this.parseOptional(options.attachments, '*',
|
|
9
|
+
const upload = this.parseOptional(options.attachments, '*', []);
|
|
10
|
+
console.log("Attachments: " + JSON.stringify(upload))
|
|
11
|
+
const upload2 = this.parseOptional(options.server_attachments, '*', []);
|
|
12
|
+
console.log("Server_Attachments: " + JSON.stringify(upload2))
|
|
13
|
+
const combinedJson = { "attachments": [upload, upload2] };
|
|
14
|
+
console.log('CombinedJson: ' + JSON.stringify(combinedJson))
|
|
15
|
+
|
|
10
16
|
|
|
11
17
|
// Ensure attachments are properly formatted and converted to system paths
|
|
12
18
|
const attachmentList = Array.isArray(upload)
|
|
@@ -58,8 +64,8 @@ exports.imapsendmail = async function (options) {
|
|
|
58
64
|
user: smtpConfig.username,
|
|
59
65
|
pass: smtpConfig.password,
|
|
60
66
|
},
|
|
61
|
-
logger:
|
|
62
|
-
debug:
|
|
67
|
+
logger: false, // Enable logging
|
|
68
|
+
debug: false, // Enable debugging
|
|
63
69
|
|
|
64
70
|
});
|
|
65
71
|
|
|
@@ -77,7 +83,9 @@ exports.imapsendmail = async function (options) {
|
|
|
77
83
|
subject: subject,
|
|
78
84
|
text: body,
|
|
79
85
|
html: body,
|
|
80
|
-
attachments: formattedAttachments
|
|
86
|
+
attachments: formattedAttachments
|
|
87
|
+
|
|
88
|
+
|
|
81
89
|
};
|
|
82
90
|
|
|
83
91
|
console.log("Final Attachments List:", formattedAttachments);
|