@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
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
3
1
|
import { MolliePayment, MollieToken, Order, Organization, PayconiqPayment, Payment, StripeAccount } from '@stamhoofd/models';
|
|
4
|
-
import { Settlement } from '@stamhoofd/structures'
|
|
2
|
+
import { Settlement } from '@stamhoofd/structures';
|
|
5
3
|
import axios from 'axios';
|
|
6
4
|
|
|
7
5
|
import { StripePayoutChecker } from './StripePayoutChecker';
|
|
@@ -11,108 +9,112 @@ type MollieSettlement = {
|
|
|
11
9
|
reference: string;
|
|
12
10
|
createdAt: string;
|
|
13
11
|
settledAt: string;
|
|
14
|
-
status:
|
|
12
|
+
status: 'open' | 'pending' | 'paidout' | 'failed';
|
|
15
13
|
amount: {
|
|
16
14
|
currenty: string;
|
|
17
15
|
value: string;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
16
|
+
};
|
|
17
|
+
};
|
|
20
18
|
|
|
21
19
|
type MolliePaymentJSON = {
|
|
22
20
|
id: string;
|
|
23
|
-
}
|
|
21
|
+
};
|
|
24
22
|
|
|
25
|
-
let lastSettlementCheck: Date | null = null
|
|
23
|
+
let lastSettlementCheck: Date | null = null;
|
|
26
24
|
|
|
27
25
|
export async function checkAllStripePayouts(checkAll = false) {
|
|
28
|
-
if (STAMHOOFD.environment !==
|
|
29
|
-
console.log(
|
|
30
|
-
return
|
|
26
|
+
if (STAMHOOFD.environment !== 'production') {
|
|
27
|
+
console.log('Skip settlement check');
|
|
28
|
+
return;
|
|
31
29
|
}
|
|
32
|
-
|
|
30
|
+
|
|
33
31
|
// Stripe payouts
|
|
34
|
-
const stripeAccounts = await StripeAccount.where({ status: 'active' })
|
|
32
|
+
const stripeAccounts = await StripeAccount.where({ status: 'active' });
|
|
35
33
|
for (const account of stripeAccounts) {
|
|
36
34
|
try {
|
|
37
|
-
console.log(
|
|
35
|
+
console.log('Checking settlements for ', account.accountId);
|
|
38
36
|
|
|
39
37
|
const checker = new StripePayoutChecker({
|
|
40
38
|
secretKey: STAMHOOFD.STRIPE_SECRET_KEY,
|
|
41
|
-
stripeAccount: account.accountId
|
|
42
|
-
})
|
|
43
|
-
await checker.checkSettlements(checkAll)
|
|
44
|
-
}
|
|
45
|
-
|
|
39
|
+
stripeAccount: account.accountId,
|
|
40
|
+
});
|
|
41
|
+
await checker.checkSettlements(checkAll);
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
console.error(e);
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
export async function checkSettlements(checkAll = false) {
|
|
51
|
-
if (STAMHOOFD.environment !==
|
|
52
|
-
return
|
|
50
|
+
if (STAMHOOFD.environment !== 'production') {
|
|
51
|
+
return;
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
if (!checkAll && lastSettlementCheck && (lastSettlementCheck > new Date(new Date().getTime() - 24 * 60 * 60 * 1000))) {
|
|
56
|
-
console.log(
|
|
57
|
-
return
|
|
55
|
+
console.log('Skip settlement check');
|
|
56
|
+
return;
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
console.log(
|
|
61
|
-
lastSettlementCheck = new Date()
|
|
59
|
+
console.log('Checking settlements...');
|
|
60
|
+
lastSettlementCheck = new Date();
|
|
62
61
|
|
|
63
62
|
// Mollie payment is required
|
|
64
|
-
const token = STAMHOOFD.MOLLIE_ORGANIZATION_TOKEN
|
|
63
|
+
const token = STAMHOOFD.MOLLIE_ORGANIZATION_TOKEN;
|
|
65
64
|
if (!token) {
|
|
66
|
-
console.error(
|
|
67
|
-
}
|
|
68
|
-
|
|
65
|
+
console.error('Missing mollie organization token');
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
await checkMollieSettlementsFor(token, checkAll);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// Loop all mollie tokens created after given date (when settlement permission was added)
|
|
72
72
|
try {
|
|
73
73
|
// Stripe payouts
|
|
74
|
-
await checkAllStripePayouts(checkAll)
|
|
74
|
+
await checkAllStripePayouts(checkAll);
|
|
75
75
|
|
|
76
|
-
const mollieTokens = await MollieToken.all()
|
|
76
|
+
const mollieTokens = await MollieToken.all();
|
|
77
77
|
for (const token of mollieTokens) {
|
|
78
78
|
if (token.createdAt < new Date(2021, 8 /* september! */, 8)) {
|
|
79
|
-
console.log(
|
|
80
|
-
}
|
|
79
|
+
console.log('Skipped mollie token that is too old');
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
81
82
|
try {
|
|
82
|
-
await token.refreshIfNeeded()
|
|
83
|
-
await checkMollieSettlementsFor(token.accessToken, checkAll)
|
|
84
|
-
}
|
|
85
|
-
|
|
83
|
+
await token.refreshIfNeeded();
|
|
84
|
+
await checkMollieSettlementsFor(token.accessToken, checkAll);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
console.error(e);
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
console.error(e);
|
|
91
94
|
}
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
// Check settlements once a week on tuesday morning/night
|
|
95
98
|
export async function checkMollieSettlementsFor(token: string, checkAll = false) {
|
|
96
99
|
// Check last 2 weeks + 3 day margin, unless we check them all
|
|
97
|
-
const d = new Date()
|
|
98
|
-
d.setDate(d.getDate() - 17)
|
|
100
|
+
const d = new Date();
|
|
101
|
+
d.setDate(d.getDate() - 17);
|
|
99
102
|
|
|
100
|
-
console.log(
|
|
103
|
+
console.log('Checking settlements for given token...');
|
|
101
104
|
|
|
102
105
|
// Loop all organizations with online paymetns the last week
|
|
103
106
|
try {
|
|
104
|
-
const request = await axios.get(
|
|
107
|
+
const request = await axios.get('https://api.mollie.com/v2/settlements?limit=' + (checkAll ? 250 : 14), {
|
|
105
108
|
headers: {
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
})
|
|
109
|
+
Authorization: 'Bearer ' + token,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
109
112
|
if (request.status === 200) {
|
|
110
113
|
// get data
|
|
111
114
|
try {
|
|
112
|
-
|
|
113
|
-
const data = request.data
|
|
115
|
+
const data = request.data;
|
|
114
116
|
// Read the data
|
|
115
|
-
|
|
117
|
+
|
|
116
118
|
if (data._embedded?.settlements) {
|
|
117
119
|
const settlements = data._embedded.settlements as MollieSettlement[];
|
|
118
120
|
|
|
@@ -122,66 +124,68 @@ export async function checkMollieSettlementsFor(token: string, checkAll = false)
|
|
|
122
124
|
continue;
|
|
123
125
|
}
|
|
124
126
|
|
|
125
|
-
const settledAt = new Date(settlement.settledAt)
|
|
126
|
-
|
|
127
|
+
const settledAt = new Date(settlement.settledAt);
|
|
128
|
+
|
|
127
129
|
if (isNaN(settledAt.getTime())) {
|
|
128
130
|
console.error('Received an invalid settledAt from Mollie', settlement, 'for token', token);
|
|
129
131
|
continue;
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
if (checkAll || settledAt > d) {
|
|
133
|
-
await updateSettlement(token, settlement)
|
|
135
|
+
await updateSettlement(token, settlement);
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
|
-
} else {
|
|
137
|
-
console.error("Unreadable settlements")
|
|
138
138
|
}
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
else {
|
|
140
|
+
console.error('Unreadable settlements');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
console.error(request.data);
|
|
141
145
|
throw e;
|
|
142
146
|
}
|
|
143
|
-
|
|
144
|
-
} else {
|
|
145
|
-
console.error("Failed to fetch settlements")
|
|
146
|
-
console.error(request.data)
|
|
147
147
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
else {
|
|
149
|
+
console.error('Failed to fetch settlements');
|
|
150
|
+
console.error(request.data);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
console.error(e);
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
157
|
|
|
154
158
|
async function updateSettlement(token: string, settlement: MollieSettlement, fromPaymentId?: string) {
|
|
155
|
-
const limit = 250
|
|
159
|
+
const limit = 250;
|
|
156
160
|
|
|
157
161
|
// Loop all payments of this settlement
|
|
158
|
-
const request = await axios.get(
|
|
162
|
+
const request = await axios.get('https://api.mollie.com/v2/settlements/' + settlement.id + '/payments?limit=' + limit + (fromPaymentId ? ('&from=' + encodeURIComponent(fromPaymentId)) : ''), {
|
|
159
163
|
headers: {
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
})
|
|
164
|
+
Authorization: 'Bearer ' + token,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
163
167
|
|
|
164
168
|
if (request.status === 200) {
|
|
165
|
-
const molliePayments = request.data._embedded.payments as MolliePaymentJSON[]
|
|
169
|
+
const molliePayments = request.data._embedded.payments as MolliePaymentJSON[];
|
|
166
170
|
|
|
167
171
|
for (const mollie of molliePayments) {
|
|
168
172
|
// Search payment
|
|
169
|
-
const mps = await MolliePayment.where({ mollieId: mollie.id })
|
|
173
|
+
const mps = await MolliePayment.where({ mollieId: mollie.id });
|
|
170
174
|
if (mps.length == 1) {
|
|
171
|
-
const mp = mps[0]
|
|
172
|
-
const payment = await Payment.getByID(mp.paymentId)
|
|
175
|
+
const mp = mps[0];
|
|
176
|
+
const payment = await Payment.getByID(mp.paymentId);
|
|
173
177
|
if (payment) {
|
|
174
178
|
payment.settlement = Settlement.create({
|
|
175
179
|
id: settlement.id,
|
|
176
180
|
reference: settlement.reference,
|
|
177
181
|
settledAt: new Date(settlement.settledAt),
|
|
178
|
-
amount: Math.round(parseFloat(settlement.amount.value)*100)
|
|
179
|
-
})
|
|
180
|
-
const saved = await payment.save()
|
|
182
|
+
amount: Math.round(parseFloat(settlement.amount.value) * 100),
|
|
183
|
+
});
|
|
184
|
+
const saved = await payment.save();
|
|
181
185
|
|
|
182
186
|
if (saved) {
|
|
183
187
|
// Mark order as 'updated', or the frontend won't pull in the updates
|
|
184
|
-
const order = await Order.getForPayment(null, payment.id)
|
|
188
|
+
const order = await Order.getForPayment(null, payment.id);
|
|
185
189
|
if (order) {
|
|
186
190
|
order.updatedAt = new Date();
|
|
187
191
|
order.forceSaveProperty('updatedAt');
|
|
@@ -190,26 +194,28 @@ async function updateSettlement(token: string, settlement: MollieSettlement, fro
|
|
|
190
194
|
|
|
191
195
|
// TODO: Mark registrations as 'saved'
|
|
192
196
|
}
|
|
193
|
-
|
|
194
197
|
|
|
195
|
-
if (STAMHOOFD.environment ===
|
|
196
|
-
console.log(
|
|
197
|
-
console.log(payment.settlement)
|
|
198
|
+
if (STAMHOOFD.environment === 'development') {
|
|
199
|
+
console.log('Updated settlement of payment ' + payment.id);
|
|
200
|
+
console.log(payment.settlement);
|
|
198
201
|
}
|
|
199
|
-
} else {
|
|
200
|
-
console.log("Missing payment "+mp.paymentId)
|
|
201
202
|
}
|
|
202
|
-
|
|
203
|
+
else {
|
|
204
|
+
console.log('Missing payment ' + mp.paymentId);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
203
208
|
// Probably a payment in a different system/platform
|
|
204
|
-
//console.log("No mollie payment found for id "+mollie.id)
|
|
209
|
+
// console.log("No mollie payment found for id "+mollie.id)
|
|
205
210
|
}
|
|
206
211
|
}
|
|
207
212
|
|
|
208
213
|
// Check next page
|
|
209
214
|
if (request.data._links.next) {
|
|
210
|
-
await updateSettlement(token, settlement, molliePayments[molliePayments.length - 1].id)
|
|
215
|
+
await updateSettlement(token, settlement, molliePayments[molliePayments.length - 1].id);
|
|
211
216
|
}
|
|
212
|
-
} else {
|
|
213
|
-
console.error(request.data)
|
|
214
217
|
}
|
|
215
|
-
|
|
218
|
+
else {
|
|
219
|
+
console.error(request.data);
|
|
220
|
+
}
|
|
221
|
+
}
|
package/src/helpers/Context.ts
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import { Request } from
|
|
2
|
-
import { SimpleError } from
|
|
3
|
-
import { I18n } from
|
|
4
|
-
import { Organization, Platform, RateLimiter, Token, User } from
|
|
5
|
-
import { AsyncLocalStorage } from
|
|
1
|
+
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
3
|
+
import { I18n } from '@stamhoofd/backend-i18n';
|
|
4
|
+
import { Organization, Platform, RateLimiter, Token, User } from '@stamhoofd/models';
|
|
5
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
6
6
|
|
|
7
|
-
import { AdminPermissionChecker } from
|
|
7
|
+
import { AdminPermissionChecker } from './AdminPermissionChecker';
|
|
8
8
|
|
|
9
9
|
export const apiUserRateLimiter = new RateLimiter({
|
|
10
10
|
limits: [
|
|
11
|
-
{
|
|
11
|
+
{
|
|
12
12
|
// Block heavy bursts (5req/s for 5s)
|
|
13
13
|
limit: 25,
|
|
14
|
-
duration: 5 * 1000
|
|
14
|
+
duration: 5 * 1000,
|
|
15
15
|
},
|
|
16
|
-
{
|
|
16
|
+
{
|
|
17
17
|
// max 1req/s during 150s
|
|
18
18
|
limit: 150,
|
|
19
|
-
duration: 150 * 1000
|
|
19
|
+
duration: 150 * 1000,
|
|
20
20
|
},
|
|
21
|
-
{
|
|
21
|
+
{
|
|
22
22
|
// 1000 requests per hour
|
|
23
23
|
limit: 1000,
|
|
24
|
-
duration: 60 * 1000 * 60
|
|
24
|
+
duration: 60 * 1000 * 60,
|
|
25
25
|
},
|
|
26
|
-
{
|
|
26
|
+
{
|
|
27
27
|
// 2000 requests per day
|
|
28
28
|
limit: 2000,
|
|
29
|
-
duration: 24 * 60 * 1000 * 60
|
|
30
|
-
}
|
|
31
|
-
]
|
|
29
|
+
duration: 24 * 60 * 1000 * 60,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
export class ContextInstance {
|
|
35
|
-
request: Request
|
|
35
|
+
request: Request;
|
|
36
36
|
|
|
37
|
-
user?: User
|
|
38
|
-
organization?: Organization
|
|
37
|
+
user?: User;
|
|
38
|
+
organization?: Organization;
|
|
39
39
|
|
|
40
|
-
#i18n: I18n|null = null
|
|
41
|
-
#auth: AdminPermissionChecker|null = null
|
|
40
|
+
#i18n: I18n | null = null;
|
|
41
|
+
#auth: AdminPermissionChecker | null = null;
|
|
42
42
|
|
|
43
43
|
constructor(request: Request) {
|
|
44
44
|
this.request = request;
|
|
@@ -53,31 +53,31 @@ export class ContextInstance {
|
|
|
53
53
|
throw new SimpleError({
|
|
54
54
|
code: 'no_context',
|
|
55
55
|
message: 'No context found',
|
|
56
|
-
statusCode: 500
|
|
57
|
-
})
|
|
56
|
+
statusCode: 500,
|
|
57
|
+
});
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
return c;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
static async startForUser<T>(user: User, organization: Organization|null, handler: () => Promise<T>): Promise<T> {
|
|
63
|
+
static async startForUser<T>(user: User, organization: Organization | null, handler: () => Promise<T>): Promise<T> {
|
|
64
64
|
const request = new Request({
|
|
65
65
|
method: 'GET',
|
|
66
66
|
url: '/',
|
|
67
|
-
host: ''
|
|
68
|
-
})
|
|
67
|
+
host: '',
|
|
68
|
+
});
|
|
69
69
|
const context = new ContextInstance(request);
|
|
70
70
|
|
|
71
71
|
if (organization) {
|
|
72
|
-
context.organization = organization
|
|
73
|
-
context.i18n.switchToLocale({ country: organization.address.country })
|
|
72
|
+
context.organization = organization;
|
|
73
|
+
context.i18n.switchToLocale({ country: organization.address.country });
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
context.user = user
|
|
76
|
+
context.user = user;
|
|
77
77
|
context.#auth = new AdminPermissionChecker(user, await Platform.getSharedPrivateStruct(), context.organization);
|
|
78
78
|
|
|
79
79
|
return await this.asyncLocalStorage.run(context, async () => {
|
|
80
|
-
return await handler()
|
|
80
|
+
return await handler();
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -85,19 +85,19 @@ export class ContextInstance {
|
|
|
85
85
|
const context = new ContextInstance(request);
|
|
86
86
|
|
|
87
87
|
return await this.asyncLocalStorage.run(context, async () => {
|
|
88
|
-
return await handler()
|
|
88
|
+
return await handler();
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
get version() {
|
|
93
|
-
return this.request.getVersion()
|
|
93
|
+
return this.request.getVersion();
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
get i18n() {
|
|
97
97
|
if (!this.#i18n) {
|
|
98
|
-
this.#i18n = I18n.fromRequest(this.request)
|
|
98
|
+
this.#i18n = I18n.fromRequest(this.request);
|
|
99
99
|
}
|
|
100
|
-
return this.#i18n
|
|
100
|
+
return this.#i18n;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
get auth() {
|
|
@@ -105,21 +105,22 @@ export class ContextInstance {
|
|
|
105
105
|
throw new SimpleError({
|
|
106
106
|
code: 'internal_error',
|
|
107
107
|
statusCode: 500,
|
|
108
|
-
message: 'AdminPermissionChecker not set in RequestContext: make sure the request is authenticated before using the permissionChecker'
|
|
109
|
-
})
|
|
108
|
+
message: 'AdminPermissionChecker not set in RequestContext: make sure the request is authenticated before using the permissionChecker',
|
|
109
|
+
});
|
|
110
110
|
}
|
|
111
|
-
return this.#auth
|
|
111
|
+
return this.#auth;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
get optionalAuth() {
|
|
115
|
-
return this.#auth
|
|
115
|
+
return this.#auth;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
async setOptionalOrganizationScope() {
|
|
119
119
|
try {
|
|
120
|
-
return await this.setOrganizationScope()
|
|
121
|
-
}
|
|
122
|
-
|
|
120
|
+
return await this.setOrganizationScope();
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
return null;
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -130,89 +131,89 @@ export class ContextInstance {
|
|
|
130
131
|
if (STAMHOOFD.userMode === 'platform') {
|
|
131
132
|
return null;
|
|
132
133
|
}
|
|
133
|
-
return await this.setOrganizationScope()
|
|
134
|
+
return await this.setOrganizationScope();
|
|
134
135
|
}
|
|
135
136
|
|
|
136
|
-
async setOrganizationScope(options?: {allowInactive?: boolean}) {
|
|
137
|
+
async setOrganizationScope(options?: { allowInactive?: boolean }) {
|
|
137
138
|
const organization = await Organization.fromApiHost(this.request.host, options);
|
|
138
139
|
|
|
139
|
-
this.organization = organization
|
|
140
|
-
this.i18n.switchToLocale({ country: organization.address.country })
|
|
140
|
+
this.organization = organization;
|
|
141
|
+
this.i18n.switchToLocale({ country: organization.address.country });
|
|
141
142
|
|
|
142
|
-
return organization
|
|
143
|
+
return organization;
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
async optionalAuthenticate({allowWithoutAccount = false}: {allowWithoutAccount?: boolean} = {}): Promise<{user?: User}> {
|
|
146
|
-
const header = this.request.headers.authorization
|
|
146
|
+
async optionalAuthenticate({ allowWithoutAccount = false }: { allowWithoutAccount?: boolean } = {}): Promise<{ user?: User }> {
|
|
147
|
+
const header = this.request.headers.authorization;
|
|
147
148
|
if (!header) {
|
|
148
|
-
return {}
|
|
149
|
+
return {};
|
|
149
150
|
}
|
|
150
|
-
return this.authenticate({allowWithoutAccount})
|
|
151
|
+
return this.authenticate({ allowWithoutAccount });
|
|
151
152
|
}
|
|
152
153
|
|
|
153
|
-
async authenticate({allowWithoutAccount = false}: {allowWithoutAccount?: boolean} = {}): Promise<{user: User
|
|
154
|
-
const header = this.request.headers.authorization
|
|
154
|
+
async authenticate({ allowWithoutAccount = false }: { allowWithoutAccount?: boolean } = {}): Promise<{ user: User; token: Token }> {
|
|
155
|
+
const header = this.request.headers.authorization;
|
|
155
156
|
if (!header) {
|
|
156
157
|
throw new SimpleError({
|
|
157
|
-
code:
|
|
158
|
-
message:
|
|
159
|
-
statusCode: 401
|
|
160
|
-
})
|
|
158
|
+
code: 'not_authenticated',
|
|
159
|
+
message: 'Missing required authorization header',
|
|
160
|
+
statusCode: 401,
|
|
161
|
+
});
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
if (!header.startsWith(
|
|
164
|
+
if (!header.startsWith('Bearer ')) {
|
|
164
165
|
throw new SimpleError({
|
|
165
|
-
code:
|
|
166
|
-
message:
|
|
167
|
-
statusCode: 401
|
|
168
|
-
})
|
|
166
|
+
code: 'not_supported_authentication',
|
|
167
|
+
message: 'Authentication method not supported. Please authenticate with OAuth2',
|
|
168
|
+
statusCode: 401,
|
|
169
|
+
});
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
const accessToken = header.substring(
|
|
172
|
+
const accessToken = header.substring('Bearer '.length);
|
|
173
|
+
|
|
174
|
+
const token = await Token.getByAccessToken(accessToken, true);
|
|
172
175
|
|
|
173
|
-
const token = await Token.getByAccessToken(accessToken, true)
|
|
174
|
-
|
|
175
176
|
if (!token || (this.organization && token.user.organizationId !== null && token.user.organizationId !== this.organization.id) || (!this.organization && token.user.organizationId)) {
|
|
176
177
|
throw new SimpleError({
|
|
177
|
-
code:
|
|
178
|
-
message:
|
|
179
|
-
human:
|
|
180
|
-
statusCode: 401
|
|
181
|
-
})
|
|
178
|
+
code: 'invalid_access_token',
|
|
179
|
+
message: 'The access token is invalid',
|
|
180
|
+
human: 'Je bent automatisch uitgelogd, log opnieuw in om verder te gaan',
|
|
181
|
+
statusCode: 401,
|
|
182
|
+
});
|
|
182
183
|
}
|
|
183
|
-
|
|
184
|
+
|
|
184
185
|
if (token.isAccessTokenExpired()) {
|
|
185
186
|
throw new SimpleError({
|
|
186
|
-
code:
|
|
187
|
-
message:
|
|
188
|
-
human:
|
|
189
|
-
statusCode: 401
|
|
190
|
-
})
|
|
187
|
+
code: 'expired_access_token',
|
|
188
|
+
message: 'The access token is expired',
|
|
189
|
+
human: 'Je bent automatisch uitgelogd, log opnieuw in om verder te gaan',
|
|
190
|
+
statusCode: 401,
|
|
191
|
+
});
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
if (!token.user.hasAccount() && !allowWithoutAccount) {
|
|
194
195
|
throw new SimpleError({
|
|
195
|
-
code:
|
|
196
|
-
message:
|
|
197
|
-
human:
|
|
198
|
-
statusCode: 401
|
|
199
|
-
})
|
|
196
|
+
code: 'not_activated',
|
|
197
|
+
message: 'This user is not yet activated',
|
|
198
|
+
human: 'Maak een account aan op dit e-mailadres om een wachtwoord in te stellen voor je inlogt.',
|
|
199
|
+
statusCode: 401,
|
|
200
|
+
});
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
// Rate limits for api users
|
|
203
204
|
if (token.user.isApiUser) {
|
|
204
|
-
apiUserRateLimiter.track(this.organization?.id ?? token.user.id)
|
|
205
|
+
apiUserRateLimiter.track(this.organization?.id ?? token.user.id);
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
const user = token.user
|
|
208
|
-
this.user = user
|
|
208
|
+
const user = token.user;
|
|
209
|
+
this.user = user;
|
|
209
210
|
|
|
210
211
|
// Load member of user
|
|
211
212
|
// todo
|
|
212
213
|
|
|
213
214
|
this.#auth = new AdminPermissionChecker(user, await Platform.getSharedPrivateStruct(), this.organization);
|
|
214
215
|
|
|
215
|
-
return {user, token};
|
|
216
|
+
return { user, token };
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
|
|
@@ -220,8 +221,8 @@ export const Context = new Proxy(ContextInstance, {
|
|
|
220
221
|
get(target, prop, receiver) {
|
|
221
222
|
const c = target.current[prop];
|
|
222
223
|
if (c && typeof c == 'function') {
|
|
223
|
-
return c.bind(target.current)
|
|
224
|
+
return c.bind(target.current);
|
|
224
225
|
}
|
|
225
226
|
return c;
|
|
226
|
-
}
|
|
227
|
+
},
|
|
227
228
|
}) as unknown as ContextInstance;
|