emailengine-app 2.61.5 → 2.62.1
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 +88 -0
- package/data/google-crawlers.json +1 -1
- package/lib/account.js +20 -7
- package/lib/api-routes/account-routes.js +28 -5
- package/lib/api-routes/chat-routes.js +1 -1
- package/lib/api-routes/export-routes.js +316 -0
- package/lib/api-routes/message-routes.js +28 -23
- package/lib/api-routes/template-routes.js +28 -7
- package/lib/arf-detect.js +1 -1
- package/lib/autodetect-imap-settings.js +5 -5
- package/lib/consts.js +16 -0
- package/lib/db.js +3 -0
- package/lib/email-client/base-client.js +6 -4
- package/lib/email-client/gmail-client.js +205 -35
- package/lib/email-client/imap/mailbox.js +99 -8
- package/lib/email-client/imap/subconnection.js +5 -5
- package/lib/email-client/imap-client.js +76 -19
- package/lib/email-client/message-builder.js +3 -1
- package/lib/email-client/notification-handler.js +12 -9
- package/lib/email-client/outlook-client.js +364 -73
- package/lib/email-client/smtp-pool-manager.js +1 -1
- package/lib/export.js +528 -0
- package/lib/oauth/gmail.js +24 -16
- package/lib/oauth/mail-ru.js +26 -13
- package/lib/oauth/outlook.js +29 -19
- package/lib/oauth/pubsub/google.js +5 -0
- package/lib/routes-ui.js +268 -9
- package/lib/schemas.js +274 -81
- package/lib/stream-encrypt.js +263 -0
- package/lib/sub-script.js +2 -2
- package/lib/tools.js +194 -12
- package/lib/ui-routes/account-routes.js +23 -0
- package/lib/ui-routes/admin-config-routes.js +13 -6
- package/lib/ui-routes/admin-entities-routes.js +18 -0
- package/lib/webhooks.js +16 -20
- package/package.json +20 -20
- package/sbom.json +1 -1
- package/server.js +66 -7
- package/static/js/ace/ace.js +1 -1
- package/static/js/ace/ext-language_tools.js +1 -1
- package/static/licenses.html +118 -149
- package/translations/de.mo +0 -0
- package/translations/de.po +63 -36
- package/translations/en.mo +0 -0
- package/translations/en.po +64 -37
- package/translations/et.mo +0 -0
- package/translations/et.po +63 -36
- package/translations/fr.mo +0 -0
- package/translations/fr.po +63 -36
- package/translations/ja.mo +0 -0
- package/translations/ja.po +63 -36
- package/translations/messages.pot +84 -51
- package/translations/nl.mo +0 -0
- package/translations/nl.po +63 -36
- package/translations/pl.mo +0 -0
- package/translations/pl.po +63 -36
- package/views/accounts/account.hbs +375 -2
- package/views/config/network.hbs +45 -0
- package/views/config/service.hbs +35 -0
- package/workers/api.js +130 -47
- package/workers/documents.js +3 -2
- package/workers/export.js +933 -0
- package/workers/imap.js +34 -1
- package/workers/submit.js +33 -6
- package/workers/webhooks.js +20 -4
|
@@ -32,6 +32,14 @@
|
|
|
32
32
|
data-toggle="tooltip" data-placement="top">
|
|
33
33
|
<i class="fas fa-redo fa-fw" id="sync-icon"></i> Run sync
|
|
34
34
|
</button>
|
|
35
|
+
|
|
36
|
+
{{#if canReadMail}}
|
|
37
|
+
<button type="button" class="btn btn-light" id="request-export" title="Export messages to file"
|
|
38
|
+
data-toggle="modal" data-target="#exportModal">
|
|
39
|
+
<i class="fas fa-file-export fa-fw"></i> Export
|
|
40
|
+
<span id="export-status-indicator" class="badge badge-pill ml-1 d-none"></span>
|
|
41
|
+
</button>
|
|
42
|
+
{{/if}}
|
|
35
43
|
</div>
|
|
36
44
|
|
|
37
45
|
<div class="btn-group mr-2 mb-1" role="group" aria-label="Third group">
|
|
@@ -64,7 +72,7 @@
|
|
|
64
72
|
{{#if canUseSmtp}}
|
|
65
73
|
<a class="dropdown-item" data-toggle="modal" data-target="#testSendModal" data-keyboard="false"
|
|
66
74
|
data-backdrop="static" data-account="{{account.account}}" href="#">Account's
|
|
67
|
-
|
|
75
|
+
Mail server</a>
|
|
68
76
|
{{#if gateways}}
|
|
69
77
|
<div class="dropdown-divider"></div>
|
|
70
78
|
{{/if}}
|
|
@@ -935,6 +943,85 @@
|
|
|
935
943
|
</div>
|
|
936
944
|
</div>
|
|
937
945
|
|
|
946
|
+
<div class="modal fade" id="exportModal" tabindex="-1" aria-labelledby="exportModalLabel" aria-hidden="true">
|
|
947
|
+
<div class="modal-dialog">
|
|
948
|
+
<div class="modal-content">
|
|
949
|
+
<div class="modal-header">
|
|
950
|
+
<h5 class="modal-title" id="exportModalLabel">Export Messages</h5>
|
|
951
|
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
952
|
+
<span aria-hidden="true">×</span>
|
|
953
|
+
</button>
|
|
954
|
+
</div>
|
|
955
|
+
<div class="modal-body">
|
|
956
|
+
<div class="alert alert-info mb-3">
|
|
957
|
+
<i class="fas fa-flask fa-fw"></i>
|
|
958
|
+
<strong>Beta Feature:</strong> The export feature is currently in beta. If you encounter any issues,
|
|
959
|
+
please contact <a href="mailto:support@postalsys.com">support@postalsys.com</a>.
|
|
960
|
+
</div>
|
|
961
|
+
<div id="export-form-view">
|
|
962
|
+
<div class="form-group">
|
|
963
|
+
<label for="export-start-date">Start Date</label>
|
|
964
|
+
<input type="date" class="form-control" id="export-start-date">
|
|
965
|
+
</div>
|
|
966
|
+
<div class="form-group">
|
|
967
|
+
<label for="export-end-date">End Date</label>
|
|
968
|
+
<input type="date" class="form-control" id="export-end-date">
|
|
969
|
+
</div>
|
|
970
|
+
<div class="form-group">
|
|
971
|
+
<div class="custom-control custom-checkbox">
|
|
972
|
+
<input type="checkbox" class="custom-control-input" id="export-include-attachments">
|
|
973
|
+
<label class="custom-control-label" for="export-include-attachments">Include
|
|
974
|
+
attachments</label>
|
|
975
|
+
</div>
|
|
976
|
+
<small class="form-text text-muted">Download and include attachment file contents in the export.
|
|
977
|
+
This increases export size and time.</small>
|
|
978
|
+
</div>
|
|
979
|
+
<small class="text-muted">Gmail accounts export from All Mail. Other accounts export all folders
|
|
980
|
+
(excluding Junk and Trash).</small>
|
|
981
|
+
</div>
|
|
982
|
+
|
|
983
|
+
<div id="export-status-view" class="d-none">
|
|
984
|
+
<div class="mb-3">
|
|
985
|
+
<strong>Status:</strong>
|
|
986
|
+
<span id="export-status-badge" class="badge badge-info">Queued</span>
|
|
987
|
+
<span id="export-phase-badge" class="badge badge-secondary ml-1 d-none"></span>
|
|
988
|
+
</div>
|
|
989
|
+
<div class="mb-3">
|
|
990
|
+
<strong>Expires:</strong>
|
|
991
|
+
<span id="export-expires-at" class="text-muted"></span>
|
|
992
|
+
</div>
|
|
993
|
+
<div id="export-progress-section">
|
|
994
|
+
<div class="progress mb-2">
|
|
995
|
+
<div id="export-progress-bar"
|
|
996
|
+
class="progress-bar progress-bar-striped progress-bar-animated bg-info"
|
|
997
|
+
role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
|
998
|
+
style="width: 0%"></div>
|
|
999
|
+
</div>
|
|
1000
|
+
<small id="export-progress-text" class="text-muted"></small>
|
|
1001
|
+
</div>
|
|
1002
|
+
<div id="export-error-section" class="d-none">
|
|
1003
|
+
<div class="alert alert-danger" id="export-error-message"></div>
|
|
1004
|
+
</div>
|
|
1005
|
+
<div id="export-download-section" class="d-none mt-3">
|
|
1006
|
+
<a href="#" id="export-download-link" class="btn btn-success btn-block">
|
|
1007
|
+
<i class="fas fa-download fa-fw"></i> Download Export
|
|
1008
|
+
</a>
|
|
1009
|
+
</div>
|
|
1010
|
+
</div>
|
|
1011
|
+
</div>
|
|
1012
|
+
<div class="modal-footer">
|
|
1013
|
+
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
|
1014
|
+
<button type="button" class="btn btn-danger d-none" id="export-reset-btn">
|
|
1015
|
+
<i class="fas fa-trash fa-fw"></i> Reset
|
|
1016
|
+
</button>
|
|
1017
|
+
<button type="button" class="btn btn-primary" id="export-start-btn">
|
|
1018
|
+
<i class="fas fa-file-export fa-fw" id="export-start-icon"></i> Start Export
|
|
1019
|
+
</button>
|
|
1020
|
+
</div>
|
|
1021
|
+
</div>
|
|
1022
|
+
</div>
|
|
1023
|
+
</div>
|
|
1024
|
+
|
|
938
1025
|
<input type="hidden" id="account-id" value="{{account.account}}">
|
|
939
1026
|
<input type="hidden" id="crumb" value="{{crumb}}">
|
|
940
1027
|
|
|
@@ -1107,7 +1194,7 @@
|
|
|
1107
1194
|
flushingLogs = false;
|
|
1108
1195
|
showToast(data.error ? data.error : (data.success ? 'Stored logs were flushed' : 'Failed to flush logs'), data.success ? 'check-circle' : 'alert-triangle');
|
|
1109
1196
|
}).catch(err => {
|
|
1110
|
-
|
|
1197
|
+
flushingLogs = false;
|
|
1111
1198
|
showToast('Request failed\n' + err.message, 'alert-triangle');
|
|
1112
1199
|
})
|
|
1113
1200
|
});
|
|
@@ -1121,6 +1208,292 @@
|
|
|
1121
1208
|
|
|
1122
1209
|
$('#renew-grant-btn').tooltip();
|
|
1123
1210
|
$('#renew-grant-btn').click(() => $('#renew-grant-btn').tooltip('hide'));
|
|
1211
|
+
|
|
1212
|
+
// Export status indicator on page load
|
|
1213
|
+
async function updateExportIndicator() {
|
|
1214
|
+
const account = document.getElementById('account-id').value.trim();
|
|
1215
|
+
const indicator = document.getElementById('export-status-indicator');
|
|
1216
|
+
if (!indicator) return;
|
|
1217
|
+
|
|
1218
|
+
try {
|
|
1219
|
+
const res = await fetch(`/admin/accounts/${account}/exports?pageSize=1`);
|
|
1220
|
+
if (!res.ok) {
|
|
1221
|
+
indicator.classList.add('d-none');
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
const data = await res.json();
|
|
1225
|
+
|
|
1226
|
+
if (data.exports && data.exports.length > 0) {
|
|
1227
|
+
const latest = data.exports[0];
|
|
1228
|
+
if (latest.status === 'queued' || latest.status === 'processing') {
|
|
1229
|
+
indicator.textContent = latest.status === 'queued' ? 'Queued' : 'Exporting';
|
|
1230
|
+
indicator.className = 'badge badge-pill badge-info ml-1';
|
|
1231
|
+
} else if (latest.status === 'completed') {
|
|
1232
|
+
indicator.textContent = 'Ready';
|
|
1233
|
+
indicator.className = 'badge badge-pill badge-success ml-1';
|
|
1234
|
+
} else {
|
|
1235
|
+
indicator.classList.add('d-none');
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
indicator.classList.remove('d-none');
|
|
1239
|
+
} else {
|
|
1240
|
+
indicator.classList.add('d-none');
|
|
1241
|
+
}
|
|
1242
|
+
} catch (err) {
|
|
1243
|
+
console.error('Failed to check export status', err);
|
|
1244
|
+
indicator.classList.add('d-none');
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// Check export status on page load
|
|
1249
|
+
updateExportIndicator();
|
|
1250
|
+
|
|
1251
|
+
// Export modal functionality
|
|
1252
|
+
let currentExportId = null;
|
|
1253
|
+
let exportPollingInterval = null;
|
|
1254
|
+
|
|
1255
|
+
function showFormView() {
|
|
1256
|
+
document.getElementById('export-form-view').classList.remove('d-none');
|
|
1257
|
+
document.getElementById('export-status-view').classList.add('d-none');
|
|
1258
|
+
document.getElementById('export-start-btn').classList.remove('d-none');
|
|
1259
|
+
document.getElementById('export-reset-btn').classList.add('d-none');
|
|
1260
|
+
|
|
1261
|
+
// Set default dates (last 1 year)
|
|
1262
|
+
const today = new Date();
|
|
1263
|
+
const lastYear = new Date(today);
|
|
1264
|
+
lastYear.setFullYear(lastYear.getFullYear() - 1);
|
|
1265
|
+
document.getElementById('export-end-date').value = today.toISOString().split('T')[0];
|
|
1266
|
+
document.getElementById('export-start-date').value = lastYear.toISOString().split('T')[0];
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
function showStatusView() {
|
|
1270
|
+
document.getElementById('export-form-view').classList.add('d-none');
|
|
1271
|
+
document.getElementById('export-status-view').classList.remove('d-none');
|
|
1272
|
+
document.getElementById('export-start-btn').classList.add('d-none');
|
|
1273
|
+
document.getElementById('export-reset-btn').classList.remove('d-none');
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function getStatusBadgeClass(status) {
|
|
1277
|
+
switch (status) {
|
|
1278
|
+
case 'completed': return 'success';
|
|
1279
|
+
case 'failed': return 'danger';
|
|
1280
|
+
case 'processing': return 'info';
|
|
1281
|
+
default: return 'secondary';
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
function startPolling() {
|
|
1286
|
+
if (exportPollingInterval) return;
|
|
1287
|
+
exportPollingInterval = setInterval(updateExportStatus, 3000);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
function stopPolling() {
|
|
1291
|
+
if (exportPollingInterval) {
|
|
1292
|
+
clearInterval(exportPollingInterval);
|
|
1293
|
+
exportPollingInterval = null;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
async function updateExportStatus() {
|
|
1298
|
+
if (!currentExportId) return;
|
|
1299
|
+
|
|
1300
|
+
const account = document.getElementById('account-id').value.trim();
|
|
1301
|
+
try {
|
|
1302
|
+
const res = await fetch(`/admin/accounts/${account}/export/${currentExportId}`);
|
|
1303
|
+
if (!res.ok) {
|
|
1304
|
+
throw new Error(`HTTP error! status: ${res.status}`);
|
|
1305
|
+
}
|
|
1306
|
+
const data = await res.json();
|
|
1307
|
+
|
|
1308
|
+
// Update status badge
|
|
1309
|
+
const badge = document.getElementById('export-status-badge');
|
|
1310
|
+
badge.textContent = data.status.charAt(0).toUpperCase() + data.status.slice(1);
|
|
1311
|
+
badge.className = 'badge badge-' + getStatusBadgeClass(data.status);
|
|
1312
|
+
|
|
1313
|
+
// Update phase badge
|
|
1314
|
+
const phaseBadge = document.getElementById('export-phase-badge');
|
|
1315
|
+
if (data.phase && data.status === 'processing') {
|
|
1316
|
+
phaseBadge.textContent = data.phase.charAt(0).toUpperCase() + data.phase.slice(1);
|
|
1317
|
+
phaseBadge.classList.remove('d-none');
|
|
1318
|
+
} else {
|
|
1319
|
+
phaseBadge.classList.add('d-none');
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// Update expiration date
|
|
1323
|
+
const expiresAtEl = document.getElementById('export-expires-at');
|
|
1324
|
+
if (data.expiresAt) {
|
|
1325
|
+
const expiresDate = new Date(data.expiresAt);
|
|
1326
|
+
expiresAtEl.textContent = expiresDate.toLocaleString();
|
|
1327
|
+
} else {
|
|
1328
|
+
expiresAtEl.textContent = 'Unknown';
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// Update progress bar
|
|
1332
|
+
const progressBar = document.getElementById('export-progress-bar');
|
|
1333
|
+
const progressText = document.getElementById('export-progress-text');
|
|
1334
|
+
|
|
1335
|
+
if (data.phase === 'indexing' && data.progress) {
|
|
1336
|
+
// During indexing, show folders scanned progress
|
|
1337
|
+
const pct = data.progress.foldersTotal > 0
|
|
1338
|
+
? Math.round((data.progress.foldersScanned / data.progress.foldersTotal) * 100)
|
|
1339
|
+
: 0;
|
|
1340
|
+
progressBar.style.width = pct + '%';
|
|
1341
|
+
progressBar.setAttribute('aria-valuenow', pct);
|
|
1342
|
+
progressText.textContent = `Scanning folders: ${data.progress.foldersScanned || 0} / ${data.progress.foldersTotal || '?'}`;
|
|
1343
|
+
} else if (data.progress) {
|
|
1344
|
+
// During exporting, show messages exported progress
|
|
1345
|
+
const pct = data.progress.messagesQueued > 0
|
|
1346
|
+
? Math.round((data.progress.messagesExported / data.progress.messagesQueued) * 100)
|
|
1347
|
+
: 0;
|
|
1348
|
+
progressBar.style.width = pct + '%';
|
|
1349
|
+
progressBar.setAttribute('aria-valuenow', pct);
|
|
1350
|
+
progressText.textContent =
|
|
1351
|
+
`${data.progress.messagesExported} / ${data.progress.messagesQueued} messages exported`;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// Handle completion
|
|
1355
|
+
if (data.status === 'completed') {
|
|
1356
|
+
stopPolling();
|
|
1357
|
+
document.getElementById('export-download-section').classList.remove('d-none');
|
|
1358
|
+
document.getElementById('export-download-link').href =
|
|
1359
|
+
`/admin/accounts/${account}/export/${currentExportId}/download`;
|
|
1360
|
+
progressBar.style.width = '100%';
|
|
1361
|
+
progressBar.setAttribute('aria-valuenow', 100);
|
|
1362
|
+
progressBar.classList.remove('bg-info', 'progress-bar-striped', 'progress-bar-animated');
|
|
1363
|
+
progressBar.classList.add('bg-success');
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// Handle failure
|
|
1367
|
+
if (data.status === 'failed') {
|
|
1368
|
+
stopPolling();
|
|
1369
|
+
document.getElementById('export-error-section').classList.remove('d-none');
|
|
1370
|
+
document.getElementById('export-error-message').textContent = data.error || 'Export failed';
|
|
1371
|
+
document.getElementById('export-progress-section').classList.add('d-none');
|
|
1372
|
+
}
|
|
1373
|
+
} catch (err) {
|
|
1374
|
+
console.error('Failed to update export status', err);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
async function checkExistingExport() {
|
|
1379
|
+
const account = document.getElementById('account-id').value.trim();
|
|
1380
|
+
try {
|
|
1381
|
+
const res = await fetch(`/admin/accounts/${account}/exports?pageSize=1`);
|
|
1382
|
+
if (!res.ok) {
|
|
1383
|
+
showFormView();
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
const data = await res.json();
|
|
1387
|
+
|
|
1388
|
+
if (data.exports && data.exports.length > 0) {
|
|
1389
|
+
const latest = data.exports[0];
|
|
1390
|
+
if (latest.status === 'queued' || latest.status === 'processing' || latest.status === 'completed') {
|
|
1391
|
+
currentExportId = latest.exportId;
|
|
1392
|
+
showStatusView();
|
|
1393
|
+
// Reset progress display state
|
|
1394
|
+
document.getElementById('export-progress-section').classList.remove('d-none');
|
|
1395
|
+
document.getElementById('export-error-section').classList.add('d-none');
|
|
1396
|
+
document.getElementById('export-download-section').classList.add('d-none');
|
|
1397
|
+
document.getElementById('export-phase-badge').classList.add('d-none');
|
|
1398
|
+
const progressBar = document.getElementById('export-progress-bar');
|
|
1399
|
+
progressBar.classList.remove('bg-success');
|
|
1400
|
+
progressBar.classList.add('bg-info', 'progress-bar-striped', 'progress-bar-animated');
|
|
1401
|
+
await updateExportStatus();
|
|
1402
|
+
if (latest.status !== 'completed' && latest.status !== 'failed') {
|
|
1403
|
+
startPolling();
|
|
1404
|
+
}
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
showFormView();
|
|
1409
|
+
} catch (err) {
|
|
1410
|
+
console.error('Failed to check existing export', err);
|
|
1411
|
+
showFormView();
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
$('#exportModal').on('show.bs.modal', async function () {
|
|
1416
|
+
await checkExistingExport();
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1419
|
+
$('#exportModal').on('hidden.bs.modal', function () {
|
|
1420
|
+
stopPolling();
|
|
1421
|
+
updateExportIndicator();
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
document.getElementById('export-start-btn').addEventListener('click', async function () {
|
|
1425
|
+
const account = document.getElementById('account-id').value.trim();
|
|
1426
|
+
const startDate = document.getElementById('export-start-date').value;
|
|
1427
|
+
const endDate = document.getElementById('export-end-date').value;
|
|
1428
|
+
const includeAttachments = document.getElementById('export-include-attachments').checked;
|
|
1429
|
+
|
|
1430
|
+
if (!startDate || !endDate) {
|
|
1431
|
+
showToast('Please select date range', 'alert-triangle');
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
document.getElementById('export-start-icon').classList.add('fa-spin');
|
|
1436
|
+
|
|
1437
|
+
try {
|
|
1438
|
+
const res = await fetch(`/admin/accounts/${account}/export`, {
|
|
1439
|
+
method: 'POST',
|
|
1440
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1441
|
+
body: JSON.stringify({
|
|
1442
|
+
crumb: document.getElementById('crumb').value,
|
|
1443
|
+
startDate: startDate + 'T00:00:00Z',
|
|
1444
|
+
endDate: endDate + 'T23:59:59Z',
|
|
1445
|
+
includeAttachments
|
|
1446
|
+
})
|
|
1447
|
+
});
|
|
1448
|
+
|
|
1449
|
+
const data = await res.json();
|
|
1450
|
+
if (data.exportId) {
|
|
1451
|
+
currentExportId = data.exportId;
|
|
1452
|
+
showStatusView();
|
|
1453
|
+
// Reset progress display state
|
|
1454
|
+
document.getElementById('export-progress-section').classList.remove('d-none');
|
|
1455
|
+
document.getElementById('export-error-section').classList.add('d-none');
|
|
1456
|
+
document.getElementById('export-download-section').classList.add('d-none');
|
|
1457
|
+
document.getElementById('export-phase-badge').classList.add('d-none');
|
|
1458
|
+
const progressBar = document.getElementById('export-progress-bar');
|
|
1459
|
+
progressBar.classList.remove('bg-success');
|
|
1460
|
+
progressBar.classList.add('bg-info', 'progress-bar-striped', 'progress-bar-animated');
|
|
1461
|
+
progressBar.style.width = '0%';
|
|
1462
|
+
progressBar.setAttribute('aria-valuenow', 0);
|
|
1463
|
+
document.getElementById('export-progress-text').textContent = '';
|
|
1464
|
+
await updateExportStatus();
|
|
1465
|
+
startPolling();
|
|
1466
|
+
showToast('Export started', 'check-circle');
|
|
1467
|
+
} else {
|
|
1468
|
+
showToast(data.error || data.message || 'Failed to start export', 'alert-triangle');
|
|
1469
|
+
}
|
|
1470
|
+
} catch (err) {
|
|
1471
|
+
showToast('Export failed: ' + err.message, 'alert-triangle');
|
|
1472
|
+
} finally {
|
|
1473
|
+
document.getElementById('export-start-icon').classList.remove('fa-spin');
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
document.getElementById('export-reset-btn').addEventListener('click', async function () {
|
|
1478
|
+
if (!currentExportId) return;
|
|
1479
|
+
|
|
1480
|
+
const account = document.getElementById('account-id').value.trim();
|
|
1481
|
+
|
|
1482
|
+
try {
|
|
1483
|
+
await fetch(`/admin/accounts/${account}/export/${currentExportId}`, {
|
|
1484
|
+
method: 'DELETE',
|
|
1485
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1486
|
+
body: JSON.stringify({ crumb: document.getElementById('crumb').value })
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
stopPolling();
|
|
1490
|
+
currentExportId = null;
|
|
1491
|
+
showFormView();
|
|
1492
|
+
showToast('Export cleared', 'check-circle');
|
|
1493
|
+
} catch (err) {
|
|
1494
|
+
showToast('Failed to clear export: ' + err.message, 'alert-triangle');
|
|
1495
|
+
}
|
|
1496
|
+
});
|
|
1124
1497
|
});
|
|
1125
1498
|
</script>
|
|
1126
1499
|
|
package/views/config/network.hbs
CHANGED
|
@@ -70,6 +70,51 @@
|
|
|
70
70
|
</div>
|
|
71
71
|
</div>
|
|
72
72
|
|
|
73
|
+
<div class="card mb-4 mt-4">
|
|
74
|
+
<div class="card-header py-3">
|
|
75
|
+
<h6 class="m-0 font-weight-bold text-primary">HTTP Proxy Settings</h6>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="card-body">
|
|
78
|
+
<p>Route all outbound HTTP/HTTPS requests through a proxy. This applies to webhook delivery, OAuth token
|
|
79
|
+
exchange, and API calls to Gmail/Outlook. This is separate from the IMAP/SMTP proxy above.</p>
|
|
80
|
+
|
|
81
|
+
<div class="form-group form-check">
|
|
82
|
+
|
|
83
|
+
<div class="text-muted float-right code-link">[<a href="/admin/swagger#/Settings/postV1Settings"
|
|
84
|
+
target="_blank" rel="noopener noreferrer">httpProxyEnabled</a>]</div>
|
|
85
|
+
|
|
86
|
+
<input type="checkbox"
|
|
87
|
+
class="form-check-input http-proxy-status {{#if errors.httpProxyEnabled}}is-invalid{{/if}}"
|
|
88
|
+
id="http_proxy_enabled" name="httpProxyEnabled" {{#if
|
|
89
|
+
values.httpProxyEnabled}}checked{{/if}} />
|
|
90
|
+
<label class="form-check-label" for="http_proxy_enabled">Enable HTTP Proxy</label>
|
|
91
|
+
{{#if errors.httpProxyEnabled}}
|
|
92
|
+
<span class="invalid-feedback">{{errors.httpProxyEnabled}}</span>
|
|
93
|
+
{{/if}}
|
|
94
|
+
<small class="form-text text-muted">Routes all outbound HTTP/HTTPS requests through the proxy
|
|
95
|
+
below.</small>
|
|
96
|
+
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div class="form-group">
|
|
100
|
+
|
|
101
|
+
<div class="text-muted float-right code-link">[<a href="/admin/swagger#/Settings/postV1Settings"
|
|
102
|
+
target="_blank" rel="noopener noreferrer">httpProxyUrl</a>]</div>
|
|
103
|
+
|
|
104
|
+
<label for="httpProxyUrl">HTTP Proxy URL</label>
|
|
105
|
+
<input type="text" class="form-control or-else-all {{#if errors.httpProxyUrl}}is-invalid{{/if}}"
|
|
106
|
+
id="httpProxyUrl" placeholder="http://proxy:8080" value="{{values.httpProxyUrl}}"
|
|
107
|
+
name="httpProxyUrl" data-target="http-proxy-status">
|
|
108
|
+
{{#if errors.httpProxyUrl}}
|
|
109
|
+
<span class="invalid-feedback">{{errors.httpProxyUrl}}</span>
|
|
110
|
+
{{/if}}
|
|
111
|
+
<small id="httpProxyUrlBlock" class="form-text text-muted">Format: <code>http://</code>,
|
|
112
|
+
<code>https://</code>, <code>socks4://</code>, or <code>socks5://</code>
|
|
113
|
+
followed by host:port</small>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
73
118
|
<div class="card mb-4 mt-4">
|
|
74
119
|
<div class="card-header py-3">
|
|
75
120
|
<h6 class="m-0 font-weight-bold text-primary">IP Address Strategy</h6>
|
package/views/config/service.hbs
CHANGED
|
@@ -341,6 +341,41 @@
|
|
|
341
341
|
</div>
|
|
342
342
|
</div>
|
|
343
343
|
|
|
344
|
+
<div id="export_settings" class="card mb-4">
|
|
345
|
+
<div class="card-header py-3">
|
|
346
|
+
<h6 class="m-0 font-weight-bold text-primary">Export Settings</h6>
|
|
347
|
+
</div>
|
|
348
|
+
<div class="card-body">
|
|
349
|
+
<p>Configure batch sizes for bulk message export operations.</p>
|
|
350
|
+
|
|
351
|
+
<div class="form-group">
|
|
352
|
+
<div class="text-muted float-right code-link">[<a href="/admin/swagger#/Settings/postV1Settings"
|
|
353
|
+
target="_blank" rel="noopener noreferrer">gmailExportBatchSize</a>]</div>
|
|
354
|
+
<label for="settingsGmailExportBatchSize">Gmail Export Batch Size</label>
|
|
355
|
+
<input type="number" min="1" max="50" class="form-control {{#if errors.gmailExportBatchSize}}is-invalid{{/if}}"
|
|
356
|
+
id="settingsGmailExportBatchSize" name="gmailExportBatchSize" value="{{values.gmailExportBatchSize}}"
|
|
357
|
+
data-lpignore="true" autocomplete="off" />
|
|
358
|
+
{{#if errors.gmailExportBatchSize}}
|
|
359
|
+
<span class="invalid-feedback">{{errors.gmailExportBatchSize}}</span>
|
|
360
|
+
{{/if}}
|
|
361
|
+
<small class="form-text text-muted">Number of parallel requests when fetching Gmail messages for export (1-50, default: 10).</small>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
<div class="form-group">
|
|
365
|
+
<div class="text-muted float-right code-link">[<a href="/admin/swagger#/Settings/postV1Settings"
|
|
366
|
+
target="_blank" rel="noopener noreferrer">outlookExportBatchSize</a>]</div>
|
|
367
|
+
<label for="settingsOutlookExportBatchSize">Outlook Export Batch Size</label>
|
|
368
|
+
<input type="number" min="1" max="20" class="form-control {{#if errors.outlookExportBatchSize}}is-invalid{{/if}}"
|
|
369
|
+
id="settingsOutlookExportBatchSize" name="outlookExportBatchSize" value="{{values.outlookExportBatchSize}}"
|
|
370
|
+
data-lpignore="true" autocomplete="off" />
|
|
371
|
+
{{#if errors.outlookExportBatchSize}}
|
|
372
|
+
<span class="invalid-feedback">{{errors.outlookExportBatchSize}}</span>
|
|
373
|
+
{{/if}}
|
|
374
|
+
<small class="form-text text-muted">Messages per batch request for Outlook exports (1-20, default: 20). Limited by Microsoft Graph API.</small>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
344
379
|
<div id="templates_settings" class="card mb-4">
|
|
345
380
|
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
|
346
381
|
<h6 class="m-0 font-weight-bold text-primary">Public Page Customization</h6>
|