@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.
Files changed (198) hide show
  1. package/eslint.config.mjs +5 -0
  2. package/index.ts +81 -74
  3. package/jest.config.cjs +10 -0
  4. package/migrations.ts +16 -14
  5. package/package.json +11 -11
  6. package/src/crons/clear-excel-cache.test.ts +48 -50
  7. package/src/crons/clear-excel-cache.ts +18 -18
  8. package/src/crons/setup-steps.ts +2 -2
  9. package/src/crons.ts +325 -306
  10. package/src/decoders/StringArrayDecoder.ts +7 -7
  11. package/src/decoders/StringNullableDecoder.ts +1 -2
  12. package/src/email-recipient-loaders/members.ts +22 -22
  13. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +8 -9
  14. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +39 -40
  15. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +8 -8
  16. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +44 -45
  17. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +58 -57
  18. package/src/endpoints/auth/CreateAdminEndpoint.ts +48 -45
  19. package/src/endpoints/auth/CreateTokenEndpoint.test.ts +31 -31
  20. package/src/endpoints/auth/CreateTokenEndpoint.ts +146 -147
  21. package/src/endpoints/auth/DeleteTokenEndpoint.ts +7 -7
  22. package/src/endpoints/auth/DeleteUserEndpoint.ts +15 -15
  23. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +17 -18
  24. package/src/endpoints/auth/GetOtherUserEndpoint.ts +9 -10
  25. package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -35
  26. package/src/endpoints/auth/GetUserEndpoint.ts +5 -6
  27. package/src/endpoints/auth/PatchApiUserEndpoint.ts +35 -33
  28. package/src/endpoints/auth/PatchUserEndpoint.ts +55 -52
  29. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +9 -9
  30. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +8 -8
  31. package/src/endpoints/auth/SignupEndpoint.ts +37 -36
  32. package/src/endpoints/auth/VerifyEmailEndpoint.ts +29 -28
  33. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +33 -33
  34. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +7 -7
  35. package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +37 -37
  36. package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -30
  37. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +13 -13
  38. package/src/endpoints/global/email/GetEmailEndpoint.ts +13 -13
  39. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +16 -16
  40. package/src/endpoints/global/email/PatchEmailEndpoint.ts +25 -25
  41. package/src/endpoints/global/events/GetEventsEndpoint.ts +43 -44
  42. package/src/endpoints/global/events/PatchEventsEndpoint.ts +127 -172
  43. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +49 -50
  44. package/src/endpoints/global/files/GetFileCache.ts +13 -13
  45. package/src/endpoints/global/files/UploadFile.ts +51 -54
  46. package/src/endpoints/global/files/UploadImage.ts +53 -53
  47. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +25 -25
  48. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +24 -23
  49. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +8 -8
  50. package/src/endpoints/global/members/GetMembersEndpoint.ts +105 -102
  51. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +240 -239
  52. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +12 -14
  53. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +32 -33
  54. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +48 -57
  55. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +21 -22
  56. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +28 -28
  57. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +18 -18
  58. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +20 -20
  59. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +17 -17
  60. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +81 -75
  61. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +14 -14
  62. package/src/endpoints/global/platform/GetPlatformEnpoint.ts +11 -11
  63. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +71 -68
  64. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +27 -27
  65. package/src/endpoints/global/registration/GetUserBillingStatusEndpoint.ts +30 -30
  66. package/src/endpoints/global/registration/GetUserDetailedBillingStatusEndpoint.ts +34 -34
  67. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +26 -26
  68. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +12 -12
  69. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +90 -90
  70. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +118 -121
  71. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +362 -350
  72. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +8 -9
  73. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +21 -21
  74. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +65 -65
  75. package/src/endpoints/organization/dashboard/billing/GetOrganizationBillingStatusEndpoint.ts +9 -9
  76. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedBillingStatusEndpoint.ts +14 -14
  77. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +17 -17
  78. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +21 -21
  79. package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +15 -15
  80. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +52 -52
  81. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +37 -37
  82. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +14 -14
  83. package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +113 -112
  84. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +29 -29
  85. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +48 -47
  86. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +22 -21
  87. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +13 -14
  88. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +12 -13
  89. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +24 -24
  90. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +10 -12
  91. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +14 -14
  92. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +13 -13
  93. package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +12 -12
  94. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +120 -124
  95. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +172 -173
  96. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +88 -89
  97. package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +12 -12
  98. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +17 -17
  99. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +8 -8
  100. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +66 -67
  101. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +47 -47
  102. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +93 -91
  103. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +16 -17
  104. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +170 -167
  105. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +25 -24
  106. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +22 -23
  107. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +22 -22
  108. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +17 -18
  109. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +8 -9
  110. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +17 -18
  111. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -15
  112. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +19 -19
  113. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +19 -19
  114. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +14 -14
  115. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +12 -12
  116. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +103 -100
  117. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +11 -12
  118. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +15 -15
  119. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +14 -14
  120. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +14 -14
  121. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +23 -23
  122. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +54 -52
  123. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +84 -81
  124. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +120 -111
  125. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +24 -24
  126. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +18 -18
  127. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +141 -130
  128. package/src/endpoints/organization/shared/GetDocumentHtml.ts +25 -25
  129. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +18 -18
  130. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +36 -37
  131. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +9 -9
  132. package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +11 -11
  133. package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +28 -27
  134. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +20 -20
  135. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +22 -22
  136. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -14
  137. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +57 -56
  138. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +65 -66
  139. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +18 -17
  140. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +124 -128
  141. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +154 -145
  142. package/src/excel-loaders/members.ts +275 -273
  143. package/src/excel-loaders/payments.ts +155 -156
  144. package/src/helpers/AddressValidator.test.ts +32 -32
  145. package/src/helpers/AddressValidator.ts +128 -122
  146. package/src/helpers/AdminPermissionChecker.ts +339 -236
  147. package/src/helpers/AuthenticatedStructures.ts +233 -134
  148. package/src/helpers/BuckarooHelper.ts +134 -134
  149. package/src/helpers/CheckSettlements.ts +94 -88
  150. package/src/helpers/Context.ts +87 -86
  151. package/src/helpers/CookieHelper.ts +23 -22
  152. package/src/helpers/EmailResumer.ts +10 -10
  153. package/src/helpers/FileCache.ts +62 -62
  154. package/src/helpers/ForwardHandler.test.ts +122 -124
  155. package/src/helpers/ForwardHandler.ts +76 -70
  156. package/src/helpers/MemberUserSyncer.ts +101 -96
  157. package/src/helpers/MembershipCharger.ts +69 -69
  158. package/src/helpers/MembershipHelper.ts +11 -12
  159. package/src/helpers/OpenIDConnectHelper.ts +85 -82
  160. package/src/helpers/PeriodHelper.ts +65 -70
  161. package/src/helpers/StripeHelper.ts +146 -137
  162. package/src/helpers/StripePayoutChecker.ts +51 -52
  163. package/src/helpers/ViesHelper.ts +46 -44
  164. package/src/helpers/fetchToAsyncIterator.ts +14 -14
  165. package/src/helpers/xlsxAddressTransformerColumnFactory.ts +58 -60
  166. package/src/middleware/ContextMiddleware.ts +5 -5
  167. package/src/migrations/1646578856-validate-addresses.ts +6 -9
  168. package/src/seeds/0000000000-example.ts +3 -5
  169. package/src/seeds/1715028563-user-permissions.ts +16 -18
  170. package/src/seeds/1722256498-group-update-occupancy.ts +12 -12
  171. package/src/seeds/1722344162-sync-member-users.ts +14 -15
  172. package/src/seeds/1722344162-update-membership.ts +6 -6
  173. package/src/seeds/1726055544-balance-item-paid.ts +4 -4
  174. package/src/seeds/1726055545-balance-item-pending.ts +4 -4
  175. package/src/seeds/1726494419-update-cached-outstanding-balance.ts +16 -16
  176. package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +12 -12
  177. package/src/seeds/1726572303-schedule-stock-updates.ts +12 -12
  178. package/src/seeds/1726847064-setup-steps.ts +16 -0
  179. package/src/sql-filters/balance-item-payments.ts +7 -7
  180. package/src/sql-filters/events.ts +14 -14
  181. package/src/sql-filters/members.ts +96 -96
  182. package/src/sql-filters/organizations.ts +139 -75
  183. package/src/sql-filters/payments.ts +28 -28
  184. package/src/sql-filters/registrations.ts +14 -14
  185. package/src/sql-sorters/events.ts +25 -25
  186. package/src/sql-sorters/members.ts +26 -26
  187. package/src/sql-sorters/organizations.ts +36 -36
  188. package/src/sql-sorters/payments.ts +26 -26
  189. package/tests/e2e/stock.test.ts +616 -621
  190. package/tests/e2e/tickets.test.ts +255 -260
  191. package/tests/helpers/StripeMocker.ts +177 -179
  192. package/tests/helpers/TestServer.ts +9 -9
  193. package/tests/jest.global.setup.ts +14 -13
  194. package/tests/jest.setup.ts +33 -32
  195. package/.eslintrc.js +0 -61
  196. package/jest.config.js +0 -11
  197. package/src/helpers/SetupStepsUpdater.ts +0 -359
  198. package/src/seeds/1724076679-setup-steps.ts +0 -16
