emailengine-app 2.63.4 → 2.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/data/google-crawlers.json +1 -1
- package/eslint.config.js +2 -0
- package/lib/account.js +6 -2
- package/lib/consts.js +17 -1
- package/lib/email-client/gmail/gmail-api.js +1 -12
- package/lib/email-client/imap-client.js +5 -3
- package/lib/email-client/outlook/graph-api.js +7 -13
- package/lib/email-client/outlook-client.js +363 -167
- package/lib/imapproxy/imap-server.js +1 -0
- package/lib/oauth/gmail.js +12 -1
- package/lib/oauth/pubsub/google.js +253 -85
- package/lib/oauth2-apps.js +554 -377
- package/lib/routes-ui.js +186 -91
- package/lib/schemas.js +18 -1
- package/lib/ui-routes/account-routes.js +1 -1
- package/lib/ui-routes/admin-entities-routes.js +3 -3
- package/lib/ui-routes/oauth-routes.js +9 -3
- package/package.json +9 -9
- package/sbom.json +1 -1
- package/server.js +54 -22
- package/static/licenses.html +27 -27
- package/translations/de.mo +0 -0
- package/translations/de.po +54 -42
- package/translations/en.mo +0 -0
- package/translations/en.po +55 -43
- package/translations/et.mo +0 -0
- package/translations/et.po +54 -42
- package/translations/fr.mo +0 -0
- package/translations/fr.po +54 -42
- package/translations/ja.mo +0 -0
- package/translations/ja.po +54 -42
- package/translations/messages.pot +74 -52
- package/translations/nl.mo +0 -0
- package/translations/nl.po +54 -42
- package/translations/pl.mo +0 -0
- package/translations/pl.po +54 -42
- package/views/config/oauth/app.hbs +12 -0
- package/views/config/oauth/index.hbs +2 -0
- package/views/config/oauth/subscriptions.hbs +175 -0
- package/views/error.hbs +4 -4
- package/views/partials/oauth_tabs.hbs +8 -0
- package/workers/api.js +174 -96
- package/workers/documents.js +1 -0
- package/workers/imap.js +30 -47
- package/workers/smtp.js +1 -0
- package/workers/submit.js +1 -0
- package/workers/webhooks.js +42 -30
package/lib/routes-ui.js
CHANGED
|
@@ -58,7 +58,6 @@ const { Client: ElasticSearch } = require('@elastic/elasticsearch');
|
|
|
58
58
|
const { llmPreProcess } = require('./llm-pre-process');
|
|
59
59
|
const { locales } = require('./translations');
|
|
60
60
|
const capa = require('./capa');
|
|
61
|
-
const exampleWebhookPayloads = require('./payload-examples-webhooks.json');
|
|
62
61
|
const exampleDocumentsPayloads = require('./payload-examples-documents.json');
|
|
63
62
|
const { defaultMappings } = require('./es');
|
|
64
63
|
const { getESClient } = require('../lib/document-store');
|
|
@@ -719,34 +718,6 @@ async function getOpenAiError(gt) {
|
|
|
719
718
|
return openAiError;
|
|
720
719
|
}
|
|
721
720
|
|
|
722
|
-
async function getExampleWebhookPayloads() {
|
|
723
|
-
let serviceUrl = await settings.get('serviceUrl');
|
|
724
|
-
let date = new Date().toISOString();
|
|
725
|
-
|
|
726
|
-
let examplePayloads = structuredClone(exampleWebhookPayloads);
|
|
727
|
-
|
|
728
|
-
examplePayloads.forEach(payload => {
|
|
729
|
-
if (payload && payload.content) {
|
|
730
|
-
if (typeof payload.content.serviceUrl === 'string') {
|
|
731
|
-
payload.content.serviceUrl = serviceUrl;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
if (typeof payload.content.date === 'string') {
|
|
735
|
-
payload.content.date = date;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
if (payload.content.data && typeof payload.content.data.date === 'string') {
|
|
739
|
-
payload.content.data.date = date;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
if (payload.content.data && typeof payload.content.data.created === 'string') {
|
|
743
|
-
payload.content.data.created = date;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
});
|
|
747
|
-
return examplePayloads;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
721
|
async function getExampleDocumentsPayloads() {
|
|
751
722
|
let date = new Date().toISOString();
|
|
752
723
|
|
|
@@ -886,6 +857,21 @@ function applyRoutes(server, call) {
|
|
|
886
857
|
throw error;
|
|
887
858
|
}
|
|
888
859
|
|
|
860
|
+
/**
|
|
861
|
+
* Fetch the list of Pub/Sub apps and mark the one matching selectedId as selected.
|
|
862
|
+
* Returns the apps array ready for template rendering.
|
|
863
|
+
*/
|
|
864
|
+
async function getPubSubAppsForSelect(selectedId) {
|
|
865
|
+
let result = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
866
|
+
let apps = (result && result.apps) || [];
|
|
867
|
+
for (let app of apps) {
|
|
868
|
+
if (app.id === selectedId) {
|
|
869
|
+
app.selected = true;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
return apps;
|
|
873
|
+
}
|
|
874
|
+
|
|
889
875
|
// List exports for account
|
|
890
876
|
server.route({
|
|
891
877
|
method: 'GET',
|
|
@@ -2568,6 +2554,7 @@ return true;`
|
|
|
2568
2554
|
pageTitle: 'OAuth2',
|
|
2569
2555
|
menuConfig: true,
|
|
2570
2556
|
menuConfigOauth: true,
|
|
2557
|
+
activeApplications: true,
|
|
2571
2558
|
|
|
2572
2559
|
newLink: newLink.pathname + newLink.search,
|
|
2573
2560
|
|
|
@@ -2614,6 +2601,160 @@ return true;`
|
|
|
2614
2601
|
}
|
|
2615
2602
|
});
|
|
2616
2603
|
|
|
2604
|
+
// GET /admin/config/oauth/subscriptions - Gmail Pub/Sub subscriptions list
|
|
2605
|
+
server.route({
|
|
2606
|
+
method: 'GET',
|
|
2607
|
+
path: '/admin/config/oauth/subscriptions',
|
|
2608
|
+
async handler(request, h) {
|
|
2609
|
+
try {
|
|
2610
|
+
let data = await oauth2Apps.list(request.query.page - 1, request.query.pageSize, { pubsub: true });
|
|
2611
|
+
|
|
2612
|
+
let gmailSubscriptionTtl = await settings.get('gmailSubscriptionTtl');
|
|
2613
|
+
|
|
2614
|
+
// Compute human-readable expiration for each app
|
|
2615
|
+
// meta.subscriptionExpiration is:
|
|
2616
|
+
// undefined - no data yet (app predates this feature or ensurePubsub hasn't run)
|
|
2617
|
+
// null - indefinite (no TTL set, ensurePubsub confirmed this)
|
|
2618
|
+
// "Ns" - TTL in seconds (e.g. "2678400s" for 31 days)
|
|
2619
|
+
let gt = request.app.gt;
|
|
2620
|
+
for (let app of data.apps) {
|
|
2621
|
+
if (!app.pubSubSubscription) {
|
|
2622
|
+
app.expirationLabel = '';
|
|
2623
|
+
continue;
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
let meta = app.meta || {};
|
|
2627
|
+
if (!('subscriptionExpiration' in meta)) {
|
|
2628
|
+
app.expirationLabel = gt.gettext('Unknown');
|
|
2629
|
+
continue;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
let seconds = parseInt(meta.subscriptionExpiration, 10);
|
|
2633
|
+
if (seconds > 0) {
|
|
2634
|
+
let days = Math.round(seconds / 86400);
|
|
2635
|
+
app.expirationLabel = util.format(gt.ngettext('%d day', '%d days', days), days);
|
|
2636
|
+
} else {
|
|
2637
|
+
app.expirationLabel = gt.gettext('Indefinite');
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
let nextPage = false;
|
|
2642
|
+
let prevPage = false;
|
|
2643
|
+
|
|
2644
|
+
let getPagingUrl = page => {
|
|
2645
|
+
let url = new URL(`admin/config/oauth/subscriptions`, 'http://localhost');
|
|
2646
|
+
|
|
2647
|
+
if (page) {
|
|
2648
|
+
url.searchParams.append('page', page);
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
if (request.query.pageSize !== DEFAULT_PAGE_SIZE) {
|
|
2652
|
+
url.searchParams.append('pageSize', request.query.pageSize);
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
return url.pathname + url.search;
|
|
2656
|
+
};
|
|
2657
|
+
|
|
2658
|
+
if (data.pages > data.page + 1) {
|
|
2659
|
+
nextPage = getPagingUrl(data.page + 2);
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
if (data.page > 0) {
|
|
2663
|
+
prevPage = getPagingUrl(data.page);
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
return h.view(
|
|
2667
|
+
'config/oauth/subscriptions',
|
|
2668
|
+
{
|
|
2669
|
+
pageTitle: 'OAuth2',
|
|
2670
|
+
menuConfig: true,
|
|
2671
|
+
menuConfigOauth: true,
|
|
2672
|
+
activeSubscriptions: true,
|
|
2673
|
+
|
|
2674
|
+
showPaging: data.pages > 1,
|
|
2675
|
+
nextPage,
|
|
2676
|
+
prevPage,
|
|
2677
|
+
firstPage: data.page === 0,
|
|
2678
|
+
pageLinks: new Array(data.pages || 1).fill(0).map((z, i) => ({
|
|
2679
|
+
url: getPagingUrl(i + 1),
|
|
2680
|
+
title: i + 1,
|
|
2681
|
+
active: i === data.page
|
|
2682
|
+
})),
|
|
2683
|
+
|
|
2684
|
+
apps: data.apps,
|
|
2685
|
+
|
|
2686
|
+
values: {
|
|
2687
|
+
gmailSubscriptionTtl: typeof gmailSubscriptionTtl === 'number' ? gmailSubscriptionTtl : ''
|
|
2688
|
+
}
|
|
2689
|
+
},
|
|
2690
|
+
{
|
|
2691
|
+
layout: 'app'
|
|
2692
|
+
}
|
|
2693
|
+
);
|
|
2694
|
+
} catch (err) {
|
|
2695
|
+
request.logger.error({ msg: 'Failed to load subscriptions page', err });
|
|
2696
|
+
throwAsBoom(err);
|
|
2697
|
+
}
|
|
2698
|
+
},
|
|
2699
|
+
|
|
2700
|
+
options: {
|
|
2701
|
+
validate: {
|
|
2702
|
+
options: {
|
|
2703
|
+
stripUnknown: true,
|
|
2704
|
+
abortEarly: false,
|
|
2705
|
+
convert: true
|
|
2706
|
+
},
|
|
2707
|
+
|
|
2708
|
+
async failAction(request, h /*, err*/) {
|
|
2709
|
+
return h.redirect('/admin/config/oauth/subscriptions').takeover();
|
|
2710
|
+
},
|
|
2711
|
+
|
|
2712
|
+
query: Joi.object({
|
|
2713
|
+
page: Joi.number().integer().min(1).max(1000000).default(1),
|
|
2714
|
+
pageSize: Joi.number().integer().min(1).max(250).default(DEFAULT_PAGE_SIZE)
|
|
2715
|
+
})
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
});
|
|
2719
|
+
|
|
2720
|
+
server.route({
|
|
2721
|
+
method: 'POST',
|
|
2722
|
+
path: '/admin/config/oauth/subscriptions',
|
|
2723
|
+
async handler(request, h) {
|
|
2724
|
+
try {
|
|
2725
|
+
// Joi .empty('').allow(null) ensures this is either null or a number
|
|
2726
|
+
let ttl = request.payload.gmailSubscriptionTtl != null ? request.payload.gmailSubscriptionTtl : null;
|
|
2727
|
+
await settings.set('gmailSubscriptionTtl', ttl);
|
|
2728
|
+
|
|
2729
|
+
await request.flash({ type: 'info', message: 'Configuration updated' });
|
|
2730
|
+
return h.redirect('/admin/config/oauth/subscriptions');
|
|
2731
|
+
} catch (err) {
|
|
2732
|
+
await request.flash({ type: 'danger', message: 'Failed to save settings' });
|
|
2733
|
+
request.logger.error({ msg: 'Failed to save subscription settings', err });
|
|
2734
|
+
return h.redirect('/admin/config/oauth/subscriptions');
|
|
2735
|
+
}
|
|
2736
|
+
},
|
|
2737
|
+
options: {
|
|
2738
|
+
validate: {
|
|
2739
|
+
options: {
|
|
2740
|
+
stripUnknown: true,
|
|
2741
|
+
abortEarly: false,
|
|
2742
|
+
convert: true
|
|
2743
|
+
},
|
|
2744
|
+
|
|
2745
|
+
async failAction(request, h /*, err*/) {
|
|
2746
|
+
await request.flash({ type: 'danger', message: 'Invalid setting value' });
|
|
2747
|
+
return h.redirect('/admin/config/oauth/subscriptions').takeover();
|
|
2748
|
+
},
|
|
2749
|
+
|
|
2750
|
+
payload: Joi.object({
|
|
2751
|
+
gmailSubscriptionTtl: Joi.number().integer().empty('').allow(null).min(0).max(365),
|
|
2752
|
+
crumb: Joi.string().max(256)
|
|
2753
|
+
})
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
});
|
|
2757
|
+
|
|
2617
2758
|
server.route({
|
|
2618
2759
|
method: 'GET',
|
|
2619
2760
|
path: '/admin/config/oauth/app/{app}',
|
|
@@ -2724,6 +2865,12 @@ return true;`
|
|
|
2724
2865
|
try {
|
|
2725
2866
|
await oauth2Apps.del(request.payload.app);
|
|
2726
2867
|
|
|
2868
|
+
try {
|
|
2869
|
+
await call({ cmd: 'googlePubSubRemove', app: request.payload.app });
|
|
2870
|
+
} catch (err) {
|
|
2871
|
+
request.logger.error({ msg: 'Failed to notify workers about OAuth2 app deletion', err, app: request.payload.app });
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2727
2874
|
await request.flash({ type: 'info', message: `OAuth2 app deleted` });
|
|
2728
2875
|
|
|
2729
2876
|
return h.redirect('/admin/config/oauth');
|
|
@@ -2743,7 +2890,7 @@ return true;`
|
|
|
2743
2890
|
|
|
2744
2891
|
async failAction(request, h, err) {
|
|
2745
2892
|
await request.flash({ type: 'danger', message: `Couldn't delete OAuth2 app. Try again.` });
|
|
2746
|
-
request.logger.error({ msg: 'Failed to delete
|
|
2893
|
+
request.logger.error({ msg: 'Failed to delete the OAuth2 application', err });
|
|
2747
2894
|
|
|
2748
2895
|
return h.redirect('/admin/config/oauth').takeover();
|
|
2749
2896
|
},
|
|
@@ -2768,8 +2915,6 @@ return true;`
|
|
|
2768
2915
|
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
2769
2916
|
}
|
|
2770
2917
|
|
|
2771
|
-
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
2772
|
-
|
|
2773
2918
|
return h.view(
|
|
2774
2919
|
'config/oauth/new',
|
|
2775
2920
|
{
|
|
@@ -2787,7 +2932,7 @@ return true;`
|
|
|
2787
2932
|
baseScopesApi: false,
|
|
2788
2933
|
baseScopesPubsub: false,
|
|
2789
2934
|
|
|
2790
|
-
pubSubApps:
|
|
2935
|
+
pubSubApps: await getPubSubAppsForSelect(null),
|
|
2791
2936
|
|
|
2792
2937
|
azureClouds: structuredClone(AZURE_CLOUDS).map(entry => {
|
|
2793
2938
|
if (entry.id === 'global') {
|
|
@@ -2858,7 +3003,7 @@ return true;`
|
|
|
2858
3003
|
throw new Error('Unexpected result');
|
|
2859
3004
|
}
|
|
2860
3005
|
|
|
2861
|
-
if (oauth2App && oauth2App.pubsubUpdates && oauth2App.pubsubUpdates.
|
|
3006
|
+
if (oauth2App && oauth2App.pubsubUpdates && Object.keys(oauth2App.pubsubUpdates).length > 0) {
|
|
2862
3007
|
await call({ cmd: 'googlePubSub', app: oauth2App.id });
|
|
2863
3008
|
}
|
|
2864
3009
|
|
|
@@ -2881,8 +3026,6 @@ return true;`
|
|
|
2881
3026
|
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
2882
3027
|
}
|
|
2883
3028
|
|
|
2884
|
-
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
2885
|
-
|
|
2886
3029
|
return h.view(
|
|
2887
3030
|
'config/oauth/new',
|
|
2888
3031
|
{
|
|
@@ -2896,15 +3039,7 @@ return true;`
|
|
|
2896
3039
|
providerData,
|
|
2897
3040
|
defaultRedirectUrl,
|
|
2898
3041
|
|
|
2899
|
-
pubSubApps:
|
|
2900
|
-
pubSubApps &&
|
|
2901
|
-
pubSubApps.apps &&
|
|
2902
|
-
pubSubApps.apps.map(app => {
|
|
2903
|
-
if (app.id === request.payload.pubSubApp) {
|
|
2904
|
-
app.selected = true;
|
|
2905
|
-
}
|
|
2906
|
-
return app;
|
|
2907
|
-
}),
|
|
3042
|
+
pubSubApps: await getPubSubAppsForSelect(request.payload.pubSubApp),
|
|
2908
3043
|
|
|
2909
3044
|
baseScopesApi: baseScopes === 'api',
|
|
2910
3045
|
baseScopesImap: baseScopes === 'imap' || !baseScopes,
|
|
@@ -2961,8 +3096,6 @@ return true;`
|
|
|
2961
3096
|
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
2962
3097
|
}
|
|
2963
3098
|
|
|
2964
|
-
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
2965
|
-
|
|
2966
3099
|
return h
|
|
2967
3100
|
.view(
|
|
2968
3101
|
'config/oauth/new',
|
|
@@ -2977,15 +3110,7 @@ return true;`
|
|
|
2977
3110
|
providerData,
|
|
2978
3111
|
defaultRedirectUrl,
|
|
2979
3112
|
|
|
2980
|
-
pubSubApps:
|
|
2981
|
-
pubSubApps &&
|
|
2982
|
-
pubSubApps.apps &&
|
|
2983
|
-
pubSubApps.apps.map(app => {
|
|
2984
|
-
if (app.id === request.payload.pubSubApp) {
|
|
2985
|
-
app.selected = true;
|
|
2986
|
-
}
|
|
2987
|
-
return app;
|
|
2988
|
-
}),
|
|
3113
|
+
pubSubApps: await getPubSubAppsForSelect(request.payload.pubSubApp),
|
|
2989
3114
|
|
|
2990
3115
|
baseScopesApi: baseScopes === 'api',
|
|
2991
3116
|
baseScopesImap: baseScopes === 'imap' || !baseScopes,
|
|
@@ -3041,8 +3166,6 @@ return true;`
|
|
|
3041
3166
|
tenant: appData.authority && !['common', 'organizations', 'consumers'].includes(appData.authority) ? appData.authority : ''
|
|
3042
3167
|
});
|
|
3043
3168
|
|
|
3044
|
-
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
3045
|
-
|
|
3046
3169
|
return h.view(
|
|
3047
3170
|
'config/oauth/edit',
|
|
3048
3171
|
{
|
|
@@ -3059,15 +3182,7 @@ return true;`
|
|
|
3059
3182
|
hasClientSecret: !!appData.clientSecret,
|
|
3060
3183
|
hasServiceKey: !!appData.serviceKey,
|
|
3061
3184
|
|
|
3062
|
-
pubSubApps:
|
|
3063
|
-
pubSubApps &&
|
|
3064
|
-
pubSubApps.apps &&
|
|
3065
|
-
pubSubApps.apps.map(app => {
|
|
3066
|
-
if (app.id === values.pubSubApp) {
|
|
3067
|
-
app.selected = true;
|
|
3068
|
-
}
|
|
3069
|
-
return app;
|
|
3070
|
-
}),
|
|
3185
|
+
pubSubApps: await getPubSubAppsForSelect(values.pubSubApp),
|
|
3071
3186
|
|
|
3072
3187
|
values,
|
|
3073
3188
|
|
|
@@ -3142,7 +3257,7 @@ return true;`
|
|
|
3142
3257
|
throw new Error('Unexpected result');
|
|
3143
3258
|
}
|
|
3144
3259
|
|
|
3145
|
-
if (oauth2App && oauth2App.pubsubUpdates && oauth2App.pubsubUpdates.
|
|
3260
|
+
if (oauth2App && oauth2App.pubsubUpdates && Object.keys(oauth2App.pubsubUpdates).length > 0) {
|
|
3146
3261
|
await call({ cmd: 'googlePubSub', app: oauth2App.id });
|
|
3147
3262
|
}
|
|
3148
3263
|
|
|
@@ -3160,8 +3275,6 @@ return true;`
|
|
|
3160
3275
|
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
3161
3276
|
}
|
|
3162
3277
|
|
|
3163
|
-
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
3164
|
-
|
|
3165
3278
|
return h.view(
|
|
3166
3279
|
'config/oauth/edit',
|
|
3167
3280
|
{
|
|
@@ -3177,15 +3290,7 @@ return true;`
|
|
|
3177
3290
|
hasClientSecret: !!appData.clientSecret,
|
|
3178
3291
|
hasServiceKey: !!appData.serviceKey,
|
|
3179
3292
|
|
|
3180
|
-
pubSubApps:
|
|
3181
|
-
pubSubApps &&
|
|
3182
|
-
pubSubApps.apps &&
|
|
3183
|
-
pubSubApps.apps.map(app => {
|
|
3184
|
-
if (app.id === request.payload.pubSubApp) {
|
|
3185
|
-
app.selected = true;
|
|
3186
|
-
}
|
|
3187
|
-
return app;
|
|
3188
|
-
}),
|
|
3293
|
+
pubSubApps: await getPubSubAppsForSelect(request.payload.pubSubApp),
|
|
3189
3294
|
|
|
3190
3295
|
baseScopesApi: request.payload.baseScopes === 'api',
|
|
3191
3296
|
baseScopesImap: request.payload.baseScopes === 'imap' || !request.payload.baseScopes,
|
|
@@ -3249,8 +3354,6 @@ return true;`
|
|
|
3249
3354
|
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
3250
3355
|
}
|
|
3251
3356
|
|
|
3252
|
-
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
3253
|
-
|
|
3254
3357
|
return h
|
|
3255
3358
|
.view(
|
|
3256
3359
|
'config/oauth/edit',
|
|
@@ -3268,15 +3371,7 @@ return true;`
|
|
|
3268
3371
|
hasClientSecret: !!appData.clientSecret,
|
|
3269
3372
|
hasServiceKey: !!appData.serviceKey,
|
|
3270
3373
|
|
|
3271
|
-
pubSubApps:
|
|
3272
|
-
pubSubApps &&
|
|
3273
|
-
pubSubApps.apps &&
|
|
3274
|
-
pubSubApps.apps.map(app => {
|
|
3275
|
-
if (app.id === request.payload.pubSubApp) {
|
|
3276
|
-
app.selected = true;
|
|
3277
|
-
}
|
|
3278
|
-
return app;
|
|
3279
|
-
}),
|
|
3374
|
+
pubSubApps: await getPubSubAppsForSelect(request.payload.pubSubApp),
|
|
3280
3375
|
|
|
3281
3376
|
baseScopesApi: request.payload.baseScopes === 'api',
|
|
3282
3377
|
baseScopesImap: request.payload.baseScopes === 'imap' || !request.payload.baseScopes,
|
|
@@ -5473,7 +5568,7 @@ ${Buffer.from(data.content, 'base64url').toString('base64')}
|
|
|
5473
5568
|
|
|
5474
5569
|
async failAction(request, h, err) {
|
|
5475
5570
|
await request.flash({ type: 'danger', message: `Couldn't delete account. Try again.` });
|
|
5476
|
-
request.logger.error({ msg: 'Failed to delete
|
|
5571
|
+
request.logger.error({ msg: 'Failed to delete the account', err });
|
|
5477
5572
|
|
|
5478
5573
|
return h.redirect('/admin/accounts').takeover();
|
|
5479
5574
|
},
|
package/lib/schemas.js
CHANGED
|
@@ -450,6 +450,14 @@ const settingsSchema = {
|
|
|
450
450
|
queueKeep: Joi.number().integer().empty('').min(0).description('Number of completed and failed queue entries to retain for debugging'),
|
|
451
451
|
deliveryAttempts: Joi.number().integer().empty('').min(0).description('Maximum number of delivery attempts before marking a message as permanently failed'),
|
|
452
452
|
|
|
453
|
+
gmailSubscriptionTtl: Joi.number()
|
|
454
|
+
.integer()
|
|
455
|
+
.empty('')
|
|
456
|
+
.min(0)
|
|
457
|
+
.max(365)
|
|
458
|
+
.description('Gmail Pub/Sub subscription inactivity expiration in days. Empty for Google default (31 days), 0 for no expiration.')
|
|
459
|
+
.label('GmailSubscriptionTTL'),
|
|
460
|
+
|
|
453
461
|
/* ────────────── Templates ────────────── */
|
|
454
462
|
|
|
455
463
|
templateHeader: Joi.string()
|
|
@@ -1159,6 +1167,13 @@ const lastErrorSchema = Joi.object({
|
|
|
1159
1167
|
.label('OAuthTokenRequestError')
|
|
1160
1168
|
}).label('AccountErrorEntry');
|
|
1161
1169
|
|
|
1170
|
+
const pubSubErrorSchema = Joi.object({
|
|
1171
|
+
message: Joi.string().example('Failed to process subscription loop').description('Error message'),
|
|
1172
|
+
description: Joi.string().allow(null).example('Subscription not found').description('Error details')
|
|
1173
|
+
})
|
|
1174
|
+
.description('Pub/Sub subscription error, if any')
|
|
1175
|
+
.label('PubSubError');
|
|
1176
|
+
|
|
1162
1177
|
const templateSchemas = {
|
|
1163
1178
|
subject: Joi.string()
|
|
1164
1179
|
.allow('')
|
|
@@ -1350,6 +1365,7 @@ const googleProjectIdSchema = Joi.string()
|
|
|
1350
1365
|
.trim()
|
|
1351
1366
|
.allow('', false, null)
|
|
1352
1367
|
.max(256)
|
|
1368
|
+
.pattern(/^[a-z][a-z0-9-]{4,28}[a-z0-9]$/)
|
|
1353
1369
|
.example('project-name-425411')
|
|
1354
1370
|
.description('Google Cloud Project ID')
|
|
1355
1371
|
.label('GoogleProjectId');
|
|
@@ -1880,7 +1896,8 @@ module.exports = {
|
|
|
1880
1896
|
exportStatusSchema,
|
|
1881
1897
|
exportListSchema,
|
|
1882
1898
|
exportProgressSchema,
|
|
1883
|
-
exportIdSchema
|
|
1899
|
+
exportIdSchema,
|
|
1900
|
+
pubSubErrorSchema
|
|
1884
1901
|
};
|
|
1885
1902
|
|
|
1886
1903
|
/*
|
|
@@ -1352,7 +1352,7 @@ function init(args) {
|
|
|
1352
1352
|
|
|
1353
1353
|
async failAction(request, h, err) {
|
|
1354
1354
|
await request.flash({ type: 'danger', message: `Couldn't delete account. Try again.` });
|
|
1355
|
-
request.logger.error({ msg: 'Failed to delete
|
|
1355
|
+
request.logger.error({ msg: 'Failed to delete the account', err });
|
|
1356
1356
|
|
|
1357
1357
|
return h.redirect('/admin/accounts').takeover();
|
|
1358
1358
|
},
|
|
@@ -766,7 +766,7 @@ return payload;`)
|
|
|
766
766
|
|
|
767
767
|
async failAction(request, h, err) {
|
|
768
768
|
await request.flash({ type: 'danger', message: `Couldn't delete webhook. Try again.` });
|
|
769
|
-
request.logger.error({ msg: 'Failed to delete
|
|
769
|
+
request.logger.error({ msg: 'Failed to delete Webhook Route', err });
|
|
770
770
|
|
|
771
771
|
return h.redirect('/admin/webhooks').takeover();
|
|
772
772
|
},
|
|
@@ -1415,7 +1415,7 @@ return payload;`)
|
|
|
1415
1415
|
|
|
1416
1416
|
async failAction(request, h, err) {
|
|
1417
1417
|
await request.flash({ type: 'danger', message: `Couldn't delete account. Try again.` });
|
|
1418
|
-
request.logger.error({ msg: 'Failed to delete
|
|
1418
|
+
request.logger.error({ msg: 'Failed to delete the account', err });
|
|
1419
1419
|
|
|
1420
1420
|
return h.redirect('/admin/templates').takeover();
|
|
1421
1421
|
},
|
|
@@ -2122,7 +2122,7 @@ return payload;`)
|
|
|
2122
2122
|
|
|
2123
2123
|
async failAction(request, h, err) {
|
|
2124
2124
|
await request.flash({ type: 'danger', message: `Couldn't delete gateway. Try again.` });
|
|
2125
|
-
request.logger.error({ msg: 'Failed to delete
|
|
2125
|
+
request.logger.error({ msg: 'Failed to delete the gateway', err });
|
|
2126
2126
|
|
|
2127
2127
|
return h.redirect('/admin/gateways').takeover();
|
|
2128
2128
|
},
|
|
@@ -402,6 +402,12 @@ function init({ server, call }) {
|
|
|
402
402
|
try {
|
|
403
403
|
await oauth2Apps.del(request.payload.app);
|
|
404
404
|
|
|
405
|
+
try {
|
|
406
|
+
await call({ cmd: 'googlePubSubRemove', app: request.payload.app });
|
|
407
|
+
} catch (err) {
|
|
408
|
+
request.logger.error({ msg: 'Failed to notify workers about OAuth2 app deletion', err, app: request.payload.app });
|
|
409
|
+
}
|
|
410
|
+
|
|
405
411
|
await request.flash({ type: 'info', message: `OAuth2 app deleted` });
|
|
406
412
|
|
|
407
413
|
return h.redirect('/admin/config/oauth');
|
|
@@ -421,7 +427,7 @@ function init({ server, call }) {
|
|
|
421
427
|
|
|
422
428
|
async failAction(request, h, err) {
|
|
423
429
|
await request.flash({ type: 'danger', message: `Couldn't delete OAuth2 app. Try again.` });
|
|
424
|
-
request.logger.error({ msg: 'Failed to delete
|
|
430
|
+
request.logger.error({ msg: 'Failed to delete the OAuth2 application', err });
|
|
425
431
|
|
|
426
432
|
return h.redirect('/admin/config/oauth').takeover();
|
|
427
433
|
},
|
|
@@ -538,7 +544,7 @@ function init({ server, call }) {
|
|
|
538
544
|
throw new Error('Unexpected result');
|
|
539
545
|
}
|
|
540
546
|
|
|
541
|
-
if (oauth2App && oauth2App.pubsubUpdates && oauth2App.pubsubUpdates.
|
|
547
|
+
if (oauth2App && oauth2App.pubsubUpdates && Object.keys(oauth2App.pubsubUpdates).length > 0) {
|
|
542
548
|
await call({ cmd: 'googlePubSub', app: oauth2App.id });
|
|
543
549
|
}
|
|
544
550
|
|
|
@@ -824,7 +830,7 @@ function init({ server, call }) {
|
|
|
824
830
|
throw new Error('Unexpected result');
|
|
825
831
|
}
|
|
826
832
|
|
|
827
|
-
if (oauth2App && oauth2App.pubsubUpdates && oauth2App.pubsubUpdates.
|
|
833
|
+
if (oauth2App && oauth2App.pubsubUpdates && Object.keys(oauth2App.pubsubUpdates).length > 0) {
|
|
828
834
|
await call({ cmd: 'googlePubSub', app: oauth2App.id });
|
|
829
835
|
}
|
|
830
836
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emailengine-app",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.64.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"productTitle": "EmailEngine",
|
|
6
6
|
"description": "Email Sync Engine",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"homepage": "https://emailengine.app/",
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@bugsnag/js": "8.8.1",
|
|
47
|
-
"@bull-board/api": "6.20.
|
|
48
|
-
"@bull-board/hapi": "6.20.
|
|
47
|
+
"@bull-board/api": "6.20.5",
|
|
48
|
+
"@bull-board/hapi": "6.20.5",
|
|
49
49
|
"@elastic/elasticsearch": "8.15.3",
|
|
50
50
|
"@hapi/accept": "6.0.3",
|
|
51
51
|
"@hapi/bell": "13.1.0",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"@hapi/inert": "7.1.0",
|
|
57
57
|
"@hapi/vision": "7.0.3",
|
|
58
58
|
"@phc/pbkdf2": "1.1.14",
|
|
59
|
-
"@postalsys/bounce-classifier": "^2.
|
|
59
|
+
"@postalsys/bounce-classifier": "^2.1.0",
|
|
60
60
|
"@postalsys/certs": "1.0.12",
|
|
61
61
|
"@postalsys/ee-client": "1.3.0",
|
|
62
|
-
"@postalsys/email-ai-tools": "1.
|
|
63
|
-
"@postalsys/email-text-tools": "2.4.
|
|
62
|
+
"@postalsys/email-ai-tools": "1.12.0",
|
|
63
|
+
"@postalsys/email-text-tools": "2.4.3",
|
|
64
64
|
"@postalsys/gettext": "4.1.1",
|
|
65
65
|
"@postalsys/joi-messages": "1.0.5",
|
|
66
66
|
"@postalsys/templates": "2.0.0",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@zone-eu/wild-config": "1.7.3",
|
|
69
69
|
"ace-builds": "1.43.6",
|
|
70
70
|
"base32.js": "0.1.0",
|
|
71
|
-
"bullmq": "5.
|
|
71
|
+
"bullmq": "5.71.0",
|
|
72
72
|
"compare-versions": "6.1.1",
|
|
73
73
|
"dotenv": "17.3.1",
|
|
74
74
|
"encoding-japanese": "2.2.0",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"html-to-text": "9.0.5",
|
|
83
83
|
"ical.js": "1.5.0",
|
|
84
84
|
"iconv-lite": "0.7.2",
|
|
85
|
-
"imapflow": "1.2.
|
|
85
|
+
"imapflow": "1.2.15",
|
|
86
86
|
"ioredfour": "1.4.0",
|
|
87
87
|
"ioredis": "5.10.0",
|
|
88
88
|
"ipaddr.js": "2.3.0",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"speakeasy": "2.0.0",
|
|
112
112
|
"startbootstrap-sb-admin-2": "3.3.7",
|
|
113
113
|
"timezones-list": "3.1.0",
|
|
114
|
-
"undici": "7.
|
|
114
|
+
"undici": "7.24.4",
|
|
115
115
|
"xml2js": "0.6.2"
|
|
116
116
|
},
|
|
117
117
|
"devDependencies": {
|