@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,12 +1,12 @@
|
|
|
1
|
-
import { DecodedRequest, Endpoint, Request, Response } from
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { Document, DocumentTemplate, Member } from '@stamhoofd/models';
|
|
3
|
-
import { Document as DocumentStruct,DocumentStatus } from
|
|
4
|
-
import { Sorter } from
|
|
3
|
+
import { Document as DocumentStruct, DocumentStatus } from '@stamhoofd/structures';
|
|
4
|
+
import { Sorter } from '@stamhoofd/utility';
|
|
5
5
|
|
|
6
|
-
import { Context } from
|
|
6
|
+
import { Context } from '../../../helpers/Context';
|
|
7
7
|
type Params = Record<string, never>;
|
|
8
8
|
type Query = undefined;
|
|
9
|
-
type Body = undefined
|
|
9
|
+
type Body = undefined;
|
|
10
10
|
type ResponseBody = DocumentStruct[];
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -14,11 +14,11 @@ type ResponseBody = DocumentStruct[];
|
|
|
14
14
|
*/
|
|
15
15
|
export class GetUserMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
16
16
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
17
|
-
if (request.method
|
|
17
|
+
if (request.method !== 'GET') {
|
|
18
18
|
return [false];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const params = Endpoint.parseParameters(request.url,
|
|
21
|
+
const params = Endpoint.parseParameters(request.url, '/documents', {});
|
|
22
22
|
|
|
23
23
|
if (params) {
|
|
24
24
|
return [true, params as Params];
|
|
@@ -29,52 +29,52 @@ export class GetUserMembersEndpoint extends Endpoint<Params, Query, Body, Respon
|
|
|
29
29
|
|
|
30
30
|
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
31
31
|
const organization = await Context.setUserOrganizationScope();
|
|
32
|
-
const {user} = await Context.authenticate()
|
|
32
|
+
const { user } = await Context.authenticate();
|
|
33
33
|
|
|
34
|
-
const members = await Member.getMembersWithRegistrationForUser(user)
|
|
35
|
-
let templates = organization ? await DocumentTemplate.where({ status: 'Published', organizationId: organization.id }) : null
|
|
36
|
-
const memberIds = members.map(m => m.id)
|
|
37
|
-
const templateIds = templates ? templates.map(t => t.id) : null
|
|
34
|
+
const members = await Member.getMembersWithRegistrationForUser(user);
|
|
35
|
+
let templates = organization ? await DocumentTemplate.where({ status: 'Published', organizationId: organization.id }) : null;
|
|
36
|
+
const memberIds = members.map(m => m.id);
|
|
37
|
+
const templateIds = templates ? templates.map(t => t.id) : null;
|
|
38
38
|
|
|
39
39
|
if (memberIds.length == 0 || (templateIds !== null && templateIds.length == 0)) {
|
|
40
40
|
return new Response([]);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const w: any = {
|
|
43
|
+
const w: any = {
|
|
44
44
|
memberId: {
|
|
45
45
|
sign: 'IN',
|
|
46
|
-
value: memberIds
|
|
46
|
+
value: memberIds,
|
|
47
47
|
},
|
|
48
48
|
status: {
|
|
49
49
|
sign: 'IN',
|
|
50
|
-
value: [DocumentStatus.MissingData, DocumentStatus.Published]
|
|
50
|
+
value: [DocumentStatus.MissingData, DocumentStatus.Published],
|
|
51
51
|
},
|
|
52
|
-
}
|
|
52
|
+
};
|
|
53
53
|
|
|
54
54
|
if (templateIds !== null) {
|
|
55
55
|
w.templateId = {
|
|
56
56
|
sign: 'IN',
|
|
57
|
-
value: templateIds
|
|
58
|
-
}
|
|
57
|
+
value: templateIds,
|
|
58
|
+
};
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
62
62
|
const documents = await Document.where(w);
|
|
63
63
|
|
|
64
64
|
if (!templates) {
|
|
65
|
-
templates = documents.length > 0 ? await DocumentTemplate.getByIDs(...documents.map(d => d.templateId)) : []
|
|
65
|
+
templates = documents.length > 0 ? await DocumentTemplate.getByIDs(...documents.map(d => d.templateId)) : [];
|
|
66
66
|
}
|
|
67
|
-
|
|
68
|
-
const filteredDocuments = documents.filter(document => {
|
|
69
|
-
const template = templates.find(t => t.id == document.templateId)
|
|
67
|
+
|
|
68
|
+
const filteredDocuments = documents.filter((document) => {
|
|
69
|
+
const template = templates.find(t => t.id == document.templateId);
|
|
70
70
|
if (!template || (!template.updatesEnabled && document.status === DocumentStatus.MissingData)) {
|
|
71
|
-
return false
|
|
71
|
+
return false;
|
|
72
72
|
}
|
|
73
73
|
return true;
|
|
74
|
-
})
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
filteredDocuments.sort((a, b) => Sorter.byDateValue(a.createdAt, b.createdAt));
|
|
75
77
|
|
|
76
|
-
filteredDocuments.sort((a, b) => Sorter.byDateValue(a.createdAt, b.createdAt))
|
|
77
|
-
|
|
78
78
|
return new Response(filteredDocuments.map(d => d.getStructure()));
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { DecodedRequest, Endpoint, Request, Response } from
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { Member } from '@stamhoofd/models';
|
|
3
|
-
import { MembersBlob } from
|
|
3
|
+
import { MembersBlob } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
|
-
import { AuthenticatedStructures } from
|
|
6
|
-
import { Context } from
|
|
5
|
+
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
6
|
+
import { Context } from '../../../helpers/Context';
|
|
7
7
|
|
|
8
8
|
type Params = Record<string, never>;
|
|
9
9
|
type Query = undefined;
|
|
10
|
-
type Body = undefined
|
|
11
|
-
type ResponseBody = MembersBlob
|
|
10
|
+
type Body = undefined;
|
|
11
|
+
type ResponseBody = MembersBlob;
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Get the members of the user
|
|
15
15
|
*/
|
|
16
16
|
export class GetUserMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
17
17
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
18
|
-
if (request.method
|
|
18
|
+
if (request.method !== 'GET') {
|
|
19
19
|
return [false];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const params = Endpoint.parseParameters(request.url,
|
|
22
|
+
const params = Endpoint.parseParameters(request.url, '/user/members', {});
|
|
23
23
|
|
|
24
24
|
if (params) {
|
|
25
25
|
return [true, params as Params];
|
|
@@ -30,12 +30,12 @@ export class GetUserMembersEndpoint extends Endpoint<Params, Query, Body, Respon
|
|
|
30
30
|
|
|
31
31
|
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
32
32
|
await Context.setUserOrganizationScope();
|
|
33
|
-
const {user} = await Context.authenticate()
|
|
33
|
+
const { user } = await Context.authenticate();
|
|
34
|
+
|
|
35
|
+
const members = await Member.getMembersWithRegistrationForUser(user);
|
|
34
36
|
|
|
35
|
-
const members = await Member.getMembersWithRegistrationForUser(user)
|
|
36
|
-
|
|
37
37
|
return new Response(
|
|
38
|
-
await AuthenticatedStructures.membersBlob(members)
|
|
38
|
+
await AuthenticatedStructures.membersBlob(members),
|
|
39
39
|
);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } from '@simonbackx/simple-encoding';
|
|
2
|
-
import { DecodedRequest, Endpoint, Request, Response } from
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { Document, Member, mergeTwoMembers, RateLimiter } from '@stamhoofd/models';
|
|
5
|
-
import { MemberDetails, MembersBlob, MemberWithRegistrationsBlob } from
|
|
5
|
+
import { MemberDetails, MembersBlob, MemberWithRegistrationsBlob } from '@stamhoofd/structures';
|
|
6
6
|
|
|
7
7
|
import { Email } from '@stamhoofd/email';
|
|
8
8
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
@@ -11,32 +11,31 @@ import { MemberUserSyncer } from '../../../helpers/MemberUserSyncer';
|
|
|
11
11
|
import { PatchOrganizationMembersEndpoint } from '../../global/members/PatchOrganizationMembersEndpoint';
|
|
12
12
|
type Params = Record<string, never>;
|
|
13
13
|
type Query = undefined;
|
|
14
|
-
type Body = PatchableArrayAutoEncoder<MemberWithRegistrationsBlob
|
|
15
|
-
type ResponseBody = MembersBlob
|
|
14
|
+
type Body = PatchableArrayAutoEncoder<MemberWithRegistrationsBlob>;
|
|
15
|
+
type ResponseBody = MembersBlob;
|
|
16
16
|
|
|
17
17
|
export const securityCodeLimiter = new RateLimiter({
|
|
18
18
|
limits: [
|
|
19
|
-
{
|
|
19
|
+
{
|
|
20
20
|
// Max 10 a day
|
|
21
21
|
limit: 10,
|
|
22
|
-
duration: 24 * 60 * 1000 * 60
|
|
23
|
-
}
|
|
24
|
-
]
|
|
22
|
+
duration: 24 * 60 * 1000 * 60,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
|
|
28
27
|
/**
|
|
29
28
|
* Allow to add, patch and delete multiple members simultaneously, which is needed in order to sync relational data that is saved encrypted in multiple members (e.g. parents)
|
|
30
29
|
*/
|
|
31
30
|
export class PatchUserMembersEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
32
|
-
bodyDecoder = new PatchableArrayDecoder(MemberWithRegistrationsBlob as Decoder<MemberWithRegistrationsBlob>, MemberWithRegistrationsBlob.patchType() as Decoder<AutoEncoderPatchType<MemberWithRegistrationsBlob>>, StringDecoder)
|
|
31
|
+
bodyDecoder = new PatchableArrayDecoder(MemberWithRegistrationsBlob as Decoder<MemberWithRegistrationsBlob>, MemberWithRegistrationsBlob.patchType() as Decoder<AutoEncoderPatchType<MemberWithRegistrationsBlob>>, StringDecoder);
|
|
33
32
|
|
|
34
33
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
35
|
-
if (request.method
|
|
34
|
+
if (request.method !== 'PATCH') {
|
|
36
35
|
return [false];
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
const params = Endpoint.parseParameters(request.url,
|
|
38
|
+
const params = Endpoint.parseParameters(request.url, '/members', {});
|
|
40
39
|
|
|
41
40
|
if (params) {
|
|
42
41
|
return [true, params as Params];
|
|
@@ -46,73 +45,72 @@ export class PatchUserMembersEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
46
45
|
|
|
47
46
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
48
47
|
const organization = await Context.setUserOrganizationScope();
|
|
49
|
-
const {user} = await Context.authenticate()
|
|
48
|
+
const { user } = await Context.authenticate();
|
|
50
49
|
|
|
51
50
|
// Process changes
|
|
52
|
-
const addedMembers: Member[] = []
|
|
51
|
+
const addedMembers: Member[] = [];
|
|
53
52
|
for (const put of request.body.getPuts()) {
|
|
54
|
-
const struct = put.put
|
|
53
|
+
const struct = put.put;
|
|
55
54
|
|
|
56
|
-
const member = new Member()
|
|
57
|
-
member.id = struct.id
|
|
58
|
-
member.organizationId = organization?.id ?? null
|
|
55
|
+
const member = new Member();
|
|
56
|
+
member.id = struct.id;
|
|
57
|
+
member.organizationId = organization?.id ?? null;
|
|
59
58
|
|
|
60
|
-
struct.details.cleanData()
|
|
61
|
-
member.details = struct.details
|
|
59
|
+
struct.details.cleanData();
|
|
60
|
+
member.details = struct.details;
|
|
62
61
|
|
|
63
62
|
this.throwIfInvalidDetails(member.details);
|
|
64
63
|
|
|
65
|
-
const duplicate = await this.checkDuplicate(member, struct.details.securityCode)
|
|
64
|
+
const duplicate = await this.checkDuplicate(member, struct.details.securityCode);
|
|
66
65
|
if (duplicate) {
|
|
67
|
-
addedMembers.push(duplicate)
|
|
68
|
-
continue
|
|
66
|
+
addedMembers.push(duplicate);
|
|
67
|
+
continue;
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
await member.save()
|
|
72
|
-
addedMembers.push(member)
|
|
70
|
+
await member.save();
|
|
71
|
+
addedMembers.push(member);
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
|
|
76
74
|
// Modify members
|
|
77
|
-
let members = await Member.getMembersWithRegistrationForUser(user)
|
|
75
|
+
let members = await Member.getMembersWithRegistrationForUser(user);
|
|
78
76
|
|
|
79
77
|
for (let struct of request.body.getPatches()) {
|
|
80
|
-
const member = members.find(
|
|
78
|
+
const member = members.find(m => m.id == struct.id);
|
|
81
79
|
if (!member) {
|
|
82
80
|
throw new SimpleError({
|
|
83
|
-
code:
|
|
81
|
+
code: 'invalid_member',
|
|
84
82
|
message: "This member does not exist or you don't have permissions to modify this member",
|
|
85
|
-
human:
|
|
86
|
-
})
|
|
83
|
+
human: 'Je probeert een lid aan te passen die niet (meer) bestaat. Er ging ergens iets mis.',
|
|
84
|
+
});
|
|
87
85
|
}
|
|
88
|
-
const securityCode = struct.details?.securityCode // will get cleared after the filter
|
|
89
|
-
struct = await Context.auth.filterMemberPatch(member, struct)
|
|
86
|
+
const securityCode = struct.details?.securityCode; // will get cleared after the filter
|
|
87
|
+
struct = await Context.auth.filterMemberPatch(member, struct);
|
|
90
88
|
|
|
91
89
|
if (struct.details) {
|
|
92
90
|
if (struct.details.isPut()) {
|
|
93
91
|
throw new SimpleError({
|
|
94
|
-
code:
|
|
95
|
-
message:
|
|
96
|
-
human:
|
|
97
|
-
field:
|
|
98
|
-
})
|
|
92
|
+
code: 'not_allowed',
|
|
93
|
+
message: 'Cannot override details',
|
|
94
|
+
human: 'Er ging iets mis bij het aanpassen van de gegevens van dit lid. Probeer het later opnieuw en neem contact op als het probleem zich blijft voordoen.',
|
|
95
|
+
field: 'details',
|
|
96
|
+
});
|
|
99
97
|
}
|
|
100
98
|
|
|
101
|
-
member.details.patchOrPut(struct.details)
|
|
99
|
+
member.details.patchOrPut(struct.details);
|
|
102
100
|
member.details.cleanData();
|
|
103
101
|
this.throwIfInvalidDetails(member.details);
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
if (!member.details) {
|
|
107
105
|
throw new SimpleError({
|
|
108
|
-
code:
|
|
109
|
-
message:
|
|
110
|
-
human:
|
|
111
|
-
field:
|
|
112
|
-
})
|
|
106
|
+
code: 'invalid_data',
|
|
107
|
+
message: 'No details provided',
|
|
108
|
+
human: 'Opgelet! Je gebruikt een oudere versie van de inschrijvingspagina die niet langer wordt ondersteund. Herlaad de website grondig en wis je browser cache.',
|
|
109
|
+
field: 'details',
|
|
110
|
+
});
|
|
113
111
|
}
|
|
114
112
|
|
|
115
|
-
/*const duplicate = await this.checkDuplicate(member, securityCode)
|
|
113
|
+
/* const duplicate = await this.checkDuplicate(member, securityCode)
|
|
116
114
|
if (duplicate) {
|
|
117
115
|
// Remove the member from the list
|
|
118
116
|
members.splice(members.findIndex(m => m.id === member.id), 1)
|
|
@@ -120,113 +118,115 @@ export class PatchUserMembersEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
120
118
|
// Add new
|
|
121
119
|
addedMembers.push(duplicate)
|
|
122
120
|
continue
|
|
123
|
-
}*/
|
|
121
|
+
} */
|
|
124
122
|
|
|
125
123
|
await member.save();
|
|
126
|
-
await MemberUserSyncer.onChangeMember(member)
|
|
124
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
127
125
|
|
|
128
126
|
// Update documents
|
|
129
|
-
await Document.updateForMember(member.id)
|
|
127
|
+
await Document.updateForMember(member.id);
|
|
130
128
|
}
|
|
131
129
|
|
|
132
130
|
// Modify members
|
|
133
131
|
if (addedMembers.length > 0) {
|
|
134
132
|
// Give access to created members
|
|
135
|
-
await Member.users.reverse(
|
|
133
|
+
await Member.users.reverse('members').link(user, addedMembers);
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
await PatchOrganizationMembersEndpoint.deleteMembers(request.body.getDeletes())
|
|
139
|
-
|
|
140
|
-
members = await Member.getMembersWithRegistrationForUser(user)
|
|
136
|
+
await PatchOrganizationMembersEndpoint.deleteMembers(request.body.getDeletes());
|
|
137
|
+
|
|
138
|
+
members = await Member.getMembersWithRegistrationForUser(user);
|
|
141
139
|
|
|
142
140
|
for (const member of addedMembers) {
|
|
143
141
|
const updatedMember = members.find(m => m.id === member.id);
|
|
144
142
|
if (updatedMember) {
|
|
145
143
|
// Make sure we also give access to other parents
|
|
146
|
-
await MemberUserSyncer.onChangeMember(updatedMember)
|
|
144
|
+
await MemberUserSyncer.onChangeMember(updatedMember);
|
|
147
145
|
|
|
148
146
|
if (!updatedMember.users.find(u => u.id === user.id)) {
|
|
149
147
|
// Also link the user to the member if the email address is missing in the details
|
|
150
|
-
await MemberUserSyncer.linkUser(user.email, updatedMember, true)
|
|
148
|
+
await MemberUserSyncer.linkUser(user.email, updatedMember, true);
|
|
151
149
|
}
|
|
152
150
|
|
|
153
|
-
await Document.updateForMember(updatedMember.id)
|
|
151
|
+
await Document.updateForMember(updatedMember.id);
|
|
154
152
|
}
|
|
155
153
|
}
|
|
156
154
|
|
|
157
|
-
|
|
158
155
|
return new Response(
|
|
159
|
-
await AuthenticatedStructures.membersBlob(members)
|
|
156
|
+
await AuthenticatedStructures.membersBlob(members),
|
|
160
157
|
);
|
|
161
158
|
}
|
|
162
159
|
|
|
163
|
-
async checkDuplicate(member: Member, securityCode: string|null|undefined) {
|
|
160
|
+
async checkDuplicate(member: Member, securityCode: string | null | undefined) {
|
|
164
161
|
// Check for duplicates and prevent creating a duplicate member by a user
|
|
165
162
|
const duplicate = await PatchOrganizationMembersEndpoint.checkDuplicate(member);
|
|
166
163
|
if (duplicate) {
|
|
167
164
|
if (await duplicate.isSafeToMergeDuplicateWithoutSecurityCode()) {
|
|
168
|
-
console.log(
|
|
169
|
-
}
|
|
165
|
+
console.log('Merging duplicate without security code: allowed for ' + duplicate.id);
|
|
166
|
+
}
|
|
167
|
+
else if (securityCode) {
|
|
170
168
|
try {
|
|
171
169
|
securityCodeLimiter.track(member.details.name, 1);
|
|
172
|
-
}
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
173
172
|
Email.sendWebmaster({
|
|
174
|
-
subject:
|
|
175
|
-
text:
|
|
176
|
-
})
|
|
177
|
-
|
|
173
|
+
subject: '[Limiet] Limiet bereikt voor aantal beveiligingscodes',
|
|
174
|
+
text: 'Beste, \nDe limiet werd bereikt voor het aantal beveiligingscodes per dag. \nNaam lid: ' + member.details.name + ' (ID: ' + duplicate.id + ')' + '\n\n' + e.message + '\n\nStamhoofd',
|
|
175
|
+
});
|
|
176
|
+
|
|
178
177
|
throw new SimpleError({
|
|
179
|
-
code:
|
|
180
|
-
message:
|
|
181
|
-
human:
|
|
182
|
-
field:
|
|
183
|
-
})
|
|
178
|
+
code: 'too_many_tries',
|
|
179
|
+
message: 'Too many securityCodes limited',
|
|
180
|
+
human: 'Oeps! Om spam te voorkomen limiteren we het aantal beveiligingscodes die je kan proberen. Probeer morgen opnieuw.',
|
|
181
|
+
field: 'details.securityCode',
|
|
182
|
+
});
|
|
184
183
|
}
|
|
185
184
|
|
|
186
185
|
// Entered the security code, so we can link the user to the member
|
|
187
186
|
if (STAMHOOFD.environment !== 'development') {
|
|
188
187
|
if (!duplicate.details.securityCode || securityCode !== duplicate.details.securityCode) {
|
|
189
188
|
throw new SimpleError({
|
|
190
|
-
code:
|
|
189
|
+
code: 'invalid_field',
|
|
191
190
|
field: 'details.securityCode',
|
|
192
|
-
message:
|
|
191
|
+
message: 'Invalid security code',
|
|
193
192
|
human: Context.i18n.$t('49753d6a-7ca4-4145-8024-0be05a9ab063'),
|
|
194
|
-
statusCode: 400
|
|
195
|
-
})
|
|
193
|
+
statusCode: 400,
|
|
194
|
+
});
|
|
196
195
|
}
|
|
197
196
|
}
|
|
198
197
|
|
|
199
|
-
console.log(
|
|
200
|
-
}
|
|
198
|
+
console.log('Merging duplicate: security code is correct - for ' + duplicate.id);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
201
|
throw new SimpleError({
|
|
202
|
-
code:
|
|
203
|
-
message:
|
|
202
|
+
code: 'known_member_missing_rights',
|
|
203
|
+
message: 'Creating known member without sufficient access rights',
|
|
204
204
|
human: `${member.details.firstName} is al gekend in ons systeem, maar jouw e-mailadres niet. Om toegang te krijgen heb je de beveiligingscode nodig.`,
|
|
205
|
-
statusCode: 400
|
|
206
|
-
})
|
|
205
|
+
statusCode: 400,
|
|
206
|
+
});
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
// Merge data
|
|
210
210
|
// NOTE: We use mergeTwoMembers instead of mergeMultipleMembers, because we should never safe 'member' , because that one does not exist in the database
|
|
211
|
-
await mergeTwoMembers(duplicate, member)
|
|
212
|
-
return duplicate
|
|
211
|
+
await mergeTwoMembers(duplicate, member);
|
|
212
|
+
return duplicate;
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
private throwIfInvalidDetails(details: MemberDetails) {
|
|
217
|
-
if(details.firstName.length < 2) {
|
|
217
|
+
if (details.firstName.length < 2) {
|
|
218
218
|
throw new SimpleError({
|
|
219
|
-
code:
|
|
220
|
-
message:
|
|
221
|
-
field:
|
|
219
|
+
code: 'invalid_field',
|
|
220
|
+
message: 'Voornaam is te kort',
|
|
221
|
+
field: 'firstName',
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
if(details.lastName.length < 2) {
|
|
225
|
+
if (details.lastName.length < 2) {
|
|
226
226
|
throw new SimpleError({
|
|
227
|
-
code:
|
|
228
|
-
message:
|
|
229
|
-
field:
|
|
227
|
+
code: 'invalid_field',
|
|
228
|
+
message: 'Achternaam is te kort',
|
|
229
|
+
field: 'lastName',
|
|
230
230
|
});
|
|
231
231
|
}
|
|
232
232
|
}
|