@@ -1,4 +1,4 @@
1
- import { Data, Decoder } from "@simonbackx/simple-encoding";
1
+ import { Data, Decoder } from '@simonbackx/simple-encoding';
2
2
 
3
3
  export class StringArrayDecoder<T> implements Decoder<T[]> {
4
4
  decoder: Decoder<T>;
@@ -11,14 +11,14 @@ export class StringArrayDecoder<T> implements Decoder<T[]> {
11
11
  const strValue = data.string;
12
12
 
13
13
  // Split on comma
14
- const parts = strValue.split(",");
14
+ const parts = strValue.split(',');
15
15
  return parts
16
16
  .map((v, index) => {
17
- return data.clone({
18
- data: v,
19
- context: data.context,
20
- field: data.addToCurrentField(index)
21
- }).decode(this.decoder)
17
+ return data.clone({
18
+ data: v,
19
+ context: data.context,
20
+ field: data.addToCurrentField(index),
21
+ }).decode(this.decoder);
22
22
  });
23
23
  }
24
24
  }
@@ -1,4 +1,4 @@
1
- import { Decoder, Data } from "@simonbackx/simple-encoding";
1
+ import { Decoder, Data } from '@simonbackx/simple-encoding';
2
2
 
3
3
  export class StringNullableDecoder<T> implements Decoder<T | null> {
4
4
  decoder: Decoder<T>;
@@ -15,4 +15,3 @@ export class StringNullableDecoder<T> implements Decoder<T | null> {
15
15
  return data.decode(this.decoder);
16
16
  }
17
17
  }
