@stamhoofd/backend 2.39.1 → 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,26 +1,26 @@
1
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
2
- import { SimpleError } from "@simonbackx/simple-errors";
1
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
+ import { SimpleError } from '@simonbackx/simple-errors';
3
3
  import { User } from '@stamhoofd/models';
4
4
 
5
- import { Context } from "../../../../helpers/Context";
5
+ import { Context } from '../../../../helpers/Context';
6
6
  type Params = { id: string };
7
7
  type Query = undefined;
8
- type Body = undefined
9
- type ResponseBody = undefined
8
+ type Body = undefined;
9
+ type ResponseBody = undefined;
10
10
 
11
11
  export class DeleteUserEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
12
12
  protected doesMatch(request: Request): [true, Params] | [false] {
13
- if (request.method != "DELETE") {
13
+ if (request.method !== 'DELETE') {
14
14
  return [false];
15
15
  }
16
16
 
17
- const params = Endpoint.parseParameters(request.url, "/user/@id", { id: String });
17
+ const params = Endpoint.parseParameters(request.url, '/user/@id', { id: String });
18
18
 
19
19
  if (params) {
20
20
  return [true, params as Params];
21
21
  }
22
22
 
23
- const params2 = Endpoint.parseParameters(request.url, "/api-keys/@id", { id: String });
23
+ const params2 = Endpoint.parseParameters(request.url, '/api-keys/@id', { id: String });
24
24
 
25
25
  if (params2) {
26
26
  return [true, params2 as Params];
@@ -31,30 +31,30 @@ export class DeleteUserEndpoint extends Endpoint<Params, Query, Body, ResponseBo
31
31
 
32
32
  async handle(request: DecodedRequest<Params, Query, Body>) {
33
33
  const organization = await Context.setOrganizationScope();
34
- const {user} = await Context.authenticate()
34
+ const { user } = await Context.authenticate();
35
35
 
36
36
  // Fast throw first (more in depth checking for patches later)
37
37
  if (!await Context.auth.canManageAdmins(organization.id)) {
38
- throw Context.auth.error()
38
+ throw Context.auth.error();
39
39
  }
40
40
 
41
41
  if (user.id == request.params.id) {
42
42
  throw new SimpleError({
43
- code: "permission_denied",
44
- message: "Je kan jezelf niet verwijderen"
45
- })
43
+ code: 'permission_denied',
44
+ message: 'Je kan jezelf niet verwijderen',
45
+ });
46
46
  }
47
47
 
48
- const editUser = await User.getByID(request.params.id)
48
+ const editUser = await User.getByID(request.params.id);
49
49
  if (!editUser || !Context.auth.checkScope(editUser.organizationId)) {
50
50
  throw new SimpleError({
51
- code: "permission_denied",
52
- message: "Je hebt geen toegang om deze gebruiker te verwijderen"
53
- })
51
+ code: 'permission_denied',
52
+ message: 'Je hebt geen toegang om deze gebruiker te verwijderen',
53
+ });
54
54
  }
55
-
55
+
56
56
  await editUser.delete();
57
57
 
58
- return new Response(undefined);
58
+ return new Response(undefined);
59
59
  }
60
60
  }
@@ -1,21 +1,21 @@
1
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
2
- import { SimpleError } from "@simonbackx/simple-errors";
1
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
+ import { SimpleError } from '@simonbackx/simple-errors';
3
3
  import { Token, User } from '@stamhoofd/models';
4
- import { ApiUser } from "@stamhoofd/structures";
4
+ import { ApiUser } from '@stamhoofd/structures';
5
5
 
6
- import { Context } from "../../../../helpers/Context";
6
+ import { Context } from '../../../../helpers/Context';
7
7
  type Params = Record<string, never>;
8
8
  type Query = undefined;
9
- type Body = undefined
10
- type ResponseBody = ApiUser[]
9
+ type Body = undefined;
10
+ type ResponseBody = ApiUser[];
11
11
 
12
12
  export class GetOrganizationAdminsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
