emailengine-app 2.68.1 → 2.70.0

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.
Files changed (95) hide show
  1. package/.github/workflows/deploy.yml +8 -3
  2. package/.github/workflows/release.yaml +6 -0
  3. package/CHANGELOG.md +59 -0
  4. package/Gruntfile.js +3 -1
  5. package/config/default.toml +2 -0
  6. package/data/google-crawlers.json +7 -1
  7. package/getswagger.sh +40 -4
  8. package/gettext-extract.js +163 -0
  9. package/lib/account.js +135 -72
  10. package/lib/api-routes/account-routes.js +684 -106
  11. package/lib/api-routes/blocklist-routes.js +344 -0
  12. package/lib/api-routes/chat-routes.js +32 -14
  13. package/lib/api-routes/delivery-test-routes.js +346 -0
  14. package/lib/api-routes/export-routes.js +28 -14
  15. package/lib/api-routes/gateway-routes.js +427 -0
  16. package/lib/api-routes/license-routes.js +156 -0
  17. package/lib/api-routes/mailbox-routes.js +344 -0
  18. package/lib/api-routes/message-routes.js +221 -187
  19. package/lib/api-routes/oauth2-app-routes.js +697 -0
  20. package/lib/api-routes/outbox-routes.js +185 -0
  21. package/lib/api-routes/pubsub-routes.js +102 -0
  22. package/lib/api-routes/route-helpers.js +58 -0
  23. package/lib/api-routes/settings-routes.js +357 -0
  24. package/lib/api-routes/stats-routes.js +111 -0
  25. package/lib/api-routes/submit-routes.js +461 -0
  26. package/lib/api-routes/template-routes.js +60 -75
  27. package/lib/api-routes/token-routes.js +297 -0
  28. package/lib/api-routes/webhook-route-routes.js +181 -0
  29. package/lib/autodetect-imap-settings.js +0 -2
  30. package/lib/consts.js +5 -0
  31. package/lib/email-client/base-client.js +28 -6
  32. package/lib/email-client/gmail-client.js +133 -112
  33. package/lib/email-client/imap/mailbox.js +34 -11
  34. package/lib/email-client/imap/subconnection.js +20 -13
  35. package/lib/email-client/imap/sync-operations.js +131 -3
  36. package/lib/email-client/imap-client.js +152 -75
  37. package/lib/email-client/notification-handler.js +1 -4
  38. package/lib/email-client/outlook-client.js +134 -75
  39. package/lib/export.js +97 -20
  40. package/lib/feature-flags.js +2 -2
  41. package/lib/gateway.js +4 -9
  42. package/lib/get-raw-email.js +5 -5
  43. package/lib/imapproxy/imap-core/lib/commands/starttls.js +18 -0
  44. package/lib/imapproxy/imap-core/lib/imap-command.js +6 -1
  45. package/lib/imapproxy/imap-core/lib/imap-connection.js +106 -24
  46. package/lib/imapproxy/imap-core/lib/imap-server.js +24 -0
  47. package/lib/imapproxy/imap-core/lib/imap-stream.js +26 -0
  48. package/lib/logger.js +24 -21
  49. package/lib/message-port-stream.js +113 -16
  50. package/lib/metrics-collector.js +0 -2
  51. package/lib/oauth2-apps.js +13 -4
  52. package/lib/outbox.js +24 -40
  53. package/lib/redis-operations.js +1 -1
  54. package/lib/reject-worker-calls.js +42 -0
  55. package/lib/routes-ui.js +37 -8778
  56. package/lib/schemas.js +429 -84
  57. package/lib/sentry.js +139 -0
  58. package/lib/settings.js +9 -3
  59. package/lib/stream-encrypt.js +1 -1
  60. package/lib/templates.js +1 -1
  61. package/lib/tokens.js +5 -3
  62. package/lib/tools.js +70 -4
  63. package/lib/ui-routes/account-routes.js +45 -212
  64. package/lib/ui-routes/admin-config-routes.js +928 -489
  65. package/lib/ui-routes/admin-entities-routes.js +1 -0
  66. package/lib/ui-routes/auth-routes.js +1339 -0
  67. package/lib/ui-routes/dashboard-routes.js +188 -0
  68. package/lib/ui-routes/document-store-routes.js +800 -0
  69. package/lib/ui-routes/export-routes.js +217 -0
  70. package/lib/ui-routes/internals-routes.js +354 -0
  71. package/lib/ui-routes/network-config-routes.js +759 -0
  72. package/lib/ui-routes/{oauth-routes.js → oauth-config-routes.js} +369 -91
  73. package/lib/ui-routes/route-helpers.js +314 -0
  74. package/lib/ui-routes/smtp-test-routes.js +236 -0
  75. package/lib/ui-routes/unsubscribe-routes.js +232 -0
  76. package/lib/webhook-request.js +36 -0
  77. package/lib/webhooks.js +8 -4
  78. package/package.json +13 -12
  79. package/sbom.json +1 -1
  80. package/server.js +222 -39
  81. package/static/licenses.html +160 -300
  82. package/translations/messages.pot +112 -132
  83. package/update-info.sh +19 -1
  84. package/views/config/logging.hbs +48 -0
  85. package/views/dashboard.hbs +7 -26
  86. package/views/internals/index.hbs +15 -0
  87. package/views/tokens/index.hbs +9 -0
  88. package/workers/api.js +200 -4424
  89. package/workers/documents.js +2 -22
  90. package/workers/export.js +103 -104
  91. package/workers/imap-proxy.js +3 -23
  92. package/workers/imap.js +32 -36
  93. package/workers/smtp.js +2 -22
  94. package/workers/submit.js +26 -35
  95. package/workers/webhooks.js +9 -43