18
-
@@ -1,61 +1,61 @@
1
- import { Email } from "@stamhoofd/models";
2
- import { SQL } from "@stamhoofd/sql";
3
- import { EmailRecipientFilterType, LimitedFilteredRequest, PaginatedResponse, mergeFilters } from "@stamhoofd/structures";
4
- import { GetMembersEndpoint } from "../endpoints/global/members/GetMembersEndpoint";
1
+ import { Email } from '@stamhoofd/models';
2
+ import { SQL } from '@stamhoofd/sql';
3
+ import { EmailRecipientFilterType, LimitedFilteredRequest, PaginatedResponse, mergeFilters } from '@stamhoofd/structures';
4
+ import { GetMembersEndpoint } from '../endpoints/global/members/GetMembersEndpoint';
5
5
 
6
6
  Email.recipientLoaders.set(EmailRecipientFilterType.Members, {
7
7
  fetch: async (query: LimitedFilteredRequest) => {
8
- const result = await GetMembersEndpoint.buildData(query)
8
+ const result = await GetMembersEndpoint.buildData(query);
9
9
 
10
10
  return new PaginatedResponse({
11
11
  results: result.results.members.flatMap(m => m.getEmailRecipients(['member'])),
12
- next: result.next
12
+ next: result.next,
13
13
  });
14
14
  },
15
15
 
16
16
  count: async (query: LimitedFilteredRequest) => {
17
17
  query.filter = mergeFilters([query.filter, {
18
- 'email': {
19
- $neq: null
20
- }
21
- }])
22
- const q = await GetMembersEndpoint.buildQuery(query)
18
+ email: {
19
+ $neq: null,
20
+ },
21
+ }]);
22
+ const q = await GetMembersEndpoint.buildQuery(query);
23
23
  return await q.count();