13
13
  protected doesMatch(request: Request): [true, Params] | [false] {
14
- if (request.method != "GET") {
14
+ if (request.method !== 'GET') {
15
15
  return [false];
16
16
  }
17
17
 
18
- const params = Endpoint.parseParameters(request.url, "/api-keys", {});
18
+ const params = Endpoint.parseParameters(request.url, '/api-keys', {});
19
19
 
20
20
  if (params) {
21
21
  return [true, params as Params];
@@ -25,23 +25,23 @@ export class GetOrganizationAdminsEndpoint extends Endpoint<Params, Query, Body,
25
25
 
26
26
  async handle(_: DecodedRequest<Params, Query, Body>) {
27
27
  const organization = await Context.setOrganizationScope();
28
- await Context.authenticate()
28
+ await Context.authenticate();
29
29
 
30
30
  // Fast throw first (more in depth checking for patches later)
31
31
  if (!await Context.auth.canManageAdmins(organization.id)) {
32
- throw Context.auth.error()
32
+ throw Context.auth.error();
33
33
  }
34
34
 
35
35
  // Get all admins
36
- const admins = await User.getApiUsers([organization.id])
36
+ const admins = await User.getApiUsers([organization.id]);
37
37
 
38
- const mapped: ApiUser[] = []
38
+ const mapped: ApiUser[] = [];
39
39
  for (const admin of admins) {
40
- mapped.push(await Token.getAPIUserWithToken(admin))
40
+ mapped.push(await Token.getAPIUserWithToken(admin));
41
41
  }
42
42
 
43
43
  return new Response(
44
- mapped
44
+ mapped,
45
45
  );
46
46
  }
47
47
  }
@@ -1,21 +1,21 @@
1
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
1
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
2
  import { User } from '@stamhoofd/models';
3
- import { OrganizationAdmins, User as UserStruct } from "@stamhoofd/structures";
3
+ import { OrganizationAdmins, User as UserStruct } from '@stamhoofd/structures';
4
4
 
5
- import { Context } from "../../../../helpers/Context";
6
- import { AuthenticatedStructures } from "../../../../helpers/AuthenticatedStructures";
5
+ import { Context } from '../../../../helpers/Context';
6
+ import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
7
7
  type Params = Record<string, never>;
8
8
  type Query = undefined;
9
- type Body = undefined
10
- type ResponseBody = OrganizationAdmins
9
+ type Body = undefined;
10
+ type ResponseBody = OrganizationAdmins;
11
11
 
12
12
  export class GetOrganizationAdminsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
13
13
  protected doesMatch(request: Request): [true, Params] | [false] {
14
- if (request.method != "GET") {
14
+ if (request.method !== 'GET') {
15
15
  return [false];
16
16
  }
17
17
 
18
- const params = Endpoint.parseParameters(request.url, "/organization/admins", {});
18
+ const params = Endpoint.parseParameters(request.url, '/organization/admins', {});
19
19
 
20
20
  if (params) {
21
21
  return [true, params as Params];
@@ -25,18 +25,18 @@ export class GetOrganizationAdminsEndpoint extends Endpoint<Params, Query, Body,
25
25
 
26
26
  async handle(_: DecodedRequest<Params, Query, Body>) {
27
27
  const organization = await Context.setOrganizationScope();
28
- await Context.authenticate()
28
+ await Context.authenticate();
29
29
 
30
30
  // Fast throw first (more in depth checking for patches later)
31
31
  if (!await Context.auth.canManageAdmins(organization.id)) {
32
- throw Context.auth.error()
32
+ throw Context.auth.error();
33
33
  }
34
34
 
35
35
  // Get all admins
36
- const admins = await User.getAdmins([organization.id])
36
+ const admins = await User.getAdmins([organization.id]);
37
37
 
38
38
  return new Response(OrganizationAdmins.create({
39
- users: await AuthenticatedStructures.usersWithMembers(admins)
39
+ users: await AuthenticatedStructures.usersWithMembers(admins),
40
40
  }));
41
41
  }
42
42
  }
@@ -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, SimpleErrors } from '@simonbackx/simple-errors';
4
4
  import { Webshop } from '@stamhoofd/models';
5
- import { PermissionLevel, PermissionsResourceType, PrivateWebshop, ResourcePermissions, Version, WebshopPrivateMetaData } from "@stamhoofd/structures";
5
+ import { PermissionLevel, PermissionsResourceType, PrivateWebshop, ResourcePermissions, Version, WebshopPrivateMetaData } from '@stamhoofd/structures';
6
6
  import { Formatter } from '@stamhoofd/utility';
7
7
 
8
8
  import { Context } from '../../../../helpers/Context';
@@ -17,14 +17,14 @@ type ResponseBody = PrivateWebshop;
17
17
  */
18
18
 
19
19
  export class CreateWebshopEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
20
- bodyDecoder = PrivateWebshop as Decoder<PrivateWebshop>
20
+ bodyDecoder = PrivateWebshop as Decoder<PrivateWebshop>;
21
21
 
22
22
  protected doesMatch(request: Request): [true, Params] | [false] {
23
- if (request.method != "POST") {
23
+ if (request.method !== 'POST') {
24
24
  return [false];
25
25
  }
26
26
 
27
- const params = Endpoint.parseParameters(request.url, "/webshop", {});
27
+ const params = Endpoint.parseParameters(request.url, '/webshop', {});
28
28
 
29
29
  if (params) {
30
30
  return [true, params as Params];
@@ -34,183 +34,186 @@ export class CreateWebshopEndpoint extends Endpoint<Params, Query, Body, Respons
34
34
 
35
35
  async handle(request: DecodedRequest<Params, Query, Body>) {
36
36
  const organization = await Context.setOrganizationScope();
37
- const {user} = await Context.authenticate()
37
+ const { user } = await Context.authenticate();
38
38
 
39
39
  // Fast throw first (more in depth checking for patches later)
40
40
  if (!await Context.auth.canCreateWebshops(organization.id)) {
41
- throw Context.auth.error("Je kan geen webshops maken, vraag aan de hoofdbeheerders om jou toegang te geven.")
41
+ throw Context.auth.error('Je kan geen webshops maken, vraag aan de hoofdbeheerders om jou toegang te geven.');
42
42
  }
43
43
 
44
- const errors = new SimpleErrors()
44
+ const errors = new SimpleErrors();
45
45
 
46
- const webshop = new Webshop()
46
+ const webshop = new Webshop();
47
47
 
48
- webshop.id = request.body.id
49
- webshop.meta = request.body.meta
48
+ webshop.id = request.body.id;
49
+ webshop.meta = request.body.meta;
50
50
  webshop.meta.domainActive = false;
51
- webshop.privateMeta = request.body.privateMeta
52
- webshop.products = request.body.products
53
- webshop.categories = request.body.categories
54
- webshop.organizationId = organization.id
51
+ webshop.privateMeta = request.body.privateMeta;
52
+ webshop.products = request.body.products;
53
+ webshop.categories = request.body.categories;
54
+ webshop.organizationId = organization.id;
55
55
  webshop.privateMeta.authorId = user.id;
56
56
  webshop.privateMeta.dnsRecords = [];
57
- let updateDNS = false
57
+ let updateDNS = false;
58
58
 
59
59
  // Check if we can decide the domain
60
60
  if (!request.body.domain && !request.body.domainUri) {
61
- const webshops = await Webshop.where({
62
- organizationId: organization.id,
63
- domain: {
61
+ const webshops = await Webshop.where({
62
+ organizationId: organization.id,
63
+ domain: {
64
64
  value: null,
65
- sign: "!="
65
+ sign: '!=',
66
66
  },
67
67
  domainUri: {
68
- value: "",
69
- sign: "!="
70
- }
71
- })
68
+ value: '',
69
+ sign: '!=',
70
+ },
71
+ });
72
72
 
73
- const counters = new Map<string, number>()
73
+ const counters = new Map<string, number>();
74
74
  for (const webshop of webshops) {
75
75
  if (!webshop.domain || !webshop.meta.domainActive) {
76
- continue
76
+ continue;
77
77
  }
78
- const count = (counters.get(webshop.domain) ?? 0) + 1
79
- counters.set(webshop.domain, count)
78
+ const count = (counters.get(webshop.domain) ?? 0) + 1;
79
+ counters.set(webshop.domain, count);
80
80
  }
81
81
 
82
82
  if (counters.size > 0) {
83
- const maxDomain = [...counters.entries()].reduce((a, e ) => e[1] > a[1] ? e : a)[0]
84
- console.log("Choosing default domain for new webshop: ", maxDomain)
85
-
86
- webshop.domain = maxDomain
87
- webshop.domainUri = Formatter.slug(webshop.meta.name)
88
- webshop.privateMeta.dnsRecords = WebshopPrivateMetaData.buildDNSRecords(maxDomain)
89
- await this.checkDomainUri(webshop)
90
- updateDNS = true
83
+ const maxDomain = [...counters.entries()].reduce((a, e) => e[1] > a[1] ? e : a)[0];
84
+ console.log('Choosing default domain for new webshop: ', maxDomain);
85
+
86
+ webshop.domain = maxDomain;
87
+ webshop.domainUri = Formatter.slug(webshop.meta.name);
88
+ webshop.privateMeta.dnsRecords = WebshopPrivateMetaData.buildDNSRecords(maxDomain);
89
+ await this.checkDomainUri(webshop);
90
+ updateDNS = true;
91
91
  }
92
-
93
- } else {
92
+ }
93
+ else {
94
94
  if (request.body.domain) {
95
- webshop.domain = request.body.domain
95
+ webshop.domain = request.body.domain;
96
96
 
97
97
  if (request.body.domainUri) {
98
- webshop.domainUri = Formatter.slug(request.body.domainUri)
98
+ webshop.domainUri = Formatter.slug(request.body.domainUri);
99
99
  }
100
100
 
101
- webshop.privateMeta.dnsRecords = WebshopPrivateMetaData.buildDNSRecords(request.body.domain)
102
- await this.checkDomainUri(webshop)
103
- updateDNS = true
101
+ webshop.privateMeta.dnsRecords = WebshopPrivateMetaData.buildDNSRecords(request.body.domain);
102
+ await this.checkDomainUri(webshop);
103
+ updateDNS = true;
104
104
  }
105
105
  }
106
106
 
107
- webshop.uri = request.body.uri.length > 0 ? Formatter.slug(request.body.uri) : Formatter.slug(webshop.meta.name)
107
+ webshop.uri = request.body.uri.length > 0 ? Formatter.slug(request.body.uri) : Formatter.slug(webshop.meta.name);
108
108
 
109
109
  // Check if this uri is inique
110
- let original = webshop.uri
111
- const possibleSuffixes = [new Date().getFullYear().toString(), Formatter.slug(organization.uri)]
110
+ let original = webshop.uri;
111
+ const possibleSuffixes = [new Date().getFullYear().toString(), Formatter.slug(organization.uri)];
112
112
 
113
113
  // Remove possible suffices from original
114
114
  for (const suffix of possibleSuffixes) {
115
- if (original.endsWith("-" + suffix)) {
116
- original = original.slice(0, -suffix.length - 1)
115
+ if (original.endsWith('-' + suffix)) {
116
+ original = original.slice(0, -suffix.length - 1);
117
117
  }
118
118
  }
119
119
 
120
- let tried = 0
120
+ let tried = 0;
121
121
  while (webshop.uri.length > 100 || await Webshop.getByURI(webshop.uri) !== undefined) {
122
+ console.log('Webshop already exists', webshop.uri);
122
123
 
123
- console.log("Webshop already exists", webshop.uri)
124
-
125
- let suffix = ""
124
+ let suffix = '';
126
125
  if (tried < possibleSuffixes.length) {
127
- suffix = "-" + possibleSuffixes[tried]
128
- } else if (tried > 9) {
129
- suffix = "-" + Math.floor(Math.random() * 100000)
130
- } else {
131
- suffix = "-" + (tried - possibleSuffixes.length + 2)
126
+ suffix = '-' + possibleSuffixes[tried];
127
+ }
128
+ else if (tried > 9) {
129
+ suffix = '-' + Math.floor(Math.random() * 100000);
130
+ }
131
+ else {
132
+ suffix = '-' + (tried - possibleSuffixes.length + 2);
132
133
  }
133
-
134
+
134
135
  webshop.uri = original.slice(0, 100 - suffix.length) + suffix;
135
-
136
- tried++
136
+
137
+ tried++;
137
138
  if (tried > 15) {
138
- console.log("Failed to generate unique webshop uri")
139
+ console.log('Failed to generate unique webshop uri');
139
140
 
140
141
  throw new SimpleError({
141
- code: "failed_to_generate_unique_uri",
142
- message: "Failed to generate unique uri",
143
- human: "Er is een fout opgetreden bij het maken van de webshop, kies een andere naam voor jouw webshop",
144
- statusCode: 500
145
- })
142
+ code: 'failed_to_generate_unique_uri',
143
+ message: 'Failed to generate unique uri',
144
+ human: 'Er is een fout opgetreden bij het maken van de webshop, kies een andere naam voor jouw webshop',
145
+ statusCode: 500,
146
+ });
146
147
  }
147
148
  }
148
149
 
149
150
  if (!await Context.auth.canAccessWebshop(webshop, PermissionLevel.Full)) {
150
151
  // Create a temporary permission role for this user
151
- const organizationPermissions = user.permissions?.organizationPermissions?.get(organization.id)
152
+ const organizationPermissions = user.permissions?.organizationPermissions?.get(organization.id);
152
153
  if (!organizationPermissions) {
153
- throw new Error('Unexpected missing permissions')
154
+ throw new Error('Unexpected missing permissions');
154
155
  }
155
156
  const resourcePermissions = ResourcePermissions.create({
156
157
  resourceName: webshop.meta.name,
157
- level: PermissionLevel.Full
158
- })
159
- const patch = resourcePermissions.createInsertPatch(PermissionsResourceType.Webshops, webshop.id, organizationPermissions)
160
- user.permissions!.organizationPermissions.set(organization.id, organizationPermissions.patch(patch))
161
- console.log('Automatically granted author full permissions to resource', 'webshop', webshop.id, 'user', user.id, 'patch', patch.encode({version: Version}))
162
- await user.save()
158
+ level: PermissionLevel.Full,
159
+ });
160
+ const patch = resourcePermissions.createInsertPatch(PermissionsResourceType.Webshops, webshop.id, organizationPermissions);
161
+ user.permissions!.organizationPermissions.set(organization.id, organizationPermissions.patch(patch));
162
+ console.log('Automatically granted author full permissions to resource', 'webshop', webshop.id, 'user', user.id, 'patch', patch.encode({ version: Version }));
163
+ await user.save();
163
164
  }
164
165
 
165
166
  // Verify if we have full access
166
167
  if (!await Context.auth.canAccessWebshop(webshop, PermissionLevel.Full)) {
167
168
  throw new SimpleError({
168
- code: "missing_permissions",
169
- message: "You cannot create a webshop without having full permissions on the created webshop",
170
- human: "Als je een webshop aanmaakt moet je ervoor zorgen dat jezelf ook volledige toegang hebt."
171
- })
169
+ code: 'missing_permissions',
170
+ message: 'You cannot create a webshop without having full permissions on the created webshop',
171
+ human: 'Als je een webshop aanmaakt moet je ervoor zorgen dat jezelf ook volledige toegang hebt.',
172
+ });
172
173
  }
173
174
 
174
- await webshop.save()
175
+ await webshop.save();
175
176
 
176
177
  if (updateDNS) {
177
- await webshop.updateDNSRecords()
178
+ await webshop.updateDNSRecords();
178
179
  }
179
-
180
- errors.throwIfNotEmpty()
180
+
181
+ errors.throwIfNotEmpty();
181
182
  return new Response(PrivateWebshop.create(webshop));
182
183
  }
183
184
 
184
185
  async checkDomainUri(webshop: Webshop) {
185
186
  if (!webshop.domain) {
186
- return
187
+ return;
187
188
  }
188
189
  // Check if this uri is inique
189
- const original = webshop.domainUri ? webshop.domainUri + '-' : ''
190
- const possibleSuffixes = [new Date().getFullYear().toString()]
191
- let tried = 0
190
+ const original = webshop.domainUri ? webshop.domainUri + '-' : '';
191
+ const possibleSuffixes = [new Date().getFullYear().toString()];
192
+ let tried = 0;
192
193
  while (await Webshop.getByDomain(webshop.domain, webshop.domainUri) !== undefined) {
193
- console.log("Webshop already exists", webshop.domainUri)
194
+ console.log('Webshop already exists', webshop.domainUri);
194
195
 
195
196
  if (tried < possibleSuffixes.length) {
196
- webshop.domainUri = original + possibleSuffixes[tried]
197
- } else if (tried > 9) {
198
- webshop.domainUri = original + Math.floor(Math.random() * 100000)
199
- } else {
200
- webshop.domainUri = original + (tried - possibleSuffixes.length + 2)
197
+ webshop.domainUri = original + possibleSuffixes[tried];
201
198
  }
202
-
203
- tried++
199
+ else if (tried > 9) {
200
+ webshop.domainUri = original + Math.floor(Math.random() * 100000);
201
+ }
202
+ else {
203
+ webshop.domainUri = original + (tried - possibleSuffixes.length + 2);
204
+ }
205
+
206
+ tried++;
204
207
 
205
208
  if (tried > 15) {
206
- console.log("Failed to generate unique webshop domainUri")
209
+ console.log('Failed to generate unique webshop domainUri');
207
210
 
208
211
  throw new SimpleError({
209
- code: "failed_to_generate_unique_domainUri",
210
- message: "Failed to generate unique domainUri",
211
- human: "Er is een fout opgetreden bij het maken van de webshop, kies een andere naam voor jouw webshop",
212
- statusCode: 500
213
- })
212
+ code: 'failed_to_generate_unique_domainUri',
213
+ message: 'Failed to generate unique domainUri',
214
+ human: 'Er is een fout opgetreden bij het maken van de webshop, kies een andere naam voor jouw webshop',
215
+ statusCode: 500,
216
+ });
214
217
  }
215
218
  }
