@stamhoofd/backend 2.81.0 → 2.83.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/package.json +10 -10
- package/src/audit-logs/GroupLogger.ts +3 -3
- package/src/audit-logs/MemberResponsibilityRecordLogger.ts +1 -1
- package/src/audit-logs/OrderLogger.ts +1 -1
- package/src/audit-logs/RegistrationLogger.ts +1 -1
- package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +4 -4
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +5 -5
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +8 -8
- package/src/endpoints/auth/CreateAdminEndpoint.ts +2 -2
- package/src/endpoints/auth/CreateTokenEndpoint.ts +10 -10
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +2 -2
- package/src/endpoints/auth/PatchUserEndpoint.ts +9 -9
- package/src/endpoints/auth/SignupEndpoint.ts +2 -2
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +3 -3
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +1 -1
- package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +139 -0
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +30 -7
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +1 -1
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +16 -35
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +1 -1
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +22 -16
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +1 -1
- package/src/endpoints/global/files/UploadFile.ts +14 -2
- package/src/endpoints/global/files/UploadImage.ts +2 -2
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +1 -1
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +19 -19
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +34 -34
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +5 -5
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +5 -1
- package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +68 -0
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +1 -1
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +2 -2
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +15 -17
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +4 -4
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +37 -37
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +2 -2
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +5 -5
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +3 -3
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +9 -9
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +4 -4
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +11 -11
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +13 -13
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +16 -16
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +106 -0
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +16 -3
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +247 -0
- package/src/endpoints/{auth → organization/dashboard/users}/PatchApiUserEndpoint.ts +25 -6
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +4 -4
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +2 -2
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +5 -0
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +2 -2
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +3 -3
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +6 -1
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +10 -8
- package/src/excel-loaders/event-notifications.ts +11 -11
- package/src/excel-loaders/members.ts +34 -34
- package/src/excel-loaders/organizations.ts +23 -23
- package/src/excel-loaders/payments.ts +39 -39
- package/src/excel-loaders/receivable-balances.ts +21 -21
- package/src/helpers/AddressValidator.ts +6 -6
- package/src/helpers/AdminPermissionChecker.ts +7 -4
- package/src/helpers/AuthenticatedStructures.ts +16 -8
- package/src/helpers/BuckarooHelper.ts +1 -1
- package/src/helpers/CheckSettlements.ts +1 -1
- package/src/helpers/Context.ts +31 -15
- package/src/helpers/FileCache.ts +7 -7
- package/src/helpers/ForwardHandler.ts +1 -1
- package/src/helpers/GlobalHelper.ts +6 -4
- package/src/helpers/MembershipCharger.ts +2 -2
- package/src/helpers/SetupStepUpdater.ts +1 -1
- package/src/helpers/StripeHelper.ts +18 -7
- package/src/helpers/XlsxTransformerColumnHelper.ts +18 -18
- package/src/services/DocumentService.ts +1 -1
- package/src/services/EventNotificationService.ts +1 -1
- package/src/services/MemberNumberService.ts +3 -3
- package/src/services/SSOService.ts +5 -5
- package/src/sql-filters/members.ts +1 -1
- package/tests/e2e/api-rate-limits.test.ts +188 -0
- package/tests/e2e/private-files.test.ts +3 -3
- package/tests/helpers/StripeMocker.ts +7 -1
- /package/src/endpoints/global/platform/{GetPlatformEnpoint.ts → GetPlatformEndpoint.ts} +0 -0
|
@@ -35,7 +35,7 @@ export class AuthenticatedStructures {
|
|
|
35
35
|
throw new SimpleError({
|
|
36
36
|
code: 'permission_denied',
|
|
37
37
|
message: 'Permission denied',
|
|
38
|
-
human:
|
|
38
|
+
human: $t(`9f5ee239-d01b-4ee1-961b-2e3224489781`),
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -423,6 +423,14 @@ export class AuthenticatedStructures {
|
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
+
if (includeContextOrganization && STAMHOOFD.singleOrganization && !Context.auth.organization) {
|
|
427
|
+
const found = organizations.get(STAMHOOFD.singleOrganization);
|
|
428
|
+
if (!found) {
|
|
429
|
+
const organization = await Context.auth.getOrganization(STAMHOOFD.singleOrganization);
|
|
430
|
+
organizations.set(organization.id, organization);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
426
434
|
const memberBlobs: MemberWithRegistrationsBlob[] = [];
|
|
427
435
|
for (const member of members) {
|
|
428
436
|
for (const registration of member.registrations) {
|
|
@@ -603,7 +611,7 @@ export class AuthenticatedStructures {
|
|
|
603
611
|
throw new SimpleError({
|
|
604
612
|
code: 'organization_not_found',
|
|
605
613
|
message: 'Organization not found',
|
|
606
|
-
human:
|
|
614
|
+
human: $t(`b6f89130-e727-4f85-b3a9-18b97c4f6ab6`),
|
|
607
615
|
});
|
|
608
616
|
}
|
|
609
617
|
|
|
@@ -639,7 +647,7 @@ export class AuthenticatedStructures {
|
|
|
639
647
|
throw new SimpleError({
|
|
640
648
|
code: 'permission_denied',
|
|
641
649
|
message: 'Permission denied',
|
|
642
|
-
human:
|
|
650
|
+
human: $t(`78cd49fe-260c-4fdc-ad83-e605734c684f`),
|
|
643
651
|
});
|
|
644
652
|
}
|
|
645
653
|
}
|
|
@@ -673,7 +681,7 @@ export class AuthenticatedStructures {
|
|
|
673
681
|
throw new SimpleError({
|
|
674
682
|
code: 'permission_denied',
|
|
675
683
|
message: 'Permission denied',
|
|
676
|
-
human:
|
|
684
|
+
human: $t(`792f644d-f3eb-4772-88b9-edb88f0b6773`),
|
|
677
685
|
});
|
|
678
686
|
}
|
|
679
687
|
}
|
|
@@ -706,7 +714,7 @@ export class AuthenticatedStructures {
|
|
|
706
714
|
throw new SimpleError({
|
|
707
715
|
code: 'permission_denied',
|
|
708
716
|
message: 'Permission denied',
|
|
709
|
-
human:
|
|
717
|
+
human: $t(`b5079e56-2480-4ce6-a3a2-3f244540fa0e`),
|
|
710
718
|
});
|
|
711
719
|
}
|
|
712
720
|
}
|
|
@@ -953,13 +961,13 @@ export class AuthenticatedStructures {
|
|
|
953
961
|
if (user.permissions?.platform !== null) {
|
|
954
962
|
userStruct = NamedObject.create({
|
|
955
963
|
id: '',
|
|
956
|
-
name:
|
|
964
|
+
name: $t(`da016ffd-45c5-41cc-90e4-d4e81105ebe0`) + ' ' + Platform.shared.config.name,
|
|
957
965
|
});
|
|
958
966
|
}
|
|
959
967
|
else {
|
|
960
968
|
userStruct = NamedObject.create({
|
|
961
969
|
id: '',
|
|
962
|
-
name:
|
|
970
|
+
name: $t(`bd1e59c8-3d4c-4097-ab35-0ce7b20d0e50`),
|
|
963
971
|
});
|
|
964
972
|
}
|
|
965
973
|
}
|
|
@@ -978,7 +986,7 @@ export class AuthenticatedStructures {
|
|
|
978
986
|
const org = organizations.find(o => o.id === log.organizationId);
|
|
979
987
|
replacements.set('org', AuditLogReplacement.create({
|
|
980
988
|
id: log.organizationId,
|
|
981
|
-
value: org?.name ??
|
|
989
|
+
value: org?.name ?? $t(`cc098cc0-d849-4808-b53a-0b99755b3f99`),
|
|
982
990
|
type: AuditLogReplacementType.Organization,
|
|
983
991
|
}));
|
|
984
992
|
}
|
|
@@ -210,7 +210,7 @@ export class BuckarooHelper {
|
|
|
210
210
|
throw new SimpleError({
|
|
211
211
|
code: 'buckaroo_error',
|
|
212
212
|
message: 'Failed to create payment',
|
|
213
|
-
human:
|
|
213
|
+
human: $t(`08f883bb-cdc7-4d50-a35e-e2fbeaf6f284`),
|
|
214
214
|
});
|
|
215
215
|
}
|
|
216
216
|
}
|
|
@@ -23,7 +23,7 @@ type MolliePaymentJSON = {
|
|
|
23
23
|
let lastSettlementCheck: Date | null = null;
|
|
24
24
|
|
|
25
25
|
export async function checkAllStripePayouts(checkAll = false) {
|
|
26
|
-
if (STAMHOOFD.environment !== 'production') {
|
|
26
|
+
if (STAMHOOFD.environment !== 'production' || !STAMHOOFD.STRIPE_SECRET_KEY) {
|
|
27
27
|
console.log('Skip settlement check');
|
|
28
28
|
return;
|
|
29
29
|
}
|
package/src/helpers/Context.ts
CHANGED
|
@@ -4,29 +4,42 @@ import { I18n } from '@stamhoofd/backend-i18n';
|
|
|
4
4
|
import { Organization, Platform, RateLimiter, Token, User } from '@stamhoofd/models';
|
|
5
5
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
6
6
|
|
|
7
|
+
import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
|
|
8
|
+
import { ApiUserRateLimits } from '@stamhoofd/structures';
|
|
7
9
|
import { AdminPermissionChecker } from './AdminPermissionChecker';
|
|
8
|
-
import { AutoEncoder, field, Decoder, StringDecoder } from '@simonbackx/simple-encoding';
|
|
9
10
|
|
|
10
11
|
export const apiUserRateLimiter = new RateLimiter({
|
|
11
12
|
limits: [
|
|
12
13
|
{
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
limit: {
|
|
15
|
+
'': 25, // (5req/s for 5s)
|
|
16
|
+
[ApiUserRateLimits.Medium]: 10 * 5, // (10req/s for 5s)
|
|
17
|
+
[ApiUserRateLimits.High]: 25 * 5, // (100req/s for 5s)
|
|
18
|
+
},
|
|
15
19
|
duration: 5 * 1000,
|
|
16
20
|
},
|
|
17
21
|
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
limit: {
|
|
23
|
+
'': 120, // max 1req/s during 150s
|
|
24
|
+
[ApiUserRateLimits.Medium]: 240, // (2req/s for 150s)
|
|
25
|
+
[ApiUserRateLimits.High]: 480, // (4req/s for 150s)
|
|
26
|
+
},
|
|
27
|
+
duration: 120 * 1000,
|
|
21
28
|
},
|
|
22
29
|
{
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
limit: {
|
|
31
|
+
'': 1000, // ± 0.27 request/s sustained for an hour = 3.6s between each request
|
|
32
|
+
[ApiUserRateLimits.Medium]: 2000, // ± 0.56 request/s sustained for an hour
|
|
33
|
+
[ApiUserRateLimits.High]: 4000, // ± 1.11 request/s sustained for an hour
|
|
34
|
+
},
|
|
25
35
|
duration: 60 * 1000 * 60,
|
|
26
36
|
},
|
|
27
37
|
{
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
limit: {
|
|
39
|
+
'': 2_000, // max 2000 requests per day
|
|
40
|
+
[ApiUserRateLimits.Medium]: 14_400, // max 4000 requests per day
|
|
41
|
+
[ApiUserRateLimits.High]: 18_000, // max 10 requests per minute, sustained for a full day
|
|
42
|
+
},
|
|
30
43
|
duration: 24 * 60 * 1000 * 60,
|
|
31
44
|
},
|
|
32
45
|
],
|
|
@@ -169,7 +182,10 @@ export class ContextInstance {
|
|
|
169
182
|
return await this.authenticate({ allowWithoutAccount });
|
|
170
183
|
}
|
|
171
184
|
catch (e) {
|
|
172
|
-
|
|
185
|
+
if (e.code === 'not_authenticated') {
|
|
186
|
+
return {};
|
|
187
|
+
}
|
|
188
|
+
throw e;
|
|
173
189
|
}
|
|
174
190
|
}
|
|
175
191
|
|
|
@@ -210,7 +226,7 @@ export class ContextInstance {
|
|
|
210
226
|
throw new SimpleError({
|
|
211
227
|
code: 'invalid_access_token',
|
|
212
228
|
message: 'The access token is invalid',
|
|
213
|
-
human:
|
|
229
|
+
human: $t(`739f88f4-e87d-4872-aef3-8124a59b160c`),
|
|
214
230
|
statusCode: 401,
|
|
215
231
|
});
|
|
216
232
|
}
|
|
@@ -219,7 +235,7 @@ export class ContextInstance {
|
|
|
219
235
|
throw new SimpleError({
|
|
220
236
|
code: 'expired_access_token',
|
|
221
237
|
message: 'The access token is expired',
|
|
222
|
-
human:
|
|
238
|
+
human: $t(`739f88f4-e87d-4872-aef3-8124a59b160c`),
|
|
223
239
|
statusCode: 401,
|
|
224
240
|
});
|
|
225
241
|
}
|
|
@@ -228,14 +244,14 @@ export class ContextInstance {
|
|
|
228
244
|
throw new SimpleError({
|
|
229
245
|
code: 'not_activated',
|
|
230
246
|
message: 'This user is not yet activated',
|
|
231
|
-
human:
|
|
247
|
+
human: $t(`28cf3aaf-d6b3-4325-8b01-4c0e754034ed`),
|
|
232
248
|
statusCode: 401,
|
|
233
249
|
});
|
|
234
250
|
}
|
|
235
251
|
|
|
236
252
|
// Rate limits for api users
|
|
237
253
|
if (token.user.isApiUser) {
|
|
238
|
-
apiUserRateLimiter.track(this.organization?.id ?? token.user.id);
|
|
254
|
+
apiUserRateLimiter.track(this.organization?.id ?? token.user.id, 1, token.user.meta?.rateLimits ?? undefined);
|
|
239
255
|
}
|
|
240
256
|
|
|
241
257
|
const user = token.user;
|
package/src/helpers/FileCache.ts
CHANGED
|
@@ -61,7 +61,7 @@ export class FileCache {
|
|
|
61
61
|
throw new SimpleError({
|
|
62
62
|
code: 'invalid_file',
|
|
63
63
|
message: 'Invalid file',
|
|
64
|
-
human:
|
|
64
|
+
human: $t(`a175829f-1075-4e04-ab5f-8f65bb715635`),
|
|
65
65
|
statusCode: 400,
|
|
66
66
|
});
|
|
67
67
|
}
|
|
@@ -73,7 +73,7 @@ export class FileCache {
|
|
|
73
73
|
throw new SimpleError({
|
|
74
74
|
code: 'invalid_file',
|
|
75
75
|
message: 'Invalid file',
|
|
76
|
-
human:
|
|
76
|
+
human: $t(`a175829f-1075-4e04-ab5f-8f65bb715635`),
|
|
77
77
|
statusCode: 400,
|
|
78
78
|
});
|
|
79
79
|
}
|
|
@@ -86,7 +86,7 @@ export class FileCache {
|
|
|
86
86
|
throw new SimpleError({
|
|
87
87
|
code: 'invalid_file',
|
|
88
88
|
message: 'Invalid file',
|
|
89
|
-
human:
|
|
89
|
+
human: $t(`d5d7b908-50a5-4b3e-94d2-1e54e549764b`),
|
|
90
90
|
statusCode: 400,
|
|
91
91
|
});
|
|
92
92
|
}
|
|
@@ -99,7 +99,7 @@ export class FileCache {
|
|
|
99
99
|
throw new SimpleError({
|
|
100
100
|
code: 'invalid_file',
|
|
101
101
|
message: 'Invalid file',
|
|
102
|
-
human:
|
|
102
|
+
human: $t(`594fab23-b257-4d51-8d7b-eb3e75bc48ba`),
|
|
103
103
|
statusCode: 400,
|
|
104
104
|
});
|
|
105
105
|
}
|
|
@@ -111,7 +111,7 @@ export class FileCache {
|
|
|
111
111
|
throw new SimpleError({
|
|
112
112
|
code: 'invalid_file',
|
|
113
113
|
message: 'Invalid file',
|
|
114
|
-
human:
|
|
114
|
+
human: $t(`796645b6-ded0-4d1b-a439-0062f9b6edc1`),
|
|
115
115
|
statusCode: 400,
|
|
116
116
|
});
|
|
117
117
|
}
|
|
@@ -126,7 +126,7 @@ export class FileCache {
|
|
|
126
126
|
throw new SimpleError({
|
|
127
127
|
code: 'file_expired',
|
|
128
128
|
message: 'File expired',
|
|
129
|
-
human:
|
|
129
|
+
human: $t(`8a9a35d5-ff86-48e3-81e1-42cf85708e06`),
|
|
130
130
|
statusCode: 404,
|
|
131
131
|
});
|
|
132
132
|
}
|
|
@@ -142,7 +142,7 @@ export class FileCache {
|
|
|
142
142
|
throw new SimpleError({
|
|
143
143
|
code: 'file_expired',
|
|
144
144
|
message: 'File expired',
|
|
145
|
-
human:
|
|
145
|
+
human: $t(`d445388b-9062-488b-9fa7-d15682e26fd4`),
|
|
146
146
|
statusCode: 404,
|
|
147
147
|
});
|
|
148
148
|
}
|
|
@@ -67,7 +67,7 @@ export class ForwardHandler {
|
|
|
67
67
|
let defaultEmail: EmailInterfaceRecipient[] = [Email.getWebmasterToEmail()];
|
|
68
68
|
let organizationEmails: EmailInterfaceRecipient[] = [];
|
|
69
69
|
const platform = await Platform.getShared();
|
|
70
|
-
const extraDescription =
|
|
70
|
+
const extraDescription = $t(`24bc9aad-bc92-4d27-bfcd-055113d792fa`) + ' ' + email + $t(`97b9b042-c5b6-42dc-8238-e0e8392fcf26`) + ' ' + platform.config.name + ' ' + $t(`f510fb0c-c180-455a-8f23-7e09e344e47a`);
|
|
71
71
|
|
|
72
72
|
function doBounce() {
|
|
73
73
|
if (!from) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { I18n } from '@stamhoofd/backend-i18n';
|
|
2
2
|
import { FileSignService } from '../services/FileSignService';
|
|
3
3
|
import { Context, ContextInstance } from './Context';
|
|
4
|
-
import { Address, Country } from '@stamhoofd/structures';
|
|
5
4
|
import { MemberRecordStore } from '../services/MemberRecordStore';
|
|
6
5
|
|
|
7
6
|
export class GlobalHelper {
|
|
@@ -13,8 +12,11 @@ export class GlobalHelper {
|
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
private static loadGlobalTranslateFunction() {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
function getI18n() {
|
|
16
|
+
return ContextInstance.optional?.i18n ?? new I18n(I18n.defaultLanguage, STAMHOOFD.fixedCountry ?? I18n.defaultCountry);
|
|
17
|
+
}
|
|
18
|
+
(global as any).$t = (key: string, replace?: Record<string, string>) => getI18n().$t(key, replace);
|
|
19
|
+
(global as any).$getLanguage = () => getI18n().language;
|
|
20
|
+
(global as any).$getCountry = () => getI18n().country;
|
|
19
21
|
}
|
|
20
22
|
}
|
|
@@ -18,7 +18,7 @@ export const MembershipCharger = {
|
|
|
18
18
|
throw new SimpleError({
|
|
19
19
|
code: 'missing_membership_organization',
|
|
20
20
|
message: 'Missing membershipOrganizationId',
|
|
21
|
-
human:
|
|
21
|
+
human: $t(`dd9d20ea-cf50-46be-8eb3-5c85bc5f30c8`),
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -98,7 +98,7 @@ export const MembershipCharger = {
|
|
|
98
98
|
const balanceItem = new BalanceItem();
|
|
99
99
|
balanceItem.unitPrice = membership.price;
|
|
100
100
|
balanceItem.amount = 1;
|
|
101
|
-
balanceItem.description = Formatter.dateNumber(membership.startDate, true) + '
|
|
101
|
+
balanceItem.description = Formatter.dateNumber(membership.startDate, true) + ' ' + $t(`3e515054-91e7-43ed-a9ce-563b626f337d`) + ' ' + Formatter.dateNumber(membership.expireDate ?? membership.endDate, true);
|
|
102
102
|
balanceItem.relations = new Map([
|
|
103
103
|
[
|
|
104
104
|
BalanceItemRelationType.Member,
|
|
@@ -6,7 +6,18 @@ import { Formatter } from '@stamhoofd/utility';
|
|
|
6
6
|
import Stripe from 'stripe';
|
|
7
7
|
|
|
8
8
|
export class StripeHelper {
|
|
9
|
+
static get notConfiguredError() {
|
|
10
|
+
return new SimpleError({
|
|
11
|
+
code: 'not_configured',
|
|
12
|
+
message: 'Stripe is not yet configured for this platform',
|
|
13
|
+
human: $t('4f1361c2-c860-46e7-a242-933bfe56a7ed'),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
static getInstance(accountId: string | null = null) {
|
|
18
|
+
if (!STAMHOOFD.STRIPE_SECRET_KEY) {
|
|
19
|
+
throw this.notConfiguredError;
|
|
20
|
+
}
|
|
10
21
|
return new Stripe(STAMHOOFD.STRIPE_SECRET_KEY, { apiVersion: '2024-06-20', typescript: true, maxNetworkRetries: 0, timeout: 10000, stripeAccount: accountId ?? undefined });
|
|
11
22
|
}
|
|
12
23
|
|
|
@@ -81,7 +92,7 @@ export class StripeHelper {
|
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
static async getStatus(payment: Payment, cancel = false, testMode = false): Promise<PaymentStatus> {
|
|
84
|
-
if (testMode && !STAMHOOFD.STRIPE_SECRET_KEY
|
|
95
|
+
if (testMode && !STAMHOOFD.STRIPE_SECRET_KEY?.startsWith('sk_test_')) {
|
|
85
96
|
// Do not query anything
|
|
86
97
|
return payment.status;
|
|
87
98
|
}
|
|
@@ -204,7 +215,7 @@ export class StripeHelper {
|
|
|
204
215
|
if (!stripeAccount) {
|
|
205
216
|
throw new SimpleError({
|
|
206
217
|
code: '',
|
|
207
|
-
message:
|
|
218
|
+
message: $t(`b77e1f68-8928-42a2-802b-059fa73bedc3`, { method: PaymentMethodHelper.getName(payment.method) }),
|
|
208
219
|
});
|
|
209
220
|
}
|
|
210
221
|
|
|
@@ -214,7 +225,7 @@ export class StripeHelper {
|
|
|
214
225
|
throw new SimpleError({
|
|
215
226
|
code: 'minmum_amount',
|
|
216
227
|
message: 'The minimum amount for an online payment is € 0,50',
|
|
217
|
-
human:
|
|
228
|
+
human: $t(`dae9058f-0aa7-4fcb-9f1d-fc918c65784b`),
|
|
218
229
|
});
|
|
219
230
|
}
|
|
220
231
|
|
|
@@ -256,7 +267,7 @@ export class StripeHelper {
|
|
|
256
267
|
const paymentMethod = await stripe.paymentMethods.create({
|
|
257
268
|
type: payment.method.toLowerCase() as 'bancontact' | 'ideal',
|
|
258
269
|
billing_details: {
|
|
259
|
-
name: payment.customer?.dynamicName || (customer.name.length > 2 ? customer.name :
|
|
270
|
+
name: payment.customer?.dynamicName || (customer.name.length > 2 ? customer.name : $t(`bd1e59c8-3d4c-4097-ab35-0ce7b20d0e50`)),
|
|
260
271
|
email: payment.customer?.dynamicEmail || customer.email,
|
|
261
272
|
address: payment.customer?.company?.address
|
|
262
273
|
? {
|
|
@@ -295,7 +306,7 @@ export class StripeHelper {
|
|
|
295
306
|
console.error('Stripe payment intent status is not requires_action', paymentIntent);
|
|
296
307
|
throw new SimpleError({
|
|
297
308
|
code: 'invalid_status',
|
|
298
|
-
message:
|
|
309
|
+
message: $t(`55d699ae-3da4-45ca-a30e-0f194b08edf7`) + ' ' + PaymentMethodHelper.getName(payment.method) + ' ' + $t(`277dc49e-922a-444f-ba2b-b3442d855358`),
|
|
299
310
|
});
|
|
300
311
|
}
|
|
301
312
|
|
|
@@ -336,7 +347,7 @@ export class StripeHelper {
|
|
|
336
347
|
throw new SimpleError({
|
|
337
348
|
code: 'invalid_price',
|
|
338
349
|
message: 'De totale prijs van de betaling komt niet overeen met de prijs van de items',
|
|
339
|
-
human:
|
|
350
|
+
human: $t(`e66b54d3-70fb-4b40-b3e5-21bc795ba704`),
|
|
340
351
|
statusCode: 500,
|
|
341
352
|
});
|
|
342
353
|
}
|
|
@@ -378,7 +389,7 @@ export class StripeHelper {
|
|
|
378
389
|
console.error('Stripe session has no url', session);
|
|
379
390
|
throw new SimpleError({
|
|
380
391
|
code: 'invalid_status',
|
|
381
|
-
message:
|
|
392
|
+
message: $t(`55d699ae-3da4-45ca-a30e-0f194b08edf7`) + ' ' + PaymentMethodHelper.getName(payment.method) + ' ' + $t(`277dc49e-922a-444f-ba2b-b3442d855358`),
|
|
382
393
|
});
|
|
383
394
|
}
|
|
384
395
|
paymentUrl = session.url;
|
|
@@ -4,11 +4,11 @@ import { Address, CountryHelper, Parent, ParentTypeHelper, PlatformMember, Recor
|
|
|
4
4
|
export class XlsxTransformerColumnHelper {
|
|
5
5
|
static formatBoolean(value: boolean | undefined | null): string {
|
|
6
6
|
if (value === true) {
|
|
7
|
-
return
|
|
7
|
+
return $t(`1ae8cbc7-9ef5-43db-b9a3-0117dfa43be1`);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
if (value === false) {
|
|
11
|
-
return
|
|
11
|
+
return $t(`b8b730fb-f1a3-4c13-8ec4-0aebe08a1449`);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
return '';
|
|
@@ -48,7 +48,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
48
48
|
return [
|
|
49
49
|
{
|
|
50
50
|
id: getId('type'),
|
|
51
|
-
name: getName(
|
|
51
|
+
name: getName($t(`f97ad8c1-31d2-4b61-9e09-3be86eaeba08`)),
|
|
52
52
|
width: 20,
|
|
53
53
|
getValue: (member: PlatformMember) => {
|
|
54
54
|
const parent = getParent(member);
|
|
@@ -60,7 +60,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
id: getId('firstName'),
|
|
63
|
-
name: getName(
|
|
63
|
+
name: getName($t(`efca0579-0543-4636-a996-384bc9f0527e`)),
|
|
64
64
|
width: 20,
|
|
65
65
|
getValue: (member: PlatformMember) => ({
|
|
66
66
|
value: getParent(member)?.firstName ?? '',
|
|
@@ -68,7 +68,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
id: getId('lastName'),
|
|
71
|
-
name: getName(
|
|
71
|
+
name: getName($t(`4a5e438e-08a1-411e-9b66-410eea7ded73`)),
|
|
72
72
|
width: 20,
|
|
73
73
|
getValue: (member: PlatformMember) => ({
|
|
74
74
|
value: getParent(member)?.lastName ?? '',
|
|
@@ -76,7 +76,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
id: getId('phone'),
|
|
79
|
-
name: getName(
|
|
79
|
+
name: getName($t(`856aaa1c-bc62-4e45-9ae5-4c7e7dca23ab`)),
|
|
80
80
|
width: 20,
|
|
81
81
|
getValue: (member: PlatformMember) => ({
|
|
82
82
|
value: getParent(member)?.phone ?? '',
|
|
@@ -84,7 +84,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
id: getId('email'),
|
|
87
|
-
name: getName(
|
|
87
|
+
name: getName($t(`82f4b6ed-afee-4655-9f07-22802e0e7ad9`)),
|
|
88
88
|
width: 20,
|
|
89
89
|
getValue: (member: PlatformMember) => ({
|
|
90
90
|
value: getParent(member)?.email ?? '',
|
|
@@ -92,7 +92,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
id: getId('nationalRegisterNumber'),
|
|
95
|
-
name: getName(
|
|
95
|
+
name: getName($t(`00881b27-7501-4c56-98de-55618be2bf11`)),
|
|
96
96
|
width: 20,
|
|
97
97
|
getValue: (member: PlatformMember) => ({
|
|
98
98
|
value: getParent(member)?.nationalRegisterNumber?.toString() ?? '',
|
|
@@ -115,7 +115,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
115
115
|
{
|
|
116
116
|
id: getId('street'),
|
|
117
117
|
name: `Straat`,
|
|
118
|
-
defaultCategory:
|
|
118
|
+
defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
|
|
119
119
|
width: 40,
|
|
120
120
|
getValue: (object: T) => {
|
|
121
121
|
const address = getAddress(object);
|
|
@@ -126,8 +126,8 @@ export class XlsxTransformerColumnHelper {
|
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
128
|
id: getId('number'),
|
|
129
|
-
name:
|
|
130
|
-
defaultCategory:
|
|
129
|
+
name: $t(`cc1cf4a7-0bd2-4fa7-8ff2-0a12470a738d`),
|
|
130
|
+
defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
|
|
131
131
|
width: 20,
|
|
132
132
|
getValue: (object: T) => {
|
|
133
133
|
const address = getAddress(object);
|
|
@@ -138,8 +138,8 @@ export class XlsxTransformerColumnHelper {
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
id: getId('postalCode'),
|
|
141
|
-
name:
|
|
142
|
-
defaultCategory:
|
|
141
|
+
name: $t(`dafc7b04-dfb2-4dbc-8bcf-f7e9c6356442`),
|
|
142
|
+
defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
|
|
143
143
|
width: 20,
|
|
144
144
|
getValue: (object: T) => {
|
|
145
145
|
const address = getAddress(object);
|
|
@@ -150,8 +150,8 @@ export class XlsxTransformerColumnHelper {
|
|
|
150
150
|
},
|
|
151
151
|
{
|
|
152
152
|
id: getId('city'),
|
|
153
|
-
name:
|
|
154
|
-
defaultCategory:
|
|
153
|
+
name: $t(`3d538399-3585-4be6-b03d-c12afa7183e8`),
|
|
154
|
+
defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
|
|
155
155
|
width: 20,
|
|
156
156
|
getValue: (object: T) => {
|
|
157
157
|
const address = getAddress(object);
|
|
@@ -162,8 +162,8 @@ export class XlsxTransformerColumnHelper {
|
|
|
162
162
|
},
|
|
163
163
|
{
|
|
164
164
|
id: getId('country'),
|
|
165
|
-
name:
|
|
166
|
-
defaultCategory:
|
|
165
|
+
name: $t(`cce830e0-6c05-405f-a800-4c217dc3235f`),
|
|
166
|
+
defaultCategory: $t(`2f10996e-ea97-4345-b997-c93198c7d67f`), // Ignore this name
|
|
167
167
|
width: 20,
|
|
168
168
|
getValue: (object: T) => {
|
|
169
169
|
const address = getAddress(object);
|
|
@@ -215,7 +215,7 @@ export class XlsxTransformerColumnHelper {
|
|
|
215
215
|
name,
|
|
216
216
|
width: width ?? 20,
|
|
217
217
|
defaultCategory,
|
|
218
|
-
category: recordCategory.name,
|
|
218
|
+
category: recordCategory.name.toString(),
|
|
219
219
|
getValue: (object: T) => {
|
|
220
220
|
const answers = getRecordAnswers(object);
|
|
221
221
|
const b = (answers.get(recordSettingId)?.excelValues[index] ?? {
|
|
@@ -7,7 +7,7 @@ function getGroupFieldsAffectingDocuments(group: Group): PlainObject {
|
|
|
7
7
|
type: group.type,
|
|
8
8
|
startDate: group.settings.startDate.getTime(),
|
|
9
9
|
endDate: group.settings.endDate.getTime(),
|
|
10
|
-
name: group.settings.name,
|
|
10
|
+
name: group.settings.name.toString(),
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -132,7 +132,7 @@ export class EventNotificationService {
|
|
|
132
132
|
}
|
|
133
133
|
const events = EventNotification.events.isLoaded(notification) ? notification.events : await EventNotification.events.load(notification);
|
|
134
134
|
const type = await this.validateType(notification);
|
|
135
|
-
let submitterName =
|
|
135
|
+
let submitterName = $t(`95c51d5c-0945-4fcf-90e9-764940e7f54d`);
|
|
136
136
|
|
|
137
137
|
if (notification.submittedBy) {
|
|
138
138
|
const user = await User.getByID(notification.submittedBy);
|
|
@@ -36,7 +36,7 @@ export class MemberNumberService {
|
|
|
36
36
|
throw new SimpleError({
|
|
37
37
|
code: 'assign_member_number',
|
|
38
38
|
message: error.message,
|
|
39
|
-
human:
|
|
39
|
+
human: $t(`3a2c3e9d-4ac8-44a1-9690-98e8e4623298`),
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -61,7 +61,7 @@ export class MemberNumberService {
|
|
|
61
61
|
throw new SimpleError({
|
|
62
62
|
code: 'assign_member_number',
|
|
63
63
|
message: 'Missing birthDay',
|
|
64
|
-
human:
|
|
64
|
+
human: $t(`3e6429f3-1fc2-42ad-b585-4da7da164267`),
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -123,7 +123,7 @@ export class MemberNumberService {
|
|
|
123
123
|
throw new SimpleError({
|
|
124
124
|
code: 'assign_member_number',
|
|
125
125
|
message: `Duplicate member numbers (last try: ${memberNumber}, tries: ${tries})`,
|
|
126
|
-
human:
|
|
126
|
+
human: $t(`49742012-1ca8-4b91-a176-9ce3e17c1fe0`),
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -204,7 +204,7 @@ export class SSOService {
|
|
|
204
204
|
throw new SimpleError({
|
|
205
205
|
code: 'invalid_user',
|
|
206
206
|
message: 'User not allowed to use this login method',
|
|
207
|
-
human:
|
|
207
|
+
human: $t(`6180b5ee-b9c1-401d-89c1-c18d8ab77d74`),
|
|
208
208
|
statusCode: 400,
|
|
209
209
|
});
|
|
210
210
|
}
|
|
@@ -463,7 +463,7 @@ export class SSOService {
|
|
|
463
463
|
return response;
|
|
464
464
|
}
|
|
465
465
|
catch (e) {
|
|
466
|
-
const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() :
|
|
466
|
+
const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() : $t(`bcfb1217-01fa-4116-b0bc-54c8c6dae284`));
|
|
467
467
|
console.error('Error in openID callback', e);
|
|
468
468
|
return SSOServiceWithSession.getErrorRedirectResponse(session, message);
|
|
469
469
|
}
|
|
@@ -574,7 +574,7 @@ export class SSOServiceWithSession {
|
|
|
574
574
|
throw new SimpleError({
|
|
575
575
|
code: 'invalid_user',
|
|
576
576
|
message: 'User not found: please log in with the same email address as the user you are trying to link',
|
|
577
|
-
human:
|
|
577
|
+
human: $t(`3321ecb9-7600-498f-8ab9-b1625197804a`),
|
|
578
578
|
statusCode: 404,
|
|
579
579
|
});
|
|
580
580
|
}
|
|
@@ -628,7 +628,7 @@ export class SSOServiceWithSession {
|
|
|
628
628
|
throw new SimpleError({
|
|
629
629
|
code: 'error',
|
|
630
630
|
message: 'Could not generate token',
|
|
631
|
-
human:
|
|
631
|
+
human: $t(`f40ddd3d-a986-4ec1-9db8-32ec1376c4e8`),
|
|
632
632
|
statusCode: 500,
|
|
633
633
|
});
|
|
634
634
|
}
|
|
@@ -648,7 +648,7 @@ export class SSOServiceWithSession {
|
|
|
648
648
|
return response;
|
|
649
649
|
}
|
|
650
650
|
catch (e) {
|
|
651
|
-
const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() :
|
|
651
|
+
const message = (isSimpleError(e) || isSimpleErrors(e) ? e.getHuman() : $t(`bcfb1217-01fa-4116-b0bc-54c8c6dae284`));
|
|
652
652
|
console.error('Error in openID callback', e);
|
|
653
653
|
return SSOServiceWithSession.getErrorRedirectResponse(session, message);
|
|
654
654
|
}
|
|
@@ -59,7 +59,7 @@ export const memberFilterCompilers: SQLFilterDefinitions = {
|
|
|
59
59
|
throw new SimpleError({
|
|
60
60
|
code: 'permission_denied',
|
|
61
61
|
message: 'No permissions for financial support filter (organization scope).',
|
|
62
|
-
human:
|
|
62
|
+
human: $t(`64d658fa-0727-4924-9448-b243fe8e10a1`),
|
|
63
63
|
statusCode: 400,
|
|
64
64
|
});
|
|
65
65
|
}
|