24
- }
24
+ },
25
25
  });
26
26
 
27
27
  Email.recipientLoaders.set(EmailRecipientFilterType.MemberParents, {
28
28
  fetch: async (query: LimitedFilteredRequest) => {
29
- const result = await GetMembersEndpoint.buildData(query)
29
+ const result = await GetMembersEndpoint.buildData(query);
30
30
 
31
31
  return new PaginatedResponse({
32
32
  results: result.results.members.flatMap(m => m.getEmailRecipients(['parents'])),
33
- next: result.next
33
+ next: result.next,
34
34
  });
35
35
  },
36
36
 
37
37
  count: async (query: LimitedFilteredRequest) => {
38
- const q = await GetMembersEndpoint.buildQuery(query)
38
+ const q = await GetMembersEndpoint.buildQuery(query);
39
39
  return await q.sum(
40
- SQL.jsonLength(SQL.column('details'), '$.value.parents[*].email')
40
+ SQL.jsonLength(SQL.column('details'), '$.value.parents[*].email'),
41
41
  );
42
- }
42
+ },
43
43
  });
44
44
 
45
45
  Email.recipientLoaders.set(EmailRecipientFilterType.MemberUnverified, {
46
46
  fetch: async (query: LimitedFilteredRequest) => {
47
- const result = await GetMembersEndpoint.buildData(query)
47
+ const result = await GetMembersEndpoint.buildData(query);
48
48
 
49
49
  return new PaginatedResponse({
50
50
  results: result.results.members.flatMap(m => m.getEmailRecipients(['unverified'])),
51
- next: result.next
51
+ next: result.next,
52
52
  });
53
53
  },
54
54
 
55
55
  count: async (query: LimitedFilteredRequest) => {
56
- const q = await GetMembersEndpoint.buildQuery(query)
56
+ const q = await GetMembersEndpoint.buildQuery(query);
57
57
  return await q.sum(
58
- SQL.jsonLength(SQL.column('details'), '$.value.unverifiedEmails')
58
+ SQL.jsonLength(SQL.column('details'), '$.value.unverifiedEmails'),
59
59
  );
60
- }
60
+ },
61
61
  });
@@ -4,7 +4,6 @@ import { QueueHandler } from '@stamhoofd/queues';
4
4
  import { Context } from '../../../helpers/Context';
5
5
  import { MembershipCharger } from '../../../helpers/MembershipCharger';
6
6
 
7
-
8
7
  type Params = Record<string, never>;
9
8
  type Query = Record<string, never>;
10
9
  type Body = undefined;
@@ -12,11 +11,11 @@ type ResponseBody = undefined;
12
11
 
13
12
  export class ChargeMembershipsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
14
13
  protected doesMatch(request: Request): [true, Params] | [false] {
15
- if (request.method != "POST") {
14
+ if (request.method !== 'POST') {
16
15
  return [false];
17
16
  }
18
17
 
19
- const params = Endpoint.parseParameters(request.url, "/admin/charge-memberships", {});
18
+ const params = Endpoint.parseParameters(request.url, '/admin/charge-memberships', {});
20
19
 
21
20
  if (params) {
22
21
  return [true, params as Params];
@@ -25,24 +24,24 @@ export class ChargeMembershipsEndpoint extends Endpoint<Params, Query, Body, Res
25
24
  }
26
25
 
27
26
  async handle(request: DecodedRequest<Params, Query, Body>) {
28
- await Context.authenticate()
27
+ await Context.authenticate();
29
28
 
30
29
  if (!Context.auth.hasPlatformFullAccess()) {
31
- throw Context.auth.error()
30
+ throw Context.auth.error();
32
31
  }
33
32
 
34
33
  if (QueueHandler.isRunning('charge-memberships')) {
35
34
  throw new SimpleError({
36
35
  code: 'charge_pending',
37
36
  message: 'Charge already pending',
38
- human: 'Er is al een aanrekening bezig, even geduld.'
39
- })
37
+ human: 'Er is al een aanrekening bezig, even geduld.',
38
+ });
40
39
  }
41
40
 
42
41
  QueueHandler.schedule('charge-memberships', async () => {
43
- await MembershipCharger.charge()
42
+ await MembershipCharger.charge();
44
43
  }).catch(console.error);
45
-
44
+
46
45
  return new Response(undefined);
47
46
  }