@@ -0,0 +1,344 @@
1
+ 'use strict';
2
+
3
+ const Boom = require('@hapi/boom');
4
+ const Joi = require('joi');
5
+ const { redis } = require('../db');
6
+ const { Account } = require('../account');
7
+ const getSecret = require('../get-secret');
8
+ const { failAction } = require('../tools');
9
+ const { handleError } = require('./route-helpers');
10
+ const { accountIdSchema, mailboxesSchema, errorResponses } = require('../schemas');
11
+
12
+ async function init(args) {
13
+ const { server, call, CORS_CONFIG, FLAG_SORT_ORDER } = args;
14
+
15
+ server.route({
16
+ method: 'GET',
17
+ path: '/v1/account/{account}/mailboxes',
18
+
19
+ async handler(request) {
20
+ let accountObject = new Account({
21
+ redis,
22
+ account: request.params.account,
23
+ call,
24
+ secret: await getSecret(),
25
+ timeout: request.headers['x-ee-timeout']
26
+ });
27
+
28
+ try {
29
+ let mailboxes = await accountObject.getMailboxListing(request.query);
30
+
31
+ if (mailboxes && Array.isArray(mailboxes)) {
32
+ mailboxes = mailboxes.sort((a, b) => {
33
+ if (a.specialUse && !b.specialUse) {
34
+ return -1;
35
+ }
36
+ if (!a.specialUse && b.specialUse) {
37
+ return 1;
38
+ }
39
+ if (a.specialUse && b.specialUse) {
40
+ return FLAG_SORT_ORDER.indexOf(a.specialUse) - FLAG_SORT_ORDER.indexOf(b.specialUse);
41
+ }
42
+
43
+ return a.path.localeCompare(b.path);
44
+ });
45
+ }
46
+
47
+ return { mailboxes };
48
+ } catch (err) {
49
+ handleError(request, err);
50
+ }
51
+ },
52
+
53
+ options: {
54
+ description: 'List mailboxes',
55
+ notes: 'Lists all available mailboxes',
56
+ tags: ['api', 'Mailbox'],
57
+
58
+ plugins: {
59
+ 'hapi-swagger': {
60
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
61
+ }
62
+ },
63
+
64
+ auth: {
65
+ strategy: 'api-token',
66
+ mode: 'required'
67
+ },
68
+ cors: CORS_CONFIG,
69
+
70
+ validate: {
71
+ options: {
72
+ stripUnknown: false,
73
+ abortEarly: false,
74
+ convert: true
75
+ },
76
+ failAction,
77
+
78
+ params: Joi.object({
79
+ account: accountIdSchema.required()
80
+ }),
81
+
82
+ query: Joi.object({
83
+ counters: Joi.boolean()
84
+ .truthy('Y', 'true', '1')
85
+ .falsy('N', 'false', 0)
86
+ .default(false)
87
+ .description('If true, then includes message counters in the response')
88
+ .label('MailboxCounters')
89
+ }).label('MailboxListQuery')
90
+ },
91
+
92
+ response: {
93
+ schema: Joi.object({
94
+ mailboxes: mailboxesSchema
95
+ }).label('MailboxesFilterResponse'),
96
+ failAction: 'log'
97
+ }
98
+ }
99
+ });
100
+
101
+ server.route({
102
+ method: 'POST',
103
+ path: '/v1/account/{account}/mailbox',
104
+
105
+ async handler(request) {
106
+ let accountObject = new Account({
107
+ redis,
108
+ account: request.params.account,
109
+ call,
110
+ secret: await getSecret(),
111
+ timeout: request.headers['x-ee-timeout']
112
+ });
113
+
114
+ try {
115
+ return await accountObject.createMailbox(request.payload.path);
116
+ } catch (err) {
117
+ request.logger.error({ msg: 'API request failed', err });
118
+ if (Boom.isBoom(err)) {
119
+ throw err;
120
+ }
121
+ let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
122
+ if (err.code) {
123
+ error.output.payload.code = err.code;
124
+ }
125
+ if (err.info) {
126
+ error.output.payload.details = err.info;
127
+ }
128
+ throw error;
129
+ }
130
+ },
131
+
132
+ options: {
133
+ description: 'Create mailbox',
134
+ notes: 'Create new mailbox folder',
135
+ tags: ['api', 'Mailbox'],
136
+
137
+ plugins: {
138
+ 'hapi-swagger': {
139
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
140
+ }
141
+ },
142
+
143
+ auth: {
144
+ strategy: 'api-token',
145
+ mode: 'required'
146
+ },
147
+ cors: CORS_CONFIG,
148
+
149
+ validate: {
150
+ options: {
151
+ stripUnknown: false,
152
+ abortEarly: false,
153
+ convert: true
154
+ },
155
+ failAction,
156
+
157
+ params: Joi.object({
158
+ account: accountIdSchema.required()
159
+ }),
160
+
161
+ payload: Joi.object({
162
+ path: Joi.array()
163
+ .items(Joi.string().max(256))
164
+ .single()
165
+ .example(['Parent folder', 'Subfolder'])
166
+ .description('Mailbox path as an array or a string. If account is namespaced then namespace prefix is added by default.')
167
+ .label('CreateMailboxPathInput')
168
+ }).label('CreateMailbox')
169
+ },
170
+
171
+ response: {
172
+ schema: Joi.object({
173
+ path: Joi.string().required().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox').label('CreatedMailboxPath'),
174
+ mailboxId: Joi.string().example('1439876283476').description('Mailbox ID (if server has support)').label('MailboxId'),
175
+ created: Joi.boolean().example(true).description('Was the mailbox created')
176
+ }).label('CreateMailboxResponse'),
177
+ failAction: 'log'
178
+ }
179
+ }
180
+ });
181
+
182
+ server.route({
183
+ method: 'PUT',
184
+ path: '/v1/account/{account}/mailbox',
185
+
186
+ async handler(request) {
187
+ let accountObject = new Account({
188
+ redis,
189
+ account: request.params.account,
190
+ call,
191
+ secret: await getSecret(),
192
+ timeout: request.headers['x-ee-timeout']
193
+ });
194
+
195
+ try {
196
+ return await accountObject.modifyMailbox(request.payload.path, request.payload.newPath, request.payload.subscribed);
197
+ } catch (err) {
198
+ request.logger.error({ msg: 'API request failed', err });
199
+ if (Boom.isBoom(err)) {
200
+ throw err;
201
+ }
202
+ let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
203
+ if (err.code) {
204
+ error.output.payload.code = err.code;
205
+ }
206
+ if (err.info) {
207
+ error.output.payload.details = err.info;
208
+ }
209
+ throw error;
210
+ }
211
+ },
212
+
213
+ options: {
214
+ description: 'Modify mailbox',
215
+ notes: 'Modify an existing mailbox folder (rename or change subscription status)',
216
+ tags: ['api', 'Mailbox'],
217
+
218
+ plugins: {
219
+ 'hapi-swagger': {
220
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
221
+ }
222
+ },
223
+
224
+ auth: {
225
+ strategy: 'api-token',
226
+ mode: 'required'
227
+ },
228
+ cors: CORS_CONFIG,
229
+
230
+ validate: {
231
+ options: {
232
+ stripUnknown: false,
233
+ abortEarly: false,
234
+ convert: true
235
+ },
236
+ failAction,
237
+
238
+ params: Joi.object({
239
+ account: accountIdSchema.required()
240
+ }),
241
+
242
+ payload: Joi.object({
243
+ path: Joi.string().required().example('Folder Name').description('Mailbox folder path to modify').label('ExistingMailboxPath'),
244
+ newPath: Joi.array()
245
+ .items(Joi.string().max(256))
246
+ .single()
247
+ .example(['Parent folder', 'Subfolder'])
248
+ .description('New mailbox path as an array or a string. If account is namespaced then namespace prefix is added by default. Optional.')
249
+ .label('TargetMailboxPath'),
250
+ subscribed: Joi.boolean()
251
+ .example(true)
252
+ .description('Change mailbox subscription status. Only applies to IMAP accounts, ignored for Gmail and Outlook.')
253
+ .label('SubscriptionStatus')
254
+ })
255
+ .or('newPath', 'subscribed')
256
+ .label('ModifyMailbox')
257
+ },
258
+
259
+ response: {
260
+ schema: Joi.object({
261
+ path: Joi.string().example('Mail').description('Mailbox folder path').label('ExistingMailboxPath'),
262
+ newPath: Joi.string().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox if renamed').label('NewMailboxPath'),
263
+ mailboxId: Joi.string()
264
+ .example('1439876283476')
265
+ .description('Mailbox ID after the rename (Gmail API and MS Graph API accounts only)')
266
+ .label('ModifyMailboxId'),
267
+ renamed: Joi.boolean().example(true).description('Was the mailbox renamed'),
268
+ subscribed: Joi.boolean().example(true).description('Subscription status after modification')
269
+ }).label('ModifyMailboxResponse'),
270
+ failAction: 'log'
271
+ }
272
+ }
273
+ });
274
+
275
+ server.route({
276
+ method: 'DELETE',
277
+ path: '/v1/account/{account}/mailbox',
278
+
279
+ async handler(request) {
280
+ let accountObject = new Account({
281
+ redis,
282
+ account: request.params.account,
283
+ call,
284
+ secret: await getSecret(),
285
+ timeout: request.headers['x-ee-timeout']
286
+ });
287
+
288
+ try {
289
+ return await accountObject.deleteMailbox(request.query.path);
290
+ } catch (err) {
291
+ handleError(request, err);
292
+ }
293
+ },
294
+
295
+ options: {
296
+ description: 'Delete mailbox',
297
+ notes: 'Delete existing mailbox folder',
298
+ tags: ['api', 'Mailbox'],
299
+
300
+ plugins: {
301
+ 'hapi-swagger': {
302
+ responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
303
+ }
304
+ },
305
+
306
+ auth: {
307
+ strategy: 'api-token',
308
+ mode: 'required'
309
+ },
310
+ cors: CORS_CONFIG,
311
+
312
+ validate: {
313
+ options: {
314
+ stripUnknown: false,
315
+ abortEarly: false,
316
+ convert: true
317
+ },
318
+ failAction,
319
+
320
+ params: Joi.object({
321
+ account: accountIdSchema.required()
322
+ }),
323
+
324
+ query: Joi.object({
325
+ path: Joi.string().required().example('My Outdated Mail').description('Mailbox folder path to delete').label('MailboxPath')
326
+ }).label('DeleteMailbox')
327
+ },
328
+
329
+ response: {
330
+ schema: Joi.object({
331
+ path: Joi.string().required().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox').label('MailboxPath'),
332
+ mailboxId: Joi.string()
333
+ .example('1439876283476')
334
+ .description('ID of the deleted mailbox (Gmail API and MS Graph API accounts only)')
335
+ .label('DeleteMailboxId'),
336
+ deleted: Joi.boolean().example(true).description('Was the mailbox deleted')
337
+ }).label('DeleteMailboxResponse'),
338
+ failAction: 'log'
339
+ }
340
+ }
341
+ });
342
+ }
343
+
344
+ module.exports = init;