@stamhoofd/backend 2.39.1 → 2.40.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/eslint.config.mjs +5 -0
- package/index.ts +81 -74
- package/jest.config.cjs +10 -0
- package/migrations.ts +16 -14
- package/package.json +11 -11
- package/src/crons/clear-excel-cache.test.ts +48 -50
- package/src/crons/clear-excel-cache.ts +18 -18
- package/src/crons/setup-steps.ts +2 -2
- package/src/crons.ts +325 -306
- package/src/decoders/StringArrayDecoder.ts +7 -7
- package/src/decoders/StringNullableDecoder.ts +1 -2
- package/src/email-recipient-loaders/members.ts +22 -22
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +8 -9
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +39 -40
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +8 -8
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +44 -45
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +58 -57
- package/src/endpoints/auth/CreateAdminEndpoint.ts +48 -45
- package/src/endpoints/auth/CreateTokenEndpoint.test.ts +31 -31
- package/src/endpoints/auth/CreateTokenEndpoint.ts +146 -147
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +7 -7
- package/src/endpoints/auth/DeleteUserEndpoint.ts +15 -15
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +17 -18
- package/src/endpoints/auth/GetOtherUserEndpoint.ts +9 -10
- package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -35
- package/src/endpoints/auth/GetUserEndpoint.ts +5 -6
- package/src/endpoints/auth/PatchApiUserEndpoint.ts +35 -33
- package/src/endpoints/auth/PatchUserEndpoint.ts +55 -52
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +9 -9
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +8 -8
- package/src/endpoints/auth/SignupEndpoint.ts +37 -36
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +29 -28
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +33 -33
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +7 -7
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +37 -37
- package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -30
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +13 -13
- package/src/endpoints/global/email/GetEmailEndpoint.ts +13 -13
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +16 -16
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +25 -25
- package/src/endpoints/global/events/GetEventsEndpoint.ts +43 -44
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +127 -172
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +49 -50
- package/src/endpoints/global/files/GetFileCache.ts +13 -13
- package/src/endpoints/global/files/UploadFile.ts +51 -54
- package/src/endpoints/global/files/UploadImage.ts +53 -53
- package/src/endpoints/global/groups/GetGroupsEndpoint.ts +25 -25
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +24 -23
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +8 -8
- package/src/endpoints/global/members/GetMembersEndpoint.ts +105 -102
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +240 -239
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +12 -14
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +32 -33
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +48 -57
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +21 -22
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +28 -28
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +18 -18
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +20 -20
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +17 -17
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +81 -75
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +14 -14
- package/src/endpoints/global/platform/GetPlatformEnpoint.ts +11 -11
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +71 -68
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +27 -27
- package/src/endpoints/global/registration/GetUserBillingStatusEndpoint.ts +30 -30
- package/src/endpoints/global/registration/GetUserDetailedBillingStatusEndpoint.ts +34 -34
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +26 -26
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +12 -12
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +90 -90
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +118 -121
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +362 -350
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +8 -9
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +21 -21
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +65 -65
- package/src/endpoints/organization/dashboard/billing/GetOrganizationBillingStatusEndpoint.ts +9 -9
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedBillingStatusEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +17 -17
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +21 -21
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +15 -15
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +52 -52
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +37 -37
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +113 -112
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +29 -29
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +48 -47
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +22 -21
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +13 -14
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +12 -13
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +24 -24
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +10 -12
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +14 -14
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +13 -13
- package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +12 -12
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +120 -124
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +172 -173
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +88 -89
- package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +12 -12
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +17 -17
- package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +66 -67
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +47 -47
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +93 -91
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +16 -17
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +170 -167
- package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +25 -24
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +22 -23
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +22 -22
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +17 -18
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +8 -9
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +17 -18
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -15
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +19 -19
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +19 -19
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +12 -12
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +103 -100
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +11 -12
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +15 -15
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +23 -23
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +54 -52
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +84 -81
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +120 -111
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +24 -24
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +18 -18
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +141 -130
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +25 -25
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +18 -18
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +36 -37
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +9 -9
- package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +11 -11
- package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +28 -27
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +20 -20
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +22 -22
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -14
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +57 -56
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +65 -66
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +18 -17
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +124 -128
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +154 -145
- package/src/excel-loaders/members.ts +275 -273
- package/src/excel-loaders/payments.ts +155 -156
- package/src/helpers/AddressValidator.test.ts +32 -32
- package/src/helpers/AddressValidator.ts +128 -122
- package/src/helpers/AdminPermissionChecker.ts +339 -236
- package/src/helpers/AuthenticatedStructures.ts +233 -134
- package/src/helpers/BuckarooHelper.ts +134 -134
- package/src/helpers/CheckSettlements.ts +94 -88
- package/src/helpers/Context.ts +87 -86
- package/src/helpers/CookieHelper.ts +23 -22
- package/src/helpers/EmailResumer.ts +10 -10
- package/src/helpers/FileCache.ts +62 -62
- package/src/helpers/ForwardHandler.test.ts +122 -124
- package/src/helpers/ForwardHandler.ts +76 -70
- package/src/helpers/MemberUserSyncer.ts +101 -96
- package/src/helpers/MembershipCharger.ts +69 -69
- package/src/helpers/MembershipHelper.ts +11 -12
- package/src/helpers/OpenIDConnectHelper.ts +85 -82
- package/src/helpers/PeriodHelper.ts +65 -70
- package/src/helpers/StripeHelper.ts +146 -137
- package/src/helpers/StripePayoutChecker.ts +51 -52
- package/src/helpers/ViesHelper.ts +46 -44
- package/src/helpers/fetchToAsyncIterator.ts +14 -14
- package/src/helpers/xlsxAddressTransformerColumnFactory.ts +58 -60
- package/src/middleware/ContextMiddleware.ts +5 -5
- package/src/migrations/1646578856-validate-addresses.ts +6 -9
- package/src/seeds/0000000000-example.ts +3 -5
- package/src/seeds/1715028563-user-permissions.ts +16 -18
- package/src/seeds/1722256498-group-update-occupancy.ts +12 -12
- package/src/seeds/1722344162-sync-member-users.ts +14 -15
- package/src/seeds/1722344162-update-membership.ts +6 -6
- package/src/seeds/1726055544-balance-item-paid.ts +4 -4
- package/src/seeds/1726055545-balance-item-pending.ts +4 -4
- package/src/seeds/1726494419-update-cached-outstanding-balance.ts +16 -16
- package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +12 -12
- package/src/seeds/1726572303-schedule-stock-updates.ts +12 -12
- package/src/seeds/1726847064-setup-steps.ts +16 -0
- package/src/sql-filters/balance-item-payments.ts +7 -7
- package/src/sql-filters/events.ts +14 -14
- package/src/sql-filters/members.ts +96 -96
- package/src/sql-filters/organizations.ts +139 -75
- package/src/sql-filters/payments.ts +28 -28
- package/src/sql-filters/registrations.ts +14 -14
- package/src/sql-sorters/events.ts +25 -25
- package/src/sql-sorters/members.ts +26 -26
- package/src/sql-sorters/organizations.ts +36 -36
- package/src/sql-sorters/payments.ts +26 -26
- package/tests/e2e/stock.test.ts +616 -621
- package/tests/e2e/tickets.test.ts +255 -260
- package/tests/helpers/StripeMocker.ts +177 -179
- package/tests/helpers/TestServer.ts +9 -9
- package/tests/jest.global.setup.ts +14 -13
- package/tests/jest.setup.ts +33 -32
- package/.eslintrc.js +0 -61
- package/jest.config.js +0 -11
- package/src/helpers/SetupStepsUpdater.ts +0 -359
- package/src/seeds/1724076679-setup-steps.ts +0 -16
package/src/crons.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
1
3
|
import { Database } from '@simonbackx/simple-database';
|
|
2
|
-
import { logger, StyledText } from
|
|
4
|
+
import { logger, StyledText } from '@simonbackx/simple-logging';
|
|
3
5
|
import { Email, EmailAddress } from '@stamhoofd/email';
|
|
4
6
|
import { Group, Organization, Payment, Registration, STPackage, Webshop } from '@stamhoofd/models';
|
|
5
7
|
import { PaymentMethod, PaymentProvider, PaymentStatus } from '@stamhoofd/structures';
|
|
@@ -13,151 +15,151 @@ import { checkSettlements } from './helpers/CheckSettlements';
|
|
|
13
15
|
import { ForwardHandler } from './helpers/ForwardHandler';
|
|
14
16
|
|
|
15
17
|
// Importing postmark returns undefined (this is a bug, so we need to use require)
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-
|
|
17
|
-
const postmark = require(
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
19
|
+
const postmark = require('postmark');
|
|
18
20
|
|
|
19
|
-
let lastDNSCheck: Date | null = null
|
|
20
|
-
let lastDNSId =
|
|
21
|
+
let lastDNSCheck: Date | null = null;
|
|
22
|
+
let lastDNSId = '';
|
|
21
23
|
async function checkDNS() {
|
|
22
|
-
if (STAMHOOFD.environment ===
|
|
24
|
+
if (STAMHOOFD.environment === 'development') {
|
|
23
25
|
return;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
// Wait 6 hours between every complete check
|
|
27
29
|
if (lastDNSCheck && lastDNSCheck > new Date(new Date().getTime() - 6 * 60 * 60 * 1000)) {
|
|
28
|
-
console.log(
|
|
29
|
-
return
|
|
30
|
+
console.log('[DNS] Skip DNS check');
|
|
31
|
+
return;
|
|
30
32
|
}
|
|
31
|
-
|
|
33
|
+
|
|
32
34
|
const organizations = await Organization.where({ id: { sign: '>', value: lastDNSId } }, {
|
|
33
35
|
limit: 50,
|
|
34
|
-
sort: [
|
|
35
|
-
})
|
|
36
|
+
sort: ['id'],
|
|
37
|
+
});
|
|
36
38
|
|
|
37
39
|
if (organizations.length == 0) {
|
|
38
40
|
// Wait an half hour before starting again
|
|
39
|
-
lastDNSId =
|
|
40
|
-
lastDNSCheck = new Date()
|
|
41
|
-
return
|
|
41
|
+
lastDNSId = '';
|
|
42
|
+
lastDNSCheck = new Date();
|
|
43
|
+
return;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
console.log(
|
|
46
|
+
console.log('[DNS] Checking DNS...');
|
|
45
47
|
|
|
46
48
|
for (const organization of organizations) {
|
|
47
|
-
if (STAMHOOFD.environment ===
|
|
48
|
-
console.log(
|
|
49
|
+
if (STAMHOOFD.environment === 'production') {
|
|
50
|
+
console.log('[DNS] ' + organization.name);
|
|
49
51
|
}
|
|
50
52
|
try {
|
|
51
|
-
await organization.updateDNSRecords()
|
|
52
|
-
}
|
|
53
|
+
await organization.updateDNSRecords();
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
53
56
|
console.error(e);
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
lastDNSId = organizations[organizations.length - 1].id
|
|
58
|
-
|
|
60
|
+
lastDNSId = organizations[organizations.length - 1].id;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
let lastExpirationCheck: Date | null = null
|
|
63
|
+
let lastExpirationCheck: Date | null = null;
|
|
62
64
|
async function checkExpirationEmails() {
|
|
63
|
-
if (STAMHOOFD.environment ===
|
|
65
|
+
if (STAMHOOFD.environment === 'development') {
|
|
64
66
|
return;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
// Wait 1 hour between every complete check
|
|
68
70
|
if (lastExpirationCheck && lastExpirationCheck > new Date(new Date().getTime() - 1 * 60 * 60 * 1000)) {
|
|
69
|
-
console.log(
|
|
70
|
-
return
|
|
71
|
+
console.log('[EXPIRATION EMAILS] Skip checkExpirationEmails');
|
|
72
|
+
return;
|
|
71
73
|
}
|
|
72
|
-
|
|
74
|
+
|
|
73
75
|
// Get all packages that expire between now and 31 days
|
|
74
|
-
const packages = await STPackage.where({
|
|
76
|
+
const packages = await STPackage.where({
|
|
75
77
|
validUntil: [
|
|
76
78
|
{ sign: '!=', value: null },
|
|
77
79
|
{ sign: '>', value: new Date() },
|
|
78
|
-
{ sign: '<', value: new Date(Date.now() + 1000 * 60 * 60 * 24 * 31) }
|
|
80
|
+
{ sign: '<', value: new Date(Date.now() + 1000 * 60 * 60 * 24 * 31) },
|
|
79
81
|
],
|
|
80
82
|
validAt: [
|
|
81
83
|
{ sign: '!=', value: null },
|
|
82
84
|
],
|
|
83
|
-
emailCount: 0
|
|
84
|
-
})
|
|
85
|
+
emailCount: 0,
|
|
86
|
+
});
|
|
85
87
|
|
|
86
|
-
console.log(
|
|
88
|
+
console.log('[EXPIRATION EMAILS] Sending expiration emails...');
|
|
87
89
|
|
|
88
90
|
for (const pack of packages) {
|
|
89
|
-
await pack.sendExpiryEmail()
|
|
90
|
-
}
|
|
91
|
-
lastExpirationCheck = new Date()
|
|
91
|
+
await pack.sendExpiryEmail();
|
|
92
|
+
}
|
|
93
|
+
lastExpirationCheck = new Date();
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
let lastWebshopDNSCheck: Date | null = null
|
|
95
|
-
let lastWebshopDNSId =
|
|
96
|
+
let lastWebshopDNSCheck: Date | null = null;
|
|
97
|
+
let lastWebshopDNSId = '';
|
|
96
98
|
async function checkWebshopDNS() {
|
|
97
|
-
if (STAMHOOFD.environment ===
|
|
99
|
+
if (STAMHOOFD.environment === 'development') {
|
|
98
100
|
return;
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
// Wait 6 hours between every complete check
|
|
102
104
|
if (lastWebshopDNSCheck && lastWebshopDNSCheck > new Date(new Date().getTime() - 6 * 60 * 60 * 1000)) {
|
|
103
|
-
console.log(
|
|
104
|
-
return
|
|
105
|
+
console.log('[DNS] Skip webshop DNS check');
|
|
106
|
+
return;
|
|
105
107
|
}
|
|
106
|
-
|
|
107
|
-
const webshops = await Webshop.where({
|
|
108
|
+
|
|
109
|
+
const webshops = await Webshop.where({
|
|
108
110
|
id: { sign: '>', value: lastWebshopDNSId },
|
|
109
|
-
domain: { sign: '!=', value: null }
|
|
111
|
+
domain: { sign: '!=', value: null },
|
|
110
112
|
}, {
|
|
111
113
|
limit: 10,
|
|
112
|
-
sort: [
|
|
113
|
-
})
|
|
114
|
+
sort: ['id'],
|
|
115
|
+
});
|
|
114
116
|
|
|
115
117
|
if (webshops.length == 0) {
|
|
116
118
|
// Wait an half hour before starting again
|
|
117
|
-
lastWebshopDNSId =
|
|
118
|
-
lastWebshopDNSCheck = new Date()
|
|
119
|
-
return
|
|
119
|
+
lastWebshopDNSId = '';
|
|
120
|
+
lastWebshopDNSCheck = new Date();
|
|
121
|
+
return;
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
console.log(
|
|
124
|
+
console.log('[DNS] Checking webshop DNS...');
|
|
123
125
|
|
|
124
126
|
for (const webshop of webshops) {
|
|
125
|
-
console.log(
|
|
126
|
-
await webshop.updateDNSRecords()
|
|
127
|
+
console.log('[DNS] Webshop ' + webshop.meta.name + ' (' + webshop.id + ')' + ' (' + webshop.domain + ')');
|
|
128
|
+
await webshop.updateDNSRecords();
|
|
127
129
|
}
|
|
128
130
|
|
|
129
|
-
lastWebshopDNSId = webshops[webshops.length - 1].id
|
|
131
|
+
lastWebshopDNSId = webshops[webshops.length - 1].id;
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
async function checkReplies() {
|
|
133
|
-
if (STAMHOOFD.environment !==
|
|
134
|
-
return
|
|
135
|
+
if (STAMHOOFD.environment !== 'production' || !STAMHOOFD.AWS_ACCESS_KEY_ID) {
|
|
136
|
+
return;
|
|
135
137
|
}
|
|
136
|
-
|
|
137
|
-
console.log(
|
|
138
|
+
|
|
139
|
+
console.log('Checking replies from AWS SQS');
|
|
138
140
|
const sqs = new AWS.SQS();
|
|
139
|
-
const messages = await sqs.receiveMessage({ QueueUrl:
|
|
141
|
+
const messages = await sqs.receiveMessage({ QueueUrl: 'https://sqs.eu-west-1.amazonaws.com/118244293157/stamhoofd-email-forwarding', MaxNumberOfMessages: 10 }).promise();
|
|
140
142
|
if (messages.Messages) {
|
|
141
143
|
for (const message of messages.Messages) {
|
|
142
|
-
console.log(
|
|
144
|
+
console.log('Received message from forwarding queue');
|
|
143
145
|
|
|
144
146
|
if (message.ReceiptHandle) {
|
|
145
|
-
if (STAMHOOFD.environment ===
|
|
147
|
+
if (STAMHOOFD.environment === 'production') {
|
|
146
148
|
await sqs.deleteMessage({
|
|
147
|
-
QueueUrl:
|
|
148
|
-
ReceiptHandle: message.ReceiptHandle
|
|
149
|
-
}).promise()
|
|
150
|
-
console.log(
|
|
149
|
+
QueueUrl: 'https://sqs.eu-west-1.amazonaws.com/118244293157/stamhoofd-email-forwarding',
|
|
150
|
+
ReceiptHandle: message.ReceiptHandle,
|
|
151
|
+
}).promise();
|
|
152
|
+
console.log('Deleted from queue');
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
155
|
|
|
154
156
|
try {
|
|
155
157
|
if (message.Body) {
|
|
156
158
|
// decode the JSON value
|
|
157
|
-
const bounce = JSON.parse(message.Body)
|
|
159
|
+
const bounce = JSON.parse(message.Body);
|
|
158
160
|
|
|
159
161
|
if (bounce.Message) {
|
|
160
|
-
const message = JSON.parse(bounce.Message)
|
|
162
|
+
const message = JSON.parse(bounce.Message);
|
|
161
163
|
|
|
162
164
|
// Read message content
|
|
163
165
|
if (message.mail && message.content && message.receipt) {
|
|
@@ -169,247 +171,258 @@ async function checkReplies() {
|
|
|
169
171
|
spfVerdict: { status: 'PASS' | string };
|
|
170
172
|
dkimVerdict: { status: 'PASS' | string };
|
|
171
173
|
dmarcVerdict: { status: 'PASS' | string };
|
|
172
|
-
}
|
|
174
|
+
};
|
|
173
175
|
|
|
174
|
-
const options = await ForwardHandler.handle(content, receipt)
|
|
176
|
+
const options = await ForwardHandler.handle(content, receipt);
|
|
175
177
|
if (options) {
|
|
176
|
-
if (STAMHOOFD.environment ===
|
|
177
|
-
Email.send(options)
|
|
178
|
+
if (STAMHOOFD.environment === 'production') {
|
|
179
|
+
Email.send(options);
|
|
178
180
|
}
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
}
|
|
182
184
|
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
console.error(e);
|
|
185
188
|
}
|
|
186
189
|
}
|
|
187
190
|
}
|
|
188
191
|
}
|
|
189
192
|
|
|
190
|
-
let lastPostmarkCheck: Date | null = null
|
|
191
|
-
let lastPostmarkId: string | null = null
|
|
193
|
+
let lastPostmarkCheck: Date | null = null;
|
|
194
|
+
let lastPostmarkId: string | null = null;
|
|
192
195
|
async function checkPostmarkBounces() {
|
|
193
|
-
if (STAMHOOFD.environment !==
|
|
196
|
+
if (STAMHOOFD.environment !== 'production') {
|
|
194
197
|
return;
|
|
195
198
|
}
|
|
196
|
-
|
|
197
|
-
const token = STAMHOOFD.POSTMARK_SERVER_TOKEN
|
|
199
|
+
|
|
200
|
+
const token = STAMHOOFD.POSTMARK_SERVER_TOKEN;
|
|
198
201
|
if (!token) {
|
|
199
|
-
console.log(
|
|
200
|
-
return
|
|
202
|
+
console.log('[POSTMARK BOUNCES] No postmark token, skipping postmark bounces');
|
|
203
|
+
return;
|
|
201
204
|
}
|
|
202
|
-
const fromDate = (lastPostmarkCheck ?? new Date(new Date().getTime() - 24 * 60 * 60 * 1000 * 2))
|
|
203
|
-
const ET = DateTime.fromJSDate(fromDate).setZone('EST').toISO({ includeOffset: false})
|
|
204
|
-
console.log(
|
|
205
|
+
const fromDate = (lastPostmarkCheck ?? new Date(new Date().getTime() - 24 * 60 * 60 * 1000 * 2));
|
|
206
|
+
const ET = DateTime.fromJSDate(fromDate).setZone('EST').toISO({ includeOffset: false });
|
|
207
|
+
console.log('[POSTMARK BOUNCES] Checking bounces from Postmark since', fromDate, ET);
|
|
205
208
|
const client = new postmark.ServerClient(token);
|
|
206
209
|
|
|
207
210
|
const bounces = await client.getBounces({
|
|
208
211
|
fromdate: ET,
|
|
209
|
-
todate: DateTime.now().setZone('EST').toISO({ includeOffset: false}),
|
|
212
|
+
todate: DateTime.now().setZone('EST').toISO({ includeOffset: false }),
|
|
210
213
|
count: 500,
|
|
211
|
-
offset: 0
|
|
212
|
-
})
|
|
214
|
+
offset: 0,
|
|
215
|
+
});
|
|
213
216
|
|
|
214
217
|
if (bounces.TotalCount == 0) {
|
|
215
|
-
console.log(
|
|
216
|
-
return
|
|
218
|
+
console.log('[POSTMARK BOUNCES] No Postmark bounces at this time');
|
|
219
|
+
return;
|
|
217
220
|
}
|
|
218
221
|
|
|
219
|
-
let lastId: string | null = null
|
|
222
|
+
let lastId: string | null = null;
|
|
220
223
|
|
|
221
224
|
for (const bounce of bounces.Bounces) {
|
|
222
225
|
// Try to get the organization, if possible, else default to global blocking: "null", which is not visible for an organization, but it is applied
|
|
223
|
-
const source = bounce.From
|
|
224
|
-
const organization = source ? await Organization.getByEmail(source) : undefined
|
|
226
|
+
const source = bounce.From;
|
|
227
|
+
const organization = source ? await Organization.getByEmail(source) : undefined;
|
|
225
228
|
|
|
226
|
-
if (bounce.Type ===
|
|
229
|
+
if (bounce.Type === 'HardBounce' || bounce.Type === 'BadEmailAddress' || bounce.Type === 'Blocked') {
|
|
227
230
|
// Block for everyone, but not visible
|
|
228
|
-
console.log(
|
|
229
|
-
const emailAddress = await EmailAddress.getOrCreate(bounce.Email, organization?.id ?? null)
|
|
230
|
-
emailAddress.hardBounce = true
|
|
231
|
-
await emailAddress.save()
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
emailAddress
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
231
|
+
console.log('[POSTMARK BOUNCES] Postmark ' + bounce.Type + ' for: ', bounce.Email, 'from', source, 'organization', organization?.name);
|
|
232
|
+
const emailAddress = await EmailAddress.getOrCreate(bounce.Email, organization?.id ?? null);
|
|
233
|
+
emailAddress.hardBounce = true;
|
|
234
|
+
await emailAddress.save();
|
|
235
|
+
}
|
|
236
|
+
else if (bounce.Type === 'SpamComplaint' || bounce.Type === 'SpamNotification' || bounce.Type === 'VirusNotification') {
|
|
237
|
+
console.log('[POSTMARK BOUNCES] Postmark ' + bounce.Type + ' for: ', bounce.Email, 'from', source, 'organization', organization?.name);
|
|
238
|
+
const emailAddress = await EmailAddress.getOrCreate(bounce.Email, organization?.id ?? null);
|
|
239
|
+
emailAddress.markedAsSpam = true;
|
|
240
|
+
await emailAddress.save();
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
console.log('[POSTMARK BOUNCES] Unhandled Postmark ' + bounce.Type + ': ', bounce.Email, 'from', source, 'organization', organization?.name);
|
|
244
|
+
console.error('[POSTMARK BOUNCES] Unhandled Postmark ' + bounce.Type + ': ', bounce.Email, 'from', source, 'organization', organization?.name);
|
|
240
245
|
}
|
|
241
246
|
|
|
242
|
-
const bouncedAt = new Date(bounce.BouncedAt)
|
|
243
|
-
lastPostmarkCheck = lastPostmarkCheck ? new Date(Math.max(bouncedAt.getTime(), lastPostmarkCheck.getTime())) : bouncedAt
|
|
247
|
+
const bouncedAt = new Date(bounce.BouncedAt);
|
|
248
|
+
lastPostmarkCheck = lastPostmarkCheck ? new Date(Math.max(bouncedAt.getTime(), lastPostmarkCheck.getTime())) : bouncedAt;
|
|
244
249
|
|
|
245
|
-
lastId = bounce.ID
|
|
250
|
+
lastId = bounce.ID;
|
|
246
251
|
}
|
|
247
252
|
|
|
248
253
|
if (lastId && lastPostmarkId) {
|
|
249
254
|
if (lastId === lastPostmarkId) {
|
|
250
|
-
console.log(
|
|
255
|
+
console.log('[POSTMARK BOUNCES] Postmark has no new bounces');
|
|
251
256
|
// Increase timestamp by one second to avoid refetching it every time
|
|
252
257
|
if (lastPostmarkCheck) {
|
|
253
|
-
lastPostmarkCheck = new Date(lastPostmarkCheck.getTime() + 1000)
|
|
258
|
+
lastPostmarkCheck = new Date(lastPostmarkCheck.getTime() + 1000);
|
|
254
259
|
}
|
|
255
260
|
}
|
|
256
261
|
}
|
|
257
|
-
lastPostmarkId = lastId
|
|
262
|
+
lastPostmarkId = lastId;
|
|
258
263
|
}
|
|
259
264
|
|
|
260
265
|
async function checkBounces() {
|
|
261
|
-
if (STAMHOOFD.environment !==
|
|
262
|
-
return
|
|
266
|
+
if (STAMHOOFD.environment !== 'production' || !STAMHOOFD.AWS_ACCESS_KEY_ID) {
|
|
267
|
+
return;
|
|
263
268
|
}
|
|
264
|
-
|
|
265
|
-
console.log(
|
|
269
|
+
|
|
270
|
+
console.log('[AWS BOUNCES] Checking bounces from AWS SQS');
|
|
266
271
|
const sqs = new AWS.SQS();
|
|
267
|
-
const messages = await sqs.receiveMessage({ QueueUrl:
|
|
272
|
+
const messages = await sqs.receiveMessage({ QueueUrl: 'https://sqs.eu-west-1.amazonaws.com/118244293157/stamhoofd-bounces-queue', MaxNumberOfMessages: 10 }).promise();
|
|
268
273
|
if (messages.Messages) {
|
|
269
274
|
for (const message of messages.Messages) {
|
|
270
|
-
console.log(
|
|
271
|
-
console.log(
|
|
275
|
+
console.log('[AWS BOUNCES] Received bounce message');
|
|
276
|
+
console.log('[AWS BOUNCES]', message);
|
|
272
277
|
|
|
273
278
|
if (message.ReceiptHandle) {
|
|
274
|
-
if (STAMHOOFD.environment ===
|
|
279
|
+
if (STAMHOOFD.environment === 'production') {
|
|
275
280
|
await sqs.deleteMessage({
|
|
276
|
-
QueueUrl:
|
|
277
|
-
ReceiptHandle: message.ReceiptHandle
|
|
278
|
-
}).promise()
|
|
279
|
-
console.log(
|
|
281
|
+
QueueUrl: 'https://sqs.eu-west-1.amazonaws.com/118244293157/stamhoofd-bounces-queue',
|
|
282
|
+
ReceiptHandle: message.ReceiptHandle,
|
|
283
|
+
}).promise();
|
|
284
|
+
console.log('[AWS BOUNCES] Deleted from queue');
|
|
280
285
|
}
|
|
281
286
|
}
|
|
282
287
|
|
|
283
288
|
try {
|
|
284
289
|
if (message.Body) {
|
|
285
290
|
// decode the JSON value
|
|
286
|
-
const bounce = JSON.parse(message.Body)
|
|
291
|
+
const bounce = JSON.parse(message.Body);
|
|
287
292
|
|
|
288
293
|
if (bounce.Message) {
|
|
289
|
-
const message = JSON.parse(bounce.Message)
|
|
294
|
+
const message = JSON.parse(bounce.Message);
|
|
290
295
|
|
|
291
296
|
if (message.bounce) {
|
|
292
|
-
const b = message.bounce
|
|
297
|
+
const b = message.bounce;
|
|
293
298
|
// Block all receivers that generate a permanent bounce
|
|
294
|
-
const type = b.bounceType
|
|
299
|
+
const type = b.bounceType;
|
|
295
300
|
|
|
296
|
-
const source = message.mail.source
|
|
301
|
+
const source = message.mail.source;
|
|
297
302
|
|
|
298
303
|
// try to find organization that is responsible for this e-mail address
|
|
299
304
|
|
|
300
305
|
for (const recipient of b.bouncedRecipients) {
|
|
301
|
-
const email = recipient.emailAddress
|
|
306
|
+
const email = recipient.emailAddress;
|
|
302
307
|
|
|
303
308
|
if (
|
|
304
|
-
type ===
|
|
309
|
+
type === 'Permanent'
|
|
305
310
|
|| (
|
|
306
311
|
recipient.diagnosticCode && (
|
|
307
|
-
(recipient.diagnosticCode as string).toLowerCase().includes(
|
|
312
|
+
(recipient.diagnosticCode as string).toLowerCase().includes('invalid domain')
|
|
308
313
|
|| (recipient.diagnosticCode as string).toLowerCase().includes('unable to lookup dns')
|
|
309
314
|
)
|
|
310
315
|
)
|
|
311
316
|
) {
|
|
312
|
-
const organization: Organization | undefined = source ? await Organization.getByEmail(source) : undefined
|
|
317
|
+
const organization: Organization | undefined = source ? await Organization.getByEmail(source) : undefined;
|
|
313
318
|
if (organization) {
|
|
314
|
-
const emailAddress = await EmailAddress.getOrCreate(email, organization.id)
|
|
315
|
-
emailAddress.hardBounce = true
|
|
316
|
-
await emailAddress.save()
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
+
const emailAddress = await EmailAddress.getOrCreate(email, organization.id);
|
|
320
|
+
emailAddress.hardBounce = true;
|
|
321
|
+
await emailAddress.save();
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
console.error('[AWS BOUNCES] Unknown organization for email address ' + source);
|
|
319
325
|
}
|
|
320
326
|
}
|
|
321
|
-
|
|
322
327
|
}
|
|
323
|
-
console.log(
|
|
324
|
-
} else {
|
|
325
|
-
console.log("[AWS BOUNCES] 'bounce' field missing in bounce message")
|
|
328
|
+
console.log('[AWS BOUNCES] For domain ' + source);
|
|
326
329
|
}
|
|
327
|
-
|
|
328
|
-
|
|
330
|
+
else {
|
|
331
|
+
console.log("[AWS BOUNCES] 'bounce' field missing in bounce message");
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
console.log("[AWS BOUNCES] 'Message' field missing in bounce message");
|
|
329
336
|
}
|
|
330
|
-
} else {
|
|
331
|
-
console.log("[AWS BOUNCES] Message Body missing in bounce")
|
|
332
337
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
338
|
+
else {
|
|
339
|
+
console.log('[AWS BOUNCES] Message Body missing in bounce');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
catch (e) {
|
|
343
|
+
console.log('[AWS BOUNCES] Bounce message processing failed:');
|
|
344
|
+
console.error('[AWS BOUNCES] Bounce message processing failed:');
|
|
345
|
+
console.error('[AWS BOUNCES]', e);
|
|
337
346
|
}
|
|
338
347
|
}
|
|
339
348
|
}
|
|
340
349
|
}
|
|
341
350
|
|
|
342
351
|
async function checkComplaints() {
|
|
343
|
-
if (STAMHOOFD.environment !==
|
|
344
|
-
return
|
|
352
|
+
if (STAMHOOFD.environment !== 'production' || !STAMHOOFD.AWS_ACCESS_KEY_ID) {
|
|
353
|
+
return;
|
|
345
354
|
}
|
|
346
355
|
|
|
347
|
-
console.log(
|
|
356
|
+
console.log('[AWS COMPLAINTS] Checking complaints from AWS SQS');
|
|
348
357
|
const sqs = new AWS.SQS();
|
|
349
|
-
const messages = await sqs.receiveMessage({ QueueUrl:
|
|
358
|
+
const messages = await sqs.receiveMessage({ QueueUrl: 'https://sqs.eu-west-1.amazonaws.com/118244293157/stamhoofd-complaints-queue', MaxNumberOfMessages: 10 }).promise();
|
|
350
359
|
if (messages.Messages) {
|
|
351
360
|
for (const message of messages.Messages) {
|
|
352
|
-
console.log(
|
|
353
|
-
console.log(
|
|
361
|
+
console.log('[AWS COMPLAINTS] Received complaint message');
|
|
362
|
+
console.log('[AWS COMPLAINTS]', message);
|
|
354
363
|
|
|
355
364
|
if (message.ReceiptHandle) {
|
|
356
|
-
if (STAMHOOFD.environment ===
|
|
365
|
+
if (STAMHOOFD.environment === 'production') {
|
|
357
366
|
await sqs.deleteMessage({
|
|
358
|
-
QueueUrl:
|
|
359
|
-
ReceiptHandle: message.ReceiptHandle
|
|
360
|
-
}).promise()
|
|
361
|
-
console.log(
|
|
367
|
+
QueueUrl: 'https://sqs.eu-west-1.amazonaws.com/118244293157/stamhoofd-complaints-queue',
|
|
368
|
+
ReceiptHandle: message.ReceiptHandle,
|
|
369
|
+
}).promise();
|
|
370
|
+
console.log('[AWS COMPLAINTS] Deleted from queue');
|
|
362
371
|
}
|
|
363
372
|
}
|
|
364
373
|
|
|
365
374
|
try {
|
|
366
375
|
if (message.Body) {
|
|
367
376
|
// decode the JSON value
|
|
368
|
-
const complaint = JSON.parse(message.Body)
|
|
369
|
-
console.log(
|
|
377
|
+
const complaint = JSON.parse(message.Body);
|
|
378
|
+
console.log('[AWS COMPLAINTS]', complaint);
|
|
370
379
|
|
|
371
380
|
if (complaint.Message) {
|
|
372
|
-
const message = JSON.parse(complaint.Message)
|
|
381
|
+
const message = JSON.parse(complaint.Message);
|
|
373
382
|
|
|
374
383
|
if (message.complaint) {
|
|
375
|
-
const b = message.complaint
|
|
376
|
-
const source = message.mail.source
|
|
377
|
-
const organization: Organization | undefined = source ? await Organization.getByEmail(source) : undefined
|
|
384
|
+
const b = message.complaint;
|
|
385
|
+
const source = message.mail.source;
|
|
386
|
+
const organization: Organization | undefined = source ? await Organization.getByEmail(source) : undefined;
|
|
378
387
|
|
|
379
|
-
const type:
|
|
388
|
+
const type: 'abuse' | 'auth-failure' | 'fraud' | 'not-spam' | 'other' | 'virus' = b.complaintFeedbackType;
|
|
380
389
|
|
|
381
390
|
if (organization) {
|
|
382
391
|
for (const recipient of b.complainedRecipients) {
|
|
383
|
-
const email = recipient.emailAddress
|
|
384
|
-
const emailAddress = await EmailAddress.getOrCreate(email, organization.id)
|
|
385
|
-
emailAddress.markedAsSpam = type !==
|
|
386
|
-
await emailAddress.save()
|
|
392
|
+
const email = recipient.emailAddress;
|
|
393
|
+
const emailAddress = await EmailAddress.getOrCreate(email, organization.id);
|
|
394
|
+
emailAddress.markedAsSpam = type !== 'not-spam';
|
|
395
|
+
await emailAddress.save();
|
|
387
396
|
}
|
|
388
|
-
}
|
|
389
|
-
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
console.error('[AWS COMPLAINTS] Unknown organization for email address ' + source);
|
|
390
400
|
}
|
|
391
401
|
|
|
392
|
-
|
|
393
|
-
console.error(
|
|
394
|
-
console.error(
|
|
395
|
-
if (STAMHOOFD.environment ===
|
|
402
|
+
if (type == 'virus' || type == 'fraud') {
|
|
403
|
+
console.error('[AWS COMPLAINTS] Received virus / fraud complaint!');
|
|
404
|
+
console.error('[AWS COMPLAINTS]', complaint);
|
|
405
|
+
if (STAMHOOFD.environment === 'production') {
|
|
396
406
|
Email.sendWebmaster({
|
|
397
|
-
subject:
|
|
398
|
-
text:
|
|
399
|
-
})
|
|
407
|
+
subject: 'Received a ' + type + ' email notification',
|
|
408
|
+
text: 'We received a ' + type + ' notification for an e-mail from the organization: ' + organization?.name + '. Please check and adjust if needed.\n',
|
|
409
|
+
});
|
|
400
410
|
}
|
|
401
411
|
}
|
|
402
|
-
} else {
|
|
403
|
-
console.log("[AWS COMPLAINTS] Missing complaint field")
|
|
404
412
|
}
|
|
405
|
-
|
|
406
|
-
|
|
413
|
+
else {
|
|
414
|
+
console.log('[AWS COMPLAINTS] Missing complaint field');
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
console.log('[AWS COMPLAINTS] Missing message field in complaint');
|
|
407
419
|
}
|
|
408
420
|
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
console.
|
|
412
|
-
console.error(
|
|
421
|
+
}
|
|
422
|
+
catch (e) {
|
|
423
|
+
console.log('[AWS COMPLAINTS] Complain message processing failed:');
|
|
424
|
+
console.error('[AWS COMPLAINTS] Complain message processing failed:');
|
|
425
|
+
console.error('[AWS COMPLAINTS]', e);
|
|
413
426
|
}
|
|
414
427
|
}
|
|
415
428
|
}
|
|
@@ -417,26 +430,26 @@ async function checkComplaints() {
|
|
|
417
430
|
|
|
418
431
|
// Keep checking pending paymetns for 3 days
|
|
419
432
|
async function checkPayments() {
|
|
420
|
-
if (STAMHOOFD.environment ===
|
|
433
|
+
if (STAMHOOFD.environment === 'development') {
|
|
421
434
|
return;
|
|
422
435
|
}
|
|
423
436
|
|
|
424
|
-
const timeout = 60*1000*31;
|
|
425
|
-
|
|
437
|
+
const timeout = 60 * 1000 * 31;
|
|
438
|
+
|
|
426
439
|
// TODO: only select the ID + organizationId
|
|
427
440
|
const payments = await Payment.where({
|
|
428
441
|
status: {
|
|
429
|
-
sign:
|
|
430
|
-
value: [PaymentStatus.Created, PaymentStatus.Pending]
|
|
442
|
+
sign: 'IN',
|
|
443
|
+
value: [PaymentStatus.Created, PaymentStatus.Pending],
|
|
431
444
|
},
|
|
432
445
|
method: {
|
|
433
|
-
sign:
|
|
434
|
-
value: [PaymentMethod.Bancontact, PaymentMethod.iDEAL, PaymentMethod.Payconiq, PaymentMethod.CreditCard]
|
|
446
|
+
sign: 'IN',
|
|
447
|
+
value: [PaymentMethod.Bancontact, PaymentMethod.iDEAL, PaymentMethod.Payconiq, PaymentMethod.CreditCard],
|
|
435
448
|
},
|
|
436
449
|
// Check all payments that are 11 minutes old and are still pending
|
|
437
450
|
createdAt: {
|
|
438
|
-
sign:
|
|
439
|
-
value: new Date(new Date().getTime() - timeout)
|
|
451
|
+
sign: '<',
|
|
452
|
+
value: new Date(new Date().getTime() - timeout),
|
|
440
453
|
},
|
|
441
454
|
}, {
|
|
442
455
|
limit: 100,
|
|
@@ -445,32 +458,34 @@ async function checkPayments() {
|
|
|
445
458
|
// If at some point, they are still pending after 1 day, their status should change to failed
|
|
446
459
|
sort: [{
|
|
447
460
|
column: 'createdAt',
|
|
448
|
-
direction: 'ASC'
|
|
449
|
-
}]
|
|
450
|
-
})
|
|
461
|
+
direction: 'ASC',
|
|
462
|
+
}],
|
|
463
|
+
});
|
|
451
464
|
|
|
452
|
-
console.log(
|
|
465
|
+
console.log('[DELAYED PAYMENTS] Checking pending payments: ' + payments.length);
|
|
453
466
|
|
|
454
467
|
for (const payment of payments) {
|
|
455
468
|
try {
|
|
456
469
|
if (payment.organizationId) {
|
|
457
|
-
const organization = await Organization.getByID(payment.organizationId)
|
|
470
|
+
const organization = await Organization.getByID(payment.organizationId);
|
|
458
471
|
if (organization) {
|
|
459
|
-
await ExchangePaymentEndpoint.pollStatus(payment.id, organization)
|
|
472
|
+
await ExchangePaymentEndpoint.pollStatus(payment.id, organization);
|
|
460
473
|
continue;
|
|
461
474
|
}
|
|
462
|
-
}
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
463
477
|
// deprecated
|
|
464
478
|
}
|
|
465
479
|
|
|
466
480
|
// Check expired
|
|
467
481
|
if (ExchangePaymentEndpoint.isManualExpired(payment.status, payment)) {
|
|
468
|
-
console.error('[DELAYED PAYMENTS] Could not resolve handler for expired payment, marking as failed', payment.id)
|
|
469
|
-
payment.status = PaymentStatus.Failed
|
|
470
|
-
await payment.save()
|
|
482
|
+
console.error('[DELAYED PAYMENTS] Could not resolve handler for expired payment, marking as failed', payment.id);
|
|
483
|
+
payment.status = PaymentStatus.Failed;
|
|
484
|
+
await payment.save();
|
|
471
485
|
}
|
|
472
|
-
}
|
|
473
|
-
|
|
486
|
+
}
|
|
487
|
+
catch (e) {
|
|
488
|
+
console.error(e);
|
|
474
489
|
}
|
|
475
490
|
}
|
|
476
491
|
}
|
|
@@ -479,118 +494,120 @@ let didCheckBuckaroo = false;
|
|
|
479
494
|
let lastBuckarooId = '';
|
|
480
495
|
|
|
481
496
|
// Time to start checking (needs to be consistent to avoid weird jumps)
|
|
482
|
-
const startBuckarooDate = new Date(new Date().getTime() - 60*1000*60*24*7*4);
|
|
497
|
+
const startBuckarooDate = new Date(new Date().getTime() - 60 * 1000 * 60 * 24 * 7 * 4);
|
|
483
498
|
|
|
484
499
|
// Keep checking pending paymetns for 3 days
|
|
485
500
|
async function checkFailedBuckarooPayments() {
|
|
486
|
-
if (STAMHOOFD.environment !==
|
|
487
|
-
return
|
|
501
|
+
if (STAMHOOFD.environment !== 'production') {
|
|
502
|
+
return;
|
|
488
503
|
}
|
|
489
504
|
|
|
490
505
|
if (didCheckBuckaroo) {
|
|
491
|
-
return
|
|
506
|
+
return;
|
|
492
507
|
}
|
|
493
508
|
|
|
494
|
-
console.log('Checking failed Buckaroo payments')
|
|
495
|
-
|
|
509
|
+
console.log('Checking failed Buckaroo payments');
|
|
510
|
+
|
|
496
511
|
// TODO: only select the ID + organizationId
|
|
497
512
|
const payments = await Payment.where({
|
|
498
513
|
status: {
|
|
499
|
-
sign:
|
|
500
|
-
value: [PaymentStatus.Failed]
|
|
514
|
+
sign: 'IN',
|
|
515
|
+
value: [PaymentStatus.Failed],
|
|
501
516
|
},
|
|
502
517
|
provider: PaymentProvider.Buckaroo,
|
|
503
518
|
|
|
504
519
|
// Only check payments of last 4 weeks
|
|
505
520
|
createdAt: {
|
|
506
|
-
sign:
|
|
507
|
-
value: startBuckarooDate
|
|
521
|
+
sign: '>',
|
|
522
|
+
value: startBuckarooDate,
|
|
508
523
|
},
|
|
509
524
|
id: {
|
|
510
|
-
sign:
|
|
511
|
-
value: lastBuckarooId
|
|
512
|
-
}
|
|
525
|
+
sign: '>',
|
|
526
|
+
value: lastBuckarooId,
|
|
527
|
+
},
|
|
513
528
|
}, {
|
|
514
529
|
limit: 100,
|
|
515
530
|
|
|
516
531
|
// Sort by ID
|
|
517
532
|
sort: [{
|
|
518
533
|
column: 'id',
|
|
519
|
-
direction: 'ASC'
|
|
520
|
-
}]
|
|
521
|
-
})
|
|
534
|
+
direction: 'ASC',
|
|
535
|
+
}],
|
|
536
|
+
});
|
|
522
537
|
|
|
523
|
-
console.log(
|
|
538
|
+
console.log('[BUCKAROO PAYMENTS] Checking failed payments: ' + payments.length);
|
|
524
539
|
|
|
525
540
|
for (const payment of payments) {
|
|
526
541
|
try {
|
|
527
542
|
if (payment.organizationId) {
|
|
528
|
-
const organization = await Organization.getByID(payment.organizationId)
|
|
543
|
+
const organization = await Organization.getByID(payment.organizationId);
|
|
529
544
|
if (organization) {
|
|
530
|
-
await ExchangePaymentEndpoint.pollStatus(payment.id, organization)
|
|
545
|
+
await ExchangePaymentEndpoint.pollStatus(payment.id, organization);
|
|
531
546
|
continue;
|
|
532
547
|
}
|
|
533
548
|
}
|
|
534
|
-
}
|
|
535
|
-
|
|
549
|
+
}
|
|
550
|
+
catch (e) {
|
|
551
|
+
console.error(e);
|
|
536
552
|
}
|
|
537
553
|
}
|
|
538
554
|
|
|
539
555
|
if (payments.length === 0) {
|
|
540
|
-
didCheckBuckaroo = true
|
|
541
|
-
lastBuckarooId = ''
|
|
542
|
-
}
|
|
543
|
-
|
|
556
|
+
didCheckBuckaroo = true;
|
|
557
|
+
lastBuckarooId = '';
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
lastBuckarooId = payments[payments.length - 1].id;
|
|
544
561
|
}
|
|
545
562
|
}
|
|
546
563
|
|
|
547
564
|
// Unreserve reserved registrations
|
|
548
565
|
async function checkReservedUntil() {
|
|
549
|
-
if (STAMHOOFD.environment !==
|
|
550
|
-
console.log(
|
|
566
|
+
if (STAMHOOFD.environment !== 'development') {
|
|
567
|
+
console.log('Check reserved until...');
|
|
551
568
|
}
|
|
552
569
|
|
|
553
570
|
const registrations = await Registration.where({
|
|
554
571
|
reservedUntil: {
|
|
555
|
-
sign:
|
|
556
|
-
value: new Date()
|
|
572
|
+
sign: '<',
|
|
573
|
+
value: new Date(),
|
|
557
574
|
},
|
|
558
575
|
}, {
|
|
559
|
-
limit: 200
|
|
560
|
-
})
|
|
576
|
+
limit: 200,
|
|
577
|
+
});
|
|
561
578
|
|
|
562
579
|
if (registrations.length === 0) {
|
|
563
|
-
return
|
|
580
|
+
return;
|
|
564
581
|
}
|
|
565
582
|
|
|
566
583
|
// Clear reservedUntil
|
|
567
|
-
const q = `UPDATE ${Registration.table} SET reservedUntil = NULL where id IN (?) AND reservedUntil <
|
|
568
|
-
await Database.update(q, [registrations.map(r => r.id), new Date()])
|
|
584
|
+
const q = `UPDATE ${Registration.table} SET reservedUntil = NULL where id IN (?) AND reservedUntil < ?`;
|
|
585
|
+
await Database.update(q, [registrations.map(r => r.id), new Date()]);
|
|
569
586
|
|
|
570
587
|
// Get groups
|
|
571
|
-
const groupIds = registrations.map(r => r.groupId)
|
|
588
|
+
const groupIds = registrations.map(r => r.groupId);
|
|
572
589
|
const groups = await Group.where({
|
|
573
590
|
id: {
|
|
574
|
-
sign:
|
|
575
|
-
value: groupIds
|
|
576
|
-
}
|
|
577
|
-
})
|
|
591
|
+
sign: 'IN',
|
|
592
|
+
value: groupIds,
|
|
593
|
+
},
|
|
594
|
+
});
|
|
578
595
|
|
|
579
|
-
for(const registration of registrations) {
|
|
580
|
-
registration.scheduleStockUpdate()
|
|
596
|
+
for (const registration of registrations) {
|
|
597
|
+
registration.scheduleStockUpdate();
|
|
581
598
|
}
|
|
582
599
|
|
|
583
600
|
// Update occupancy
|
|
584
601
|
for (const group of groups) {
|
|
585
|
-
await group.updateOccupancy()
|
|
586
|
-
await group.save()
|
|
602
|
+
await group.updateOccupancy();
|
|
603
|
+
await group.save();
|
|
587
604
|
}
|
|
588
605
|
}
|
|
589
606
|
|
|
590
|
-
let lastDripCheck: Date | null = null
|
|
591
|
-
let lastDripId =
|
|
607
|
+
let lastDripCheck: Date | null = null;
|
|
608
|
+
let lastDripId = '';
|
|
592
609
|
async function checkDrips() {
|
|
593
|
-
if (STAMHOOFD.environment ===
|
|
610
|
+
if (STAMHOOFD.environment === 'development') {
|
|
594
611
|
return;
|
|
595
612
|
}
|
|
596
613
|
|
|
@@ -599,146 +616,148 @@ async function checkDrips() {
|
|
|
599
616
|
}
|
|
600
617
|
|
|
601
618
|
if (lastDripCheck && lastDripCheck > new Date(new Date().getTime() - 6 * 60 * 60 * 1000)) {
|
|
602
|
-
console.log(
|
|
603
|
-
return
|
|
619
|
+
console.log('Skip Drip check');
|
|
620
|
+
return;
|
|
604
621
|
}
|
|
605
622
|
|
|
606
623
|
// Only send emails between 8:00 - 18:00 CET
|
|
607
|
-
const CETTime = Formatter.timeIso(new Date())
|
|
608
|
-
if ((CETTime <
|
|
609
|
-
console.log(
|
|
624
|
+
const CETTime = Formatter.timeIso(new Date());
|
|
625
|
+
if ((CETTime < '08:00' || CETTime > '18:00') && STAMHOOFD.environment === 'production') {
|
|
626
|
+
console.log('Skip Drip check: outside hours');
|
|
610
627
|
return;
|
|
611
628
|
}
|
|
612
|
-
|
|
629
|
+
|
|
613
630
|
const organizations = await Organization.where({ id: { sign: '>', value: lastDripId } }, {
|
|
614
|
-
limit: STAMHOOFD.environment ===
|
|
615
|
-
sort: [
|
|
616
|
-
})
|
|
631
|
+
limit: STAMHOOFD.environment === 'production' ? 30 : 100,
|
|
632
|
+
sort: ['id'],
|
|
633
|
+
});
|
|
617
634
|
|
|
618
635
|
if (organizations.length == 0) {
|
|
619
636
|
// Wait before starting again
|
|
620
|
-
lastDripId =
|
|
621
|
-
lastDripCheck = new Date()
|
|
622
|
-
return
|
|
637
|
+
lastDripId = '';
|
|
638
|
+
lastDripCheck = new Date();
|
|
639
|
+
return;
|
|
623
640
|
}
|
|
624
641
|
|
|
625
|
-
console.log(
|
|
642
|
+
console.log('Checking drips...');
|
|
626
643
|
|
|
627
644
|
for (const organization of organizations) {
|
|
628
|
-
console.log(organization.name)
|
|
645
|
+
console.log(organization.name);
|
|
629
646
|
try {
|
|
630
|
-
await organization.checkDrips()
|
|
631
|
-
}
|
|
647
|
+
await organization.checkDrips();
|
|
648
|
+
}
|
|
649
|
+
catch (e) {
|
|
632
650
|
console.error(e);
|
|
633
651
|
}
|
|
634
652
|
}
|
|
635
653
|
|
|
636
|
-
lastDripId = organizations[organizations.length - 1].id
|
|
637
|
-
|
|
654
|
+
lastDripId = organizations[organizations.length - 1].id;
|
|
638
655
|
}
|
|
639
656
|
|
|
640
657
|
type CronJobDefinition = {
|
|
641
|
-
name: string
|
|
642
|
-
method: () => Promise<void
|
|
643
|
-
running: boolean
|
|
644
|
-
}
|
|
658
|
+
name: string;
|
|
659
|
+
method: () => Promise<void>;
|
|
660
|
+
running: boolean;
|
|
661
|
+
};
|
|
645
662
|
|
|
646
|
-
const registeredCronJobs: CronJobDefinition[] = []
|
|
663
|
+
const registeredCronJobs: CronJobDefinition[] = [];
|
|
647
664
|
|
|
648
665
|
registeredCronJobs.push({
|
|
649
666
|
name: 'checkSettlements',
|
|
650
667
|
method: checkSettlements,
|
|
651
|
-
running: false
|
|
668
|
+
running: false,
|
|
652
669
|
});
|
|
653
670
|
|
|
654
671
|
registeredCronJobs.push({
|
|
655
672
|
name: 'checkFailedBuckarooPayments',
|
|
656
673
|
method: checkFailedBuckarooPayments,
|
|
657
|
-
running: false
|
|
674
|
+
running: false,
|
|
658
675
|
});
|
|
659
676
|
|
|
660
677
|
registeredCronJobs.push({
|
|
661
678
|
name: 'checkExpirationEmails',
|
|
662
679
|
method: checkExpirationEmails,
|
|
663
|
-
running: false
|
|
680
|
+
running: false,
|
|
664
681
|
});
|
|
665
682
|
|
|
666
683
|
registeredCronJobs.push({
|
|
667
684
|
name: 'checkPostmarkBounces',
|
|
668
685
|
method: checkPostmarkBounces,
|
|
669
|
-
running: false
|
|
686
|
+
running: false,
|
|
670
687
|
});
|
|
671
688
|
|
|
672
689
|
registeredCronJobs.push({
|
|
673
690
|
name: 'checkReservedUntil',
|
|
674
691
|
method: checkReservedUntil,
|
|
675
|
-
running: false
|
|
692
|
+
running: false,
|
|
676
693
|
});
|
|
677
694
|
|
|
678
695
|
registeredCronJobs.push({
|
|
679
696
|
name: 'checkComplaints',
|
|
680
697
|
method: checkComplaints,
|
|
681
|
-
running: false
|
|
698
|
+
running: false,
|
|
682
699
|
});
|
|
683
700
|
|
|
684
701
|
registeredCronJobs.push({
|
|
685
702
|
name: 'checkReplies',
|
|
686
703
|
method: checkReplies,
|
|
687
|
-
running: false
|
|
704
|
+
running: false,
|
|
688
705
|
});
|
|
689
706
|
|
|
690
707
|
registeredCronJobs.push({
|
|
691
708
|
name: 'checkBounces',
|
|
692
709
|
method: checkBounces,
|
|
693
|
-
running: false
|
|
710
|
+
running: false,
|
|
694
711
|
});
|
|
695
712
|
|
|
696
713
|
registeredCronJobs.push({
|
|
697
714
|
name: 'checkDNS',
|
|
698
715
|
method: checkDNS,
|
|
699
|
-
running: false
|
|
716
|
+
running: false,
|
|
700
717
|
});
|
|
701
718
|
|
|
702
719
|
registeredCronJobs.push({
|
|
703
720
|
name: 'checkWebshopDNS',
|
|
704
721
|
method: checkWebshopDNS,
|
|
705
|
-
running: false
|
|
722
|
+
running: false,
|
|
706
723
|
});
|
|
707
724
|
|
|
708
725
|
registeredCronJobs.push({
|
|
709
726
|
name: 'checkPayments',
|
|
710
727
|
method: checkPayments,
|
|
711
|
-
running: false
|
|
728
|
+
running: false,
|
|
712
729
|
});
|
|
713
730
|
|
|
714
731
|
registeredCronJobs.push({
|
|
715
732
|
name: 'checkDrips',
|
|
716
733
|
method: checkDrips,
|
|
717
|
-
running: false
|
|
734
|
+
running: false,
|
|
718
735
|
});
|
|
719
736
|
|
|
720
737
|
registeredCronJobs.push({
|
|
721
738
|
name: 'clearExcelCache',
|
|
722
739
|
method: clearExcelCache,
|
|
723
|
-
running: false
|
|
724
|
-
})
|
|
740
|
+
running: false,
|
|
741
|
+
});
|
|
725
742
|
|
|
726
743
|
async function run(name: string, handler: () => Promise<void>) {
|
|
727
744
|
try {
|
|
728
745
|
await logger.setContext({
|
|
729
746
|
prefixes: [
|
|
730
|
-
new StyledText(`[${name}] `).addClass('crons', 'tag')
|
|
747
|
+
new StyledText(`[${name}] `).addClass('crons', 'tag'),
|
|
731
748
|
],
|
|
732
|
-
tags: ['crons']
|
|
749
|
+
tags: ['crons'],
|
|
733
750
|
}, async () => {
|
|
734
751
|
try {
|
|
735
|
-
await handler()
|
|
736
|
-
} catch (e) {
|
|
737
|
-
console.error(new StyledText(e).addClass('error'))
|
|
752
|
+
await handler();
|
|
738
753
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
754
|
+
catch (e) {
|
|
755
|
+
console.error(new StyledText(e).addClass('error'));
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
catch (e) {
|
|
760
|
+
console.error(new StyledText(e).addClass('error'));
|
|
742
761
|
}
|
|
743
762
|
}
|
|
744
763
|
|
|
@@ -750,23 +769,23 @@ export function stopCronScheduling() {
|
|
|
750
769
|
let schedulingJobs = false;
|
|
751
770
|
export function areCronsRunning(): boolean {
|
|
752
771
|
if (schedulingJobs && !stopCrons) {
|
|
753
|
-
return true
|
|
772
|
+
return true;
|
|
754
773
|
}
|
|
755
774
|
|
|
756
775
|
for (const job of registeredCronJobs) {
|
|
757
776
|
if (job.running) {
|
|
758
|
-
return true
|
|
777
|
+
return true;
|
|
759
778
|
}
|
|
760
779
|
}
|
|
761
|
-
return false
|
|
780
|
+
return false;
|
|
762
781
|
}
|
|
763
782
|
|
|
764
783
|
export const crons = async () => {
|
|
765
784
|
if (STAMHOOFD.CRONS_DISABLED) {
|
|
766
|
-
console.log(
|
|
785
|
+
console.log('Crons are disabled. Make sure to enable them in the environment variables.');
|
|
767
786
|
return;
|
|
768
787
|
}
|
|
769
|
-
|
|
788
|
+
|
|
770
789
|
schedulingJobs = true;
|
|
771
790
|
for (const job of registeredCronJobs) {
|
|
772
791
|
if (stopCrons) {
|
|
@@ -775,15 +794,15 @@ export const crons = async () => {
|
|
|
775
794
|
if (job.running) {
|
|
776
795
|
continue;
|
|
777
796
|
}
|
|
778
|
-
job.running = true
|
|
797
|
+
job.running = true;
|
|
779
798
|
run(job.name, job.method).finally(() => {
|
|
780
|
-
job.running = false
|
|
781
|
-
}).catch(e => {
|
|
782
|
-
console.error(e)
|
|
799
|
+
job.running = false;
|
|
800
|
+
}).catch((e) => {
|
|
801
|
+
console.error(e);
|
|
783
802
|
});
|
|
784
803
|
|
|
785
804
|
// Prevent starting too many jobs at once
|
|
786
|
-
if (STAMHOOFD.environment !==
|
|
805
|
+
if (STAMHOOFD.environment !== 'development') {
|
|
787
806
|
await sleep(10 * 1000);
|
|
788
807
|
}
|
|
789
808
|
}
|