48
47
  }
@@ -6,7 +6,6 @@ import { QueueHandler } from '@stamhoofd/queues';
6
6
  import { Platform } from '@stamhoofd/models';
7
7
  import { SimpleError } from '@simonbackx/simple-errors';
8
8
 
9
-
10
9
  type Params = Record<string, never>;
11
10
  type Query = Record<string, never>;
12
11
  type Body = undefined;
@@ -14,11 +13,11 @@ type ResponseBody = ChargeMembershipsSummary;
14
13
 
15
14
  export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
16
15
  protected doesMatch(request: Request): [true, Params] | [false] {
17
- if (request.method != "GET") {
16
+ if (request.method !== 'GET') {
18
17
  return [false];
19
18
  }
20
19
 
21
- const params = Endpoint.parseParameters(request.url, "/admin/charge-memberships/summary", {});
20
+ const params = Endpoint.parseParameters(request.url, '/admin/charge-memberships/summary', {});
22
21
 
23
22
  if (params) {
24
23
  return [true, params as Params];
@@ -27,60 +26,60 @@ export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query,
27
26
  }
28
27
 
29
28
  async handle(request: DecodedRequest<Params, Query, Body>) {
30
- await Context.authenticate()
29
+ await Context.authenticate();
31
30
 
32
31
  if (!Context.auth.hasPlatformFullAccess()) {
33
- throw Context.auth.error()
32
+ throw Context.auth.error();
34
33
  }
35
34
 
36
35
  if (QueueHandler.isRunning('charge-memberships')) {
37
36
  return new Response(
38
37
  ChargeMembershipsSummary.create({
39
- running: true
40
- })
38
+ running: true,
39
+ }),
41
40
  );
42
41
  }
43
-
44
- const platform = await Platform.getShared()
45
- const chargeVia = platform.membershipOrganizationId
42
+
43
+ const platform = await Platform.getShared();
44
+ const chargeVia = platform.membershipOrganizationId;
46
45
 
47
46
  const query = SQL
48
47
  .select(
49
48
  new SQLSelectAs(
50
49
  new SQLCount(
51
50
  new SQLDistinct(
52
- SQL.column('member_platform_memberships', 'id')
53
- )
51
+ SQL.column('member_platform_memberships', 'id'),
52
+ ),
54
53
  ),
55
- new SQLAlias('data__memberships')
54
+ new SQLAlias('data__memberships'),
56
55
  ),
57
56
  new SQLSelectAs(
58
57
  new SQLCount(
59
58
  new SQLDistinct(
60
- SQL.column('member_platform_memberships', 'memberId')
61
- )
59
+ SQL.column('member_platform_memberships', 'memberId'),
60
+ ),
62
61
  ),
63
- new SQLAlias('data__members')
62
+ new SQLAlias('data__members'),
64
63
  ),
65
64
  new SQLSelectAs(
66
65
  new SQLCount(
67
66
  new SQLDistinct(
68
- SQL.column('member_platform_memberships', 'organizationId')
69
- )
67
+ SQL.column('member_platform_memberships', 'organizationId'),
68
+ ),
70
69
  ),
71
- new SQLAlias('data__organizations')
70
+ new SQLAlias('data__organizations'),
72
71
  ),
73
72
  new SQLSelectAs(
74
73
  new SQLSum(
75
- SQL.column('member_platform_memberships', 'price')
74
+ SQL.column('member_platform_memberships', 'price'),
76
75
  ),
77
- new SQLAlias('data__price')
78
- )
76
+ new SQLAlias('data__price'),
77
+ ),
79
78
  )
80
79
  .from('member_platform_memberships')
81
80
  .where('balanceItemId', null)
82
81
  .where('deletedAt', null)
