@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.
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 +102 -103
  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 +50 -52
  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,8 +1,8 @@
1
1
  import { Decoder } from '@simonbackx/simple-encoding';
2
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { EmailVerificationCode, Token, User } from '@stamhoofd/models';
5
- import { Token as TokenStruct, VerifyEmailRequest } from "@stamhoofd/structures";
5
+ import { Token as TokenStruct, VerifyEmailRequest } from '@stamhoofd/structures';
6
6
 
7
7
  import { Context } from '../../helpers/Context';
8
8
 
@@ -15,11 +15,11 @@ export class VerifyEmailEndpoint extends Endpoint<Params, Query, Body, ResponseB
15
15
  bodyDecoder = VerifyEmailRequest as Decoder<VerifyEmailRequest>;
16
16
 
17
17
  protected doesMatch(request: Request): [true, Params] | [false] {
18
- if (request.method != "POST") {
18
+ if (request.method !== 'POST') {
19
19
  return [false];
20
20
  }
21
21
 
22
- const params = Endpoint.parseParameters(request.url, "/verify-email", {});
22
+ const params = Endpoint.parseParameters(request.url, '/verify-email', {});
23
23
 
24
24
  if (params) {
25
25
  return [true, params as Params];
@@ -30,54 +30,55 @@ export class VerifyEmailEndpoint extends Endpoint<Params, Query, Body, ResponseB
30
30
  async handle(request: DecodedRequest<Params, Query, Body>) {
31
31
  const organization = await Context.setOptionalOrganizationScope();
32
32
 
33
- const code = await EmailVerificationCode.verify(organization?.id ?? null, request.body.token, request.body.code)
33
+ const code = await EmailVerificationCode.verify(organization?.id ?? null, request.body.token, request.body.code);
34
34
 
35
35
  if (!code) {
36
36
  throw new SimpleError({
37
- code: "invalid_code",
38
- message: "This code is invalid",
39
- human: "Deze code is ongeldig of vervallen.",
40
- statusCode: 400
41
- })
37
+ code: 'invalid_code',
38
+ message: 'This code is invalid',
39
+ human: 'Deze code is ongeldig of vervallen.',
40
+ statusCode: 400,
41
+ });
42
42
  }
43
43
 
44
- const user = await User.getByID(code.userId)
44
+ const user = await User.getByID(code.userId);
45
45
 
46
46
  if (!user || (user.organizationId !== null && user.organizationId !== (organization?.id ?? null))) {
47
47
  throw new SimpleError({
48
- code: "invalid_code",
49
- message: "This code is invalid",
50
- human: "Deze code is ongeldig of vervallen.",
51
- statusCode: 400
52
- })
48
+ code: 'invalid_code',
49
+ message: 'This code is invalid',
50
+ human: 'Deze code is ongeldig of vervallen.',
51
+ statusCode: 400,
52
+ });
53
53
  }
54
54
 
55
- if (user.email != code.email) {
56
- const other = await User.getForAuthentication(user.organizationId, code.email, {allowWithoutAccount: true})
55
+ if (user.email !== code.email) {
56
+ const other = await User.getForAuthentication(user.organizationId, code.email, { allowWithoutAccount: true });
57
57
 
58
58
  if (other) {
59
59
  // Delete the other user, but merge data
60
60
  await user.merge(other);
61
61
  }
62
-
62
+
63
63
  // change user email
64
- user.email = code.email
64
+ user.email = code.email;
65
65
 
66
66
  // If already in use: will fail, so will verification
67
67
  }
68
68
 
69
- user.verified = true
69
+ user.verified = true;
70
70
 
71
71
  try {
72
- await user.save()
73
- } catch (e) {
72
+ await user.save();
73
+ }
74
+ catch (e) {
74
75
  // Duplicate key probably
75
- if (e.code && e.code == "ER_DUP_ENTRY") {
76
+ if (e.code && e.code == 'ER_DUP_ENTRY') {
76
77
  throw new SimpleError({
77
- code: "email_in_use",
78
- message: "This e-mail is already in use, we cannot set it",
79
- human: "We kunnen het e-mailadres van deze gebruiker niet instellen naar "+code.email+", omdat die al in gebruik is. Waarschijnlijk heb je meerdere accounts. Probeer met dat e-mailadres in te loggen of contacteer ons ("+request.$t("59b85264-c4c3-4cf6-8923-9b43282b2787")+") als we de gebruikers moeten combineren tot één gebruiker."
80
- })
78
+ code: 'email_in_use',
79
+ message: 'This e-mail is already in use, we cannot set it',
80
+ human: 'We kunnen het e-mailadres van deze gebruiker niet instellen naar ' + code.email + ', omdat die al in gebruik is. Waarschijnlijk heb je meerdere accounts. Probeer met dat e-mailadres in te loggen of contacteer ons (' + request.$t('59b85264-c4c3-4cf6-8923-9b43282b2787') + ') als we de gebruikers moeten combineren tot één gebruiker.',
81
+ });
81
82
  }
82
83
  throw e;
83
84
  }
