@stamhoofd/backend 2.39.0 โ 2.40.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/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 +102 -103
- 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 +50 -52
- 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,8 +1,8 @@
|
|
|
1
1
|
import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } from '@simonbackx/simple-encoding';
|
|
2
|
-
import { DecodedRequest, Endpoint, Request, Response } from
|
|
3
|
-
import { SimpleError } from
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { Organization, OrganizationRegistrationPeriod, Platform } from '@stamhoofd/models';
|
|
5
|
-
import { OrganizationMetaData, Organization as OrganizationStruct } from
|
|
5
|
+
import { OrganizationMetaData, Organization as OrganizationStruct } from '@stamhoofd/structures';
|
|
6
6
|
|
|
7
7
|
import { Context } from '../../../helpers/Context';
|
|
8
8
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
@@ -10,18 +10,18 @@ import { Formatter } from '@stamhoofd/utility';
|
|
|
10
10
|
|
|
11
11
|
type Params = Record<string, never>;
|
|
12
12
|
type Query = undefined;
|
|
13
|
-
type Body = PatchableArrayAutoEncoder<OrganizationStruct
|
|
14
|
-
type ResponseBody = OrganizationStruct[]
|
|
13
|
+
type Body = PatchableArrayAutoEncoder<OrganizationStruct>;
|
|
14
|
+
type ResponseBody = OrganizationStruct[];
|
|
15
15
|
|
|
16
16
|
export class PatchOrganizationsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
17
|
-
bodyDecoder = new PatchableArrayDecoder(OrganizationStruct as Decoder<OrganizationStruct>, OrganizationStruct.patchType() as Decoder<AutoEncoderPatchType<OrganizationStruct>>, StringDecoder)
|
|
17
|
+
bodyDecoder = new PatchableArrayDecoder(OrganizationStruct as Decoder<OrganizationStruct>, OrganizationStruct.patchType() as Decoder<AutoEncoderPatchType<OrganizationStruct>>, StringDecoder);
|
|
18
18
|
|
|
19
19
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
20
|
-
if (request.method
|
|
20
|
+
if (request.method !== 'PATCH') {
|
|
21
21
|
return [false];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const params = Endpoint.parseParameters(request.url,
|
|
24
|
+
const params = Endpoint.parseParameters(request.url, '/admin/organizations', {});
|
|
25
25
|
|
|
26
26
|
if (params) {
|
|
27
27
|
return [true, params as Params];
|
|
@@ -30,26 +30,26 @@ export class PatchOrganizationsEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
33
|
-
await Context.authenticate()
|
|
33
|
+
await Context.authenticate();
|
|
34
34
|
if (!Context.auth.hasPlatformFullAccess()) {
|
|
35
|
-
throw Context.auth.error()
|
|
35
|
+
throw Context.auth.error();
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
if (request.body.changes.length == 0) {
|
|
39
39
|
return new Response([]);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const result: Organization[] = [];
|
|
43
|
-
const platform = await Platform.getShared()
|
|
43
|
+
const platform = await Platform.getShared();
|
|
44
44
|
|
|
45
45
|
for (const id of request.body.getDeletes()) {
|
|
46
46
|
if (!Context.auth.hasPlatformFullAccess()) {
|
|
47
|
-
throw Context.auth.error('Enkel een platform hoofdbeheerder kan groepen verwijderen')
|
|
47
|
+
throw Context.auth.error('Enkel een platform hoofdbeheerder kan groepen verwijderen');
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const organization = await Organization.getByID(id);
|
|
51
51
|
if (!organization) {
|
|
52
|
-
throw new SimpleError({ code:
|
|
52
|
+
throw new SimpleError({ code: 'not_found', message: 'Organization not found', statusCode: 404 });
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
await organization.delete();
|
|
@@ -59,17 +59,17 @@ export class PatchOrganizationsEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
59
59
|
for (const patch of request.body.getPatches()) {
|
|
60
60
|
const organization = await Organization.getByID(patch.id);
|
|
61
61
|
if (!organization) {
|
|
62
|
-
throw new SimpleError({ code:
|
|
62
|
+
throw new SimpleError({ code: 'not_found', message: 'Organization not found', statusCode: 404 });
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
if (patch.meta?.tags) {
|
|
66
66
|
const cleanedPatch = OrganizationMetaData.patch({
|
|
67
|
-
tags: patch.meta.tags as any
|
|
68
|
-
})
|
|
67
|
+
tags: patch.meta.tags as any,
|
|
68
|
+
});
|
|
69
69
|
const patchedMeta = organization.meta.patch(cleanedPatch);
|
|
70
70
|
for (const tag of patchedMeta.tags) {
|
|
71
71
|
if (!platform.config.tags.find(t => t.id === tag)) {
|
|
72
|
-
throw new SimpleError({ code:
|
|
72
|
+
throw new SimpleError({ code: 'invalid_tag', message: 'Invalid tag', statusCode: 400 });
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -78,7 +78,7 @@ export class PatchOrganizationsEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
78
78
|
const aIndex = platform.config.tags.findIndex(t => t.id === a);
|
|
79
79
|
const bIndex = platform.config.tags.findIndex(t => t.id === b);
|
|
80
80
|
return aIndex - bIndex;
|
|
81
|
-
})
|
|
81
|
+
});
|
|
82
82
|
|
|
83
83
|
organization.meta.tags = patchedMeta.tags;
|
|
84
84
|
}
|
|
@@ -88,58 +88,58 @@ export class PatchOrganizationsEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// Organization creation
|
|
91
|
-
for (const {put} of request.body.getPuts()) {
|
|
91
|
+
for (const { put } of request.body.getPuts()) {
|
|
92
92
|
if (!Context.auth.hasPlatformFullAccess()) {
|
|
93
|
-
throw Context.auth.error('Enkel een platform hoofdbeheerder kan nieuwe groepen aanmaken')
|
|
93
|
+
throw Context.auth.error('Enkel een platform hoofdbeheerder kan nieuwe groepen aanmaken');
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
if (put.name.length < 4) {
|
|
97
97
|
if (put.name.length == 0) {
|
|
98
98
|
throw new SimpleError({
|
|
99
|
-
code:
|
|
100
|
-
message:
|
|
101
|
-
human:
|
|
102
|
-
field:
|
|
103
|
-
})
|
|
99
|
+
code: 'invalid_field',
|
|
100
|
+
message: 'Should not be empty',
|
|
101
|
+
human: 'Je bent de naam van je organisatie vergeten in te vullen',
|
|
102
|
+
field: 'organization.name',
|
|
103
|
+
});
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
throw new SimpleError({
|
|
107
|
-
code:
|
|
108
|
-
message:
|
|
109
|
-
human:
|
|
110
|
-
field:
|
|
111
|
-
})
|
|
107
|
+
code: 'invalid_field',
|
|
108
|
+
message: 'Field is too short',
|
|
109
|
+
human: 'Kijk de naam van je organisatie na, deze is te kort. Vul eventueel aan met de gemeente.',
|
|
110
|
+
field: 'organization.name',
|
|
111
|
+
});
|
|
112
112
|
}
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
const uri = put.uri || Formatter.slug(put.name);
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
if (uri.length > 100) {
|
|
117
117
|
throw new SimpleError({
|
|
118
|
-
code:
|
|
119
|
-
message:
|
|
120
|
-
human:
|
|
121
|
-
field:
|
|
122
|
-
})
|
|
118
|
+
code: 'invalid_field',
|
|
119
|
+
message: 'Field is too long',
|
|
120
|
+
human: 'De naam van de vereniging is te lang. Probeer de naam wat te verkorten en probeer opnieuw.',
|
|
121
|
+
field: 'organization.name',
|
|
122
|
+
});
|
|
123
123
|
}
|
|
124
124
|
const uriExists = await Organization.getByURI(uri);
|
|
125
|
-
|
|
125
|
+
|
|
126
126
|
if (uriExists) {
|
|
127
127
|
throw new SimpleError({
|
|
128
|
-
code:
|
|
129
|
-
message:
|
|
130
|
-
human:
|
|
131
|
-
field:
|
|
128
|
+
code: 'name_taken',
|
|
129
|
+
message: 'An organization with the same name already exists',
|
|
130
|
+
human: 'Er bestaat al een vereniging met dezelfde URI. Pas deze aan zodat deze uniek is, en controleer of deze vereniging niet al bestaat.',
|
|
131
|
+
field: 'name',
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
const alreadyExists = await Organization.getByURI(Formatter.slug(put.name));
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
if (alreadyExists) {
|
|
138
138
|
throw new SimpleError({
|
|
139
|
-
code:
|
|
140
|
-
message:
|
|
141
|
-
human:
|
|
142
|
-
field:
|
|
139
|
+
code: 'name_taken',
|
|
140
|
+
message: 'An organization with the same name already exists',
|
|
141
|
+
human: 'Er bestaat al een vereniging met dezelfde naam. Voeg bijvoorbeeld de naam van je gemeente toe.',
|
|
142
|
+
field: 'name',
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -148,27 +148,28 @@ export class PatchOrganizationsEndpoint extends Endpoint<Params, Query, Body, Re
|
|
|
148
148
|
organization.name = put.name;
|
|
149
149
|
|
|
150
150
|
organization.uri = put.uri;
|
|
151
|
-
organization.meta = put.meta
|
|
152
|
-
organization.address = put.address
|
|
151
|
+
organization.meta = put.meta;
|
|
152
|
+
organization.address = put.address;
|
|
153
153
|
|
|
154
154
|
if (put.privateMeta) {
|
|
155
|
-
organization.privateMeta = put.privateMeta
|
|
155
|
+
organization.privateMeta = put.privateMeta;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
try {
|
|
159
159
|
await organization.save();
|
|
160
|
-
}
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
161
162
|
console.error(e);
|
|
162
163
|
throw new SimpleError({
|
|
163
|
-
code:
|
|
164
|
-
message:
|
|
165
|
-
statusCode: 500
|
|
164
|
+
code: 'creating_organization',
|
|
165
|
+
message: 'Something went wrong while creating the organization. Please try again later or contact us.',
|
|
166
|
+
statusCode: 500,
|
|
166
167
|
});
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
const organizationPeriod = new OrganizationRegistrationPeriod();
|
|
170
171
|
organizationPeriod.organizationId = organization.id;
|
|
171
|
-
organizationPeriod.periodId = (await Platform.getShared()).periodId
|
|
172
|
+
organizationPeriod.periodId = (await Platform.getShared()).periodId;
|
|
172
173
|
await organizationPeriod.save();
|
|
173
174
|
|
|
174
175
|
result.push(organization);
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
|
-
import { DecodedRequest, Endpoint, Request, Response } from
|
|
3
|
-
import { SimpleError } from
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { PasswordToken, Platform, sendEmailTemplate, User } from '@stamhoofd/models';
|
|
5
|
-
import { EmailTemplateType, Recipient, Replacement, UserPermissions, User as UserStruct, UserWithMembers } from
|
|
5
|
+
import { EmailTemplateType, Recipient, Replacement, UserPermissions, User as UserStruct, UserWithMembers } from '@stamhoofd/structures';
|
|
6
6
|
import { Formatter } from '@stamhoofd/utility';
|
|
7
7
|
|
|
8
8
|
import { AuthenticatedStructures } from '../../helpers/AuthenticatedStructures';
|
|
9
9
|
import { Context } from '../../helpers/Context';
|
|
10
10
|
type Params = Record<string, never>;
|
|
11
11
|
type Query = undefined;
|
|
12
|
-
type Body = UserStruct
|
|
13
|
-
type ResponseBody = UserWithMembers
|
|
12
|
+
type Body = UserStruct;
|
|
13
|
+
type ResponseBody = UserWithMembers;
|
|
14
14
|
|
|
15
15
|
export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
16
|
-
bodyDecoder = UserStruct as Decoder<UserStruct
|
|
16
|
+
bodyDecoder = UserStruct as Decoder<UserStruct>;
|
|
17
17
|
|
|
18
18
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
19
|
-
if (request.method
|
|
19
|
+
if (request.method !== 'POST') {
|
|
20
20
|
return [false];
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const params = Endpoint.parseParameters(request.url,
|
|
23
|
+
const params = Endpoint.parseParameters(request.url, '/user', {});
|
|
24
24
|
|
|
25
25
|
if (params) {
|
|
26
26
|
return [true, params as Params];
|
|
@@ -30,37 +30,38 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
30
30
|
|
|
31
31
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
32
32
|
const organization = await Context.setOptionalOrganizationScope();
|
|
33
|
-
const {user} = await Context.authenticate()
|
|
33
|
+
const { user } = await Context.authenticate();
|
|
34
34
|
|
|
35
35
|
// Fast throw first (more in depth checking for patches later)
|
|
36
36
|
if (organization) {
|
|
37
37
|
if (!await Context.auth.canManageAdmins(organization.id)) {
|
|
38
|
-
throw Context.auth.error()
|
|
38
|
+
throw Context.auth.error();
|
|
39
39
|
}
|
|
40
|
-
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
41
42
|
// Fast throw first (more in depth checking for patches later)
|
|
42
43
|
if (!Context.auth.canManagePlatformAdmins()) {
|
|
43
|
-
throw Context.auth.error()
|
|
44
|
+
throw Context.auth.error();
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
// First check if a user exists with this email?
|
|
48
|
-
const existing = await User.getForRegister(organization?.id ?? null, request.body.email)
|
|
49
|
+
const existing = await User.getForRegister(organization?.id ?? null, request.body.email);
|
|
49
50
|
|
|
50
51
|
const admin = existing ?? (await User.createInvited(organization, {
|
|
51
52
|
firstName: request.body.firstName,
|
|
52
53
|
lastName: request.body.lastName,
|
|
53
54
|
email: request.body.email,
|
|
54
|
-
allowPlatform: true
|
|
55
|
-
}))
|
|
55
|
+
allowPlatform: true,
|
|
56
|
+
}));
|
|
56
57
|
|
|
57
58
|
if (!admin) {
|
|
58
59
|
throw new SimpleError({
|
|
59
60
|
code: 'internal_error',
|
|
60
61
|
message: 'Something went wrong while creating the admin',
|
|
61
62
|
human: 'Er ging iets mis bij het aanmaken van dit account',
|
|
62
|
-
statusCode: 500
|
|
63
|
-
})
|
|
63
|
+
statusCode: 500,
|
|
64
|
+
});
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
// Merge permissions
|
|
@@ -68,28 +69,30 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
68
69
|
throw new SimpleError({
|
|
69
70
|
code: 'missing_field',
|
|
70
71
|
message: 'When creating administrators, you are required to specify permissions in the request',
|
|
71
|
-
field: 'permissions'
|
|
72
|
-
})
|
|
72
|
+
field: 'permissions',
|
|
73
|
+
});
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
if (organization) {
|
|
76
|
-
admin.permissions = UserPermissions.limitedPatch(admin.permissions, request.body.permissions, organization.id)
|
|
77
|
-
}
|
|
77
|
+
admin.permissions = UserPermissions.limitedPatch(admin.permissions, request.body.permissions, organization.id);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
78
80
|
if (admin.permissions) {
|
|
79
|
-
admin.permissions.patchOrPut(request.body.permissions)
|
|
80
|
-
}
|
|
81
|
-
|
|
81
|
+
admin.permissions.patchOrPut(request.body.permissions);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
admin.permissions = request.body.permissions.isPut() ? request.body.permissions : null;
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
if (!admin.firstName && request.body.firstName) {
|
|
86
89
|
// Allow setting the name if the user didn't had a name yet
|
|
87
|
-
admin.firstName = request.body.firstName
|
|
90
|
+
admin.firstName = request.body.firstName;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
if (!admin.lastName && request.body.lastName) {
|
|
91
94
|
// Allow setting the name if the user didn't had a name yet
|
|
92
|
-
admin.lastName = request.body.lastName
|
|
95
|
+
admin.lastName = request.body.lastName;
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
await admin.save();
|
|
@@ -98,12 +101,12 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
98
101
|
const validUntil = new Date();
|
|
99
102
|
validUntil.setTime(validUntil.getTime() + 7 * 24 * 3600 * 1000);
|
|
100
103
|
|
|
101
|
-
const dateTime = Formatter.dateTime(validUntil)
|
|
102
|
-
const recoveryUrl = await PasswordToken.getPasswordRecoveryUrl(admin, organization, request.i18n, validUntil)
|
|
103
|
-
const platformName =
|
|
104
|
-
|
|
105
|
-
const name = organization?.name ?? platformName
|
|
106
|
-
const what = organization ? `de vereniging ${name} op ${platformName}` : platformName
|
|
104
|
+
const dateTime = Formatter.dateTime(validUntil);
|
|
105
|
+
const recoveryUrl = await PasswordToken.getPasswordRecoveryUrl(admin, organization, request.i18n, validUntil);
|
|
106
|
+
const platformName = ((await Platform.getSharedStruct()).config.name);
|
|
107
|
+
|
|
108
|
+
const name = organization?.name ?? platformName;
|
|
109
|
+
const what = organization ? `de vereniging ${name} op ${platformName}` : platformName;
|
|
107
110
|
|
|
108
111
|
const emailTo = admin.getEmailTo();
|
|
109
112
|
const email: string = typeof emailTo === 'string' ? emailTo : emailTo[0]?.email;
|
|
@@ -115,39 +118,39 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
115
118
|
replacements: [
|
|
116
119
|
Replacement.create({
|
|
117
120
|
token: 'greeting',
|
|
118
|
-
value: admin.firstName ? `Dag ${admin.firstName},` : 'Hallo!'
|
|
121
|
+
value: admin.firstName ? `Dag ${admin.firstName},` : 'Hallo!',
|
|
119
122
|
}),
|
|
120
123
|
Replacement.create({
|
|
121
124
|
token: 'resetUrl',
|
|
122
|
-
value: recoveryUrl
|
|
125
|
+
value: recoveryUrl,
|
|
123
126
|
}),
|
|
124
127
|
Replacement.create({
|
|
125
128
|
token: 'platformOrOrganizationName',
|
|
126
|
-
value: what
|
|
129
|
+
value: what,
|
|
127
130
|
}),
|
|
128
131
|
Replacement.create({
|
|
129
132
|
token: 'inviterName',
|
|
130
|
-
value: user.firstName ?? 'Iemand'
|
|
133
|
+
value: user.firstName ?? 'Iemand',
|
|
131
134
|
}),
|
|
132
135
|
Replacement.create({
|
|
133
136
|
token: 'validUntil',
|
|
134
|
-
value: dateTime
|
|
137
|
+
value: dateTime,
|
|
135
138
|
}),
|
|
136
139
|
Replacement.create({
|
|
137
140
|
token: 'email',
|
|
138
|
-
value: admin.email
|
|
139
|
-
})
|
|
140
|
-
]
|
|
141
|
-
})
|
|
141
|
+
value: admin.email,
|
|
142
|
+
}),
|
|
143
|
+
],
|
|
144
|
+
}),
|
|
142
145
|
],
|
|
143
146
|
template: {
|
|
144
|
-
type: admin.hasAccount() ? EmailTemplateType.AdminInvitation : EmailTemplateType.AdminInvitationNewUser
|
|
147
|
+
type: admin.hasAccount() ? EmailTemplateType.AdminInvitation : EmailTemplateType.AdminInvitationNewUser,
|
|
145
148
|
},
|
|
146
|
-
type: 'transactional'
|
|
149
|
+
type: 'transactional',
|
|
147
150
|
});
|
|
148
151
|
|
|
149
152
|
return new Response(
|
|
150
|
-
await AuthenticatedStructures.userWithMembers(admin)
|
|
151
|
-
);
|
|
153
|
+
await AuthenticatedStructures.userWithMembers(admin),
|
|
154
|
+
);
|
|
152
155
|
}
|
|
153
156
|
}
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import { Request } from
|
|
2
|
-
import { OrganizationFactory, Token, UserFactory } from
|
|
3
|
-
import { Token as TokenStruct } from
|
|
1
|
+
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
3
|
+
import { Token as TokenStruct } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
|
-
import { testServer } from
|
|
5
|
+
import { testServer } from '../../../tests/helpers/TestServer';
|
|
6
6
|
import { CreateTokenEndpoint } from './CreateTokenEndpoint';
|
|
7
7
|
|
|
8
|
-
describe(
|
|
8
|
+
describe('Endpoint.CreateToken', () => {
|
|
9
9
|
// Test endpoint
|
|
10
10
|
const endpoint = new CreateTokenEndpoint();
|
|
11
11
|
|
|
12
|
-
test(
|
|
13
|
-
const organization = await new OrganizationFactory({}).create()
|
|
12
|
+
test('Can get a token via password flow', async () => {
|
|
13
|
+
const organization = await new OrganizationFactory({}).create();
|
|
14
14
|
// Also check UTF8 passwords
|
|
15
|
-
const password =
|
|
16
|
-
const user = await new UserFactory({ organization, password }).create()
|
|
15
|
+
const password = '54๐test๐๐พ86s&รฉ';
|
|
16
|
+
const user = await new UserFactory({ organization, password }).create();
|
|
17
17
|
|
|
18
|
-
const r = Request.buildJson(
|
|
19
|
-
grant_type:
|
|
18
|
+
const r = Request.buildJson('POST', '/oauth/token', organization.getApiHost(), {
|
|
19
|
+
grant_type: 'password',
|
|
20
20
|
username: user.email,
|
|
21
|
-
password: password
|
|
21
|
+
password: password,
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
const response = await testServer.test(endpoint, r);
|
|
25
25
|
expect(response.body).toBeDefined();
|
|
26
26
|
|
|
27
27
|
if (!(response.body instanceof TokenStruct)) {
|
|
28
|
-
throw new Error(
|
|
28
|
+
throw new Error('Expected TokenStruct');
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// Check token is valid
|
|
32
|
-
const token = await Token.getByAccessToken(response.body.accessToken)
|
|
33
|
-
expect(token).toBeDefined()
|
|
32
|
+
const token = await Token.getByAccessToken(response.body.accessToken);
|
|
33
|
+
expect(token).toBeDefined();
|
|
34
34
|
|
|
35
|
-
const byRefresh = await Token.getByRefreshToken(response.body.refreshToken)
|
|
36
|
-
expect(byRefresh).toBeDefined()
|
|
35
|
+
const byRefresh = await Token.getByRefreshToken(response.body.refreshToken);
|
|
36
|
+
expect(byRefresh).toBeDefined();
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
test(
|
|
40
|
-
const organization = await new OrganizationFactory({}).create()
|
|
39
|
+
test('Can get a token via refresh flow', async () => {
|
|
40
|
+
const organization = await new OrganizationFactory({}).create();
|
|
41
41
|
// Also check UTF8 passwords
|
|
42
|
-
const password =
|
|
43
|
-
const user = await new UserFactory({ organization, password }).create()
|
|
42
|
+
const password = '54๐test๐๐พ86s&รฉ';
|
|
43
|
+
const user = await new UserFactory({ organization, password }).create();
|
|
44
44
|
const token = await Token.createToken(user);
|
|
45
45
|
|
|
46
|
-
const r = Request.buildJson(
|
|
47
|
-
grant_type:
|
|
48
|
-
refresh_token: token.refreshToken
|
|
46
|
+
const r = Request.buildJson('POST', '/oauth/token', organization.getApiHost(), {
|
|
47
|
+
grant_type: 'refresh_token',
|
|
48
|
+
refresh_token: token.refreshToken,
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
const response = await testServer.test(endpoint, r);
|
|
52
52
|
expect(response.body).toBeDefined();
|
|
53
53
|
|
|
54
54
|
if (!(response.body instanceof TokenStruct)) {
|
|
55
|
-
throw new Error(
|
|
55
|
+
throw new Error('Expected TokenStruct');
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
expect(response.body.accessToken).not.toEqual(token.accessToken)
|
|
59
|
-
expect(response.body.refreshToken).not.toEqual(token.refreshToken)
|
|
58
|
+
expect(response.body.accessToken).not.toEqual(token.accessToken);
|
|
59
|
+
expect(response.body.refreshToken).not.toEqual(token.refreshToken);
|
|
60
60
|
|
|
61
61
|
// Check token is valid
|
|
62
|
-
const byAccess = await Token.getByAccessToken(response.body.accessToken)
|
|
63
|
-
expect(byAccess).toBeDefined()
|
|
62
|
+
const byAccess = await Token.getByAccessToken(response.body.accessToken);
|
|
63
|
+
expect(byAccess).toBeDefined();
|
|
64
64
|
|
|
65
|
-
const byRefresh = await Token.getByRefreshToken(response.body.refreshToken)
|
|
66
|
-
expect(byRefresh).toBeDefined()
|
|
65
|
+
const byRefresh = await Token.getByRefreshToken(response.body.refreshToken);
|
|
66
|
+
expect(byRefresh).toBeDefined();
|
|
67
67
|
});
|
|
68
68
|
});
|