83
- .whereNot('organizationId', chargeVia)
82
+ .whereNot('organizationId', chargeVia);
84
83
 
85
84
  const result = await query.fetch();
86
85
  const members = result[0]['data']['members'] as number;
@@ -95,52 +94,52 @@ export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query,
95
94
  members: members ?? 0,
96
95
  price: price ?? 0,
97
96
  organizations: organizations ?? 0,
98
- membershipsPerType: await this.fetchPerType(chargeVia)
99
- })
97
+ membershipsPerType: await this.fetchPerType(chargeVia),
98
+ }),
100
99
  );
101
100
  }
102
101
 
103
- async fetchPerType(chargeVia: string|null) {
102
+ async fetchPerType(chargeVia: string | null) {
104
103
  const query = SQL
105
104
  .select(
106
105
  SQL.column('member_platform_memberships', 'membershipTypeId'),
107
106
  new SQLSelectAs(
108
107
  new SQLCount(
109
108
  new SQLDistinct(
110
- SQL.column('member_platform_memberships', 'id')
111
- )
109
+ SQL.column('member_platform_memberships', 'id'),
110
+ ),
112
111
  ),
113
- new SQLAlias('data__memberships')
112
+ new SQLAlias('data__memberships'),
114
113
  ),
115
114
  new SQLSelectAs(
116
115
  new SQLCount(
117
116
  new SQLDistinct(
118
- SQL.column('member_platform_memberships', 'memberId')
119
- )
117
+ SQL.column('member_platform_memberships', 'memberId'),
118
+ ),
120
119
  ),
121
- new SQLAlias('data__members')
120
+ new SQLAlias('data__members'),
122
121
  ),
123
122
  new SQLSelectAs(
124
123
  new SQLCount(
125
124
  new SQLDistinct(
126
- SQL.column('member_platform_memberships', 'organizationId')
127
- )
125
+ SQL.column('member_platform_memberships', 'organizationId'),
126
+ ),
128
127
  ),
129
- new SQLAlias('data__organizations')
128
+ new SQLAlias('data__organizations'),
130
129
  ),
131
130
  new SQLSelectAs(
132
131
  new SQLSum(
133
- SQL.column('member_platform_memberships', 'price')
132
+ SQL.column('member_platform_memberships', 'price'),
134
133
  ),
135
- new SQLAlias('data__price')
136
- )
134
+ new SQLAlias('data__price'),
135
+ ),
137
136
  )
138
137
  .from('member_platform_memberships')
139
138
  .where('balanceItemId', null)
140
139
  .where('deletedAt', null)
141
140
  .whereNot('organizationId', chargeVia)
142
141
  .groupBy(
143
- SQL.column('member_platform_memberships', 'membershipTypeId')
142
+ SQL.column('member_platform_memberships', 'membershipTypeId'),
144
143
  );
145
144
 
146
145
  const result = await query.fetch();
@@ -151,7 +150,7 @@ export class GetChargeMembershipsSummaryEndpoint extends Endpoint<Params, Query,
151
150
  memberships: row['data']['memberships'] as number,
152
151
  members: row['data']['members'] as number,
153
152
  price: row['data']['price'] as number,
154
- organizations: row['data']['organizations'] as number
153
+ organizations: row['data']['organizations'] as number,
155
154
  }));
156
155
  }
157
156
 
@@ -11,14 +11,14 @@ type Body = undefined;
11
11
  type ResponseBody = CountResponse;
12
12
 
13
13
  export class GetOrganizationsCountEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
14
- queryDecoder = CountFilteredRequest as Decoder<CountFilteredRequest>
14
+ queryDecoder = CountFilteredRequest as Decoder<CountFilteredRequest>;
15
15
 