216
219
  }
@@ -1,9 +1,9 @@
1
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
1
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
2
  import { SimpleError } from '@simonbackx/simple-errors';
3
3
  import { BalanceItem, Order, Webshop } from '@stamhoofd/models';
4
- import { PermissionLevel } from "@stamhoofd/structures";
4
+ import { PermissionLevel } from '@stamhoofd/structures';
5
5
 
6
- import { Context } from "../../../../helpers/Context";
6
+ import { Context } from '../../../../helpers/Context';
7
7
 
8
8
  type Params = { id: string };
9
9
  type Query = undefined;
@@ -15,13 +15,12 @@ type ResponseBody = undefined;
15
15
  */
16
16
 
17
17
  export class DeleteWebshopEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
18
-
19
18
  protected doesMatch(request: Request): [true, Params] | [false] {
20
- if (request.method != "DELETE") {
19
+ if (request.method !== 'DELETE') {
21
20
  return [false];
22
21
  }
23
22
 
24
- const params = Endpoint.parseParameters(request.url, "/webshop/@id", { id: String });
23
+ const params = Endpoint.parseParameters(request.url, '/webshop/@id', { id: String });
25
24
 
26
25
  if (params) {
27
26
  return [true, params as Params];
@@ -31,21 +30,21 @@ export class DeleteWebshopEndpoint extends Endpoint<Params, Query, Body, Respons
31
30
 
32
31
  async handle(request: DecodedRequest<Params, Query, Body>) {
33
32
  const organization = await Context.setOrganizationScope();
34
- await Context.authenticate()
33
+ await Context.authenticate();
35
34
 
36
35
  // Fast throw first (more in depth checking for patches later)
37
36
  if (!await Context.auth.hasSomeAccess(organization.id)) {
38
- throw Context.auth.error()
37
+ throw Context.auth.error();
39
38
  }
40
39
 
41
- const webshop = await Webshop.getByID(request.params.id)
40
+ const webshop = await Webshop.getByID(request.params.id);
42
41
  if (!webshop || !await Context.auth.canAccessWebshop(webshop, PermissionLevel.Full)) {
43
- throw Context.auth.notFoundOrNoAccess()
42
+ throw Context.auth.notFoundOrNoAccess();
44
43
  }
45
44
 
46
45
  const orders = await Order.where({ webshopId: webshop.id });
47
- await BalanceItem.deleteForDeletedOrders(orders.map(o => o.id))
48
- await webshop.delete()
46
+ await BalanceItem.deleteForDeletedOrders(orders.map(o => o.id));
47
+ await webshop.delete();
49
48
  return new Response(undefined);
50
49
  }
51
50
  }
@@ -1,22 +1,22 @@
1
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
1
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
2
  import { SimpleError } from '@simonbackx/simple-errors';
3
3
  import { Webshop, WebshopDiscountCode } from '@stamhoofd/models';
4
- import { DiscountCode, PermissionLevel } from "@stamhoofd/structures";
4
+ import { DiscountCode, PermissionLevel } from '@stamhoofd/structures';
5
5
 
6
- import { Context } from "../../../../helpers/Context";
6
+ import { Context } from '../../../../helpers/Context';
7
7
 
8
8
  type Params = { id: string };
9
- type Query = undefined
10
- type Body = undefined
11
- type ResponseBody = DiscountCode[]
9
+ type Query = undefined;
10
+ type Body = undefined;
11
+ type ResponseBody = DiscountCode[];
12
12
 
13
13
  export class GetWebshopDiscountCodesEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
14
14
  protected doesMatch(request: Request): [true, Params] | [false] {
15
- if (request.method != "GET") {
15
+ if (request.method !== 'GET') {
16
16
  return [false];
17
17
  }
18
18
 
19
- const params = Endpoint.parseParameters(request.url, "/webshop/@id/discount-codes", { id: String });
19
+ const params = Endpoint.parseParameters(request.url, '/webshop/@id/discount-codes', { id: String });
20
20
 
21
21
  if (params) {
22
22
  return [true, params as Params];
@@ -26,22 +26,22 @@ export class GetWebshopDiscountCodesEndpoint extends Endpoint<Params, Query, Bod
26
26
 
27
27
  async handle(request: DecodedRequest<Params, Query, Body>) {
28
28
  const organization = await Context.setOrganizationScope();
29
- await Context.authenticate()
29
+ await Context.authenticate();
30
30
 
31
31
  // Fast throw first (more in depth checking for patches later)
32
32
  if (!await Context.auth.hasSomeAccess(organization.id)) {
33
- throw Context.auth.error()
33
+ throw Context.auth.error();
34
34
  }
35
35
 
36
- const webshop = await Webshop.getByID(request.params.id)
36
+ const webshop = await Webshop.getByID(request.params.id);
37
37
  if (!webshop || !await Context.auth.canAccessWebshop(webshop, PermissionLevel.Full)) {
38
- throw Context.auth.notFoundOrNoAccess()
38
+ throw Context.auth.notFoundOrNoAccess();
39
39
  }
40
-
41
- const discountCodes = await WebshopDiscountCode.where({webshopId: request.params.id})
40
+
41
+ const discountCodes = await WebshopDiscountCode.where({ webshopId: request.params.id });
42
42
 
43
43
  return new Response(
44
- discountCodes.map(d => d.getStructure())
44
+ discountCodes.map(d => d.getStructure()),
45
45
  );
46
46
  }
47
47
  }