@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,28 +1,28 @@
1
- import { Request } from "@simonbackx/simple-endpoints";
1
+ import { Request } from '@simonbackx/simple-endpoints';
2
2
  import { OrganizationFactory } from '@stamhoofd/models';
3
3
  import { OrganizationSimple } from '@stamhoofd/structures';
4
- import { v4 as uuidv4 } from "uuid";
4
+ import { v4 as uuidv4 } from 'uuid';
5
5
 
6
6
  import { testServer } from '../../../../tests/helpers/TestServer';
7
- import { SearchOrganizationEndpoint } from "./SearchOrganizationEndpoint";
7
+ import { SearchOrganizationEndpoint } from './SearchOrganizationEndpoint';
8
8
 
9
- describe("Endpoint.SearchOrganization", () => {
9
+ describe('Endpoint.SearchOrganization', () => {
10
10
  // Test endpoint
11
11
  const endpoint = new SearchOrganizationEndpoint();
12
12
 
13
- test("Search for a given organization using exact search", async () => {
13
+ test('Search for a given organization using exact search', async () => {
14
14
  const organization = await new OrganizationFactory({
15
- name: (uuidv4()).replace(/-/g, "")
16
- }).create()
15
+ name: (uuidv4()).replace(/-/g, ''),
16
+ }).create();
17
17
 
18
- const r = Request.buildJson("GET", "/v19/organizations/search");
18
+ const r = Request.buildJson('GET', '/v19/organizations/search');
19
19
  r.query = {
20
- query: organization.name
20
+ query: organization.name,
21
21
  };
22
22
 
23
23
  const response = await testServer.test(endpoint, r);
24
24
  expect(response.body).toBeDefined();
25
- expect(response.body).toHaveLength(1)
25
+ expect(response.body).toHaveLength(1);
26
26
 
27
27
  // Access token should be expired
28
28
  expect(response.body[0]).toBeInstanceOf(OrganizationSimple);
@@ -30,29 +30,29 @@ describe("Endpoint.SearchOrganization", () => {
30
30
  expect(response.body[0]).toMatchObject({
31
31
  id: organization.id,
32
32
  name: organization.name,
33
- address: organization.address
34
- })
33
+ address: organization.address,
34
+ });
35
35
  });
36
36
 
37
- test("Search for a given organization on city name using exact search", async () => {
38
- const city = uuidv4()
37
+ test('Search for a given organization on city name using exact search', async () => {
38
+ const city = uuidv4();
39
39
  const organizations = await new OrganizationFactory({
40
- city
41
- }).createMultiple(2)
40
+ city,
41
+ }).createMultiple(2);
42
42
 
43
- const r = Request.buildJson("GET", "/v1/organizations/search");
43
+ const r = Request.buildJson('GET', '/v1/organizations/search');
44
44
  r.query = {
45
- query: city
45
+ query: city,
46
46
  };
47
47
 
48
48
  const response = await testServer.test(endpoint, r);
49
49
  expect(response.body).toBeDefined();
50
- expect(response.body).toHaveLength(2)
50
+ expect(response.body).toHaveLength(2);
51
51
 
52
52
  // Access token should be expired
53
53
  expect(response.body[0]).toBeInstanceOf(OrganizationSimple);
54
54
  expect(response.body[1]).toBeInstanceOf(OrganizationSimple);
55
55
  expect(response.status).toEqual(200);
56
- expect(response.body.map(o => o.id).sort()).toEqual(organizations.map(o => o.id).sort())
56
+ expect(response.body.map(o => o.id).sort()).toEqual(organizations.map(o => o.id).sort());
57
57
  });
58
58
  });
@@ -1,28 +1,28 @@
1
1
  import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
2
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
3
- import { Organization } from "@stamhoofd/models";
4
- import { Organization as OrganizationStruct,OrganizationSimple } from "@stamhoofd/structures";
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
+ import { Organization } from '@stamhoofd/models';
4
+ import { Organization as OrganizationStruct, OrganizationSimple } from '@stamhoofd/structures';
5
5
  import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
6
6
 
7
7
  type Params = Record<string, never>;
8
8
 
9
9
  class Query extends AutoEncoder {
10
10
  @field({ decoder: StringDecoder })
11
- query: string
11
+ query: string;
12
12
  }
13
13
 
14
14
  type Body = undefined;
15
- type ResponseBody = (OrganizationSimple | OrganizationStruct)[]
15
+ type ResponseBody = (OrganizationSimple | OrganizationStruct)[];
16
16
 
17
17
  export class SearchOrganizationEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
18
- protected queryDecoder = Query as Decoder<Query>
18
+ protected queryDecoder = Query as Decoder<Query>;
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, "/organizations/search", {});
25
+ const params = Endpoint.parseParameters(request.url, '/organizations/search', {});
26
26
 
27
27
  if (params) {
28
28
  return [true, params as Params];
@@ -32,16 +32,16 @@ export class SearchOrganizationEndpoint extends Endpoint<Params, Query, Body, Re
32
32
 
33
33
  async handle(request: DecodedRequest<Params, Query, Body>) {
34
34
  // Escape query
35
- const query = request.query.query.replace(/([-+><()~*"@\s]+)/g, " ").replace(/[^\w\d]+$/, "")
35
+ const query = request.query.query.replace(/([-+><()~*"@\s]+)/g, ' ').replace(/[^\w\d]+$/, '');
36
36
  if (query.length == 0) {
37
37
  // Do not try searching...
38
- return new Response([])
38
+ return new Response([]);
39
39
  }
40
40
 
41
41
  const match = {
42
- sign: "MATCH",
43
- value: query + "*", // We replace special operators in boolean mode with spaces since special characters aren't indexed anyway
44
- mode: "BOOLEAN"
42
+ sign: 'MATCH',
43
+ value: query + '*', // We replace special operators in boolean mode with spaces since special characters aren't indexed anyway
44
+ mode: 'BOOLEAN',
45
45
  };
46
46
 
47
47
  // 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
@@ -50,11 +50,11 @@ export class SearchOrganizationEndpoint extends Endpoint<Params, Query, Body, Re
50
50
  sort: [
51
51
  {
52
52
  column: { searchIndex: match },
53
- direction: "DESC"
54
- }
55
- ]
53
+ direction: 'DESC',
54
+ },
55
+ ],
56
56
  });
57
-
57
+
58
58
  if (request.request.getVersion() < 169) {
59
59
  return new Response(organizations.map(o => OrganizationSimple.create(o)));
60
60
  }
@@ -1,4 +1,3 @@
1
-
2
1
  import { AnyDecoder, AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
3
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
4
3
  import { SimpleError } from '@simonbackx/simple-errors';
@@ -13,33 +12,33 @@ class Body extends AutoEncoder {
13
12
  * The account id (internal id, not the stripe id)
14
13
  */
15
14
  @field({ decoder: StringDecoder })
16
- type: string
15
+ type: string;
17
16
 
18
17
  @field({ decoder: StringDecoder })
19
- id: string
18
+ id: string;
20
19
 
21
20
  /**
22
21
  * Set for connect events
23
22
  */
24
23
  @field({ decoder: StringDecoder, nullable: true, optional: true })
25
- account: string|null = null
24
+ account: string | null = null;
26
25
 
27
26
  @field({ decoder: AnyDecoder })
28
- data: any
27
+ data: any;
29
28
  }
30
29
 
31
- type Query = undefined
32
- type ResponseBody = undefined
30
+ type Query = undefined;
31
+ type ResponseBody = undefined;
33
32
 
34
33
  export class StripeWebookEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
35
- bodyDecoder = Body as Decoder<Body>
34
+ bodyDecoder = Body as Decoder<Body>;
36
35
 
37
36
  protected doesMatch(request: Request): [true, Params] | [false] {
38
- if (request.method !== "POST") {
37
+ if (request.method !== 'POST') {
39
38
  return [false];
40
39
  }
41
40
 
42
- const params = Endpoint.parseParameters(request.url, "/stripe/webhooks", {});
41
+ const params = Endpoint.parseParameters(request.url, '/stripe/webhooks', {});
43
42
 
44
43
  if (params) {
45
44
  return [true, params as Params];
@@ -49,104 +48,109 @@ export class StripeWebookEndpoint extends Endpoint<Params, Query, Body, Response
49
48
  }
50
49
 
51
50
  async handle(request: DecodedRequest<Params, Query, Body>) {
52
- console.log("Received Stripe Webhook", request.body.type);
51
+ console.log('Received Stripe Webhook', request.body.type);
53
52
 
54
53
  // Verify webhook signature and extract the event.
55
54
  // See https://stripe.com/docs/webhooks/signatures for more information.
56
55
  let event;
57
56
  try {
58
- const stripe = StripeHelper.getInstance()
57
+ const stripe = StripeHelper.getInstance();
59
58
  const sig = request.headers['stripe-signature'];
60
59
  if (!sig) {
61
60
  throw new SimpleError({
62
- code: "invalid_signature",
63
- message: "Invalid signature",
64
- statusCode: 400
65
- })
61
+ code: 'invalid_signature',
62
+ message: 'Invalid signature',
63
+ statusCode: 400,
64
+ });
66
65
  }
67
- const secret = request.body.account ? STAMHOOFD.STRIPE_CONNECT_ENDPOINT_SECRET : STAMHOOFD.STRIPE_ENDPOINT_SECRET
66
+ const secret = request.body.account ? STAMHOOFD.STRIPE_CONNECT_ENDPOINT_SECRET : STAMHOOFD.STRIPE_ENDPOINT_SECRET;
68
67
  event = await stripe.webhooks.constructEventAsync(await request.request.bodyPromise!, sig, secret);
69
- } catch (err) {
68
+ }
69
+ catch (err) {
70
70
  throw new SimpleError({
71
- code: "invalid_signature",
72
- message: "Invalid signature",
73
- statusCode: 400
74
- })
71
+ code: 'invalid_signature',
72
+ message: 'Invalid signature',
73
+ statusCode: 400,
74
+ });
75
75
  }
76
76
 
77
77
  // Check type
78
78
  switch (request.body.type) {
79
- case "account.updated": {
79
+ case 'account.updated': {
80
80
  console.log(event);
81
81
  const account = request.body.data.object;
82
82
  if (account && account.id) {
83
83
  const id = account.id as string;
84
- const [model] = await StripeAccount.where({accountId: id}, {limit: 1});
84
+ const [model] = await StripeAccount.where({ accountId: id }, { limit: 1 });
85
85
  if (model) {
86
- model.setMetaFromStripeAccount(account)
87
- await model.save()
88
- } else {
89
- console.warn("Could not find stripe account with id", id)
86
+ model.setMetaFromStripeAccount(account);
87
+ await model.save();
88
+ }
89
+ else {
90
+ console.warn('Could not find stripe account with id', id);
90
91
  }
91
92
  }
92
93
  break;
93
94
  }
94
- case "payment_intent.amount_capturable_updated":
95
- case "payment_intent.canceled":
96
- case "payment_intent.created":
97
- case "payment_intent.partially_funded":
98
- case "payment_intent.payment_failed":
99
- case "payment_intent.processing":
100
- case "payment_intent.requires_action":
101
- case "payment_intent.succeeded": {
95
+ case 'payment_intent.amount_capturable_updated':
96
+ case 'payment_intent.canceled':
97
+ case 'payment_intent.created':
98
+ case 'payment_intent.partially_funded':
99
+ case 'payment_intent.payment_failed':
100
+ case 'payment_intent.processing':
101
+ case 'payment_intent.requires_action':
102
+ case 'payment_intent.succeeded': {
102
103
  const intentId = request.body.data.object.id;
103
-
104
- if (intentId && typeof intentId === "string") {
105
- await this.updateIntent(intentId)
104
+
105
+ if (intentId && typeof intentId === 'string') {
106
+ await this.updateIntent(intentId);
106
107
  }
107
108
  break;
108
109
  }
109
- case "checkout.session.async_payment_failed":
110
- case "checkout.session.async_payment_succeeded":
111
- case "checkout.session.completed":
112
- case "checkout.session.expired": {
110
+ case 'checkout.session.async_payment_failed':
111
+ case 'checkout.session.async_payment_succeeded':
112
+ case 'checkout.session.completed':
113
+ case 'checkout.session.expired': {
113
114
  const checkoutId = request.body.data.object.id;
114
- const [model] = await StripeCheckoutSession.where({stripeSessionId: checkoutId}, {limit: 1})
115
+ const [model] = await StripeCheckoutSession.where({ stripeSessionId: checkoutId }, { limit: 1 });
115
116
  if (model && model.organizationId) {
116
- const organization = await Organization.getByID(model.organizationId)
117
+ const organization = await Organization.getByID(model.organizationId);
117
118
  if (organization) {
118
- await ExchangePaymentEndpoint.pollStatus(model.paymentId, organization)
119
- } else {
120
- console.warn("Could not find organization with id", model.organizationId)
119
+ await ExchangePaymentEndpoint.pollStatus(model.paymentId, organization);
120
+ }
121
+ else {
122
+ console.warn('Could not find organization with id', model.organizationId);
121
123
  }
122
- } else {
123
- console.warn("Could not find stripe checkout session with id", checkoutId)
124
+ }
125
+ else {
126
+ console.warn('Could not find stripe checkout session with id', checkoutId);
124
127
  }
125
128
  break;
126
129
  }
127
130
  // Listen for charge changes (transaction fees will be added here, after the payment intent succeeded)
128
- case "charge.captured":
129
- case "charge.expired":
130
- case "charge.failed":
131
- case "charge.pending":
132
- case "charge.refunded":
133
- case "charge.succeeded":
134
- case "charge.dispute.created":
135
- case "charge.dispute.updated":
136
- case "charge.dispute.closed":
137
- case "charge.refund.updated":
138
- case "charge.refund.succeeded":
139
- case "charge.updated": {
131
+ case 'charge.captured':
132
+ case 'charge.expired':
133
+ case 'charge.failed':
134
+ case 'charge.pending':
135
+ case 'charge.refunded':
136
+ case 'charge.succeeded':
137
+ case 'charge.dispute.created':
138
+ case 'charge.dispute.updated':
139
+ case 'charge.dispute.closed':
140
+ case 'charge.refund.updated':
141
+ case 'charge.refund.succeeded':
142
+ case 'charge.updated': {
140
143
  const intentId = request.body.data.object.payment_intent;
141
- if (intentId && typeof intentId === "string") {
142
- await this.updateIntent(intentId)
143
- } else {
144
- console.log('Received charge event without payment intent', request.body)
144
+ if (intentId && typeof intentId === 'string') {
145
+ await this.updateIntent(intentId);
146
+ }
147
+ else {
148
+ console.log('Received charge event without payment intent', request.body);
145
149
  }
146
150
  break;
147
151
  }
148
152
  default: {
149
- console.log("Unhandled stripe webhook type", request.body.type);
153
+ console.log('Unhandled stripe webhook type', request.body.type);
150
154
  break;
151
155
  }
152
156
  }
@@ -154,17 +158,19 @@ export class StripeWebookEndpoint extends Endpoint<Params, Query, Body, Response
154
158
  }
155
159
 
156
160
  async updateIntent(intentId: string) {
157
- console.log("[Webooks] Updating intent", intentId)
158
- const [model] = await StripePaymentIntent.where({stripeIntentId: intentId}, {limit: 1})
161
+ console.log('[Webooks] Updating intent', intentId);
162
+ const [model] = await StripePaymentIntent.where({ stripeIntentId: intentId }, { limit: 1 });
159
163
  if (model && model.organizationId) {
160
- const organization = await Organization.getByID(model.organizationId)
164
+ const organization = await Organization.getByID(model.organizationId);
161
165
  if (organization) {
162
- await ExchangePaymentEndpoint.pollStatus(model.paymentId, organization)
163
- } else {
164
- console.warn("Could not find organization with id", model.organizationId)
166
+ await ExchangePaymentEndpoint.pollStatus(model.paymentId, organization);
165
167
  }
166
- } else {
167
- console.warn("Could not find stripe payment intent with id", intentId)
168
+ else {
169
+ console.warn('Could not find organization with id', model.organizationId);
170
+ }
171
+ }
172
+ else {
173
+ console.warn('Could not find stripe payment intent with id', intentId);
168
174
  }
169
175
  }
170
176
  }
@@ -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 { User as UserStruct } from "@stamhoofd/structures";
3
+ import { 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 = UserStruct[]
9
+ type Body = undefined;
10
+ type ResponseBody = UserStruct[];
11
11
 
12
12
  export class GetPlatformAdminsEndpoint 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, "/platform/admins", {});
18
+ const params = Endpoint.parseParameters(request.url, '/platform/admins', {});
19
19
 
20
20
  if (params) {
21
21
  return [true, params as Params];
@@ -24,22 +24,22 @@ export class GetPlatformAdminsEndpoint extends Endpoint<Params, Query, Body, Res
24
24
  }
25
25
 
26
26
  async handle(_: DecodedRequest<Params, Query, Body>) {
27
- await Context.authenticate()
27
+ await Context.authenticate();
28
28
 
29
29
  // Fast throw first (more in depth checking for patches later)
30
30
  if (!Context.auth.canManagePlatformAdmins()) {
31
- throw Context.auth.error()
31
+ throw Context.auth.error();
32
32
  }
33
33
 
34
34
  // Get all admins
35
- let admins = await User.where({ organizationId: null, permissions: { sign: "!=", value: null }})
35
+ let admins = await User.where({ organizationId: null, permissions: { sign: '!=', value: null } });
36
36
 
37
37
  // Hide api accounts
38
- admins = admins.filter(a => !a.isApiUser)
39
- admins = admins.filter(a => !!a.permissions?.globalPermissions)
38
+ admins = admins.filter(a => !a.isApiUser);
39
+ admins = admins.filter(a => !!a.permissions?.globalPermissions);
40
40
 
41
41
  return new Response(
42
- await AuthenticatedStructures.usersWithMembers(admins)
42
+ await AuthenticatedStructures.usersWithMembers(admins),
43
43
  );
44
44
  }
45
45
  }
@@ -1,21 +1,21 @@
1
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
2
- import { Platform } from "@stamhoofd/models";
3
- import { Platform as PlatformStruct } from "@stamhoofd/structures";
1
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
2
+ import { Platform } from '@stamhoofd/models';
3
+ import { Platform as PlatformStruct } from '@stamhoofd/structures';
4
4
 
5
- import { Context } from "../../../helpers/Context";
5
+ import { Context } from '../../../helpers/Context';
6
6
 
7
7
  type Params = Record<string, never>;
8
8
  type Query = undefined;
9
- type Body = undefined
9
+ type Body = undefined;
10
10
  type ResponseBody = PlatformStruct;
11
11
 
12
12
  export class GetPlatformEndpoint 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, "/platform", {});
18
+ const params = Endpoint.parseParameters(request.url, '/platform', {});
19
19
 
20
20
  if (params) {
21
21
  return [true, params as Params];
@@ -24,16 +24,16 @@ export class GetPlatformEndpoint extends Endpoint<Params, Query, Body, ResponseB
24
24
  }
25
25
 
26
26
  async handle(_: DecodedRequest<Params, Query, Body>) {
27
- await Context.optionalAuthenticate({allowWithoutAccount: false})
27
+ await Context.optionalAuthenticate({ allowWithoutAccount: false });
28
28
 
29
29
  if (Context.optionalAuth?.hasSomePlatformAccess()) {
30
- const platform = await Platform.getSharedPrivateStruct()
30
+ const platform = await Platform.getSharedPrivateStruct();
31
31
  if (!platform.privateConfig) {
32
- throw new Error("Private config not found")
32
+ throw new Error('Private config not found');
33
33
  }
34
34
  return new Response(platform);
35
35
  }
36
- const platform = await Platform.getSharedStruct()
36
+ const platform = await Platform.getSharedStruct();
37
37
  return new Response(platform);
38
38
  }
39
39
  }