16
16
  protected doesMatch(request: Request): [true, Params] | [false] {
17
- if (request.method != "GET") {
17
+ if (request.method !== 'GET') {
18
18
  return [false];
19
19
  }
20
20
 
21
- const params = Endpoint.parseParameters(request.url, "/admin/organizations/count", {});
21
+ const params = Endpoint.parseParameters(request.url, '/admin/organizations/count', {});
22
22
 
23
23
  if (params) {
24
24
  return [true, params as Params];
@@ -27,16 +27,16 @@ export class GetOrganizationsCountEndpoint extends Endpoint<Params, Query, Body,
27
27
  }
28
28
 
29
29
  async handle(request: DecodedRequest<Params, Query, Body>) {
30
- await Context.authenticate()
31
- const query = GetOrganizationsEndpoint.buildQuery(request.query)
32
-
30
+ await Context.authenticate();
31
+ const query = GetOrganizationsEndpoint.buildQuery(request.query);
32
+
33
33
  const count = await query
34
34
  .count();
35
35
 
36
36
  return new Response(
37
37
  CountResponse.create({
38
- count
39
- })
38
+ count,
39
+ }),
40
40
  );
41
41
  }
42
42
  }
@@ -1,9 +1,8 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-argument */
2
1
  import { Decoder } from '@simonbackx/simple-encoding';
3
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
4
3
  import { SimpleError } from '@simonbackx/simple-errors';
5
4
  import { Organization } from '@stamhoofd/models';
6
- import { SQL, compileToSQLFilter, compileToSQLSorter } from "@stamhoofd/sql";
5
+ import { SQL, compileToSQLFilter, compileToSQLSorter } from '@stamhoofd/sql';
7
6
  import { CountFilteredRequest, LimitedFilteredRequest, Organization as OrganizationStruct, PaginatedResponse, PermissionLevel, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
8
7
 
9
8
  import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
@@ -14,20 +13,20 @@ import { organizationSorters } from '../../../sql-sorters/organizations';
14
13
  type Params = Record<string, never>;
15
14
  type Query = LimitedFilteredRequest;
16
15
  type Body = undefined;
17
- type ResponseBody = PaginatedResponse<OrganizationStruct[], LimitedFilteredRequest>
16
+ type ResponseBody = PaginatedResponse<OrganizationStruct[], LimitedFilteredRequest>;
18
17
 
19
- const sorters = organizationSorters
20
- const filterCompilers = organizationFilterCompilers
18
+ const sorters = organizationSorters;
19
+ const filterCompilers = organizationFilterCompilers;
21
20
 
22
21
  export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
23
- queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest>
22
+ queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest>;
24
23
 
25
24
  protected doesMatch(request: Request): [true, Params] | [false] {
26
- if (request.method != "GET") {
25
+ if (request.method !== 'GET') {
27
26
  return [false];
28
27
  }
29
28
 
30
- const params = Endpoint.parseParameters(request.url, "/admin/organizations", {});
29
+ const params = Endpoint.parseParameters(request.url, '/admin/organizations', {});
31
30
 
32
31
  if (params) {
33
32
  return [true, params as Params];
@@ -35,69 +34,69 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
35
34
  return [false];
36
35
  }
37
36
 
38
- static buildQuery(q: CountFilteredRequest|LimitedFilteredRequest) {
39
- const tags = Context.auth.getPlatformAccessibleOrganizationTags(PermissionLevel.Read)
40
- if (tags != 'all' && tags.length === 0) {
41
- throw Context.auth.error()
37
+ static buildQuery(q: CountFilteredRequest | LimitedFilteredRequest) {
38
+ const tags = Context.auth.getPlatformAccessibleOrganizationTags(PermissionLevel.Read);
39
+ if (tags !== 'all' && tags.length === 0) {
40
+ throw Context.auth.error();
42
41
  }
43
42
 
44
- let scopeFilter: StamhoofdFilter|undefined = undefined;
43
+ let scopeFilter: StamhoofdFilter | undefined = undefined;
45
44
 
46
45
  if (tags !== 'all') {
47
46
  // Add organization scope filter
48
47
  scopeFilter = {
49
48
  tags: {
50
- $in: tags
51
- }
49
+ $in: tags,
50
+ },
52
51
  };
53
52
  }
54
-
53
+
55
54
  const query = SQL
56
55
  .select(
57
- SQL.wildcard('organizations')
56
+ SQL.wildcard('organizations'),
58
57
  )
59
58
  .from(
60
- SQL.table('organizations')
59
+ SQL.table('organizations'),
61
60
  );
62
-
61
+
63
62
  if (scopeFilter) {
64
- query.where(compileToSQLFilter(scopeFilter, filterCompilers))
63
+ query.where(compileToSQLFilter(scopeFilter, filterCompilers));
65
64
  }
66
65
 
67
66
  if (q.filter) {
68
- query.where(compileToSQLFilter(q.filter, filterCompilers))
67
+ query.where(compileToSQLFilter(q.filter, filterCompilers));
69
68
  }
70
69
 
71
70
  if (q.search) {
72
- let searchFilter: StamhoofdFilter|null = null
71
+ let searchFilter: StamhoofdFilter | null = null;
73
72
 
74
73
  // todo: auto detect e-mailaddresses and search on admins
75
74
  searchFilter = {
76
75
  name: {
77
- $contains: q.search
78
- }
79
- }
76
+ $contains: q.search,
77
+ },
78
+ };
80
79
 
81
80
  if (searchFilter) {
82
- query.where(compileToSQLFilter(searchFilter, filterCompilers))
81
+ query.where(compileToSQLFilter(searchFilter, filterCompilers));
83
82
  }
84
83
  }
85
84
 
86
85
  if (q instanceof LimitedFilteredRequest) {
87
86
  if (q.pageFilter) {
88
- query.where(compileToSQLFilter(q.pageFilter, filterCompilers))
87
+ query.where(compileToSQLFilter(q.pageFilter, filterCompilers));
89
88
  }
90
89
 
91
- q.sort = assertSort(q.sort, [{key: 'id'}])
92
- query.orderBy(compileToSQLSorter(q.sort, sorters))
93
- query.limit(q.limit)
90
+ q.sort = assertSort(q.sort, [{ key: 'id' }]);
91
+ query.orderBy(compileToSQLSorter(q.sort, sorters));
92
+ query.limit(q.limit);
94
93
  }
95
-
96
- return query
94
+
95
+ return query;
97
96
  }
98
97
 
99
98
  async handle(request: DecodedRequest<Params, Query, Body>) {
100
- await Context.authenticate()
99
+ await Context.authenticate();
101
100
 
102
101
  const maxLimit = Context.auth.hasSomePlatformAccess() ? 1000 : 100;
103
102
 
@@ -105,22 +104,22 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
105
104
  throw new SimpleError({
106
105
  code: 'invalid_field',
107
106
  field: 'limit',
108
- message: 'Limit can not be more than ' + maxLimit
109
- })
107
+ message: 'Limit can not be more than ' + maxLimit,
108
+ });
110
109
  }
