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