@@ -1,27 +1,27 @@
1
1
  import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
2
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { City } from '@stamhoofd/models';
4
4
  import { Province } from '@stamhoofd/models';
5
- import { City as CityStruct, Country, Province as ProvinceStruct,SearchRegions } from "@stamhoofd/structures";
5
+ import { City as CityStruct, Country, Province as ProvinceStruct, SearchRegions } from '@stamhoofd/structures';
6
6
  import { StringCompare } from '@stamhoofd/utility';
7
7
 
8
8
  type Params = Record<string, never>;
9
9
  class Query extends AutoEncoder {
10
10
  @field({ decoder: StringDecoder })
11
- query: string
11
+ query: string;
12
12
  }
13
13
  type Body = undefined;
14
14
  type ResponseBody = SearchRegions;
15
15
 
16
16
  export class SearchRegionsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
17
- queryDecoder = Query as Decoder<Query>
18
-
17
+ queryDecoder = Query as Decoder<Query>;
18
+
19
19
  protected doesMatch(request: Request): [true, Params] | [false] {
20
- if (request.method != "GET") {
20
+ if (request.method !== 'GET') {
21
21
  return [false];
22
22
  }
23
23
 
24
- const params = Endpoint.parseParameters(request.url, "/address/search", {});
24
+ const params = Endpoint.parseParameters(request.url, '/address/search', {});
25
25
 
26
26
  if (params) {
27
27
  return [true, params as Params];
@@ -30,8 +30,8 @@ export class SearchRegionsEndpoint extends Endpoint<Params, Query, Body, Respons
30
30
  }
31
31
 
32
32
  async handle(request: DecodedRequest<Params, Query, Body>) {
33
- // Escape query
34
- const query = request.query.query.replace(/([-+><()~*"@\s]+)/g, " ").replace(/[^\w\d]+$/, "")
33
+ // Escape query
34
+ const query = request.query.query.replace(/([-+><()~*"@\s]+)/g, ' ').replace(/[^\w\d]+$/, '');
35
35
  if (query.length == 0) {
36
36
  // Do not try searching...
37
37
  return new Response(SearchRegions.create({
@@ -42,9 +42,9 @@ export class SearchRegionsEndpoint extends Endpoint<Params, Query, Body, Respons
42
42
  }
43
43
 
44
44
  const match = {
45
- sign: "MATCH",
46
- value: query + "*", // We replace special operators in boolean mode with spaces since special characters aren't indexed anyway
47
- mode: "BOOLEAN"
45
+ sign: 'MATCH',
46
+ value: query + '*', // We replace special operators in boolean mode with spaces since special characters aren't indexed anyway
47
+ mode: 'BOOLEAN',
48
48
  };
49
49
 
50
50
  // We had to add an order by in the query to fix the limit. MySQL doesn't want to limit the results correctly if we don't explicitly sort the results on their relevance
@@ -53,11 +53,11 @@ export class SearchRegionsEndpoint extends Endpoint<Params, Query, Body, Respons
53
53
  sort: [
54
54
  {
55
55
  column: { name: match },
56
- direction: "DESC"
57
- }
58
- ]
56
+ direction: 'DESC',
57
+ },
58
+ ],
59
59
  });
60
- const loadedCities: (City & { province: Province })[] = []
60
+ const loadedCities: (City & { province: Province })[] = [];
61
61
 
62
62
  // We had to add an order by in the query to fix the limit. MySQL doesn't want to limit the results correctly if we don't explicitly sort the results on their relevance
63
63
  const allProvinces = await Province.all();
@@ -67,41 +67,41 @@ export class SearchRegionsEndpoint extends Endpoint<Params, Query, Body, Respons
67
67
  if (!province) {
68
68
  continue;
69
69
  }
70
- loadedCities.push(city.setRelation(City.province, province))
70
+ loadedCities.push(city.setRelation(City.province, province));
71
71
  }
72
72
 
73
- const provinces: Province[] = []
73
+ const provinces: Province[] = [];
74
74
  for (const province of allProvinces) {
75
75
  if (StringCompare.typoCount(request.query.query, province.name) < 3) {
76
- provinces.push(province)
76
+ provinces.push(province);
77
77
  }
78
78
  }
79
79
 
80
- const countries: Country[] = []
81
- if (StringCompare.typoCount(request.query.query, "België") < 3) {
82
- countries.push(Country.Belgium)
80
+ const countries: Country[] = [];
81
+ if (StringCompare.typoCount(request.query.query, 'België') < 3) {
82
+ countries.push(Country.Belgium);
83
83
  }
84
84
 
85
- if (StringCompare.typoCount(request.query.query, "Nederland") < 3) {
86
- countries.push(Country.Netherlands)
85
+ if (StringCompare.typoCount(request.query.query, 'Nederland') < 3) {
86
+ countries.push(Country.Netherlands);
87
87
  }
88
88
 
89
- if (StringCompare.typoCount(request.query.query, "Luxemburg") < 3) {
90
- countries.push(Country.Luxembourg)
89
+ if (StringCompare.typoCount(request.query.query, 'Luxemburg') < 3) {
90
+ countries.push(Country.Luxembourg);
91
91
  }
92
92
 
93
- if (StringCompare.typoCount(request.query.query, "Duitsland") < 3) {
94
- countries.push(Country.Germany)
93
+ if (StringCompare.typoCount(request.query.query, 'Duitsland') < 3) {
94
+ countries.push(Country.Germany);
95
95
  }
96
96
 
97
- if (StringCompare.typoCount(request.query.query, "Frankrijk") < 3) {
98
- countries.push(Country.France)
97
+ if (StringCompare.typoCount(request.query.query, 'Frankrijk') < 3) {
98
+ countries.push(Country.France);
99
99
  }
100
-
100
+
101
101
  return new Response(SearchRegions.create({
102
- cities: loadedCities.map(c => CityStruct.create(Object.assign({...c}, { province: ProvinceStruct.create(c.province) }))),
102
+ cities: loadedCities.map(c => CityStruct.create(Object.assign({ ...c }, { province: ProvinceStruct.create(c.province) }))),
103
103
  provinces: provinces.map(c => ProvinceStruct.create(c)),
104
- countries: countries
104
+ countries: countries,
105
105
  }));
106
106
  }
107
107
  }
@@ -1,23 +1,23 @@
1
1
  import { Decoder } from '@simonbackx/simple-encoding';
2
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
3
- import { Address, ValidatedAddress } from "@stamhoofd/structures";
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
+ import { Address, ValidatedAddress } from '@stamhoofd/structures';
4
4
 
5
5
  import { AddressValidator } from '../../../helpers/AddressValidator';
6
6
 
7
7
  type Params = Record<string, never>;
8
8
  type Query = undefined;
9
- type Body = Address
9
+ type Body = Address;
10
10
  type ResponseBody = ValidatedAddress;
11
11
 
12
12
  export class ValidateAddressEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
13
- bodyDecoder = Address as Decoder<Address>
14
-
13
+ bodyDecoder = Address as Decoder<Address>;
14
+
15
15
  protected doesMatch(request: Request): [true, Params] | [false] {
16
- if (request.method != "POST") {
16
+ if (request.method !== 'POST') {
17
17
  return [false];
18
18
  }
19
19
 
20
- const params = Endpoint.parseParameters(request.url, "/address/validate", {});
20
+ const params = Endpoint.parseParameters(request.url, '/address/validate', {});
21
21
 
22
22
  if (params) {
23
23
  return [true, params as Params];
@@ -1,5 +1,5 @@
1
- import { AutoEncoder, Decoder, field, StringDecoder } from "@simonbackx/simple-encoding";
2
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
1
+ import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { Organization, Webshop } from '@stamhoofd/models';
5
5
  type Params = Record<string, never>;
@@ -9,20 +9,20 @@ class Query extends AutoEncoder {
9
9
  domain: string;
10
10
  }
11
11
 
12
- type Body = undefined
12
+ type Body = undefined;
13
13
  type ResponseBody = undefined;
14
14
 
15
15
  export class CheckDomainCertEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
16
16
  queryDecoder = Query as Decoder<Query>;
17
17
 
18
- registrationDomains = [... new Set(Object.values(STAMHOOFD.domains.registration ?? {}))]
18
+ registrationDomains = [...new Set(Object.values(STAMHOOFD.domains.registration ?? {}))];
19
19
 
20
20
  protected doesMatch(request: Request): [true, Params] | [false] {
21
- if (request.method != "GET") {
21
+ if (request.method !== 'GET') {
22
22
  return [false];
23
23
  }
24
24
 
25
- const params = Endpoint.parseParameters(request.url, "/check-domain-cert", {});
25
+ const params = Endpoint.parseParameters(request.url, '/check-domain-cert', {});
26
26
 
27
27
  if (params) {
28
28
  return [true, params as Params];
@@ -33,69 +33,69 @@ export class CheckDomainCertEndpoint extends Endpoint<Params, Query, Body, Respo
33
33
  async handle(request: DecodedRequest<Params, Query, Body>) {
34
34
  // check if the domain ends on one of our localized registration domains
35
35
  for (const domain of this.registrationDomains) {
36
- if (request.query.domain.endsWith("." + domain)) {
37
- const strippped = request.query.domain.substr(0, request.query.domain.length - ("." + domain).length )
38
- if (strippped.includes(".")) {
36
+ if (request.query.domain.endsWith('.' + domain)) {
37
+ const strippped = request.query.domain.substr(0, request.query.domain.length - ('.' + domain).length);
38
+ if (strippped.includes('.')) {
39
39
  throw new SimpleError({
40
- code: "invalid_domain",
41
- message: "This domain format is not supported",
42
- statusCode: 404
43
- })
40
+ code: 'invalid_domain',
41
+ message: 'This domain format is not supported',
42
+ statusCode: 404,
43
+ });
44
44
  }
45
45
 
46
46
  // Search for the URI
47
- const organization = await Organization.getByURI(strippped)
47
+ const organization = await Organization.getByURI(strippped);
48
48
 
49
49
  if (!organization) {
50
50
  throw new SimpleError({
51
- code: "unknown_domain",
52
- message: "Not known",
53
- statusCode: 404
54
- })
51
+ code: 'unknown_domain',
52
+ message: 'Not known',
53
+ statusCode: 404,
54
+ });
55
55
  }
56
56
  return new Response(undefined);
57
57
  }
58
58
  }
59
-
60
- if (STAMHOOFD.domains.legacyWebshop && request.query.domain.endsWith("." + STAMHOOFD.domains.legacyWebshop)) {
61
- const strippped = request.query.domain.substr(0, request.query.domain.length - ("." + STAMHOOFD.domains.legacyWebshop).length )
62
- if (strippped.includes(".")) {
59
+
60
+ if (STAMHOOFD.domains.legacyWebshop && request.query.domain.endsWith('.' + STAMHOOFD.domains.legacyWebshop)) {
61
+ const strippped = request.query.domain.substr(0, request.query.domain.length - ('.' + STAMHOOFD.domains.legacyWebshop).length);
62
+ if (strippped.includes('.')) {
63
63
  throw new SimpleError({
64
- code: "invalid_domain",
65
- message: "This domain format is not supported",
66
- statusCode: 404
67
- })
64
+ code: 'invalid_domain',
65
+ message: 'This domain format is not supported',
66
+ statusCode: 404,
67
+ });
68
68
  }
69
69
 
70
70
  // Search for the URI
71
- const organization = await Organization.getByURI(strippped)
71
+ const organization = await Organization.getByURI(strippped);
72
72
 
73
73
  if (!organization) {
74
74
  throw new SimpleError({
75
- code: "unknown_domain",
76
- message: "Not known",
77
- statusCode: 404
78
- })
75
+ code: 'unknown_domain',
76
+ message: 'Not known',
77
+ statusCode: 404,
78
+ });
79
79
  }
80
80
  return new Response(undefined);
81
81
  }
82
82
 
83
83
  // Check if we have an organization with a custom domain name
84
- const organization = await Organization.getByRegisterDomain(request.query.domain)
84
+ const organization = await Organization.getByRegisterDomain(request.query.domain);
85
85
 
86
86
  if (organization) {
87
87
  return new Response(undefined);
88
88
  }
89
89
 
90
- const webshops = await Webshop.getByDomainOnly(request.query.domain)
90
+ const webshops = await Webshop.getByDomainOnly(request.query.domain);
91
91
  if (webshops.length > 0) {
92
92
  return new Response(undefined);
93
93
  }
94
94
 
95
95
  throw new SimpleError({
96
- code: "unknown_domain",
97
- message: "Not known",
98
- statusCode: 404
99
- })
96
+ code: 'unknown_domain',
97
+ message: 'Not known',
98
+ statusCode: 404,
99
+ });
100
100
  }
101
101
  }
@@ -1,44 +1,44 @@
1
1
  import { Decoder } from '@simonbackx/simple-encoding';
2
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { Email, EmailTemplate, RateLimiter } from '@stamhoofd/models';
4
- import { EmailPreview, EmailStatus, Email as EmailStruct, Version, EmailTemplate as EmailTemplateStruct } from "@stamhoofd/structures";
4
+ import { EmailPreview, EmailStatus, Email as EmailStruct, Version, EmailTemplate as EmailTemplateStruct } from '@stamhoofd/structures';
5
5
 
6
6
  import { Context } from '../../../helpers/Context';
7
7
  import { SQL } from '@stamhoofd/sql';
8
8
 
9
9
  type Params = Record<string, never>;
10
10
  type Query = undefined;
11
- type Body = EmailStruct
11
+ type Body = EmailStruct;
12
12
  type ResponseBody = EmailPreview;
13
13
 
14
14
  export const paidEmailRateLimiter = new RateLimiter({
15
15
  limits: [
16
- {
16
+ {
17
17
  // Max 5.000 emails a day
18
18
  limit: 5000,
19
- duration: 24 * 60 * 1000 * 60
19
+ duration: 24 * 60 * 1000 * 60,
20
20
  },
21
- {
21
+ {
22
22
  // 10.000 requests per week
23
23
  limit: 10000,
24
- duration: 24 * 60 * 1000 * 60 * 7
25
- }
26
- ]
24
+ duration: 24 * 60 * 1000 * 60 * 7,
25
+ },
26
+ ],
27
27
  });
28
28
 
29
29
  export const freeEmailRateLimiter = new RateLimiter({
30
30
  limits: [
31
- {
31
+ {
32
32
  // Max 100 a day
33
33
  limit: 100,
34
- duration: 24 * 60 * 1000 * 60
34
+ duration: 24 * 60 * 1000 * 60,
35
35
  },
36
- {
36
+ {
37
37
  // Max 200 a week
38
38
  limit: 200,
39
- duration: 7 * 24 * 60 * 1000 * 60
40
- }
41
- ]
39
+ duration: 7 * 24 * 60 * 1000 * 60,
40
+ },
41
+ ],
42
42
  });
43
43
 
44
44
  /**
@@ -46,14 +46,14 @@ export const freeEmailRateLimiter = new RateLimiter({
46
46
  */
47
47
 
48
48
  export class CreateEmailEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
49
- bodyDecoder = EmailStruct as Decoder<EmailStruct>
49
+ bodyDecoder = EmailStruct as Decoder<EmailStruct>;
50
50
 
51
51
  protected doesMatch(request: Request): [true, Params] | [false] {
52
- if (request.method != "POST") {
52
+ if (request.method !== 'POST') {
53
53
  return [false];
54
54
  }
55
55
 
56
- const params = Endpoint.parseParameters(request.url, "/email", {});
56
+ const params = Endpoint.parseParameters(request.url, '/email', {});
57
57
 
58
58
  if (params) {
59
59
  return [true, params as Params];
@@ -63,10 +63,10 @@ export class CreateEmailEndpoint extends Endpoint<Params, Query, Body, ResponseB
63
63
 
64
64
  async handle(request: DecodedRequest<Params, Query, Body>) {
65
65
  const organization = await Context.setOptionalOrganizationScope();
66
- const {user} = await Context.authenticate()
66
+ const { user } = await Context.authenticate();
67
67
 
68
68
  if (!Context.auth.canSendEmails()) {
69
- throw Context.auth.error()
69
+ throw Context.auth.error();
70
70
  }
71
71
 
72
72
  const model = new Email();
@@ -83,24 +83,25 @@ export class CreateEmailEndpoint extends Endpoint<Params, Query, Body, ResponseB
83
83
  model.fromAddress = request.body.fromAddress;
84
84
  model.fromName = request.body.fromName;
85
85
 
86
- model.validateAttachments()
86
+ model.validateAttachments();
87
87
 
88
88
  // Check default
89
89
  if (JSON.stringify(model.json).length < 3 && model.recipientFilter.filters[0].type && EmailTemplateStruct.getDefaultForRecipient(model.recipientFilter.filters[0].type)) {
90
- const type = EmailTemplateStruct.getDefaultForRecipient(model.recipientFilter.filters[0].type)
91
-
90
+ const type = EmailTemplateStruct.getDefaultForRecipient(model.recipientFilter.filters[0].type);
91
+
92
92
  // Most specific template: for specific group
93
- let templates = (await EmailTemplate.where({ type, organizationId: organization?.id ?? null, groupId: null }))
93
+ let templates = (await EmailTemplate.where({ type, organizationId: organization?.id ?? null, groupId: null }));
94
94
 
95
95
  // Then default
96
96
  if (templates.length == 0 && organization) {
97
- templates = (await EmailTemplate.where({ type, organizationId: null, groupId: null }))
97
+ templates = (await EmailTemplate.where({ type, organizationId: null, groupId: null }));
98
98
  }
99
99
 
100
100
  if (templates.length == 0) {
101
101
  // No default
102
- } else {
103
- const defaultTemplate = templates[0]
102
+ }
103
+ else {
104
+ const defaultTemplate = templates[0];
104
105
  model.html = defaultTemplate.html;
105
106
  model.text = defaultTemplate.text;
106
107
  model.subject = defaultTemplate.subject;
@@ -109,14 +110,13 @@ export class CreateEmailEndpoint extends Endpoint<Params, Query, Body, ResponseB
109
110
  }
110
111
 
111
112
  await model.save();
112
- await model.buildExampleRecipient()
113
+ await model.buildExampleRecipient();
113
114
  model.updateCount();
114
115
 
115
116
  if (request.body.status === EmailStatus.Sending || request.body.status === EmailStatus.Sent) {
116
- model.send().catch(console.error)
117
+ model.send().catch(console.error);
117
118
  }
118
119
 
119
-
120
120
  return new Response(await model.getPreviewStructure());
121
121
  }
122
122
  }
@@ -1,5 +1,5 @@
1
1
  import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
2
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { EmailAddress } from '@stamhoofd/email';
5
5
  import { Organization } from '@stamhoofd/models';
@@ -10,10 +10,10 @@ type Body = undefined;
10
10
 
11
11
  class Query extends AutoEncoder {
12
12
  @field({ decoder: StringDecoder })
13
- id: string
13
+ id: string;
14
14
 
15
15
  @field({ decoder: StringDecoder })
16
- token: string
16
+ token: string;
17
17
  }
18
18
 
19
19
  type ResponseBody = EmailAddressSettings;
@@ -22,11 +22,11 @@ export class ManageEmailAddressEndpoint extends Endpoint<Params, Query, Body, Re
22
22
  queryDecoder = Query as Decoder<Query>;
23
23
 
24
24
  protected doesMatch(request: Request): [true, Params] | [false] {
25
- if (request.method != "GET") {
25
+ if (request.method !== 'GET') {
26
26
  return [false];
27
27
  }
28
28
 
29
- const params = Endpoint.parseParameters(request.url, "/email/manage", {});
29
+ const params = Endpoint.parseParameters(request.url, '/email/manage', {});
30
30
 
31
31
  if (params) {
32
32
  return [true, params as Params];
@@ -35,19 +35,19 @@ export class ManageEmailAddressEndpoint extends Endpoint<Params, Query, Body, Re
35
35
  }
36
36
 
37
37
  async handle(request: DecodedRequest<Params, Query, Body>) {
38
- const email = await EmailAddress.getByID(request.query.id)
38
+ const email = await EmailAddress.getByID(request.query.id);
39
39
  if (!email || email.token !== request.query.token || request.query.token.length < 10 || request.query.id.length < 10) {
40
40
  throw new SimpleError({
41
- code: "invalid_fields",
42
- message: "Invalid token or id",
43
- human: "Deze link is vervallen. Probeer het opnieuw in een recentere e-mail"
44
- })
41
+ code: 'invalid_fields',
42
+ message: 'Invalid token or id',
43
+ human: 'Deze link is vervallen. Probeer het opnieuw in een recentere e-mail',
44
+ });
45
45
  }
46
46
 
47
- const organization = email.organizationId ? (await Organization.getByID(email.organizationId)) : undefined
47
+ const organization = email.organizationId ? (await Organization.getByID(email.organizationId)) : undefined;
48
48
  return new Response(EmailAddressSettings.create({
49
49
  ...email,
50
- organization: organization ? OrganizationSimple.create(organization) : null
50
+ organization: organization ? OrganizationSimple.create(organization) : null,
51
51
  }));
52
52
  }
53
- }
53
+ }