111
110
 
112
111
  if (request.query.limit < 1) {
113
112
  throw new SimpleError({
114
113
  code: 'invalid_field',
115
114
  field: 'limit',
116
- message: 'Limit can not be less than 1'
117
- })
115
+ message: 'Limit can not be less than 1',
116
+ });
118
117
  }
119
-
120
- const data = await GetOrganizationsEndpoint.buildQuery(request.query).fetch()
118
+
119
+ const data = await GetOrganizationsEndpoint.buildQuery(request.query).fetch();
121
120
  const organizations = Organization.fromRows(data, 'organizations');
122
121
 
123
- let next: LimitedFilteredRequest|undefined;
122
+ let next: LimitedFilteredRequest | undefined;
124
123
 
125
124
  if (organizations.length >= request.query.limit) {
126
125
  const lastObject = organizations[organizations.length - 1];
@@ -131,8 +130,8 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
131
130
  pageFilter: nextFilter,
132
131
  sort: request.query.sort,
133
132
  limit: request.query.limit,
134
- search: request.query.search
135
- })
133
+ search: request.query.search,
134
+ });
136
135
 
137
136
  if (JSON.stringify(nextFilter) === JSON.stringify(request.query.pageFilter)) {
138
137
  console.error('Found infinite loading loop for', request.query);
@@ -142,9 +141,9 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
142
141
 
143
142
  return new Response(
144
143
  new PaginatedResponse<OrganizationStruct[], LimitedFilteredRequest>({
145
- results: await AuthenticatedStructures.adminOrganizations(organizations),
146
- next
147
- })
144
+ results: await AuthenticatedStructures.organizations(organizations),
145
+ next,
146
+ }),
148
147
  );
149
148
  }
150
149
  }