emailengine-app 2.61.1 → 2.61.2
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/CHANGELOG.md +9 -0
- package/data/google-crawlers.json +1 -1
- package/lib/account/account-state.js +248 -0
- package/lib/account.js +17 -178
- package/lib/api-routes/account-routes.js +1006 -0
- package/lib/api-routes/message-routes.js +1377 -0
- package/lib/consts.js +12 -2
- package/lib/email-client/base-client.js +282 -771
- package/lib/email-client/gmail/gmail-api.js +243 -0
- package/lib/email-client/gmail-client.js +145 -53
- package/lib/email-client/imap/mailbox.js +24 -698
- package/lib/email-client/imap/sync-operations.js +812 -0
- package/lib/email-client/imap-client.js +1 -1
- package/lib/email-client/message-builder.js +566 -0
- package/lib/email-client/notification-handler.js +314 -0
- package/lib/email-client/outlook/graph-api.js +326 -0
- package/lib/email-client/outlook-client.js +159 -113
- package/lib/email-client/smtp-pool-manager.js +196 -0
- package/lib/imapproxy/imap-server.js +3 -12
- package/lib/oauth/gmail.js +4 -4
- package/lib/oauth/mail-ru.js +30 -5
- package/lib/oauth/outlook.js +57 -3
- package/lib/oauth/pubsub/google.js +30 -11
- package/lib/oauth/scope-checker.js +202 -0
- package/lib/oauth2-apps.js +8 -4
- package/lib/redis-operations.js +484 -0
- package/lib/routes-ui.js +283 -2582
- package/lib/tools.js +4 -196
- package/lib/ui-routes/account-routes.js +1931 -0
- package/lib/ui-routes/admin-config-routes.js +1233 -0
- package/lib/ui-routes/admin-entities-routes.js +2367 -0
- package/lib/ui-routes/oauth-routes.js +992 -0
- package/lib/utils/network.js +237 -0
- package/package.json +9 -9
- package/sbom.json +1 -1
- package/static/js/app.js +5 -5
- package/static/licenses.html +78 -18
- package/translations/de.mo +0 -0
- package/translations/de.po +85 -82
- package/translations/en.mo +0 -0
- package/translations/en.po +63 -71
- package/translations/et.mo +0 -0
- package/translations/et.po +84 -82
- package/translations/fr.mo +0 -0
- package/translations/fr.po +85 -82
- package/translations/ja.mo +0 -0
- package/translations/ja.po +84 -82
- package/translations/messages.pot +74 -87
- package/translations/nl.mo +0 -0
- package/translations/nl.po +86 -82
- package/translations/pl.mo +0 -0
- package/translations/pl.po +84 -82
- package/views/account/security.hbs +4 -4
- package/views/accounts/account.hbs +13 -13
- package/views/accounts/register/imap-server.hbs +12 -12
- package/views/config/document-store/pre-processing/index.hbs +4 -2
- package/views/config/oauth/app.hbs +6 -7
- package/views/config/oauth/index.hbs +2 -2
- package/views/config/service.hbs +3 -4
- package/views/dashboard.hbs +5 -7
- package/views/error.hbs +22 -7
- package/views/gateways/gateway.hbs +2 -2
- package/views/partials/add_account_modal.hbs +7 -10
- package/views/partials/document_store_header.hbs +1 -1
- package/views/partials/editor_scope_info.hbs +0 -1
- package/views/partials/oauth_config_header.hbs +1 -1
- package/views/partials/side_menu.hbs +3 -3
- package/views/partials/webhook_form.hbs +2 -2
- package/views/templates/index.hbs +1 -1
- package/views/templates/template.hbs +8 -8
- package/views/tokens/index.hbs +6 -6
- package/views/tokens/new.hbs +1 -1
- package/views/webhooks/index.hbs +4 -4
- package/views/webhooks/webhook.hbs +7 -7
- package/workers/api.js +148 -2436
- package/workers/smtp.js +2 -1
- package/lib/imapproxy/imap-core/test/client.js +0 -46
- package/lib/imapproxy/imap-core/test/fixtures/append.eml +0 -1196
- package/lib/imapproxy/imap-core/test/fixtures/chunks.js +0 -44
- package/lib/imapproxy/imap-core/test/fixtures/fix1.eml +0 -6
- package/lib/imapproxy/imap-core/test/fixtures/fix2.eml +0 -599
- package/lib/imapproxy/imap-core/test/fixtures/fix3.eml +0 -32
- package/lib/imapproxy/imap-core/test/fixtures/fix4.eml +0 -6
- package/lib/imapproxy/imap-core/test/fixtures/mimetorture.eml +0 -599
- package/lib/imapproxy/imap-core/test/fixtures/mimetorture.js +0 -2740
- package/lib/imapproxy/imap-core/test/fixtures/mimetorture.json +0 -1411
- package/lib/imapproxy/imap-core/test/fixtures/mimetree.js +0 -85
- package/lib/imapproxy/imap-core/test/fixtures/nodemailer.eml +0 -582
- package/lib/imapproxy/imap-core/test/fixtures/ryan_finnie_mime_torture.eml +0 -599
- package/lib/imapproxy/imap-core/test/fixtures/simple.eml +0 -42
- package/lib/imapproxy/imap-core/test/fixtures/simple.json +0 -164
- package/lib/imapproxy/imap-core/test/imap-compile-stream-test.js +0 -671
- package/lib/imapproxy/imap-core/test/imap-compiler-test.js +0 -272
- package/lib/imapproxy/imap-core/test/imap-indexer-test.js +0 -236
- package/lib/imapproxy/imap-core/test/imap-parser-test.js +0 -922
- package/lib/imapproxy/imap-core/test/memory-notifier.js +0 -129
- package/lib/imapproxy/imap-core/test/prepare.sh +0 -74
- package/lib/imapproxy/imap-core/test/protocol-test.js +0 -1756
- package/lib/imapproxy/imap-core/test/search-test.js +0 -1356
- package/lib/imapproxy/imap-core/test/test-client.js +0 -152
- package/lib/imapproxy/imap-core/test/test-server.js +0 -623
- package/lib/imapproxy/imap-core/test/tools-test.js +0 -22
- package/test/api-test.js +0 -899
- package/test/autoreply-test.js +0 -327
- package/test/bounce-test.js +0 -151
- package/test/complaint-test.js +0 -256
- package/test/fixtures/autoreply/LICENSE +0 -27
- package/test/fixtures/autoreply/rfc3834-01.eml +0 -23
- package/test/fixtures/autoreply/rfc3834-02.eml +0 -24
- package/test/fixtures/autoreply/rfc3834-03.eml +0 -26
- package/test/fixtures/autoreply/rfc3834-04.eml +0 -48
- package/test/fixtures/autoreply/rfc3834-05.eml +0 -19
- package/test/fixtures/autoreply/rfc3834-06.eml +0 -59
- package/test/fixtures/bounces/163.eml +0 -2521
- package/test/fixtures/bounces/fastmail.eml +0 -242
- package/test/fixtures/bounces/gmail.eml +0 -252
- package/test/fixtures/bounces/hotmail.eml +0 -655
- package/test/fixtures/bounces/mailru.eml +0 -121
- package/test/fixtures/bounces/outlook.eml +0 -1107
- package/test/fixtures/bounces/postfix.eml +0 -101
- package/test/fixtures/bounces/rambler.eml +0 -116
- package/test/fixtures/bounces/workmail.eml +0 -142
- package/test/fixtures/bounces/yahoo.eml +0 -139
- package/test/fixtures/bounces/zoho.eml +0 -83
- package/test/fixtures/bounces/zonemta.eml +0 -100
- package/test/fixtures/complaints/LICENSE +0 -27
- package/test/fixtures/complaints/amazonses.eml +0 -72
- package/test/fixtures/complaints/dmarc.eml +0 -59
- package/test/fixtures/complaints/hotmail.eml +0 -49
- package/test/fixtures/complaints/optout.eml +0 -40
- package/test/fixtures/complaints/standard-arf.eml +0 -68
- package/test/fixtures/complaints/yahoo.eml +0 -68
- package/test/oauth2-apps-test.js +0 -301
- package/test/sendonly-test.js +0 -160
- package/test/test-config.js +0 -34
- package/test/webhooks-server.js +0 -39
package/workers/api.js
CHANGED
|
@@ -34,15 +34,14 @@ const {
|
|
|
34
34
|
runPrechecks,
|
|
35
35
|
matcher,
|
|
36
36
|
readEnvValue,
|
|
37
|
-
matchIp,
|
|
38
37
|
getSignedFormData,
|
|
39
38
|
threadStats,
|
|
40
|
-
detectAutomatedRequest,
|
|
41
39
|
hasEnvValue,
|
|
42
40
|
getBoolean,
|
|
43
41
|
loadTlsConfig,
|
|
44
42
|
retryAgent
|
|
45
43
|
} = require('../lib/tools');
|
|
44
|
+
const { matchIp, detectAutomatedRequest } = require('../lib/utils/network');
|
|
46
45
|
|
|
47
46
|
const Bugsnag = require('@bugsnag/js');
|
|
48
47
|
if (readEnvValue('BUGSNAG_API_KEY')) {
|
|
@@ -131,33 +130,26 @@ const { fetch: fetchCmd } = require('undici');
|
|
|
131
130
|
const templateRoutes = require('../lib/api-routes/template-routes');
|
|
132
131
|
const chatRoutes = require('../lib/api-routes/chat-routes');
|
|
133
132
|
const bullBoardRoutes = require('../lib/api-routes/bull-board-routes');
|
|
133
|
+
const accountRoutes = require('../lib/api-routes/account-routes');
|
|
134
|
+
const messageRoutes = require('../lib/api-routes/message-routes');
|
|
134
135
|
|
|
135
136
|
const {
|
|
136
137
|
settingsSchema,
|
|
137
138
|
addressSchema,
|
|
138
139
|
settingsQuerySchema,
|
|
139
140
|
imapSchema,
|
|
140
|
-
imapUpdateSchema,
|
|
141
141
|
smtpSchema,
|
|
142
|
-
smtpUpdateSchema,
|
|
143
142
|
oauth2Schema,
|
|
144
|
-
oauth2UpdateSchema,
|
|
145
|
-
messageDetailsSchema,
|
|
146
|
-
messageListSchema,
|
|
147
143
|
mailboxesSchema,
|
|
148
144
|
shortMailboxesSchema,
|
|
149
145
|
licenseSchema,
|
|
150
146
|
lastErrorSchema,
|
|
151
147
|
templateSchemas,
|
|
152
|
-
documentStoreSchema,
|
|
153
|
-
searchSchema,
|
|
154
|
-
messageUpdateSchema,
|
|
155
148
|
accountSchemas,
|
|
156
149
|
oauthCreateSchema,
|
|
157
150
|
tokenRestrictionsSchema,
|
|
158
151
|
accountIdSchema,
|
|
159
152
|
ipSchema,
|
|
160
|
-
accountCountersSchema,
|
|
161
153
|
accountPathSchema,
|
|
162
154
|
defaultAccountTypeSchema,
|
|
163
155
|
fromAddressSchema,
|
|
@@ -171,9 +163,6 @@ const {
|
|
|
171
163
|
headerTimeoutSchema
|
|
172
164
|
} = require('../lib/schemas');
|
|
173
165
|
|
|
174
|
-
const listMessageFolderPathDescription =
|
|
175
|
-
'Mailbox folder path. Can use special use labels like "\\Sent". Special value "\\All" is available for Gmail IMAP, Gmail API, MS Graph API accounts.';
|
|
176
|
-
|
|
177
166
|
const OAuth2ProviderSchema = Joi.string()
|
|
178
167
|
.valid(...Object.keys(OAUTH_PROVIDERS))
|
|
179
168
|
.required()
|
|
@@ -842,7 +831,7 @@ const init = async () => {
|
|
|
842
831
|
'error',
|
|
843
832
|
{
|
|
844
833
|
pageTitle: 'Access Denied',
|
|
845
|
-
message: '
|
|
834
|
+
message: `You don't have permission to view this page`
|
|
846
835
|
},
|
|
847
836
|
{
|
|
848
837
|
layout: 'public'
|
|
@@ -1803,18 +1792,16 @@ Include your token in requests using one of these methods:
|
|
|
1803
1792
|
for (let entry of (request.payload && request.payload.value) || []) {
|
|
1804
1793
|
// enumerate and queue all entries
|
|
1805
1794
|
if (entry.subscriptionId !== outlookSubscription.id || entry.clientState !== outlookSubscription.clientState) {
|
|
1795
|
+
// Security: Log webhook validation failures - could indicate spoofed notifications
|
|
1806
1796
|
request.logger.warn({
|
|
1807
|
-
msg: '
|
|
1797
|
+
msg: 'Webhook validation failed - potential security issue',
|
|
1798
|
+
securityEvent: 'webhook_validation_failure',
|
|
1808
1799
|
account: request.query.account,
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
subscriptionId: entry.subscriptionId,
|
|
1815
|
-
clientState: entry.clientState
|
|
1816
|
-
},
|
|
1817
|
-
entry
|
|
1800
|
+
subscriptionIdMatch: entry.subscriptionId === outlookSubscription.id,
|
|
1801
|
+
clientStateMatch: entry.clientState === outlookSubscription.clientState,
|
|
1802
|
+
receivedSubscriptionId: entry.subscriptionId,
|
|
1803
|
+
changeType: entry.changeType,
|
|
1804
|
+
resource: entry.resource
|
|
1818
1805
|
});
|
|
1819
1806
|
continue;
|
|
1820
1807
|
}
|
|
@@ -1905,18 +1892,15 @@ Include your token in requests using one of these methods:
|
|
|
1905
1892
|
|
|
1906
1893
|
// enumerate and queue all entries
|
|
1907
1894
|
if (entry.subscriptionId !== outlookSubscription.id || entry.clientState !== outlookSubscription.clientState) {
|
|
1908
|
-
|
|
1909
|
-
|
|
1895
|
+
// Security: Log lifecycle webhook validation failures - could indicate spoofed notifications
|
|
1896
|
+
request.logger.warn({
|
|
1897
|
+
msg: 'Lifecycle webhook validation failed - potential security issue',
|
|
1898
|
+
securityEvent: 'lifecycle_webhook_validation_failure',
|
|
1910
1899
|
account: request.query.account,
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
actual: {
|
|
1916
|
-
subscriptionId: entry.subscriptionId,
|
|
1917
|
-
clientState: entry.clientState
|
|
1918
|
-
},
|
|
1919
|
-
entry
|
|
1900
|
+
subscriptionIdMatch: entry.subscriptionId === outlookSubscription.id,
|
|
1901
|
+
clientStateMatch: entry.clientState === outlookSubscription.clientState,
|
|
1902
|
+
receivedSubscriptionId: entry.subscriptionId,
|
|
1903
|
+
lifecycleEvent: entry.lifecycleEvent
|
|
1920
1904
|
});
|
|
1921
1905
|
continue;
|
|
1922
1906
|
}
|
|
@@ -2042,6 +2026,13 @@ Include your token in requests using one of these methods:
|
|
|
2042
2026
|
throw error;
|
|
2043
2027
|
}
|
|
2044
2028
|
|
|
2029
|
+
// Validate nonce format: 16 bytes base64url encoded = 21-22 characters
|
|
2030
|
+
const stateNonce = request.query.state.slice('account:add:'.length);
|
|
2031
|
+
if (!/^[A-Za-z0-9_-]{21,22}$/.test(stateNonce)) {
|
|
2032
|
+
let error = Boom.boomify(new Error(`Oauth failed: invalid state format`), { statusCode: 400 });
|
|
2033
|
+
throw error;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2045
2036
|
let [[, accountData]] = await redis.multi().get(`${REDIS_PREFIX}${request.query.state}`).del(`${REDIS_PREFIX}${request.query.state}`).exec();
|
|
2046
2037
|
if (!accountData) {
|
|
2047
2038
|
let error = Boom.boomify(new Error(`Oauth failed: session expired`), { statusCode: 400 });
|
|
@@ -2766,208 +2757,6 @@ Include your token in requests using one of these methods:
|
|
|
2766
2757
|
}
|
|
2767
2758
|
});
|
|
2768
2759
|
|
|
2769
|
-
server.route({
|
|
2770
|
-
method: 'POST',
|
|
2771
|
-
path: '/v1/account',
|
|
2772
|
-
|
|
2773
|
-
async handler(request) {
|
|
2774
|
-
let accountObject = new Account({
|
|
2775
|
-
redis,
|
|
2776
|
-
call,
|
|
2777
|
-
secret: await getSecret(),
|
|
2778
|
-
timeout: request.headers['x-ee-timeout']
|
|
2779
|
-
});
|
|
2780
|
-
|
|
2781
|
-
try {
|
|
2782
|
-
if (request.payload.oauth2 && request.payload.oauth2.authorize) {
|
|
2783
|
-
// redirect to OAuth2 consent screen
|
|
2784
|
-
|
|
2785
|
-
const oAuth2Client = await oauth2Apps.getClient(request.payload.oauth2.provider);
|
|
2786
|
-
const nonce = crypto.randomBytes(NONCE_BYTES).toString('base64url');
|
|
2787
|
-
|
|
2788
|
-
const accountData = request.payload;
|
|
2789
|
-
|
|
2790
|
-
if (accountData.oauth2.redirectUrl) {
|
|
2791
|
-
accountData._meta = {
|
|
2792
|
-
redirectUrl: accountData.oauth2.redirectUrl
|
|
2793
|
-
};
|
|
2794
|
-
delete accountData.oauth2.redirectUrl;
|
|
2795
|
-
}
|
|
2796
|
-
|
|
2797
|
-
delete accountData.oauth2.authorize; // do not store this property
|
|
2798
|
-
// store account data
|
|
2799
|
-
await redis
|
|
2800
|
-
.multi()
|
|
2801
|
-
.set(`${REDIS_PREFIX}account:add:${nonce}`, JSON.stringify(accountData))
|
|
2802
|
-
.expire(`${REDIS_PREFIX}account:add:${nonce}`, Math.floor(MAX_FORM_TTL / 1000))
|
|
2803
|
-
.exec();
|
|
2804
|
-
|
|
2805
|
-
// Generate the url that will be used for the consent dialog.
|
|
2806
|
-
let authorizeUrl;
|
|
2807
|
-
switch (oAuth2Client.provider) {
|
|
2808
|
-
case 'gmail': {
|
|
2809
|
-
let requestData = {
|
|
2810
|
-
state: `account:add:${nonce}`
|
|
2811
|
-
};
|
|
2812
|
-
|
|
2813
|
-
if (accountData.email) {
|
|
2814
|
-
requestData.email = accountData.email;
|
|
2815
|
-
}
|
|
2816
|
-
|
|
2817
|
-
authorizeUrl = oAuth2Client.generateAuthUrl(requestData);
|
|
2818
|
-
|
|
2819
|
-
break;
|
|
2820
|
-
}
|
|
2821
|
-
|
|
2822
|
-
case 'outlook':
|
|
2823
|
-
case 'mailRu':
|
|
2824
|
-
authorizeUrl = oAuth2Client.generateAuthUrl({
|
|
2825
|
-
state: `account:add:${nonce}`
|
|
2826
|
-
});
|
|
2827
|
-
break;
|
|
2828
|
-
|
|
2829
|
-
default: {
|
|
2830
|
-
let error = Boom.boomify(new Error('Unknown OAuth provider'), { statusCode: 400 });
|
|
2831
|
-
throw error;
|
|
2832
|
-
}
|
|
2833
|
-
}
|
|
2834
|
-
|
|
2835
|
-
return {
|
|
2836
|
-
redirect: authorizeUrl
|
|
2837
|
-
};
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
let result = await accountObject.create(request.payload);
|
|
2841
|
-
return result;
|
|
2842
|
-
} catch (err) {
|
|
2843
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
2844
|
-
if (Boom.isBoom(err)) {
|
|
2845
|
-
throw err;
|
|
2846
|
-
}
|
|
2847
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
2848
|
-
if (err.code) {
|
|
2849
|
-
error.output.payload.code = err.code;
|
|
2850
|
-
}
|
|
2851
|
-
throw error;
|
|
2852
|
-
}
|
|
2853
|
-
},
|
|
2854
|
-
|
|
2855
|
-
options: {
|
|
2856
|
-
description: 'Register new account',
|
|
2857
|
-
notes: 'Registers new IMAP account to be synced',
|
|
2858
|
-
tags: ['api', 'Account'],
|
|
2859
|
-
|
|
2860
|
-
plugins: {},
|
|
2861
|
-
|
|
2862
|
-
auth: {
|
|
2863
|
-
strategy: 'api-token',
|
|
2864
|
-
mode: 'required'
|
|
2865
|
-
},
|
|
2866
|
-
cors: CORS_CONFIG,
|
|
2867
|
-
|
|
2868
|
-
validate: {
|
|
2869
|
-
options: {
|
|
2870
|
-
stripUnknown: false,
|
|
2871
|
-
abortEarly: false,
|
|
2872
|
-
convert: true
|
|
2873
|
-
},
|
|
2874
|
-
failAction,
|
|
2875
|
-
|
|
2876
|
-
payload: Joi.object({
|
|
2877
|
-
account: Joi.string()
|
|
2878
|
-
.empty('')
|
|
2879
|
-
.trim()
|
|
2880
|
-
.max(256)
|
|
2881
|
-
.allow(null)
|
|
2882
|
-
.example('example')
|
|
2883
|
-
.description(
|
|
2884
|
-
'Account ID. If set to `null`, a unique ID will be generated automatically. If you provide an existing account ID, the settings for that account will be updated instead'
|
|
2885
|
-
)
|
|
2886
|
-
.required(),
|
|
2887
|
-
|
|
2888
|
-
name: Joi.string().max(256).required().example('My Email Account').description('Display name for the account'),
|
|
2889
|
-
email: Joi.string().empty('').email().example('user@example.com').description('Default email address of the account'),
|
|
2890
|
-
|
|
2891
|
-
path: accountPathSchema.example(['*']).label('AccountPath'),
|
|
2892
|
-
|
|
2893
|
-
subconnections: accountSchemas.subconnections,
|
|
2894
|
-
|
|
2895
|
-
webhooks: Joi.string()
|
|
2896
|
-
.uri({
|
|
2897
|
-
scheme: ['http', 'https'],
|
|
2898
|
-
allowRelative: false
|
|
2899
|
-
})
|
|
2900
|
-
.allow('')
|
|
2901
|
-
.example('https://myservice.com/imap/webhooks')
|
|
2902
|
-
.description('Account-specific webhook URL'),
|
|
2903
|
-
|
|
2904
|
-
copy: Joi.boolean()
|
|
2905
|
-
.allow(null)
|
|
2906
|
-
.example(null)
|
|
2907
|
-
.description('Copy submitted messages to Sent folder. Set to `null` to unset and use provider specific default.'),
|
|
2908
|
-
|
|
2909
|
-
logs: Joi.boolean().example(false).description('Store recent logs').default(false),
|
|
2910
|
-
|
|
2911
|
-
notifyFrom: accountSchemas.notifyFrom.default('now'),
|
|
2912
|
-
syncFrom: accountSchemas.syncFrom.default(null),
|
|
2913
|
-
|
|
2914
|
-
proxy: settingsSchema.proxyUrl,
|
|
2915
|
-
smtpEhloName: settingsSchema.smtpEhloName,
|
|
2916
|
-
|
|
2917
|
-
imapIndexer: accountSchemas.imapIndexer,
|
|
2918
|
-
|
|
2919
|
-
imap: Joi.object(imapSchema).allow(false).description('IMAP configuration').label('ImapConfiguration'),
|
|
2920
|
-
|
|
2921
|
-
smtp: Joi.object(smtpSchema).allow(false).description('SMTP configuration').label('SmtpConfiguration'),
|
|
2922
|
-
|
|
2923
|
-
oauth2: Joi.object(oauth2Schema).allow(false).description('OAuth2 configuration').label('OAuth2'),
|
|
2924
|
-
|
|
2925
|
-
webhooksCustomHeaders: settingsSchema.webhooksCustomHeaders.label('AccountWebhooksCustomHeaders'),
|
|
2926
|
-
|
|
2927
|
-
locale: Joi.string().empty('').max(100).example('fr').description('Optional locale'),
|
|
2928
|
-
tz: Joi.string().empty('').max(100).example('Europe/Tallinn').description('Optional timezone')
|
|
2929
|
-
})
|
|
2930
|
-
.label('CreateAccount')
|
|
2931
|
-
.example({
|
|
2932
|
-
account: 'example',
|
|
2933
|
-
name: 'Nyan Cat',
|
|
2934
|
-
email: 'nyan.cat@example.com',
|
|
2935
|
-
imap: {
|
|
2936
|
-
auth: {
|
|
2937
|
-
user: 'nyan.cat',
|
|
2938
|
-
pass: 'sercretpass'
|
|
2939
|
-
},
|
|
2940
|
-
host: 'mail.example.com',
|
|
2941
|
-
port: 993,
|
|
2942
|
-
secure: true
|
|
2943
|
-
},
|
|
2944
|
-
smtp: {
|
|
2945
|
-
auth: {
|
|
2946
|
-
user: 'nyan.cat',
|
|
2947
|
-
pass: 'secretpass'
|
|
2948
|
-
},
|
|
2949
|
-
host: 'mail.example.com',
|
|
2950
|
-
port: 465,
|
|
2951
|
-
secure: true
|
|
2952
|
-
}
|
|
2953
|
-
})
|
|
2954
|
-
},
|
|
2955
|
-
|
|
2956
|
-
response: {
|
|
2957
|
-
schema: Joi.object({
|
|
2958
|
-
account: accountIdSchema.required(),
|
|
2959
|
-
state: Joi.string()
|
|
2960
|
-
.required()
|
|
2961
|
-
.valid('existing', 'new')
|
|
2962
|
-
.example('new')
|
|
2963
|
-
.description('Is the account new or updated existing')
|
|
2964
|
-
.label('CreateAccountState')
|
|
2965
|
-
}).label('CreateAccountResponse'),
|
|
2966
|
-
failAction: 'log'
|
|
2967
|
-
}
|
|
2968
|
-
}
|
|
2969
|
-
});
|
|
2970
|
-
|
|
2971
2760
|
server.route({
|
|
2972
2761
|
method: 'POST',
|
|
2973
2762
|
path: '/v1/authentication/form',
|
|
@@ -3119,8 +2908,8 @@ Include your token in requests using one of these methods:
|
|
|
3119
2908
|
});
|
|
3120
2909
|
|
|
3121
2910
|
server.route({
|
|
3122
|
-
method: '
|
|
3123
|
-
path: '/v1/account/{account}',
|
|
2911
|
+
method: 'GET',
|
|
2912
|
+
path: '/v1/account/{account}/mailboxes',
|
|
3124
2913
|
|
|
3125
2914
|
async handler(request) {
|
|
3126
2915
|
let accountObject = new Account({
|
|
@@ -3132,7 +2921,25 @@ Include your token in requests using one of these methods:
|
|
|
3132
2921
|
});
|
|
3133
2922
|
|
|
3134
2923
|
try {
|
|
3135
|
-
|
|
2924
|
+
let mailboxes = await accountObject.getMailboxListing(request.query);
|
|
2925
|
+
|
|
2926
|
+
if (mailboxes && Array.isArray(mailboxes)) {
|
|
2927
|
+
mailboxes = mailboxes.sort((a, b) => {
|
|
2928
|
+
if (a.specialUse && !b.specialUse) {
|
|
2929
|
+
return -1;
|
|
2930
|
+
}
|
|
2931
|
+
if (!a.specialUse && b.specialUse) {
|
|
2932
|
+
return 1;
|
|
2933
|
+
}
|
|
2934
|
+
if (a.specialUse && b.specialUse) {
|
|
2935
|
+
return FLAG_SORT_ORDER.indexOf(a.specialUse) - FLAG_SORT_ORDER.indexOf(b.specialUse);
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
return a.path.localeCompare(b.path);
|
|
2939
|
+
});
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
return { mailboxes };
|
|
3136
2943
|
} catch (err) {
|
|
3137
2944
|
request.logger.error({ msg: 'API request failed', err });
|
|
3138
2945
|
if (Boom.isBoom(err)) {
|
|
@@ -3145,12 +2952,11 @@ Include your token in requests using one of these methods:
|
|
|
3145
2952
|
throw error;
|
|
3146
2953
|
}
|
|
3147
2954
|
},
|
|
3148
|
-
options: {
|
|
3149
|
-
description: 'Update account info',
|
|
3150
|
-
notes: 'Updates account information',
|
|
3151
|
-
tags: ['api', 'Account'],
|
|
3152
2955
|
|
|
3153
|
-
|
|
2956
|
+
options: {
|
|
2957
|
+
description: 'List mailboxes',
|
|
2958
|
+
notes: 'Lists all available mailboxes',
|
|
2959
|
+
tags: ['api', 'Mailbox'],
|
|
3154
2960
|
|
|
3155
2961
|
auth: {
|
|
3156
2962
|
strategy: 'api-token',
|
|
@@ -3170,72 +2976,28 @@ Include your token in requests using one of these methods:
|
|
|
3170
2976
|
account: accountIdSchema.required()
|
|
3171
2977
|
}),
|
|
3172
2978
|
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
webhooks: Joi.string()
|
|
3182
|
-
.uri({
|
|
3183
|
-
scheme: ['http', 'https'],
|
|
3184
|
-
allowRelative: false
|
|
3185
|
-
})
|
|
3186
|
-
.allow('')
|
|
3187
|
-
.example('https://myservice.com/imap/webhooks')
|
|
3188
|
-
.description('Account-specific webhook URL'),
|
|
3189
|
-
|
|
3190
|
-
copy: Joi.boolean()
|
|
3191
|
-
.allow(null)
|
|
3192
|
-
.example(null)
|
|
3193
|
-
.description('Copy submitted messages to Sent folder. Set to `null` to unset and use provider specific default.'),
|
|
3194
|
-
|
|
3195
|
-
logs: Joi.boolean().example(false).description('Store recent logs'),
|
|
3196
|
-
|
|
3197
|
-
notifyFrom: accountSchemas.notifyFrom,
|
|
3198
|
-
syncFrom: accountSchemas.syncFrom,
|
|
3199
|
-
|
|
3200
|
-
proxy: settingsSchema.proxyUrl,
|
|
3201
|
-
smtpEhloName: settingsSchema.smtpEhloName,
|
|
3202
|
-
|
|
3203
|
-
imap: Joi.object(imapUpdateSchema).allow(false).description('IMAP configuration').label('IMAPUpdate'),
|
|
3204
|
-
smtp: Joi.object(smtpUpdateSchema).allow(false).description('SMTP configuration').label('SMTPUpdate'),
|
|
3205
|
-
oauth2: Joi.object(oauth2UpdateSchema).allow(false).description('OAuth2 configuration').label('OAuth2Update'),
|
|
3206
|
-
|
|
3207
|
-
webhooksCustomHeaders: settingsSchema.webhooksCustomHeaders.label('AccountWebhooksCustomHeaders'),
|
|
3208
|
-
|
|
3209
|
-
locale: Joi.string().empty('').max(100).example('fr').description('Optional locale'),
|
|
3210
|
-
tz: Joi.string().empty('').max(100).example('Europe/Tallinn').description('Optional timezone')
|
|
3211
|
-
})
|
|
3212
|
-
.label('UpdateAccount')
|
|
3213
|
-
.example({
|
|
3214
|
-
name: 'Nyan Cat',
|
|
3215
|
-
email: 'nyan.cat@example.com',
|
|
3216
|
-
imap: {
|
|
3217
|
-
partial: true,
|
|
3218
|
-
disabled: true
|
|
3219
|
-
},
|
|
3220
|
-
smtp: {
|
|
3221
|
-
partial: true,
|
|
3222
|
-
host: 'mail.example.com'
|
|
3223
|
-
}
|
|
3224
|
-
})
|
|
2979
|
+
query: Joi.object({
|
|
2980
|
+
counters: Joi.boolean()
|
|
2981
|
+
.truthy('Y', 'true', '1')
|
|
2982
|
+
.falsy('N', 'false', 0)
|
|
2983
|
+
.default(false)
|
|
2984
|
+
.description('If true, then includes message counters in the response')
|
|
2985
|
+
.label('MailboxCounters')
|
|
2986
|
+
}).label('MailboxListQuery')
|
|
3225
2987
|
},
|
|
3226
2988
|
|
|
3227
2989
|
response: {
|
|
3228
2990
|
schema: Joi.object({
|
|
3229
|
-
|
|
3230
|
-
}),
|
|
2991
|
+
mailboxes: mailboxesSchema
|
|
2992
|
+
}).label('MailboxesFilterResponse'),
|
|
3231
2993
|
failAction: 'log'
|
|
3232
2994
|
}
|
|
3233
2995
|
}
|
|
3234
2996
|
});
|
|
3235
2997
|
|
|
3236
2998
|
server.route({
|
|
3237
|
-
method: '
|
|
3238
|
-
path: '/v1/account/{account}/
|
|
2999
|
+
method: 'POST',
|
|
3000
|
+
path: '/v1/account/{account}/mailbox',
|
|
3239
3001
|
|
|
3240
3002
|
async handler(request) {
|
|
3241
3003
|
let accountObject = new Account({
|
|
@@ -3247,7 +3009,7 @@ Include your token in requests using one of these methods:
|
|
|
3247
3009
|
});
|
|
3248
3010
|
|
|
3249
3011
|
try {
|
|
3250
|
-
return
|
|
3012
|
+
return await accountObject.createMailbox(request.payload.path);
|
|
3251
3013
|
} catch (err) {
|
|
3252
3014
|
request.logger.error({ msg: 'API request failed', err });
|
|
3253
3015
|
if (Boom.isBoom(err)) {
|
|
@@ -3257,13 +3019,17 @@ Include your token in requests using one of these methods:
|
|
|
3257
3019
|
if (err.code) {
|
|
3258
3020
|
error.output.payload.code = err.code;
|
|
3259
3021
|
}
|
|
3022
|
+
if (err.info) {
|
|
3023
|
+
error.output.payload.details = err.info;
|
|
3024
|
+
}
|
|
3260
3025
|
throw error;
|
|
3261
3026
|
}
|
|
3262
3027
|
},
|
|
3028
|
+
|
|
3263
3029
|
options: {
|
|
3264
|
-
description: '
|
|
3265
|
-
notes: '
|
|
3266
|
-
tags: ['api', '
|
|
3030
|
+
description: 'Create mailbox',
|
|
3031
|
+
notes: 'Create new mailbox folder',
|
|
3032
|
+
tags: ['api', 'Mailbox'],
|
|
3267
3033
|
|
|
3268
3034
|
plugins: {},
|
|
3269
3035
|
|
|
@@ -3286,14 +3052,21 @@ Include your token in requests using one of these methods:
|
|
|
3286
3052
|
}),
|
|
3287
3053
|
|
|
3288
3054
|
payload: Joi.object({
|
|
3289
|
-
|
|
3290
|
-
|
|
3055
|
+
path: Joi.array()
|
|
3056
|
+
.items(Joi.string().max(256))
|
|
3057
|
+
.single()
|
|
3058
|
+
.example(['Parent folder', 'Subfolder'])
|
|
3059
|
+
.description('Mailbox path as an array or a string. If account is namespaced then namespace prefix is added by default.')
|
|
3060
|
+
.label('MailboxPath')
|
|
3061
|
+
}).label('CreateMailbox')
|
|
3291
3062
|
},
|
|
3292
3063
|
|
|
3293
3064
|
response: {
|
|
3294
3065
|
schema: Joi.object({
|
|
3295
|
-
|
|
3296
|
-
|
|
3066
|
+
path: Joi.string().required().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox').label('MailboxPath'),
|
|
3067
|
+
mailboxId: Joi.string().example('1439876283476').description('Mailbox ID (if server has support)').label('MailboxId'),
|
|
3068
|
+
created: Joi.boolean().example(true).description('Was the mailbox created')
|
|
3069
|
+
}).label('CreateMailboxResponse'),
|
|
3297
3070
|
failAction: 'log'
|
|
3298
3071
|
}
|
|
3299
3072
|
}
|
|
@@ -3301,7 +3074,7 @@ Include your token in requests using one of these methods:
|
|
|
3301
3074
|
|
|
3302
3075
|
server.route({
|
|
3303
3076
|
method: 'PUT',
|
|
3304
|
-
path: '/v1/account/{account}/
|
|
3077
|
+
path: '/v1/account/{account}/mailbox',
|
|
3305
3078
|
|
|
3306
3079
|
async handler(request) {
|
|
3307
3080
|
let accountObject = new Account({
|
|
@@ -3313,7 +3086,7 @@ Include your token in requests using one of these methods:
|
|
|
3313
3086
|
});
|
|
3314
3087
|
|
|
3315
3088
|
try {
|
|
3316
|
-
return
|
|
3089
|
+
return await accountObject.modifyMailbox(request.payload.path, request.payload.newPath, request.payload.subscribed);
|
|
3317
3090
|
} catch (err) {
|
|
3318
3091
|
request.logger.error({ msg: 'API request failed', err });
|
|
3319
3092
|
if (Boom.isBoom(err)) {
|
|
@@ -3323,13 +3096,17 @@ Include your token in requests using one of these methods:
|
|
|
3323
3096
|
if (err.code) {
|
|
3324
3097
|
error.output.payload.code = err.code;
|
|
3325
3098
|
}
|
|
3099
|
+
if (err.info) {
|
|
3100
|
+
error.output.payload.details = err.info;
|
|
3101
|
+
}
|
|
3326
3102
|
throw error;
|
|
3327
3103
|
}
|
|
3328
3104
|
},
|
|
3105
|
+
|
|
3329
3106
|
options: {
|
|
3330
|
-
description: '
|
|
3331
|
-
notes: '
|
|
3332
|
-
tags: ['api', '
|
|
3107
|
+
description: 'Modify mailbox',
|
|
3108
|
+
notes: 'Modify an existing mailbox folder (rename or change subscription status)',
|
|
3109
|
+
tags: ['api', 'Mailbox'],
|
|
3333
3110
|
|
|
3334
3111
|
plugins: {},
|
|
3335
3112
|
|
|
@@ -3352,1846 +3129,49 @@ Include your token in requests using one of these methods:
|
|
|
3352
3129
|
}),
|
|
3353
3130
|
|
|
3354
3131
|
payload: Joi.object({
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
method: 'DELETE',
|
|
3370
|
-
path: '/v1/account/{account}',
|
|
3371
|
-
|
|
3372
|
-
async handler(request) {
|
|
3373
|
-
let accountObject = new Account({
|
|
3374
|
-
redis,
|
|
3375
|
-
account: request.params.account,
|
|
3376
|
-
documentsQueue,
|
|
3377
|
-
call,
|
|
3378
|
-
secret: await getSecret(),
|
|
3379
|
-
timeout: request.headers['x-ee-timeout']
|
|
3380
|
-
});
|
|
3381
|
-
|
|
3382
|
-
try {
|
|
3383
|
-
return await accountObject.delete();
|
|
3384
|
-
} catch (err) {
|
|
3385
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
3386
|
-
if (Boom.isBoom(err)) {
|
|
3387
|
-
throw err;
|
|
3388
|
-
}
|
|
3389
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
3390
|
-
if (err.code) {
|
|
3391
|
-
error.output.payload.code = err.code;
|
|
3392
|
-
}
|
|
3393
|
-
throw error;
|
|
3394
|
-
}
|
|
3395
|
-
},
|
|
3396
|
-
options: {
|
|
3397
|
-
description: 'Remove account',
|
|
3398
|
-
notes: "Stop processing and clear the account's cache",
|
|
3399
|
-
|
|
3400
|
-
tags: ['api', 'Account'],
|
|
3401
|
-
|
|
3402
|
-
plugins: {},
|
|
3403
|
-
|
|
3404
|
-
auth: {
|
|
3405
|
-
strategy: 'api-token',
|
|
3406
|
-
mode: 'required'
|
|
3407
|
-
},
|
|
3408
|
-
cors: CORS_CONFIG,
|
|
3409
|
-
|
|
3410
|
-
validate: {
|
|
3411
|
-
options: {
|
|
3412
|
-
stripUnknown: false,
|
|
3413
|
-
abortEarly: false,
|
|
3414
|
-
convert: true
|
|
3415
|
-
},
|
|
3416
|
-
failAction,
|
|
3417
|
-
|
|
3418
|
-
params: Joi.object({
|
|
3419
|
-
account: accountIdSchema.required()
|
|
3420
|
-
})
|
|
3421
|
-
},
|
|
3422
|
-
|
|
3423
|
-
response: {
|
|
3424
|
-
schema: Joi.object({
|
|
3425
|
-
account: accountIdSchema.required(),
|
|
3426
|
-
deleted: Joi.boolean().truthy('Y', 'true', '1').falsy('N', 'false', 0).default(true).description('Was the account deleted')
|
|
3427
|
-
}).label('DeleteRequestResponse'),
|
|
3428
|
-
failAction: 'log'
|
|
3429
|
-
}
|
|
3430
|
-
}
|
|
3431
|
-
});
|
|
3432
|
-
|
|
3433
|
-
server.route({
|
|
3434
|
-
method: 'PUT',
|
|
3435
|
-
path: '/v1/account/{account}/flush',
|
|
3436
|
-
|
|
3437
|
-
async handler(request, h) {
|
|
3438
|
-
let accountObject = new Account({
|
|
3439
|
-
redis,
|
|
3440
|
-
account: request.params.account,
|
|
3441
|
-
call,
|
|
3442
|
-
secret: await getSecret(),
|
|
3443
|
-
esClient: await h.getESClient(request.logger),
|
|
3444
|
-
timeout: request.headers['x-ee-timeout']
|
|
3445
|
-
});
|
|
3446
|
-
|
|
3447
|
-
try {
|
|
3448
|
-
return { flush: await accountObject.flush(request.payload) };
|
|
3449
|
-
} catch (err) {
|
|
3450
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
3451
|
-
if (Boom.isBoom(err)) {
|
|
3452
|
-
throw err;
|
|
3453
|
-
}
|
|
3454
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
3455
|
-
if (err.code) {
|
|
3456
|
-
error.output.payload.code = err.code;
|
|
3457
|
-
}
|
|
3458
|
-
throw error;
|
|
3459
|
-
}
|
|
3460
|
-
},
|
|
3461
|
-
options: {
|
|
3462
|
-
description: 'Request account flush',
|
|
3463
|
-
notes: 'Deletes all email indexes from Redis and ElasticSearch and re-creates the index for that account. You can only run a single flush operation at a time, so you must wait until the previous flush has finished before initiating a new one.',
|
|
3464
|
-
tags: ['api', 'Account'],
|
|
3465
|
-
|
|
3466
|
-
plugins: {},
|
|
3467
|
-
|
|
3468
|
-
auth: {
|
|
3469
|
-
strategy: 'api-token',
|
|
3470
|
-
mode: 'required'
|
|
3471
|
-
},
|
|
3472
|
-
cors: CORS_CONFIG,
|
|
3473
|
-
|
|
3474
|
-
validate: {
|
|
3475
|
-
options: {
|
|
3476
|
-
stripUnknown: false,
|
|
3477
|
-
abortEarly: false,
|
|
3478
|
-
convert: true
|
|
3479
|
-
},
|
|
3480
|
-
failAction,
|
|
3481
|
-
|
|
3482
|
-
params: Joi.object({
|
|
3483
|
-
account: accountIdSchema.required()
|
|
3484
|
-
}),
|
|
3485
|
-
|
|
3486
|
-
payload: Joi.object({
|
|
3487
|
-
flush: Joi.boolean().truthy('Y', 'true', '1').falsy('N', 'false', 0).default(false).description('Only flush the account if true'),
|
|
3488
|
-
notifyFrom: accountSchemas.notifyFrom.default('now'),
|
|
3489
|
-
imapIndexer: accountSchemas.imapIndexer,
|
|
3490
|
-
syncFrom: accountSchemas.syncFrom
|
|
3491
|
-
}).label('RequestFlush')
|
|
3492
|
-
},
|
|
3493
|
-
|
|
3494
|
-
response: {
|
|
3495
|
-
schema: Joi.object({
|
|
3496
|
-
flush: Joi.boolean().truthy('Y', 'true', '1').falsy('N', 'false', 0).default(false).description('Flush status')
|
|
3497
|
-
}).label('RequestFlushResponse'),
|
|
3498
|
-
failAction: 'log'
|
|
3499
|
-
}
|
|
3500
|
-
}
|
|
3501
|
-
});
|
|
3502
|
-
|
|
3503
|
-
server.route({
|
|
3504
|
-
method: 'GET',
|
|
3505
|
-
path: '/v1/accounts',
|
|
3506
|
-
|
|
3507
|
-
async handler(request) {
|
|
3508
|
-
try {
|
|
3509
|
-
let accountObject = new Account({
|
|
3510
|
-
redis,
|
|
3511
|
-
account: request.params.account,
|
|
3512
|
-
call,
|
|
3513
|
-
secret: await getSecret(),
|
|
3514
|
-
timeout: request.headers['x-ee-timeout']
|
|
3515
|
-
});
|
|
3516
|
-
|
|
3517
|
-
return await accountObject.listAccounts(request.query.state, request.query.query, request.query.page, request.query.pageSize);
|
|
3518
|
-
} catch (err) {
|
|
3519
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
3520
|
-
if (Boom.isBoom(err)) {
|
|
3521
|
-
throw err;
|
|
3522
|
-
}
|
|
3523
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
3524
|
-
if (err.code) {
|
|
3525
|
-
error.output.payload.code = err.code;
|
|
3526
|
-
}
|
|
3527
|
-
throw error;
|
|
3528
|
-
}
|
|
3529
|
-
},
|
|
3530
|
-
|
|
3531
|
-
options: {
|
|
3532
|
-
description: 'List accounts',
|
|
3533
|
-
notes: 'Lists registered accounts',
|
|
3534
|
-
tags: ['api', 'Account'],
|
|
3535
|
-
|
|
3536
|
-
plugins: {},
|
|
3537
|
-
|
|
3538
|
-
auth: {
|
|
3539
|
-
strategy: 'api-token',
|
|
3540
|
-
mode: 'required'
|
|
3541
|
-
},
|
|
3542
|
-
cors: CORS_CONFIG,
|
|
3543
|
-
|
|
3544
|
-
validate: {
|
|
3545
|
-
options: {
|
|
3546
|
-
stripUnknown: false,
|
|
3547
|
-
abortEarly: false,
|
|
3548
|
-
convert: true
|
|
3549
|
-
},
|
|
3550
|
-
failAction,
|
|
3551
|
-
|
|
3552
|
-
query: Joi.object({
|
|
3553
|
-
page: Joi.number()
|
|
3554
|
-
.integer()
|
|
3555
|
-
.min(0)
|
|
3556
|
-
.max(1024 * 1024)
|
|
3557
|
-
.default(0)
|
|
3558
|
-
.example(0)
|
|
3559
|
-
.description('Page number (zero indexed, so use 0 for first page)')
|
|
3560
|
-
.label('PageNumber'),
|
|
3561
|
-
pageSize: Joi.number().integer().min(1).max(1000).default(20).example(20).description('How many entries per page').label('PageSize'),
|
|
3562
|
-
state: Joi.string()
|
|
3563
|
-
.valid('init', 'syncing', 'connecting', 'connected', 'authenticationError', 'connectError', 'unset', 'disconnected')
|
|
3564
|
-
.example('connected')
|
|
3565
|
-
.description('Filter accounts by state')
|
|
3566
|
-
.label('AccountState'),
|
|
3567
|
-
query: Joi.string().example('user@example.com').description('Filter accounts by string match').label('AccountQuery')
|
|
3568
|
-
}).label('AccountsFilter')
|
|
3569
|
-
},
|
|
3570
|
-
|
|
3571
|
-
response: {
|
|
3572
|
-
schema: Joi.object({
|
|
3573
|
-
total: Joi.number().integer().example(120).description('How many matching entries').label('TotalNumber'),
|
|
3574
|
-
page: Joi.number().integer().example(0).description('Current page (0-based index)').label('PageNumber'),
|
|
3575
|
-
pages: Joi.number().integer().example(24).description('Total page count').label('PagesNumber'),
|
|
3576
|
-
|
|
3577
|
-
accounts: Joi.array()
|
|
3578
|
-
.items(
|
|
3579
|
-
Joi.object({
|
|
3580
|
-
account: accountIdSchema.required(),
|
|
3581
|
-
name: Joi.string().max(256).example('My Email Account').description('Display name for the account'),
|
|
3582
|
-
email: Joi.string().empty('').email().example('user@example.com').description('Default email address of the account'),
|
|
3583
|
-
type: AccountTypeSchema,
|
|
3584
|
-
app: Joi.string().max(256).example('AAABhaBPHscAAAAH').description('OAuth2 application ID'),
|
|
3585
|
-
state: Joi.string()
|
|
3586
|
-
.required()
|
|
3587
|
-
.valid('init', 'syncing', 'connecting', 'connected', 'authenticationError', 'connectError', 'unset', 'disconnected')
|
|
3588
|
-
.example('connected')
|
|
3589
|
-
.description('Account state'),
|
|
3590
|
-
webhooks: Joi.string()
|
|
3591
|
-
.uri({
|
|
3592
|
-
scheme: ['http', 'https'],
|
|
3593
|
-
allowRelative: false
|
|
3594
|
-
})
|
|
3595
|
-
.example('https://myservice.com/imap/webhooks')
|
|
3596
|
-
.description('Account-specific webhook URL'),
|
|
3597
|
-
proxy: settingsSchema.proxyUrl,
|
|
3598
|
-
smtpEhloName: settingsSchema.smtpEhloName,
|
|
3599
|
-
|
|
3600
|
-
counters: accountCountersSchema,
|
|
3601
|
-
|
|
3602
|
-
syncTime: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('Last sync time'),
|
|
3603
|
-
lastError: lastErrorSchema.allow(null)
|
|
3604
|
-
}).label('AccountResponseItem')
|
|
3605
|
-
)
|
|
3606
|
-
.label('AccountEntries')
|
|
3607
|
-
}).label('AccountsFilterResponse'),
|
|
3608
|
-
failAction: 'log'
|
|
3609
|
-
}
|
|
3610
|
-
}
|
|
3611
|
-
});
|
|
3612
|
-
|
|
3613
|
-
server.route({
|
|
3614
|
-
method: 'GET',
|
|
3615
|
-
path: '/v1/account/{account}',
|
|
3616
|
-
|
|
3617
|
-
async handler(request) {
|
|
3618
|
-
let accountObject = new Account({
|
|
3619
|
-
redis,
|
|
3620
|
-
account: request.params.account,
|
|
3621
|
-
call,
|
|
3622
|
-
secret: await getSecret(),
|
|
3623
|
-
timeout: request.headers['x-ee-timeout']
|
|
3624
|
-
});
|
|
3625
|
-
try {
|
|
3626
|
-
let accountData = await accountObject.loadAccountData();
|
|
3627
|
-
|
|
3628
|
-
// remove secrets
|
|
3629
|
-
for (let type of ['imap', 'smtp', 'oauth2']) {
|
|
3630
|
-
if (accountData[type] && accountData[type].auth) {
|
|
3631
|
-
for (let key of ['pass', 'accessToken', 'refreshToken']) {
|
|
3632
|
-
if (key in accountData[type].auth) {
|
|
3633
|
-
accountData[type].auth[key] = '******';
|
|
3634
|
-
}
|
|
3635
|
-
}
|
|
3636
|
-
}
|
|
3637
|
-
|
|
3638
|
-
if (accountData[type]) {
|
|
3639
|
-
for (let key of ['accessToken', 'refreshToken']) {
|
|
3640
|
-
if (key in accountData[type]) {
|
|
3641
|
-
accountData[type][key] = '******';
|
|
3642
|
-
}
|
|
3643
|
-
}
|
|
3644
|
-
}
|
|
3645
|
-
}
|
|
3646
|
-
|
|
3647
|
-
let result = {};
|
|
3648
|
-
|
|
3649
|
-
for (let key of [
|
|
3650
|
-
'account',
|
|
3651
|
-
'name',
|
|
3652
|
-
'email',
|
|
3653
|
-
'copy',
|
|
3654
|
-
'logs',
|
|
3655
|
-
'notifyFrom',
|
|
3656
|
-
'syncFrom',
|
|
3657
|
-
'path',
|
|
3658
|
-
'subconnections',
|
|
3659
|
-
'webhooks',
|
|
3660
|
-
'proxy',
|
|
3661
|
-
'smtpEhloName',
|
|
3662
|
-
'imapIndexer',
|
|
3663
|
-
'imap',
|
|
3664
|
-
'smtp',
|
|
3665
|
-
'oauth2',
|
|
3666
|
-
'state',
|
|
3667
|
-
'smtpStatus',
|
|
3668
|
-
'syncError',
|
|
3669
|
-
'connections',
|
|
3670
|
-
'webhooksCustomHeaders',
|
|
3671
|
-
'locale',
|
|
3672
|
-
'tz'
|
|
3673
|
-
]) {
|
|
3674
|
-
if (key in accountData) {
|
|
3675
|
-
result[key] = accountData[key];
|
|
3676
|
-
}
|
|
3677
|
-
}
|
|
3678
|
-
|
|
3679
|
-
// default false
|
|
3680
|
-
for (let key of ['logs']) {
|
|
3681
|
-
result[key] = !!result[key];
|
|
3682
|
-
}
|
|
3683
|
-
|
|
3684
|
-
// default null
|
|
3685
|
-
for (let key of ['notifyFrom', 'syncFrom', 'lastError', 'smtpStatus']) {
|
|
3686
|
-
result[key] = result[key] || null;
|
|
3687
|
-
}
|
|
3688
|
-
|
|
3689
|
-
let oauth2App;
|
|
3690
|
-
if (accountData.oauth2 && accountData.oauth2.provider) {
|
|
3691
|
-
oauth2App = await oauth2Apps.get(accountData.oauth2.provider);
|
|
3692
|
-
|
|
3693
|
-
if (oauth2App) {
|
|
3694
|
-
// Check if account is already marked as send-only
|
|
3695
|
-
if (accountData.sendOnly) {
|
|
3696
|
-
result.sendOnly = true;
|
|
3697
|
-
} else {
|
|
3698
|
-
result.type = oauth2App.provider;
|
|
3699
|
-
}
|
|
3700
|
-
if (oauth2App.id !== oauth2App.provider) {
|
|
3701
|
-
result.app = oauth2App.id;
|
|
3702
|
-
}
|
|
3703
|
-
result.baseScopes = oauth2App.baseScope || 'imap';
|
|
3704
|
-
} else {
|
|
3705
|
-
result.type = 'oauth2';
|
|
3706
|
-
}
|
|
3707
|
-
} else if (accountData.oauth2 && accountData.oauth2.auth && accountData.oauth2.auth.delegatedAccount) {
|
|
3708
|
-
result.type = 'delegated';
|
|
3709
|
-
} else if (accountData.imap && !accountData.imap.disabled) {
|
|
3710
|
-
result.type = 'imap';
|
|
3711
|
-
} else {
|
|
3712
|
-
result.type = 'sending';
|
|
3713
|
-
result.sendOnly = true;
|
|
3714
|
-
}
|
|
3715
|
-
|
|
3716
|
-
if ((accountData.imap || (oauth2App && (!oauth2App.baseScopes || oauth2App.baseScopes === 'imap'))) && !result.imapIndexer) {
|
|
3717
|
-
result.imapIndexer = 'full';
|
|
3718
|
-
}
|
|
3719
|
-
|
|
3720
|
-
if (accountData.sync) {
|
|
3721
|
-
result.syncTime = accountData.sync;
|
|
3722
|
-
}
|
|
3723
|
-
|
|
3724
|
-
if (accountData.state) {
|
|
3725
|
-
result.lastError = accountData.state === 'connected' ? null : accountData.lastErrorState;
|
|
3726
|
-
}
|
|
3727
|
-
|
|
3728
|
-
if (accountData.counters) {
|
|
3729
|
-
result.counters = accountData.counters;
|
|
3730
|
-
}
|
|
3731
|
-
|
|
3732
|
-
if (request.query.quota && !result.sendOnly) {
|
|
3733
|
-
result.quota = await accountObject.getQuota();
|
|
3734
|
-
}
|
|
3735
|
-
|
|
3736
|
-
return result;
|
|
3737
|
-
} catch (err) {
|
|
3738
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
3739
|
-
if (Boom.isBoom(err)) {
|
|
3740
|
-
throw err;
|
|
3741
|
-
}
|
|
3742
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
3743
|
-
if (err.code) {
|
|
3744
|
-
error.output.payload.code = err.code;
|
|
3745
|
-
}
|
|
3746
|
-
throw error;
|
|
3747
|
-
}
|
|
3748
|
-
},
|
|
3749
|
-
options: {
|
|
3750
|
-
description: 'Get account info',
|
|
3751
|
-
notes: 'Returns stored information about the account. Passwords are not included.',
|
|
3752
|
-
tags: ['api', 'Account'],
|
|
3753
|
-
|
|
3754
|
-
auth: {
|
|
3755
|
-
strategy: 'api-token',
|
|
3756
|
-
mode: 'required'
|
|
3757
|
-
},
|
|
3758
|
-
cors: CORS_CONFIG,
|
|
3759
|
-
|
|
3760
|
-
validate: {
|
|
3761
|
-
options: {
|
|
3762
|
-
stripUnknown: false,
|
|
3763
|
-
abortEarly: false,
|
|
3764
|
-
convert: true
|
|
3765
|
-
},
|
|
3766
|
-
failAction,
|
|
3767
|
-
|
|
3768
|
-
params: Joi.object({
|
|
3769
|
-
account: accountIdSchema.required()
|
|
3770
|
-
}),
|
|
3771
|
-
|
|
3772
|
-
query: Joi.object({
|
|
3773
|
-
quota: Joi.boolean()
|
|
3774
|
-
.truthy('Y', 'true', '1')
|
|
3775
|
-
.falsy('N', 'false', 0)
|
|
3776
|
-
.default(false)
|
|
3777
|
-
.description('If true, then include quota information in the response')
|
|
3778
|
-
.label('AccountQuota')
|
|
3779
|
-
})
|
|
3780
|
-
},
|
|
3781
|
-
|
|
3782
|
-
response: {
|
|
3783
|
-
schema: Joi.object({
|
|
3784
|
-
account: accountIdSchema.required(),
|
|
3785
|
-
|
|
3786
|
-
name: Joi.string().max(256).example('My Email Account').description('Display name for the account'),
|
|
3787
|
-
email: Joi.string().empty('').email().example('user@example.com').description('Default email address of the account'),
|
|
3788
|
-
|
|
3789
|
-
copy: Joi.boolean().example(true).description('Copy submitted messages to Sent folder'),
|
|
3790
|
-
logs: Joi.boolean().example(false).description('Store recent logs'),
|
|
3791
|
-
|
|
3792
|
-
notifyFrom: accountSchemas.notifyFrom,
|
|
3793
|
-
syncFrom: accountSchemas.syncFrom,
|
|
3794
|
-
|
|
3795
|
-
path: accountPathSchema.example(['*']).label('AccountPath'),
|
|
3796
|
-
|
|
3797
|
-
imapIndexer: accountSchemas.imapIndexer,
|
|
3798
|
-
|
|
3799
|
-
subconnections: accountSchemas.subconnections,
|
|
3800
|
-
|
|
3801
|
-
webhooks: Joi.string()
|
|
3802
|
-
.uri({
|
|
3803
|
-
scheme: ['http', 'https'],
|
|
3804
|
-
allowRelative: false
|
|
3805
|
-
})
|
|
3806
|
-
.example('https://myservice.com/imap/webhooks')
|
|
3807
|
-
.description('Account-specific webhook URL'),
|
|
3808
|
-
proxy: settingsSchema.proxyUrl,
|
|
3809
|
-
smtpEhloName: settingsSchema.smtpEhloName,
|
|
3810
|
-
|
|
3811
|
-
imap: Joi.object(imapSchema).description('IMAP configuration').label('IMAPResponse'),
|
|
3812
|
-
|
|
3813
|
-
smtp: Joi.object(smtpSchema).description('SMTP configuration').label('SMTPResponse'),
|
|
3814
|
-
|
|
3815
|
-
oauth2: Joi.object(oauth2Schema).description('OAuth2 configuration').label('Oauth2Response'),
|
|
3816
|
-
|
|
3817
|
-
state: Joi.string()
|
|
3818
|
-
.valid('init', 'syncing', 'connecting', 'connected', 'authenticationError', 'connectError', 'unset', 'disconnected')
|
|
3819
|
-
.example('connected')
|
|
3820
|
-
.description('Informational account state')
|
|
3821
|
-
.label('AccountInfoState'),
|
|
3822
|
-
|
|
3823
|
-
smtpStatus: Joi.object({
|
|
3824
|
-
created: Joi.date()
|
|
3825
|
-
.iso()
|
|
3826
|
-
.allow(null)
|
|
3827
|
-
.example('2021-07-08T07:06:34.336Z')
|
|
3828
|
-
.description('When was the status for SMTP connection last updated'),
|
|
3829
|
-
status: Joi.string().valid('ok', 'error').description('Was the last SMTP attempt successful or not').label('SMTPStatusStatus'),
|
|
3830
|
-
response: Joi.string().example('250 OK').description('SMTP response message for delivery attempt'),
|
|
3831
|
-
description: Joi.string().example('Authentication failed').description('Error information'),
|
|
3832
|
-
responseCode: Joi.number().integer().example(500).description('Error status code'),
|
|
3833
|
-
code: Joi.string().example('EAUTH').description('Error type identifier'),
|
|
3834
|
-
command: Joi.string().example('AUTH PLAIN').description('SMTP command that failed')
|
|
3835
|
-
})
|
|
3836
|
-
.description('Information about the last SMTP connection attempt')
|
|
3837
|
-
.label('SMTPInfoStatus'),
|
|
3838
|
-
|
|
3839
|
-
webhooksCustomHeaders: settingsSchema.webhooksCustomHeaders.label('AccountWebhooksCustomHeaders'),
|
|
3840
|
-
|
|
3841
|
-
locale: Joi.string().empty('').max(100).example('fr').description('Optional locale'),
|
|
3842
|
-
tz: Joi.string().empty('').max(100).example('Europe/Tallinn').description('Optional timezone'),
|
|
3843
|
-
|
|
3844
|
-
type: AccountTypeSchema,
|
|
3845
|
-
app: Joi.string().max(256).example('AAABhaBPHscAAAAH').description('OAuth2 application ID'),
|
|
3846
|
-
baseScopes: Joi.string()
|
|
3847
|
-
.empty('')
|
|
3848
|
-
.trim()
|
|
3849
|
-
.valid(...['imap', 'api', 'pubsub'])
|
|
3850
|
-
.example('imap')
|
|
3851
|
-
.description('OAuth2 Base Scopes'),
|
|
3852
|
-
|
|
3853
|
-
counters: accountCountersSchema,
|
|
3854
|
-
|
|
3855
|
-
quota: Joi.object({
|
|
3856
|
-
usage: Joi.number().integer().example(8547884032).description('How many bytes has the account stored in emails'),
|
|
3857
|
-
limit: Joi.number().integer().example(16106127360).description('How many bytes can the account store emails'),
|
|
3858
|
-
status: Joi.string().example('53%').description('Textual information about the usage')
|
|
3859
|
-
})
|
|
3860
|
-
.label('AccountQuota')
|
|
3861
|
-
.allow(false)
|
|
3862
|
-
.description(
|
|
3863
|
-
'Account quota information if query argument quota=true. This value will be false if the server does not provide quota information.'
|
|
3864
|
-
),
|
|
3865
|
-
|
|
3866
|
-
syncTime: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('Last sync time'),
|
|
3867
|
-
|
|
3868
|
-
lastError: lastErrorSchema.allow(null)
|
|
3869
|
-
}).label('AccountResponse'),
|
|
3870
|
-
failAction: 'log'
|
|
3871
|
-
}
|
|
3872
|
-
}
|
|
3873
|
-
});
|
|
3874
|
-
|
|
3875
|
-
server.route({
|
|
3876
|
-
method: 'GET',
|
|
3877
|
-
path: '/v1/account/{account}/mailboxes',
|
|
3878
|
-
|
|
3879
|
-
async handler(request) {
|
|
3880
|
-
let accountObject = new Account({
|
|
3881
|
-
redis,
|
|
3882
|
-
account: request.params.account,
|
|
3883
|
-
call,
|
|
3884
|
-
secret: await getSecret(),
|
|
3885
|
-
timeout: request.headers['x-ee-timeout']
|
|
3886
|
-
});
|
|
3887
|
-
|
|
3888
|
-
try {
|
|
3889
|
-
let mailboxes = await accountObject.getMailboxListing(request.query);
|
|
3890
|
-
|
|
3891
|
-
if (mailboxes && Array.isArray(mailboxes)) {
|
|
3892
|
-
mailboxes = mailboxes.sort((a, b) => {
|
|
3893
|
-
if (a.specialUse && !b.specialUse) {
|
|
3894
|
-
return -1;
|
|
3895
|
-
}
|
|
3896
|
-
if (!a.specialUse && b.specialUse) {
|
|
3897
|
-
return 1;
|
|
3898
|
-
}
|
|
3899
|
-
if (a.specialUse && b.specialUse) {
|
|
3900
|
-
return FLAG_SORT_ORDER.indexOf(a.specialUse) - FLAG_SORT_ORDER.indexOf(b.specialUse);
|
|
3901
|
-
}
|
|
3902
|
-
|
|
3903
|
-
return a.path.localeCompare(b.path);
|
|
3904
|
-
});
|
|
3905
|
-
}
|
|
3906
|
-
|
|
3907
|
-
return { mailboxes };
|
|
3908
|
-
} catch (err) {
|
|
3909
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
3910
|
-
if (Boom.isBoom(err)) {
|
|
3911
|
-
throw err;
|
|
3912
|
-
}
|
|
3913
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
3914
|
-
if (err.code) {
|
|
3915
|
-
error.output.payload.code = err.code;
|
|
3916
|
-
}
|
|
3917
|
-
throw error;
|
|
3918
|
-
}
|
|
3919
|
-
},
|
|
3920
|
-
|
|
3921
|
-
options: {
|
|
3922
|
-
description: 'List mailboxes',
|
|
3923
|
-
notes: 'Lists all available mailboxes',
|
|
3924
|
-
tags: ['api', 'Mailbox'],
|
|
3925
|
-
|
|
3926
|
-
auth: {
|
|
3927
|
-
strategy: 'api-token',
|
|
3928
|
-
mode: 'required'
|
|
3929
|
-
},
|
|
3930
|
-
cors: CORS_CONFIG,
|
|
3931
|
-
|
|
3932
|
-
validate: {
|
|
3933
|
-
options: {
|
|
3934
|
-
stripUnknown: false,
|
|
3935
|
-
abortEarly: false,
|
|
3936
|
-
convert: true
|
|
3937
|
-
},
|
|
3938
|
-
failAction,
|
|
3939
|
-
|
|
3940
|
-
params: Joi.object({
|
|
3941
|
-
account: accountIdSchema.required()
|
|
3942
|
-
}),
|
|
3943
|
-
|
|
3944
|
-
query: Joi.object({
|
|
3945
|
-
counters: Joi.boolean()
|
|
3946
|
-
.truthy('Y', 'true', '1')
|
|
3947
|
-
.falsy('N', 'false', 0)
|
|
3948
|
-
.default(false)
|
|
3949
|
-
.description('If true, then includes message counters in the response')
|
|
3950
|
-
.label('MailboxCounters')
|
|
3951
|
-
}).label('MailboxListQuery')
|
|
3952
|
-
},
|
|
3953
|
-
|
|
3954
|
-
response: {
|
|
3955
|
-
schema: Joi.object({
|
|
3956
|
-
mailboxes: mailboxesSchema
|
|
3957
|
-
}).label('MailboxesFilterResponse'),
|
|
3958
|
-
failAction: 'log'
|
|
3959
|
-
}
|
|
3960
|
-
}
|
|
3961
|
-
});
|
|
3962
|
-
|
|
3963
|
-
server.route({
|
|
3964
|
-
method: 'POST',
|
|
3965
|
-
path: '/v1/account/{account}/mailbox',
|
|
3966
|
-
|
|
3967
|
-
async handler(request) {
|
|
3968
|
-
let accountObject = new Account({
|
|
3969
|
-
redis,
|
|
3970
|
-
account: request.params.account,
|
|
3971
|
-
call,
|
|
3972
|
-
secret: await getSecret(),
|
|
3973
|
-
timeout: request.headers['x-ee-timeout']
|
|
3974
|
-
});
|
|
3975
|
-
|
|
3976
|
-
try {
|
|
3977
|
-
return await accountObject.createMailbox(request.payload.path);
|
|
3978
|
-
} catch (err) {
|
|
3979
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
3980
|
-
if (Boom.isBoom(err)) {
|
|
3981
|
-
throw err;
|
|
3982
|
-
}
|
|
3983
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
3984
|
-
if (err.code) {
|
|
3985
|
-
error.output.payload.code = err.code;
|
|
3986
|
-
}
|
|
3987
|
-
if (err.info) {
|
|
3988
|
-
error.output.payload.details = err.info;
|
|
3989
|
-
}
|
|
3990
|
-
throw error;
|
|
3991
|
-
}
|
|
3992
|
-
},
|
|
3993
|
-
|
|
3994
|
-
options: {
|
|
3995
|
-
description: 'Create mailbox',
|
|
3996
|
-
notes: 'Create new mailbox folder',
|
|
3997
|
-
tags: ['api', 'Mailbox'],
|
|
3998
|
-
|
|
3999
|
-
plugins: {},
|
|
4000
|
-
|
|
4001
|
-
auth: {
|
|
4002
|
-
strategy: 'api-token',
|
|
4003
|
-
mode: 'required'
|
|
4004
|
-
},
|
|
4005
|
-
cors: CORS_CONFIG,
|
|
4006
|
-
|
|
4007
|
-
validate: {
|
|
4008
|
-
options: {
|
|
4009
|
-
stripUnknown: false,
|
|
4010
|
-
abortEarly: false,
|
|
4011
|
-
convert: true
|
|
4012
|
-
},
|
|
4013
|
-
failAction,
|
|
4014
|
-
|
|
4015
|
-
params: Joi.object({
|
|
4016
|
-
account: accountIdSchema.required()
|
|
4017
|
-
}),
|
|
4018
|
-
|
|
4019
|
-
payload: Joi.object({
|
|
4020
|
-
path: Joi.array()
|
|
4021
|
-
.items(Joi.string().max(256))
|
|
4022
|
-
.single()
|
|
4023
|
-
.example(['Parent folder', 'Subfolder'])
|
|
4024
|
-
.description('Mailbox path as an array or a string. If account is namespaced then namespace prefix is added by default.')
|
|
4025
|
-
.label('MailboxPath')
|
|
4026
|
-
}).label('CreateMailbox')
|
|
4027
|
-
},
|
|
4028
|
-
|
|
4029
|
-
response: {
|
|
4030
|
-
schema: Joi.object({
|
|
4031
|
-
path: Joi.string().required().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox').label('MailboxPath'),
|
|
4032
|
-
mailboxId: Joi.string().example('1439876283476').description('Mailbox ID (if server has support)').label('MailboxId'),
|
|
4033
|
-
created: Joi.boolean().example(true).description('Was the mailbox created')
|
|
4034
|
-
}).label('CreateMailboxResponse'),
|
|
4035
|
-
failAction: 'log'
|
|
4036
|
-
}
|
|
4037
|
-
}
|
|
4038
|
-
});
|
|
4039
|
-
|
|
4040
|
-
server.route({
|
|
4041
|
-
method: 'PUT',
|
|
4042
|
-
path: '/v1/account/{account}/mailbox',
|
|
4043
|
-
|
|
4044
|
-
async handler(request) {
|
|
4045
|
-
let accountObject = new Account({
|
|
4046
|
-
redis,
|
|
4047
|
-
account: request.params.account,
|
|
4048
|
-
call,
|
|
4049
|
-
secret: await getSecret(),
|
|
4050
|
-
timeout: request.headers['x-ee-timeout']
|
|
4051
|
-
});
|
|
4052
|
-
|
|
4053
|
-
try {
|
|
4054
|
-
return await accountObject.modifyMailbox(request.payload.path, request.payload.newPath, request.payload.subscribed);
|
|
4055
|
-
} catch (err) {
|
|
4056
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4057
|
-
if (Boom.isBoom(err)) {
|
|
4058
|
-
throw err;
|
|
4059
|
-
}
|
|
4060
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4061
|
-
if (err.code) {
|
|
4062
|
-
error.output.payload.code = err.code;
|
|
4063
|
-
}
|
|
4064
|
-
if (err.info) {
|
|
4065
|
-
error.output.payload.details = err.info;
|
|
4066
|
-
}
|
|
4067
|
-
throw error;
|
|
4068
|
-
}
|
|
4069
|
-
},
|
|
4070
|
-
|
|
4071
|
-
options: {
|
|
4072
|
-
description: 'Modify mailbox',
|
|
4073
|
-
notes: 'Modify an existing mailbox folder (rename or change subscription status)',
|
|
4074
|
-
tags: ['api', 'Mailbox'],
|
|
4075
|
-
|
|
4076
|
-
plugins: {},
|
|
4077
|
-
|
|
4078
|
-
auth: {
|
|
4079
|
-
strategy: 'api-token',
|
|
4080
|
-
mode: 'required'
|
|
4081
|
-
},
|
|
4082
|
-
cors: CORS_CONFIG,
|
|
4083
|
-
|
|
4084
|
-
validate: {
|
|
4085
|
-
options: {
|
|
4086
|
-
stripUnknown: false,
|
|
4087
|
-
abortEarly: false,
|
|
4088
|
-
convert: true
|
|
4089
|
-
},
|
|
4090
|
-
failAction,
|
|
4091
|
-
|
|
4092
|
-
params: Joi.object({
|
|
4093
|
-
account: accountIdSchema.required()
|
|
4094
|
-
}),
|
|
4095
|
-
|
|
4096
|
-
payload: Joi.object({
|
|
4097
|
-
path: Joi.string().required().example('Folder Name').description('Mailbox folder path to modify').label('ExistingMailboxPath'),
|
|
4098
|
-
newPath: Joi.array()
|
|
4099
|
-
.items(Joi.string().max(256))
|
|
4100
|
-
.single()
|
|
4101
|
-
.example(['Parent folder', 'Subfolder'])
|
|
4102
|
-
.description('New mailbox path as an array or a string. If account is namespaced then namespace prefix is added by default. Optional.')
|
|
4103
|
-
.label('TargetMailboxPath'),
|
|
4104
|
-
subscribed: Joi.boolean()
|
|
4105
|
-
.example(true)
|
|
4106
|
-
.description('Change mailbox subscription status. Only applies to IMAP accounts, ignored for Gmail and Outlook.')
|
|
4107
|
-
.label('SubscriptionStatus')
|
|
4108
|
-
})
|
|
4109
|
-
.or('newPath', 'subscribed')
|
|
4110
|
-
.label('ModifyMailbox')
|
|
4111
|
-
},
|
|
4112
|
-
|
|
4113
|
-
response: {
|
|
4114
|
-
schema: Joi.object({
|
|
4115
|
-
path: Joi.string().required().example('Mail').description('Mailbox folder path').label('ExistingMailboxPath'),
|
|
4116
|
-
newPath: Joi.string().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox if renamed').label('NewMailboxPath'),
|
|
4117
|
-
renamed: Joi.boolean().example(true).description('Was the mailbox renamed'),
|
|
4118
|
-
subscribed: Joi.boolean().example(true).description('Subscription status after modification')
|
|
4119
|
-
}).label('ModifyMailboxResponse'),
|
|
4120
|
-
failAction: 'log'
|
|
4121
|
-
}
|
|
4122
|
-
}
|
|
4123
|
-
});
|
|
4124
|
-
|
|
4125
|
-
server.route({
|
|
4126
|
-
method: 'DELETE',
|
|
4127
|
-
path: '/v1/account/{account}/mailbox',
|
|
4128
|
-
|
|
4129
|
-
async handler(request) {
|
|
4130
|
-
let accountObject = new Account({
|
|
4131
|
-
redis,
|
|
4132
|
-
account: request.params.account,
|
|
4133
|
-
call,
|
|
4134
|
-
secret: await getSecret(),
|
|
4135
|
-
timeout: request.headers['x-ee-timeout']
|
|
4136
|
-
});
|
|
4137
|
-
|
|
4138
|
-
try {
|
|
4139
|
-
return await accountObject.deleteMailbox(request.query.path);
|
|
4140
|
-
} catch (err) {
|
|
4141
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4142
|
-
if (Boom.isBoom(err)) {
|
|
4143
|
-
throw err;
|
|
4144
|
-
}
|
|
4145
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4146
|
-
if (err.code) {
|
|
4147
|
-
error.output.payload.code = err.code;
|
|
4148
|
-
}
|
|
4149
|
-
throw error;
|
|
4150
|
-
}
|
|
4151
|
-
},
|
|
4152
|
-
|
|
4153
|
-
options: {
|
|
4154
|
-
description: 'Delete mailbox',
|
|
4155
|
-
notes: 'Delete existing mailbox folder',
|
|
4156
|
-
tags: ['api', 'Mailbox'],
|
|
4157
|
-
|
|
4158
|
-
plugins: {},
|
|
4159
|
-
|
|
4160
|
-
auth: {
|
|
4161
|
-
strategy: 'api-token',
|
|
4162
|
-
mode: 'required'
|
|
4163
|
-
},
|
|
4164
|
-
cors: CORS_CONFIG,
|
|
4165
|
-
|
|
4166
|
-
validate: {
|
|
4167
|
-
options: {
|
|
4168
|
-
stripUnknown: false,
|
|
4169
|
-
abortEarly: false,
|
|
4170
|
-
convert: true
|
|
4171
|
-
},
|
|
4172
|
-
failAction,
|
|
4173
|
-
|
|
4174
|
-
params: Joi.object({
|
|
4175
|
-
account: accountIdSchema.required()
|
|
4176
|
-
}),
|
|
4177
|
-
|
|
4178
|
-
query: Joi.object({
|
|
4179
|
-
path: Joi.string().required().example('My Outdated Mail').description('Mailbox folder path to delete').label('MailboxPath')
|
|
4180
|
-
}).label('DeleteMailbox')
|
|
4181
|
-
},
|
|
4182
|
-
|
|
4183
|
-
response: {
|
|
4184
|
-
schema: Joi.object({
|
|
4185
|
-
path: Joi.string().required().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox').label('MailboxPath'),
|
|
4186
|
-
deleted: Joi.boolean().example(true).description('Was the mailbox deleted')
|
|
4187
|
-
}).label('DeleteMailboxResponse'),
|
|
4188
|
-
failAction: 'log'
|
|
4189
|
-
}
|
|
4190
|
-
}
|
|
4191
|
-
});
|
|
4192
|
-
|
|
4193
|
-
server.route({
|
|
4194
|
-
method: 'GET',
|
|
4195
|
-
path: '/v1/account/{account}/message/{message}/source',
|
|
4196
|
-
|
|
4197
|
-
async handler(request, h) {
|
|
4198
|
-
let accountObject = new Account({
|
|
4199
|
-
redis,
|
|
4200
|
-
account: request.params.account,
|
|
4201
|
-
call,
|
|
4202
|
-
secret: await getSecret(),
|
|
4203
|
-
timeout: request.headers['x-ee-timeout']
|
|
4204
|
-
});
|
|
4205
|
-
|
|
4206
|
-
try {
|
|
4207
|
-
const response = await accountObject.getRawMessage(request.params.message);
|
|
4208
|
-
return h.response(response);
|
|
4209
|
-
} catch (err) {
|
|
4210
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4211
|
-
if (Boom.isBoom(err)) {
|
|
4212
|
-
throw err;
|
|
4213
|
-
}
|
|
4214
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4215
|
-
if (err.code) {
|
|
4216
|
-
error.output.payload.code = err.code;
|
|
4217
|
-
}
|
|
4218
|
-
throw error;
|
|
4219
|
-
}
|
|
4220
|
-
},
|
|
4221
|
-
options: {
|
|
4222
|
-
description: 'Download raw message',
|
|
4223
|
-
notes: 'Fetches raw message as a stream',
|
|
4224
|
-
tags: ['api', 'Message'],
|
|
4225
|
-
|
|
4226
|
-
auth: {
|
|
4227
|
-
strategy: 'api-token',
|
|
4228
|
-
mode: 'required'
|
|
4229
|
-
},
|
|
4230
|
-
cors: CORS_CONFIG,
|
|
4231
|
-
|
|
4232
|
-
plugins: {
|
|
4233
|
-
'hapi-swagger': {
|
|
4234
|
-
produces: ['message/rfc822']
|
|
4235
|
-
}
|
|
4236
|
-
},
|
|
4237
|
-
|
|
4238
|
-
validate: {
|
|
4239
|
-
options: {
|
|
4240
|
-
stripUnknown: false,
|
|
4241
|
-
abortEarly: false,
|
|
4242
|
-
convert: true
|
|
4243
|
-
},
|
|
4244
|
-
failAction,
|
|
4245
|
-
|
|
4246
|
-
params: Joi.object({
|
|
4247
|
-
account: accountIdSchema.required(),
|
|
4248
|
-
message: Joi.string().base64({ paddingRequired: false, urlSafe: true }).max(256).example('AAAAAQAACnA').required().description('Message ID')
|
|
4249
|
-
}).label('RawMessageRequest')
|
|
4250
|
-
} /*,
|
|
4251
|
-
|
|
4252
|
-
response: {
|
|
4253
|
-
schema: Joi.binary().example('MIME-Version: 1.0...').description('RFC822 formatted email').label('RawMessageResponse'),
|
|
4254
|
-
failAction: 'log'
|
|
4255
|
-
}
|
|
4256
|
-
*/
|
|
4257
|
-
}
|
|
4258
|
-
});
|
|
4259
|
-
|
|
4260
|
-
server.route({
|
|
4261
|
-
method: 'GET',
|
|
4262
|
-
path: '/v1/account/{account}/attachment/{attachment}',
|
|
4263
|
-
|
|
4264
|
-
async handler(request) {
|
|
4265
|
-
let accountObject = new Account({
|
|
4266
|
-
redis,
|
|
4267
|
-
account: request.params.account,
|
|
4268
|
-
call,
|
|
4269
|
-
secret: await getSecret(),
|
|
4270
|
-
timeout: request.headers['x-ee-timeout']
|
|
4271
|
-
});
|
|
4272
|
-
|
|
4273
|
-
try {
|
|
4274
|
-
return await accountObject.getAttachment(request.params.attachment);
|
|
4275
|
-
} catch (err) {
|
|
4276
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4277
|
-
if (Boom.isBoom(err)) {
|
|
4278
|
-
throw err;
|
|
4279
|
-
}
|
|
4280
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4281
|
-
if (err.code) {
|
|
4282
|
-
error.output.payload.code = err.code;
|
|
4283
|
-
}
|
|
4284
|
-
throw error;
|
|
4285
|
-
}
|
|
4286
|
-
},
|
|
4287
|
-
options: {
|
|
4288
|
-
description: 'Download attachment',
|
|
4289
|
-
notes: 'Fetches attachment file as a binary stream',
|
|
4290
|
-
tags: ['api', 'Message'],
|
|
4291
|
-
|
|
4292
|
-
auth: {
|
|
4293
|
-
strategy: 'api-token',
|
|
4294
|
-
mode: 'required'
|
|
4295
|
-
},
|
|
4296
|
-
cors: CORS_CONFIG,
|
|
4297
|
-
|
|
4298
|
-
plugins: {
|
|
4299
|
-
'hapi-swagger': {
|
|
4300
|
-
produces: ['application/octet-stream']
|
|
4301
|
-
}
|
|
4302
|
-
},
|
|
4303
|
-
|
|
4304
|
-
validate: {
|
|
4305
|
-
options: {
|
|
4306
|
-
stripUnknown: false,
|
|
4307
|
-
abortEarly: false,
|
|
4308
|
-
convert: true
|
|
4309
|
-
},
|
|
4310
|
-
failAction,
|
|
4311
|
-
|
|
4312
|
-
params: Joi.object({
|
|
4313
|
-
account: accountIdSchema.required(),
|
|
4314
|
-
attachment: Joi.string()
|
|
4315
|
-
.base64({ paddingRequired: false, urlSafe: true })
|
|
4316
|
-
.max(2 * 1024)
|
|
4317
|
-
.required()
|
|
4318
|
-
.example('AAAAAQAACnAcde')
|
|
4319
|
-
.description('Attachment ID')
|
|
4320
|
-
})
|
|
4321
|
-
}
|
|
4322
|
-
}
|
|
4323
|
-
});
|
|
4324
|
-
|
|
4325
|
-
server.route({
|
|
4326
|
-
method: 'GET',
|
|
4327
|
-
path: '/v1/account/{account}/message/{message}',
|
|
4328
|
-
|
|
4329
|
-
async handler(request, h) {
|
|
4330
|
-
let accountObject = new Account({
|
|
4331
|
-
redis,
|
|
4332
|
-
account: request.params.account,
|
|
4333
|
-
call,
|
|
4334
|
-
secret: await getSecret(),
|
|
4335
|
-
esClient: await h.getESClient(request.logger),
|
|
4336
|
-
timeout: request.headers['x-ee-timeout']
|
|
4337
|
-
});
|
|
4338
|
-
|
|
4339
|
-
try {
|
|
4340
|
-
return await accountObject.getMessage(request.params.message, request.query);
|
|
4341
|
-
} catch (err) {
|
|
4342
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4343
|
-
if (Boom.isBoom(err)) {
|
|
4344
|
-
throw err;
|
|
4345
|
-
}
|
|
4346
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4347
|
-
if (err.code) {
|
|
4348
|
-
error.output.payload.code = err.code;
|
|
4349
|
-
}
|
|
4350
|
-
throw error;
|
|
4351
|
-
}
|
|
4352
|
-
},
|
|
4353
|
-
options: {
|
|
4354
|
-
description: 'Get message information',
|
|
4355
|
-
notes: 'Returns details of a specific message. By default text content is not included, use textType value to force retrieving text',
|
|
4356
|
-
tags: ['api', 'Message'],
|
|
4357
|
-
|
|
4358
|
-
auth: {
|
|
4359
|
-
strategy: 'api-token',
|
|
4360
|
-
mode: 'required'
|
|
4361
|
-
},
|
|
4362
|
-
cors: CORS_CONFIG,
|
|
4363
|
-
|
|
4364
|
-
validate: {
|
|
4365
|
-
options: {
|
|
4366
|
-
stripUnknown: false,
|
|
4367
|
-
abortEarly: false,
|
|
4368
|
-
convert: true
|
|
4369
|
-
},
|
|
4370
|
-
failAction,
|
|
4371
|
-
|
|
4372
|
-
query: Joi.object({
|
|
4373
|
-
maxBytes: Joi.number()
|
|
4374
|
-
.integer()
|
|
4375
|
-
.min(0)
|
|
4376
|
-
.max(1024 * 1024 * 1024)
|
|
4377
|
-
.example(5 * 1025 * 1024)
|
|
4378
|
-
.description('Max length of text content'),
|
|
4379
|
-
textType: Joi.string()
|
|
4380
|
-
.lowercase()
|
|
4381
|
-
.valid('html', 'plain', '*')
|
|
4382
|
-
.example('*')
|
|
4383
|
-
.description('Which text content to return, use * for all. By default text content is not returned.'),
|
|
4384
|
-
|
|
4385
|
-
webSafeHtml: Joi.boolean()
|
|
4386
|
-
.truthy('Y', 'true', '1')
|
|
4387
|
-
.falsy('N', 'false', 0)
|
|
4388
|
-
.default(false)
|
|
4389
|
-
.description(
|
|
4390
|
-
'Shorthand option to fetch and preprocess HTML and inline images. Overrides `textType`, `preProcessHtml`, and `embedAttachedImages` options.'
|
|
4391
|
-
)
|
|
4392
|
-
.label('WebSafeHtml'),
|
|
4393
|
-
|
|
4394
|
-
embedAttachedImages: Joi.boolean()
|
|
4395
|
-
.truthy('Y', 'true', '1')
|
|
4396
|
-
.falsy('N', 'false', 0)
|
|
4397
|
-
.default(false)
|
|
4398
|
-
.description('If true, then fetches attached images and embeds these in the HTML as data URIs')
|
|
4399
|
-
.label('EmbedImages'),
|
|
4400
|
-
|
|
4401
|
-
preProcessHtml: Joi.boolean()
|
|
4402
|
-
.truthy('Y', 'true', '1')
|
|
4403
|
-
.falsy('N', 'false', 0)
|
|
4404
|
-
.default(false)
|
|
4405
|
-
.description('If true, then pre-processes HTML for compatibility')
|
|
4406
|
-
.label('PreProcess'),
|
|
4407
|
-
|
|
4408
|
-
markAsSeen: Joi.boolean()
|
|
4409
|
-
.truthy('Y', 'true', '1')
|
|
4410
|
-
.falsy('N', 'false', 0)
|
|
4411
|
-
.default(false)
|
|
4412
|
-
.description('If true, then marks unseen email as seen while returning the message')
|
|
4413
|
-
.label('MarkAsSeen'),
|
|
4414
|
-
|
|
4415
|
-
documentStore: documentStoreSchema.default(false)
|
|
4416
|
-
}),
|
|
4417
|
-
|
|
4418
|
-
params: Joi.object({
|
|
4419
|
-
account: accountIdSchema.required(),
|
|
4420
|
-
message: Joi.string().base64({ paddingRequired: false, urlSafe: true }).max(256).required().example('AAAAAQAACnA').description('Message ID')
|
|
4421
|
-
})
|
|
4422
|
-
},
|
|
4423
|
-
|
|
4424
|
-
response: {
|
|
4425
|
-
schema: messageDetailsSchema,
|
|
4426
|
-
failAction: 'log'
|
|
4427
|
-
}
|
|
4428
|
-
}
|
|
4429
|
-
});
|
|
4430
|
-
|
|
4431
|
-
server.route({
|
|
4432
|
-
method: 'POST',
|
|
4433
|
-
path: '/v1/account/{account}/message',
|
|
4434
|
-
|
|
4435
|
-
async handler(request) {
|
|
4436
|
-
let accountObject = new Account({
|
|
4437
|
-
redis,
|
|
4438
|
-
account: request.params.account,
|
|
4439
|
-
call,
|
|
4440
|
-
secret: await getSecret(),
|
|
4441
|
-
timeout: request.headers['x-ee-timeout']
|
|
4442
|
-
});
|
|
4443
|
-
|
|
4444
|
-
try {
|
|
4445
|
-
return await accountObject.uploadMessage(request.payload);
|
|
4446
|
-
} catch (err) {
|
|
4447
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4448
|
-
if (Boom.isBoom(err)) {
|
|
4449
|
-
throw err;
|
|
4450
|
-
}
|
|
4451
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4452
|
-
if (err.code) {
|
|
4453
|
-
error.output.payload.code = err.code;
|
|
4454
|
-
}
|
|
4455
|
-
throw error;
|
|
4456
|
-
}
|
|
4457
|
-
},
|
|
4458
|
-
options: {
|
|
4459
|
-
payload: {
|
|
4460
|
-
maxBytes: MAX_BODY_SIZE,
|
|
4461
|
-
timeout: MAX_PAYLOAD_TIMEOUT
|
|
4462
|
-
},
|
|
4463
|
-
|
|
4464
|
-
description: 'Upload message',
|
|
4465
|
-
notes: 'Upload a message structure, compile it into an EML file and store it into selected mailbox.',
|
|
4466
|
-
tags: ['api', 'Message'],
|
|
4467
|
-
|
|
4468
|
-
plugins: {},
|
|
4469
|
-
|
|
4470
|
-
auth: {
|
|
4471
|
-
strategy: 'api-token',
|
|
4472
|
-
mode: 'required'
|
|
4473
|
-
},
|
|
4474
|
-
cors: CORS_CONFIG,
|
|
4475
|
-
|
|
4476
|
-
validate: {
|
|
4477
|
-
options: {
|
|
4478
|
-
stripUnknown: false,
|
|
4479
|
-
abortEarly: false,
|
|
4480
|
-
convert: true
|
|
4481
|
-
},
|
|
4482
|
-
failAction,
|
|
4483
|
-
|
|
4484
|
-
params: Joi.object({
|
|
4485
|
-
account: accountIdSchema.required()
|
|
4486
|
-
}),
|
|
4487
|
-
|
|
4488
|
-
payload: Joi.object({
|
|
4489
|
-
path: Joi.string().required().example('INBOX').description('Target mailbox folder path'),
|
|
4490
|
-
|
|
4491
|
-
flags: Joi.array().items(Joi.string().max(128)).example(['\\Seen', '\\Draft']).default([]).description('Message flags').label('Flags'),
|
|
4492
|
-
internalDate: Joi.date().iso().example('2021-07-08T07:06:34.336Z').description('Sets the internal date for this message'),
|
|
4493
|
-
|
|
4494
|
-
reference: messageReferenceSchema,
|
|
4495
|
-
|
|
4496
|
-
raw: Joi.string()
|
|
4497
|
-
.base64()
|
|
4498
|
-
.max(MAX_ATTACHMENT_SIZE)
|
|
4499
|
-
.example('TUlNRS1WZXJzaW9uOiAxLjANClN1YmplY3Q6IGhlbGxvIHdvcmxkDQoNCkhlbGxvIQ0K')
|
|
4500
|
-
.description(
|
|
4501
|
-
'A Base64-encoded email message in RFC 822 format. If you provide other fields along with raw, those fields will override the corresponding values in the raw message.'
|
|
4502
|
-
)
|
|
4503
|
-
.label('RFC822Raw'),
|
|
4504
|
-
|
|
4505
|
-
from: fromAddressSchema,
|
|
4506
|
-
|
|
4507
|
-
to: Joi.array()
|
|
4508
|
-
.items(addressSchema)
|
|
4509
|
-
.single()
|
|
4510
|
-
.description('List of addresses')
|
|
4511
|
-
.example([{ address: 'recipient@example.com' }])
|
|
4512
|
-
.label('AddressList'),
|
|
4513
|
-
|
|
4514
|
-
cc: Joi.array().items(addressSchema).single().description('List of addresses').label('AddressList'),
|
|
4515
|
-
|
|
4516
|
-
bcc: Joi.array().items(addressSchema).single().description('List of addresses').label('AddressList'),
|
|
4517
|
-
|
|
4518
|
-
subject: Joi.string()
|
|
4519
|
-
.allow('')
|
|
4520
|
-
.max(10 * 1024)
|
|
4521
|
-
.example('What a wonderful message')
|
|
4522
|
-
.description('Message subject'),
|
|
4523
|
-
|
|
4524
|
-
text: Joi.string().max(MAX_ATTACHMENT_SIZE).example('Hello from myself!').description('Message Text'),
|
|
4525
|
-
|
|
4526
|
-
html: Joi.string().max(MAX_ATTACHMENT_SIZE).example('<p>Hello from myself!</p>').description('Message HTML'),
|
|
4527
|
-
|
|
4528
|
-
attachments: Joi.array()
|
|
4529
|
-
.items(
|
|
4530
|
-
Joi.object({
|
|
4531
|
-
filename: Joi.string().max(256).example('transparent.gif'),
|
|
4532
|
-
content: Joi.string()
|
|
4533
|
-
.base64()
|
|
4534
|
-
.max(MAX_ATTACHMENT_SIZE)
|
|
4535
|
-
.required()
|
|
4536
|
-
.example('R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=')
|
|
4537
|
-
.description('Base64 formatted attachment file')
|
|
4538
|
-
.when('reference', {
|
|
4539
|
-
is: Joi.exist().not(false, null),
|
|
4540
|
-
then: Joi.forbidden(),
|
|
4541
|
-
otherwise: Joi.required()
|
|
4542
|
-
}),
|
|
4543
|
-
|
|
4544
|
-
contentType: Joi.string().lowercase().max(256).example('image/gif'),
|
|
4545
|
-
contentDisposition: Joi.string().lowercase().valid('inline', 'attachment'),
|
|
4546
|
-
cid: Joi.string().max(256).example('unique-image-id@localhost').description('Content-ID value for embedded images'),
|
|
4547
|
-
encoding: Joi.string().valid('base64').default('base64'),
|
|
4548
|
-
|
|
4549
|
-
reference: Joi.string()
|
|
4550
|
-
.base64({ paddingRequired: false, urlSafe: true })
|
|
4551
|
-
.max(256)
|
|
4552
|
-
.allow(false, null)
|
|
4553
|
-
.example('AAAAAQAACnAcde')
|
|
4554
|
-
.description(
|
|
4555
|
-
'References an existing attachment by its ID instead of providing new attachment content. If this field is set, the `content` field must not be included. If not set, the `content` field is required.'
|
|
4556
|
-
)
|
|
4557
|
-
}).label('UploadAttachment')
|
|
4558
|
-
)
|
|
4559
|
-
.description('List of attachments')
|
|
4560
|
-
.label('UploadAttachmentList'),
|
|
4561
|
-
|
|
4562
|
-
messageId: Joi.string().max(996).example('<test123@example.com>').description('Message ID'),
|
|
4563
|
-
headers: Joi.object().label('CustomHeaders').description('Custom Headers').unknown().example({
|
|
4564
|
-
'X-My-Custom-Header': 'Custom header value'
|
|
4565
|
-
}),
|
|
4566
|
-
|
|
4567
|
-
locale: Joi.string().empty('').max(100).example('fr').description('Optional locale'),
|
|
4568
|
-
tz: Joi.string().empty('').max(100).example('Europe/Tallinn').description('Optional timezone')
|
|
4569
|
-
}).label('MessageUpload')
|
|
4570
|
-
},
|
|
4571
|
-
|
|
4572
|
-
response: {
|
|
4573
|
-
schema: Joi.object({
|
|
4574
|
-
id: Joi.string()
|
|
4575
|
-
.example('AAAAAgAACrI')
|
|
4576
|
-
.description(
|
|
4577
|
-
'Unique identifier for the message. NB! This and other fields might not be present if server did not provide enough information'
|
|
4578
|
-
)
|
|
4579
|
-
.label('MessageAppendId'),
|
|
4580
|
-
path: Joi.string().example('INBOX').description('Folder this message was uploaded to').label('MessageAppendPath'),
|
|
4581
|
-
uid: Joi.number().integer().example(12345).description('UID of uploaded message'),
|
|
4582
|
-
uidValidity: Joi.string().example('12345').description('UIDVALIDITY of the target folder. Numeric value cast as string.'),
|
|
4583
|
-
seq: Joi.number().integer().example(12345).description('Sequence number of uploaded message'),
|
|
4584
|
-
|
|
4585
|
-
messageId: Joi.string().max(996).example('<test123@example.com>').description('Message ID'),
|
|
4586
|
-
|
|
4587
|
-
reference: Joi.object({
|
|
4588
|
-
message: Joi.string()
|
|
4589
|
-
.base64({ paddingRequired: false, urlSafe: true })
|
|
4590
|
-
.max(256)
|
|
4591
|
-
.required()
|
|
4592
|
-
.example('AAAAAQAACnA')
|
|
4593
|
-
.description('Referenced message ID'),
|
|
4594
|
-
success: Joi.boolean().example(true).description('Was the referenced message processed').label('ResponseReferenceSuccess'),
|
|
4595
|
-
documentStore: documentStoreSchema.default(false),
|
|
4596
|
-
error: Joi.string().example('Referenced message was not found').description('An error message if referenced message processing failed')
|
|
4597
|
-
})
|
|
4598
|
-
.description('Reference info if referencing was requested')
|
|
4599
|
-
.label('ResponseReference')
|
|
4600
|
-
}).label('MessageUploadResponse'),
|
|
4601
|
-
failAction: 'log'
|
|
4602
|
-
}
|
|
4603
|
-
}
|
|
4604
|
-
});
|
|
4605
|
-
|
|
4606
|
-
server.route({
|
|
4607
|
-
method: 'PUT',
|
|
4608
|
-
path: '/v1/account/{account}/message/{message}',
|
|
4609
|
-
|
|
4610
|
-
async handler(request) {
|
|
4611
|
-
let accountObject = new Account({
|
|
4612
|
-
redis,
|
|
4613
|
-
account: request.params.account,
|
|
4614
|
-
call,
|
|
4615
|
-
secret: await getSecret(),
|
|
4616
|
-
timeout: request.headers['x-ee-timeout']
|
|
4617
|
-
});
|
|
4618
|
-
|
|
4619
|
-
try {
|
|
4620
|
-
return await accountObject.updateMessage(request.params.message, request.payload);
|
|
4621
|
-
} catch (err) {
|
|
4622
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4623
|
-
if (Boom.isBoom(err)) {
|
|
4624
|
-
throw err;
|
|
4625
|
-
}
|
|
4626
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4627
|
-
if (err.code) {
|
|
4628
|
-
error.output.payload.code = err.code;
|
|
4629
|
-
}
|
|
4630
|
-
throw error;
|
|
4631
|
-
}
|
|
4632
|
-
},
|
|
4633
|
-
options: {
|
|
4634
|
-
description: 'Update message',
|
|
4635
|
-
notes: 'Update message information. Mainly this means changing message flag values',
|
|
4636
|
-
tags: ['api', 'Message'],
|
|
4637
|
-
|
|
4638
|
-
plugins: {},
|
|
4639
|
-
|
|
4640
|
-
auth: {
|
|
4641
|
-
strategy: 'api-token',
|
|
4642
|
-
mode: 'required'
|
|
4643
|
-
},
|
|
4644
|
-
cors: CORS_CONFIG,
|
|
4645
|
-
|
|
4646
|
-
validate: {
|
|
4647
|
-
options: {
|
|
4648
|
-
stripUnknown: false,
|
|
4649
|
-
abortEarly: false,
|
|
4650
|
-
convert: true
|
|
4651
|
-
},
|
|
4652
|
-
failAction,
|
|
4653
|
-
|
|
4654
|
-
params: Joi.object({
|
|
4655
|
-
account: accountIdSchema.required(),
|
|
4656
|
-
message: Joi.string().max(256).required().example('AAAAAQAACnA').description('Message ID')
|
|
4657
|
-
}),
|
|
4658
|
-
|
|
4659
|
-
payload: messageUpdateSchema
|
|
4660
|
-
},
|
|
4661
|
-
response: {
|
|
4662
|
-
schema: Joi.object({
|
|
4663
|
-
flags: Joi.object({
|
|
4664
|
-
add: Joi.array().items(Joi.string()).example(['\\Seen', '\\Flagged']),
|
|
4665
|
-
delete: Joi.array().items(Joi.string()).example(['\\Draft']),
|
|
4666
|
-
set: Joi.array().items(Joi.string()).example(['\\Seen'])
|
|
4667
|
-
}).label('FlagResponse'),
|
|
4668
|
-
labels: Joi.object({
|
|
4669
|
-
add: Joi.array().items(Joi.string()).example(['Label1', 'Label2']),
|
|
4670
|
-
delete: Joi.array().items(Joi.string()).example(['Label3']),
|
|
4671
|
-
set: Joi.array().items(Joi.string()).example(['Label1'])
|
|
4672
|
-
}).label('FlagResponse')
|
|
4673
|
-
}).label('MessageUpdateResponse'),
|
|
4674
|
-
failAction: 'log'
|
|
4675
|
-
}
|
|
4676
|
-
}
|
|
4677
|
-
});
|
|
4678
|
-
|
|
4679
|
-
server.route({
|
|
4680
|
-
method: 'PUT',
|
|
4681
|
-
path: '/v1/account/{account}/messages',
|
|
4682
|
-
|
|
4683
|
-
async handler(request) {
|
|
4684
|
-
let accountObject = new Account({
|
|
4685
|
-
redis,
|
|
4686
|
-
account: request.params.account,
|
|
4687
|
-
call,
|
|
4688
|
-
secret: await getSecret(),
|
|
4689
|
-
timeout: request.headers['x-ee-timeout']
|
|
4690
|
-
});
|
|
4691
|
-
|
|
4692
|
-
try {
|
|
4693
|
-
return await accountObject.updateMessages(request.query.path, request.payload.search, request.payload.update);
|
|
4694
|
-
} catch (err) {
|
|
4695
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4696
|
-
if (Boom.isBoom(err)) {
|
|
4697
|
-
throw err;
|
|
4698
|
-
}
|
|
4699
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4700
|
-
if (err.code) {
|
|
4701
|
-
error.output.payload.code = err.code;
|
|
4702
|
-
}
|
|
4703
|
-
throw error;
|
|
4704
|
-
}
|
|
4705
|
-
},
|
|
4706
|
-
options: {
|
|
4707
|
-
description: 'Update messages',
|
|
4708
|
-
notes: 'Update message information for matching emails',
|
|
4709
|
-
tags: ['api', 'Multi Message Actions'],
|
|
4710
|
-
|
|
4711
|
-
plugins: {},
|
|
4712
|
-
|
|
4713
|
-
auth: {
|
|
4714
|
-
strategy: 'api-token',
|
|
4715
|
-
mode: 'required'
|
|
4716
|
-
},
|
|
4717
|
-
cors: CORS_CONFIG,
|
|
4718
|
-
|
|
4719
|
-
validate: {
|
|
4720
|
-
options: {
|
|
4721
|
-
stripUnknown: false,
|
|
4722
|
-
abortEarly: false,
|
|
4723
|
-
convert: true
|
|
4724
|
-
},
|
|
4725
|
-
failAction,
|
|
4726
|
-
|
|
4727
|
-
params: Joi.object({
|
|
4728
|
-
account: accountIdSchema.required()
|
|
4729
|
-
}),
|
|
4730
|
-
|
|
4731
|
-
query: Joi.object({
|
|
4732
|
-
path: Joi.string().empty('').required().example('INBOX').description(listMessageFolderPathDescription)
|
|
4733
|
-
}).label('MessagesUpdateQuery'),
|
|
4734
|
-
|
|
4735
|
-
payload: Joi.object({
|
|
4736
|
-
search: searchSchema,
|
|
4737
|
-
update: messageUpdateSchema
|
|
4738
|
-
}).label('MessagesUpdateRequest')
|
|
4739
|
-
},
|
|
4740
|
-
response: {
|
|
4741
|
-
schema: Joi.object({
|
|
4742
|
-
flags: Joi.object({
|
|
4743
|
-
add: Joi.array().items(Joi.string()).example(['\\Seen', '\\Flagged']),
|
|
4744
|
-
delete: Joi.array().items(Joi.string()).example(['\\Draft']),
|
|
4745
|
-
set: Joi.array().items(Joi.string()).example(['\\Seen'])
|
|
4746
|
-
}).label('FlagResponse'),
|
|
4747
|
-
labels: Joi.object({
|
|
4748
|
-
add: Joi.array().items(Joi.string()).example(['Label1', 'Label2']),
|
|
4749
|
-
delete: Joi.array().items(Joi.string()).example(['Label3']),
|
|
4750
|
-
set: Joi.array().items(Joi.string()).example(['Label1'])
|
|
4751
|
-
}).label('FlagResponse')
|
|
4752
|
-
}).label('MessageUpdateResponse'),
|
|
4753
|
-
failAction: 'log'
|
|
4754
|
-
}
|
|
4755
|
-
}
|
|
4756
|
-
});
|
|
4757
|
-
|
|
4758
|
-
server.route({
|
|
4759
|
-
method: 'PUT',
|
|
4760
|
-
path: '/v1/account/{account}/message/{message}/move',
|
|
4761
|
-
|
|
4762
|
-
async handler(request) {
|
|
4763
|
-
let accountObject = new Account({
|
|
4764
|
-
redis,
|
|
4765
|
-
account: request.params.account,
|
|
4766
|
-
call,
|
|
4767
|
-
secret: await getSecret(),
|
|
4768
|
-
timeout: request.headers['x-ee-timeout']
|
|
4769
|
-
});
|
|
4770
|
-
|
|
4771
|
-
try {
|
|
4772
|
-
return await accountObject.moveMessage(
|
|
4773
|
-
request.params.message,
|
|
4774
|
-
{ path: request.payload.path },
|
|
4775
|
-
{
|
|
4776
|
-
source: request.payload.source
|
|
4777
|
-
? {
|
|
4778
|
-
path: request.payload.source
|
|
4779
|
-
}
|
|
4780
|
-
: null
|
|
4781
|
-
}
|
|
4782
|
-
);
|
|
4783
|
-
} catch (err) {
|
|
4784
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4785
|
-
if (Boom.isBoom(err)) {
|
|
4786
|
-
throw err;
|
|
4787
|
-
}
|
|
4788
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4789
|
-
if (err.code) {
|
|
4790
|
-
error.output.payload.code = err.code;
|
|
4791
|
-
}
|
|
4792
|
-
throw error;
|
|
4793
|
-
}
|
|
4794
|
-
},
|
|
4795
|
-
options: {
|
|
4796
|
-
description: 'Move a message to a specified folder',
|
|
4797
|
-
notes: 'Moves a message to a target folder',
|
|
4798
|
-
tags: ['api', 'Message'],
|
|
4799
|
-
|
|
4800
|
-
plugins: {},
|
|
4801
|
-
|
|
4802
|
-
auth: {
|
|
4803
|
-
strategy: 'api-token',
|
|
4804
|
-
mode: 'required'
|
|
4805
|
-
},
|
|
4806
|
-
cors: CORS_CONFIG,
|
|
4807
|
-
|
|
4808
|
-
validate: {
|
|
4809
|
-
options: {
|
|
4810
|
-
stripUnknown: false,
|
|
4811
|
-
abortEarly: false,
|
|
4812
|
-
convert: true
|
|
4813
|
-
},
|
|
4814
|
-
failAction,
|
|
4815
|
-
|
|
4816
|
-
params: Joi.object({
|
|
4817
|
-
account: accountIdSchema.required(),
|
|
4818
|
-
message: Joi.string().max(256).required().example('AAAAAQAACnA').description('Message ID')
|
|
4819
|
-
}),
|
|
4820
|
-
|
|
4821
|
-
payload: Joi.object({
|
|
4822
|
-
path: Joi.string().required().example('INBOX').description('Destination mailbox folder path'),
|
|
4823
|
-
source: Joi.string()
|
|
4824
|
-
.example('INBOX')
|
|
4825
|
-
.description('Source mailbox folder path (Gmail API only). Needed to remove the label from the message.')
|
|
4826
|
-
})
|
|
4827
|
-
.example({ path: 'Target/Folder' })
|
|
4828
|
-
.label('MessageMove')
|
|
4829
|
-
},
|
|
4830
|
-
|
|
4831
|
-
response: {
|
|
4832
|
-
schema: Joi.object({
|
|
4833
|
-
path: Joi.string().required().example('INBOX').description('Destination mailbox folder path'),
|
|
4834
|
-
id: Joi.string().max(256).example('AAAAAQAACnA').description('ID of the moved message. Only included if the server provides it.'),
|
|
4835
|
-
uid: Joi.number()
|
|
4836
|
-
.integer()
|
|
4837
|
-
.example(12345)
|
|
4838
|
-
.description('UID of the moved message, applies only to IMAP accounts. Only included if the server provides it.')
|
|
4839
|
-
}).label('MessageMoveResponse'),
|
|
4840
|
-
failAction: 'log'
|
|
4841
|
-
}
|
|
4842
|
-
}
|
|
4843
|
-
});
|
|
4844
|
-
|
|
4845
|
-
server.route({
|
|
4846
|
-
method: 'PUT',
|
|
4847
|
-
path: '/v1/account/{account}/messages/move',
|
|
4848
|
-
|
|
4849
|
-
async handler(request) {
|
|
4850
|
-
let accountObject = new Account({
|
|
4851
|
-
redis,
|
|
4852
|
-
account: request.params.account,
|
|
4853
|
-
call,
|
|
4854
|
-
secret: await getSecret(),
|
|
4855
|
-
timeout: request.headers['x-ee-timeout']
|
|
4856
|
-
});
|
|
4857
|
-
|
|
4858
|
-
try {
|
|
4859
|
-
return await accountObject.moveMessages(request.query.path, request.payload.search, { path: request.payload.path });
|
|
4860
|
-
} catch (err) {
|
|
4861
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4862
|
-
if (Boom.isBoom(err)) {
|
|
4863
|
-
throw err;
|
|
4864
|
-
}
|
|
4865
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4866
|
-
if (err.code) {
|
|
4867
|
-
error.output.payload.code = err.code;
|
|
4868
|
-
}
|
|
4869
|
-
throw error;
|
|
4870
|
-
}
|
|
4871
|
-
},
|
|
4872
|
-
options: {
|
|
4873
|
-
description: 'Move messages',
|
|
4874
|
-
notes: 'Move messages matching to a search query to another folder',
|
|
4875
|
-
tags: ['api', 'Multi Message Actions'],
|
|
4876
|
-
|
|
4877
|
-
plugins: {},
|
|
4878
|
-
|
|
4879
|
-
auth: {
|
|
4880
|
-
strategy: 'api-token',
|
|
4881
|
-
mode: 'required'
|
|
4882
|
-
},
|
|
4883
|
-
cors: CORS_CONFIG,
|
|
4884
|
-
|
|
4885
|
-
validate: {
|
|
4886
|
-
options: {
|
|
4887
|
-
stripUnknown: false,
|
|
4888
|
-
abortEarly: false,
|
|
4889
|
-
convert: true
|
|
4890
|
-
},
|
|
4891
|
-
failAction,
|
|
4892
|
-
|
|
4893
|
-
params: Joi.object({
|
|
4894
|
-
account: accountIdSchema.required()
|
|
4895
|
-
}),
|
|
4896
|
-
|
|
4897
|
-
query: Joi.object({
|
|
4898
|
-
path: Joi.string().empty('').required().example('INBOX').description(listMessageFolderPathDescription)
|
|
4899
|
-
}).label('MessagesMoveQuery'),
|
|
4900
|
-
|
|
4901
|
-
payload: Joi.object({
|
|
4902
|
-
search: searchSchema,
|
|
4903
|
-
path: Joi.string().required().example('INBOX').description('Target mailbox folder path')
|
|
4904
|
-
}).label('MessagesMoveRequest')
|
|
4905
|
-
},
|
|
4906
|
-
|
|
4907
|
-
response: {
|
|
4908
|
-
schema: Joi.object({
|
|
4909
|
-
path: Joi.string().required().example('INBOX').description('Target mailbox folder path'),
|
|
4910
|
-
|
|
4911
|
-
idMap: Joi.array()
|
|
4912
|
-
.items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
|
|
4913
|
-
.example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
|
|
4914
|
-
.description('An optional map of source and target ID values, if the server provided this info')
|
|
4915
|
-
.label('IdMapArray'),
|
|
4916
|
-
|
|
4917
|
-
emailIds: Joi.array()
|
|
4918
|
-
.items(Joi.string().example('1278455344230334865'))
|
|
4919
|
-
.description('An optional list of emailId values, if the server supports unique email IDs')
|
|
4920
|
-
.label('EmailIdsArray')
|
|
4921
|
-
}).label('MessagesMoveResponse'),
|
|
4922
|
-
failAction: 'log'
|
|
4923
|
-
}
|
|
4924
|
-
}
|
|
4925
|
-
});
|
|
4926
|
-
|
|
4927
|
-
server.route({
|
|
4928
|
-
method: 'DELETE',
|
|
4929
|
-
path: '/v1/account/{account}/message/{message}',
|
|
4930
|
-
|
|
4931
|
-
async handler(request) {
|
|
4932
|
-
let accountObject = new Account({
|
|
4933
|
-
redis,
|
|
4934
|
-
account: request.params.account,
|
|
4935
|
-
call,
|
|
4936
|
-
secret: await getSecret(),
|
|
4937
|
-
timeout: request.headers['x-ee-timeout']
|
|
4938
|
-
});
|
|
4939
|
-
|
|
4940
|
-
try {
|
|
4941
|
-
return await accountObject.deleteMessage(request.params.message, request.query.force);
|
|
4942
|
-
} catch (err) {
|
|
4943
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
4944
|
-
if (Boom.isBoom(err)) {
|
|
4945
|
-
throw err;
|
|
4946
|
-
}
|
|
4947
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
4948
|
-
if (err.code) {
|
|
4949
|
-
error.output.payload.code = err.code;
|
|
4950
|
-
}
|
|
4951
|
-
throw error;
|
|
4952
|
-
}
|
|
4953
|
-
},
|
|
4954
|
-
options: {
|
|
4955
|
-
description: 'Delete message',
|
|
4956
|
-
notes: 'Move message to Trash or delete it if already in Trash',
|
|
4957
|
-
tags: ['api', 'Message'],
|
|
4958
|
-
|
|
4959
|
-
plugins: {},
|
|
4960
|
-
|
|
4961
|
-
auth: {
|
|
4962
|
-
strategy: 'api-token',
|
|
4963
|
-
mode: 'required'
|
|
4964
|
-
},
|
|
4965
|
-
cors: CORS_CONFIG,
|
|
4966
|
-
|
|
4967
|
-
validate: {
|
|
4968
|
-
options: {
|
|
4969
|
-
stripUnknown: false,
|
|
4970
|
-
abortEarly: false,
|
|
4971
|
-
convert: true
|
|
4972
|
-
},
|
|
4973
|
-
failAction,
|
|
4974
|
-
|
|
4975
|
-
query: Joi.object({
|
|
4976
|
-
force: Joi.boolean()
|
|
4977
|
-
.truthy('Y', 'true', '1')
|
|
4978
|
-
.falsy('N', 'false', 0)
|
|
4979
|
-
.default(false)
|
|
4980
|
-
.description('Delete message even if not in Trash. Not supported for Gmail API accounts.')
|
|
4981
|
-
.label('ForceDelete')
|
|
4982
|
-
}).label('MessageDeleteQuery'),
|
|
4983
|
-
|
|
4984
|
-
params: Joi.object({
|
|
4985
|
-
account: accountIdSchema.required(),
|
|
4986
|
-
message: Joi.string().max(256).required().example('AAAAAQAACnA').description('Message ID')
|
|
4987
|
-
}).label('MessageDelete')
|
|
4988
|
-
},
|
|
4989
|
-
response: {
|
|
4990
|
-
schema: Joi.object({
|
|
4991
|
-
deleted: Joi.boolean().example(false).description('Was the delete action executed'),
|
|
4992
|
-
moved: Joi.object({
|
|
4993
|
-
destination: Joi.string().required().example('Trash').description('Trash folder path').label('TrashPath'),
|
|
4994
|
-
message: Joi.string().required().example('AAAAAwAAAWg').description('Message ID in Trash').label('TrashMessageId')
|
|
4995
|
-
}).description('Present if message was moved to Trash')
|
|
4996
|
-
}).label('MessageDeleteResponse'),
|
|
4997
|
-
failAction: 'log'
|
|
4998
|
-
}
|
|
4999
|
-
}
|
|
5000
|
-
});
|
|
5001
|
-
|
|
5002
|
-
server.route({
|
|
5003
|
-
method: 'PUT',
|
|
5004
|
-
path: '/v1/account/{account}/messages/delete',
|
|
5005
|
-
|
|
5006
|
-
async handler(request) {
|
|
5007
|
-
let accountObject = new Account({
|
|
5008
|
-
redis,
|
|
5009
|
-
account: request.params.account,
|
|
5010
|
-
call,
|
|
5011
|
-
secret: await getSecret(),
|
|
5012
|
-
timeout: request.headers['x-ee-timeout']
|
|
5013
|
-
});
|
|
5014
|
-
|
|
5015
|
-
try {
|
|
5016
|
-
return await accountObject.deleteMessages(request.query.path, request.payload.search, request.query.force);
|
|
5017
|
-
} catch (err) {
|
|
5018
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
5019
|
-
if (Boom.isBoom(err)) {
|
|
5020
|
-
throw err;
|
|
5021
|
-
}
|
|
5022
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
5023
|
-
if (err.code) {
|
|
5024
|
-
error.output.payload.code = err.code;
|
|
5025
|
-
}
|
|
5026
|
-
throw error;
|
|
5027
|
-
}
|
|
5028
|
-
},
|
|
5029
|
-
options: {
|
|
5030
|
-
description: 'Delete messages',
|
|
5031
|
-
notes: 'Move messages to Trash or delete these if already in Trash',
|
|
5032
|
-
tags: ['api', 'Multi Message Actions'],
|
|
5033
|
-
|
|
5034
|
-
plugins: {},
|
|
5035
|
-
|
|
5036
|
-
auth: {
|
|
5037
|
-
strategy: 'api-token',
|
|
5038
|
-
mode: 'required'
|
|
5039
|
-
},
|
|
5040
|
-
cors: CORS_CONFIG,
|
|
5041
|
-
|
|
5042
|
-
validate: {
|
|
5043
|
-
options: {
|
|
5044
|
-
stripUnknown: false,
|
|
5045
|
-
abortEarly: false,
|
|
5046
|
-
convert: true
|
|
5047
|
-
},
|
|
5048
|
-
failAction,
|
|
5049
|
-
|
|
5050
|
-
params: Joi.object({
|
|
5051
|
-
account: accountIdSchema.required()
|
|
5052
|
-
}),
|
|
5053
|
-
|
|
5054
|
-
query: Joi.object({
|
|
5055
|
-
path: Joi.string().empty('').required().example('INBOX').description(listMessageFolderPathDescription),
|
|
5056
|
-
force: Joi.boolean()
|
|
5057
|
-
.truthy('Y', 'true', '1')
|
|
5058
|
-
.falsy('N', 'false', 0)
|
|
5059
|
-
.default(false)
|
|
5060
|
-
.description('Delete messages even if not in Trash')
|
|
5061
|
-
.label('ForceDelete')
|
|
5062
|
-
}).label('MessagesDeleteQuery'),
|
|
5063
|
-
|
|
5064
|
-
payload: Joi.object({
|
|
5065
|
-
search: searchSchema
|
|
5066
|
-
}).label('MessagesDeleteRequest')
|
|
5067
|
-
},
|
|
5068
|
-
|
|
5069
|
-
response: {
|
|
5070
|
-
schema: Joi.object({
|
|
5071
|
-
deleted: Joi.boolean().example(false).description('Was the delete action executed'),
|
|
5072
|
-
moved: Joi.object({
|
|
5073
|
-
destination: Joi.string().required().example('Trash').description('Trash folder path').label('TrashPath'),
|
|
5074
|
-
|
|
5075
|
-
idMap: Joi.array()
|
|
5076
|
-
.items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
|
|
5077
|
-
.example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
|
|
5078
|
-
.description('An optional map of source and target ID values, if the server provided this info')
|
|
5079
|
-
.label('IdMapArray'),
|
|
5080
|
-
|
|
5081
|
-
emailIds: Joi.array()
|
|
5082
|
-
.items(Joi.string().example('1278455344230334865'))
|
|
5083
|
-
.description('An optional list of emailId values, if the server supports unique email IDs')
|
|
5084
|
-
.label('EmailIdsArray')
|
|
5085
|
-
})
|
|
5086
|
-
.label('MessagesMovedToTrash')
|
|
5087
|
-
.description('Value is present if messages were moved to Trash')
|
|
5088
|
-
}).label('MessagesDeleteResponse'),
|
|
5089
|
-
failAction: 'log'
|
|
5090
|
-
}
|
|
5091
|
-
}
|
|
5092
|
-
});
|
|
5093
|
-
|
|
5094
|
-
server.route({
|
|
5095
|
-
method: 'GET',
|
|
5096
|
-
path: '/v1/account/{account}/text/{text}',
|
|
5097
|
-
|
|
5098
|
-
async handler(request, h) {
|
|
5099
|
-
let accountObject = new Account({
|
|
5100
|
-
redis,
|
|
5101
|
-
account: request.params.account,
|
|
5102
|
-
call,
|
|
5103
|
-
secret: await getSecret(),
|
|
5104
|
-
esClient: await h.getESClient(request.logger),
|
|
5105
|
-
timeout: request.headers['x-ee-timeout']
|
|
5106
|
-
});
|
|
5107
|
-
|
|
5108
|
-
try {
|
|
5109
|
-
return await accountObject.getText(request.params.text, request.query);
|
|
5110
|
-
} catch (err) {
|
|
5111
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
5112
|
-
if (Boom.isBoom(err)) {
|
|
5113
|
-
throw err;
|
|
5114
|
-
}
|
|
5115
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
5116
|
-
if (err.code) {
|
|
5117
|
-
error.output.payload.code = err.code;
|
|
5118
|
-
}
|
|
5119
|
-
throw error;
|
|
5120
|
-
}
|
|
5121
|
-
},
|
|
5122
|
-
options: {
|
|
5123
|
-
description: 'Retrieve message text',
|
|
5124
|
-
notes: 'Retrieves message text',
|
|
5125
|
-
tags: ['api', 'Message'],
|
|
5126
|
-
|
|
5127
|
-
auth: {
|
|
5128
|
-
strategy: 'api-token',
|
|
5129
|
-
mode: 'required'
|
|
5130
|
-
},
|
|
5131
|
-
cors: CORS_CONFIG,
|
|
5132
|
-
|
|
5133
|
-
validate: {
|
|
5134
|
-
options: {
|
|
5135
|
-
stripUnknown: false,
|
|
5136
|
-
abortEarly: false,
|
|
5137
|
-
convert: true
|
|
5138
|
-
},
|
|
5139
|
-
failAction,
|
|
5140
|
-
|
|
5141
|
-
query: Joi.object({
|
|
5142
|
-
maxBytes: Joi.number()
|
|
5143
|
-
.integer()
|
|
5144
|
-
.min(0)
|
|
5145
|
-
.max(1024 * 1024 * 1024)
|
|
5146
|
-
.example(MAX_ATTACHMENT_SIZE)
|
|
5147
|
-
.description('Max length of text content'),
|
|
5148
|
-
textType: Joi.string()
|
|
5149
|
-
.lowercase()
|
|
5150
|
-
.valid('html', 'plain', '*')
|
|
5151
|
-
.default('*')
|
|
5152
|
-
.example('*')
|
|
5153
|
-
.description('Which text content to return, use * for all. By default all contents are returned.'),
|
|
5154
|
-
documentStore: documentStoreSchema.default(false)
|
|
5155
|
-
}),
|
|
5156
|
-
|
|
5157
|
-
params: Joi.object({
|
|
5158
|
-
account: accountIdSchema.required(),
|
|
5159
|
-
text: Joi.string()
|
|
5160
|
-
.base64({ paddingRequired: false, urlSafe: true })
|
|
5161
|
-
.max(10 * 1024)
|
|
5162
|
-
.required()
|
|
5163
|
-
.example('AAAAAQAACnAcdfaaN')
|
|
5164
|
-
.description('Message text ID')
|
|
5165
|
-
}).label('Text')
|
|
3132
|
+
path: Joi.string().required().example('Folder Name').description('Mailbox folder path to modify').label('ExistingMailboxPath'),
|
|
3133
|
+
newPath: Joi.array()
|
|
3134
|
+
.items(Joi.string().max(256))
|
|
3135
|
+
.single()
|
|
3136
|
+
.example(['Parent folder', 'Subfolder'])
|
|
3137
|
+
.description('New mailbox path as an array or a string. If account is namespaced then namespace prefix is added by default. Optional.')
|
|
3138
|
+
.label('TargetMailboxPath'),
|
|
3139
|
+
subscribed: Joi.boolean()
|
|
3140
|
+
.example(true)
|
|
3141
|
+
.description('Change mailbox subscription status. Only applies to IMAP accounts, ignored for Gmail and Outlook.')
|
|
3142
|
+
.label('SubscriptionStatus')
|
|
3143
|
+
})
|
|
3144
|
+
.or('newPath', 'subscribed')
|
|
3145
|
+
.label('ModifyMailbox')
|
|
5166
3146
|
},
|
|
5167
3147
|
|
|
5168
3148
|
response: {
|
|
5169
3149
|
schema: Joi.object({
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
3150
|
+
path: Joi.string().required().example('Mail').description('Mailbox folder path').label('ExistingMailboxPath'),
|
|
3151
|
+
newPath: Joi.string().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox if renamed').label('NewMailboxPath'),
|
|
3152
|
+
renamed: Joi.boolean().example(true).description('Was the mailbox renamed'),
|
|
3153
|
+
subscribed: Joi.boolean().example(true).description('Subscription status after modification')
|
|
3154
|
+
}).label('ModifyMailboxResponse'),
|
|
5174
3155
|
failAction: 'log'
|
|
5175
3156
|
}
|
|
5176
3157
|
}
|
|
5177
3158
|
});
|
|
5178
3159
|
|
|
5179
3160
|
server.route({
|
|
5180
|
-
method: '
|
|
5181
|
-
path: '/v1/account/{account}/
|
|
3161
|
+
method: 'DELETE',
|
|
3162
|
+
path: '/v1/account/{account}/mailbox',
|
|
5182
3163
|
|
|
5183
|
-
async handler(request
|
|
3164
|
+
async handler(request) {
|
|
5184
3165
|
let accountObject = new Account({
|
|
5185
3166
|
redis,
|
|
5186
3167
|
account: request.params.account,
|
|
5187
3168
|
call,
|
|
5188
3169
|
secret: await getSecret(),
|
|
5189
|
-
esClient: await h.getESClient(request.logger),
|
|
5190
3170
|
timeout: request.headers['x-ee-timeout']
|
|
5191
3171
|
});
|
|
5192
3172
|
|
|
5193
3173
|
try {
|
|
5194
|
-
return await accountObject.
|
|
3174
|
+
return await accountObject.deleteMailbox(request.query.path);
|
|
5195
3175
|
} catch (err) {
|
|
5196
3176
|
request.logger.error({ msg: 'API request failed', err });
|
|
5197
3177
|
if (Boom.isBoom(err)) {
|
|
@@ -5204,116 +3184,11 @@ Include your token in requests using one of these methods:
|
|
|
5204
3184
|
throw error;
|
|
5205
3185
|
}
|
|
5206
3186
|
},
|
|
5207
|
-
options: {
|
|
5208
|
-
description: 'List messages in a folder',
|
|
5209
|
-
notes: 'Lists messages in a mailbox folder',
|
|
5210
|
-
tags: ['api', 'Message'],
|
|
5211
|
-
|
|
5212
|
-
auth: {
|
|
5213
|
-
strategy: 'api-token',
|
|
5214
|
-
mode: 'required'
|
|
5215
|
-
},
|
|
5216
|
-
cors: CORS_CONFIG,
|
|
5217
|
-
|
|
5218
|
-
validate: {
|
|
5219
|
-
options: {
|
|
5220
|
-
stripUnknown: false,
|
|
5221
|
-
abortEarly: false,
|
|
5222
|
-
convert: true
|
|
5223
|
-
},
|
|
5224
|
-
failAction,
|
|
5225
|
-
|
|
5226
|
-
params: Joi.object({
|
|
5227
|
-
account: accountIdSchema.required().label('AccountId')
|
|
5228
|
-
}),
|
|
5229
|
-
|
|
5230
|
-
query: Joi.object({
|
|
5231
|
-
path: Joi.string().required().example('INBOX').description(listMessageFolderPathDescription).label('SpecialPath'),
|
|
5232
|
-
|
|
5233
|
-
cursor: Joi.string()
|
|
5234
|
-
.trim()
|
|
5235
|
-
.empty('')
|
|
5236
|
-
.max(1024 * 1024)
|
|
5237
|
-
.example('imap_kcQIji3UobDDTxc')
|
|
5238
|
-
.description('Paging cursor from `nextPageCursor` or `prevPageCursor` value')
|
|
5239
|
-
.label('PageCursor'),
|
|
5240
|
-
page: Joi.number()
|
|
5241
|
-
.integer()
|
|
5242
|
-
.min(0)
|
|
5243
|
-
.max(1024 * 1024)
|
|
5244
|
-
.default(0)
|
|
5245
|
-
.example(0)
|
|
5246
|
-
.description(
|
|
5247
|
-
'Page number (zero-indexed, so use 0 for the first page). Only supported for IMAP accounts. Deprecated; use the paging cursor instead. If the page cursor value is provided, then the page number value is ignored.'
|
|
5248
|
-
)
|
|
5249
|
-
.label('PageNumber'),
|
|
5250
|
-
|
|
5251
|
-
pageSize: Joi.number().integer().min(1).max(1000).default(20).example(20).description('How many entries per page').label('PageSize'),
|
|
5252
|
-
documentStore: documentStoreSchema.default(false)
|
|
5253
|
-
}).label('MessageQuery')
|
|
5254
|
-
},
|
|
5255
|
-
|
|
5256
|
-
response: {
|
|
5257
|
-
schema: messageListSchema,
|
|
5258
|
-
failAction: 'log'
|
|
5259
|
-
}
|
|
5260
|
-
}
|
|
5261
|
-
});
|
|
5262
|
-
|
|
5263
|
-
server.route({
|
|
5264
|
-
method: 'POST',
|
|
5265
|
-
path: '/v1/account/{account}/search',
|
|
5266
|
-
|
|
5267
|
-
async handler(request, h) {
|
|
5268
|
-
let accountObject = new Account({
|
|
5269
|
-
redis,
|
|
5270
|
-
account: request.params.account,
|
|
5271
|
-
call,
|
|
5272
|
-
secret: await getSecret(),
|
|
5273
|
-
esClient: await h.getESClient(request.logger),
|
|
5274
|
-
timeout: request.headers['x-ee-timeout']
|
|
5275
|
-
});
|
|
5276
|
-
|
|
5277
|
-
let extraValidationErrors = [];
|
|
5278
3187
|
|
|
5279
|
-
if (request.query.documentStore) {
|
|
5280
|
-
for (let key of ['seq', 'modseq']) {
|
|
5281
|
-
if (request.payload.search && key in request.payload.search) {
|
|
5282
|
-
extraValidationErrors.push({ message: 'Not allowed with documentStore', context: { key } });
|
|
5283
|
-
}
|
|
5284
|
-
}
|
|
5285
|
-
} else {
|
|
5286
|
-
for (let key of ['documentQuery']) {
|
|
5287
|
-
if (key in request.payload) {
|
|
5288
|
-
extraValidationErrors.push({ message: 'Not allowed without documentStore', context: { key } });
|
|
5289
|
-
}
|
|
5290
|
-
}
|
|
5291
|
-
}
|
|
5292
|
-
|
|
5293
|
-
if (extraValidationErrors.length) {
|
|
5294
|
-
let error = new Error('Input validation failed');
|
|
5295
|
-
error.details = extraValidationErrors;
|
|
5296
|
-
return failAction(request, h, error);
|
|
5297
|
-
}
|
|
5298
|
-
|
|
5299
|
-
try {
|
|
5300
|
-
return await accountObject.searchMessages(Object.assign(request.query, request.payload));
|
|
5301
|
-
} catch (err) {
|
|
5302
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
5303
|
-
if (Boom.isBoom(err)) {
|
|
5304
|
-
throw err;
|
|
5305
|
-
}
|
|
5306
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
5307
|
-
if (err.code) {
|
|
5308
|
-
error.output.payload.code = err.code;
|
|
5309
|
-
}
|
|
5310
|
-
throw error;
|
|
5311
|
-
}
|
|
5312
|
-
},
|
|
5313
3188
|
options: {
|
|
5314
|
-
description: '
|
|
5315
|
-
notes: '
|
|
5316
|
-
tags: ['api', '
|
|
3189
|
+
description: 'Delete mailbox',
|
|
3190
|
+
notes: 'Delete existing mailbox folder',
|
|
3191
|
+
tags: ['api', 'Mailbox'],
|
|
5317
3192
|
|
|
5318
3193
|
plugins: {},
|
|
5319
3194
|
|
|
@@ -5336,200 +3211,15 @@ Include your token in requests using one of these methods:
|
|
|
5336
3211
|
}),
|
|
5337
3212
|
|
|
5338
3213
|
query: Joi.object({
|
|
5339
|
-
path: Joi.string()
|
|
5340
|
-
|
|
5341
|
-
is: true,
|
|
5342
|
-
then: Joi.optional(),
|
|
5343
|
-
otherwise: Joi.required()
|
|
5344
|
-
})
|
|
5345
|
-
.example('INBOX')
|
|
5346
|
-
.description(listMessageFolderPathDescription)
|
|
5347
|
-
.label('Path'),
|
|
5348
|
-
|
|
5349
|
-
cursor: Joi.string()
|
|
5350
|
-
.trim()
|
|
5351
|
-
.empty('')
|
|
5352
|
-
.max(1024 * 1024)
|
|
5353
|
-
.example('imap_kcQIji3UobDDTxc')
|
|
5354
|
-
.description('Paging cursor from `nextPageCursor` or `prevPageCursor` value')
|
|
5355
|
-
.label('PageCursor'),
|
|
5356
|
-
page: Joi.number()
|
|
5357
|
-
.integer()
|
|
5358
|
-
.min(0)
|
|
5359
|
-
.max(1024 * 1024)
|
|
5360
|
-
.default(0)
|
|
5361
|
-
.example(0)
|
|
5362
|
-
.description(
|
|
5363
|
-
'Page number (zero-indexed, so use 0 for the first page). Only supported for IMAP accounts. Deprecated; use the paging cursor instead. If the page cursor value is provided, then the page number value is ignored.'
|
|
5364
|
-
)
|
|
5365
|
-
.label('PageNumber'),
|
|
5366
|
-
|
|
5367
|
-
pageSize: Joi.number().integer().min(1).max(1000).default(20).example(20).description('How many entries per page'),
|
|
5368
|
-
|
|
5369
|
-
useOutlookSearch: Joi.boolean()
|
|
5370
|
-
.truthy('Y', 'true', '1')
|
|
5371
|
-
.falsy('N', 'false', 0)
|
|
5372
|
-
.description(
|
|
5373
|
-
'MS Graph only. If enabled, uses the $search parameter for MS Graph search queries instead of $filter. This allows searching the "to", "cc", "bcc", "larger", "smaller", "body", "before", "sentBefore", "since", and the "sentSince" fields. Note that $search returns up to 1,000 results, does not indicate the total number of matching results or pages, and returns results sorted by relevance rather than date.'
|
|
5374
|
-
)
|
|
5375
|
-
.label('useOutlookSearch')
|
|
5376
|
-
.optional(),
|
|
5377
|
-
|
|
5378
|
-
documentStore: documentStoreSchema.default(false).meta({ swaggerHidden: true }),
|
|
5379
|
-
exposeQuery: Joi.boolean()
|
|
5380
|
-
.truthy('Y', 'true', '1')
|
|
5381
|
-
.falsy('N', 'false', 0)
|
|
5382
|
-
.description('If enabled then returns the ElasticSearch query for debugging as part of the response')
|
|
5383
|
-
.label('exposeQuery')
|
|
5384
|
-
.when('documentStore', {
|
|
5385
|
-
is: true,
|
|
5386
|
-
then: Joi.optional(),
|
|
5387
|
-
otherwise: Joi.forbidden()
|
|
5388
|
-
})
|
|
5389
|
-
.meta({ swaggerHidden: true })
|
|
5390
|
-
}),
|
|
5391
|
-
|
|
5392
|
-
payload: Joi.object({
|
|
5393
|
-
search: searchSchema,
|
|
5394
|
-
documentQuery: Joi.object()
|
|
5395
|
-
.min(1)
|
|
5396
|
-
.description('Document Store query. Only allowed with `documentStore`.')
|
|
5397
|
-
.label('DocumentQuery')
|
|
5398
|
-
.unknown()
|
|
5399
|
-
.meta({ swaggerHidden: true })
|
|
5400
|
-
})
|
|
5401
|
-
.label('SearchQuery')
|
|
5402
|
-
.example({
|
|
5403
|
-
search: {
|
|
5404
|
-
unseen: true,
|
|
5405
|
-
flagged: true,
|
|
5406
|
-
from: 'nyan.cat@example.com',
|
|
5407
|
-
body: 'Hello world',
|
|
5408
|
-
subject: 'Hello world',
|
|
5409
|
-
sentBefore: '2024-08-09',
|
|
5410
|
-
sentSince: '2022-08-09',
|
|
5411
|
-
emailId: '1278455344230334865',
|
|
5412
|
-
threadId: '1266894439832287888',
|
|
5413
|
-
header: {
|
|
5414
|
-
'Message-ID': '<12345@example.com>'
|
|
5415
|
-
},
|
|
5416
|
-
gmailRaw: 'has:attachment in:unread'
|
|
5417
|
-
}
|
|
5418
|
-
})
|
|
5419
|
-
},
|
|
5420
|
-
|
|
5421
|
-
response: {
|
|
5422
|
-
schema: messageListSchema,
|
|
5423
|
-
failAction: 'log'
|
|
5424
|
-
}
|
|
5425
|
-
}
|
|
5426
|
-
});
|
|
5427
|
-
|
|
5428
|
-
server.route({
|
|
5429
|
-
method: 'POST',
|
|
5430
|
-
path: '/v1/unified/search',
|
|
5431
|
-
|
|
5432
|
-
async handler(request, h) {
|
|
5433
|
-
let accountObject = new Account({
|
|
5434
|
-
redis,
|
|
5435
|
-
call,
|
|
5436
|
-
secret: await getSecret(),
|
|
5437
|
-
esClient: await h.getESClient(request.logger),
|
|
5438
|
-
timeout: request.headers['x-ee-timeout']
|
|
5439
|
-
});
|
|
5440
|
-
|
|
5441
|
-
let extraValidationErrors = [];
|
|
5442
|
-
|
|
5443
|
-
for (let key of ['seq', 'modseq']) {
|
|
5444
|
-
if (request.payload.search && key in request.payload.search) {
|
|
5445
|
-
extraValidationErrors.push({ message: 'Not allowed with documentStore', context: { key } });
|
|
5446
|
-
}
|
|
5447
|
-
}
|
|
5448
|
-
|
|
5449
|
-
if (extraValidationErrors.length) {
|
|
5450
|
-
let error = new Error('Input validation failed');
|
|
5451
|
-
error.details = extraValidationErrors;
|
|
5452
|
-
return failAction(request, h, error);
|
|
5453
|
-
}
|
|
5454
|
-
|
|
5455
|
-
let documentStoreEnabled = await settings.get('documentStoreEnabled');
|
|
5456
|
-
if (!documentStoreEnabled) {
|
|
5457
|
-
let error = new Error('Document store not enabled');
|
|
5458
|
-
error.details = extraValidationErrors;
|
|
5459
|
-
return failAction(request, h, error);
|
|
5460
|
-
}
|
|
5461
|
-
|
|
5462
|
-
try {
|
|
5463
|
-
return await accountObject.searchMessages(Object.assign({ documentStore: true }, request.query, request.payload), { unified: true });
|
|
5464
|
-
} catch (err) {
|
|
5465
|
-
request.logger.error({ msg: 'API request failed', err });
|
|
5466
|
-
if (Boom.isBoom(err)) {
|
|
5467
|
-
throw err;
|
|
5468
|
-
}
|
|
5469
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
5470
|
-
if (err.code) {
|
|
5471
|
-
error.output.payload.code = err.code;
|
|
5472
|
-
}
|
|
5473
|
-
throw error;
|
|
5474
|
-
}
|
|
5475
|
-
},
|
|
5476
|
-
options: {
|
|
5477
|
-
description: 'Unified search for messages',
|
|
5478
|
-
notes: 'Filter messages from the Document Store for multiple accounts or paths. Document Store must be enabled for the unified search to work.',
|
|
5479
|
-
tags: ['Deprecated endpoints (Document Store)'],
|
|
5480
|
-
|
|
5481
|
-
plugins: {},
|
|
5482
|
-
|
|
5483
|
-
auth: {
|
|
5484
|
-
strategy: 'api-token',
|
|
5485
|
-
mode: 'required'
|
|
5486
|
-
},
|
|
5487
|
-
cors: CORS_CONFIG,
|
|
5488
|
-
|
|
5489
|
-
validate: {
|
|
5490
|
-
options: {
|
|
5491
|
-
stripUnknown: false,
|
|
5492
|
-
abortEarly: false,
|
|
5493
|
-
convert: true
|
|
5494
|
-
},
|
|
5495
|
-
failAction,
|
|
5496
|
-
|
|
5497
|
-
query: Joi.object({
|
|
5498
|
-
page: Joi.number()
|
|
5499
|
-
.integer()
|
|
5500
|
-
.min(0)
|
|
5501
|
-
.max(1024 * 1024)
|
|
5502
|
-
.default(0)
|
|
5503
|
-
.example(0)
|
|
5504
|
-
.description('Page number (zero indexed, so use 0 for first page)'),
|
|
5505
|
-
pageSize: Joi.number().integer().min(1).max(1000).default(20).example(20).description('How many entries per page'),
|
|
5506
|
-
exposeQuery: Joi.boolean()
|
|
5507
|
-
.truthy('Y', 'true', '1')
|
|
5508
|
-
.falsy('N', 'false', 0)
|
|
5509
|
-
.description('If enabled then returns the ElasticSearch query for debugging as part of the response')
|
|
5510
|
-
.label('exposeQuery')
|
|
5511
|
-
.optional()
|
|
5512
|
-
.meta({ swaggerHidden: true })
|
|
5513
|
-
}),
|
|
5514
|
-
|
|
5515
|
-
payload: Joi.object({
|
|
5516
|
-
accounts: Joi.array()
|
|
5517
|
-
.items(Joi.string().empty('').trim().max(256).example('example'))
|
|
5518
|
-
.single()
|
|
5519
|
-
.description('Optional list of account ID values')
|
|
5520
|
-
.label('UnifiedSearchAccounts'),
|
|
5521
|
-
paths: Joi.array()
|
|
5522
|
-
.items(Joi.string().optional().example('INBOX'))
|
|
5523
|
-
.single()
|
|
5524
|
-
.description('Optional list of mailbox folder paths or specialUse flags')
|
|
5525
|
-
.label('UnifiedSearchPaths'),
|
|
5526
|
-
search: searchSchema,
|
|
5527
|
-
documentQuery: Joi.object().min(1).description('Document Store query').label('DocumentQuery').unknown().meta({ swaggerHidden: true })
|
|
5528
|
-
}).label('UnifiedSearchQuery')
|
|
3214
|
+
path: Joi.string().required().example('My Outdated Mail').description('Mailbox folder path to delete').label('MailboxPath')
|
|
3215
|
+
}).label('DeleteMailbox')
|
|
5529
3216
|
},
|
|
5530
3217
|
|
|
5531
3218
|
response: {
|
|
5532
|
-
schema:
|
|
3219
|
+
schema: Joi.object({
|
|
3220
|
+
path: Joi.string().required().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox').label('MailboxPath'),
|
|
3221
|
+
deleted: Joi.boolean().example(true).description('Was the mailbox deleted')
|
|
3222
|
+
}).label('DeleteMailboxResponse'),
|
|
5533
3223
|
failAction: 'log'
|
|
5534
3224
|
}
|
|
5535
3225
|
}
|
|
@@ -6885,6 +4575,28 @@ Include your token in requests using one of these methods:
|
|
|
6885
4575
|
// setup "chat with email" routes
|
|
6886
4576
|
await chatRoutes({ server, call, CORS_CONFIG });
|
|
6887
4577
|
|
|
4578
|
+
// setup account CRUD routes
|
|
4579
|
+
await accountRoutes({
|
|
4580
|
+
server,
|
|
4581
|
+
call,
|
|
4582
|
+
documentsQueue,
|
|
4583
|
+
oauth2Schema,
|
|
4584
|
+
imapSchema,
|
|
4585
|
+
smtpSchema,
|
|
4586
|
+
CORS_CONFIG,
|
|
4587
|
+
AccountTypeSchema
|
|
4588
|
+
});
|
|
4589
|
+
|
|
4590
|
+
// setup message routes
|
|
4591
|
+
await messageRoutes({
|
|
4592
|
+
server,
|
|
4593
|
+
call,
|
|
4594
|
+
CORS_CONFIG,
|
|
4595
|
+
MAX_ATTACHMENT_SIZE,
|
|
4596
|
+
MAX_BODY_SIZE,
|
|
4597
|
+
MAX_PAYLOAD_TIMEOUT
|
|
4598
|
+
});
|
|
4599
|
+
|
|
6888
4600
|
server.route({
|
|
6889
4601
|
method: 'GET',
|
|
6890
4602
|
path: '/v1/webhookRoutes',
|
|
@@ -8996,7 +6708,7 @@ ${now}`,
|
|
|
8996
6708
|
url: '/admin/config/webhooks',
|
|
8997
6709
|
level: 'danger',
|
|
8998
6710
|
icon: 'link',
|
|
8999
|
-
message: '
|
|
6711
|
+
message: 'Webhook delivery is failing'
|
|
9000
6712
|
});
|
|
9001
6713
|
}
|
|
9002
6714
|
|
|
@@ -9005,7 +6717,7 @@ ${now}`,
|
|
|
9005
6717
|
url: '/admin/config/license',
|
|
9006
6718
|
level: 'warning',
|
|
9007
6719
|
icon: 'key',
|
|
9008
|
-
message: '
|
|
6720
|
+
message: 'No license key registered'
|
|
9009
6721
|
});
|
|
9010
6722
|
}
|
|
9011
6723
|
|