@stamhoofd/backend 2.39.1 → 2.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/eslint.config.mjs +5 -0
  2. package/index.ts +81 -74
  3. package/jest.config.cjs +10 -0
  4. package/migrations.ts +16 -14
  5. package/package.json +11 -11
  6. package/src/crons/clear-excel-cache.test.ts +48 -50
  7. package/src/crons/clear-excel-cache.ts +18 -18
  8. package/src/crons/setup-steps.ts +2 -2
  9. package/src/crons.ts +325 -306
  10. package/src/decoders/StringArrayDecoder.ts +7 -7
  11. package/src/decoders/StringNullableDecoder.ts +1 -2
  12. package/src/email-recipient-loaders/members.ts +22 -22
  13. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +8 -9
  14. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +39 -40
  15. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +8 -8
  16. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +44 -45
  17. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +58 -57
  18. package/src/endpoints/auth/CreateAdminEndpoint.ts +48 -45
  19. package/src/endpoints/auth/CreateTokenEndpoint.test.ts +31 -31
  20. package/src/endpoints/auth/CreateTokenEndpoint.ts +146 -147
  21. package/src/endpoints/auth/DeleteTokenEndpoint.ts +7 -7
  22. package/src/endpoints/auth/DeleteUserEndpoint.ts +15 -15
  23. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +17 -18
  24. package/src/endpoints/auth/GetOtherUserEndpoint.ts +9 -10
  25. package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -35
  26. package/src/endpoints/auth/GetUserEndpoint.ts +5 -6
  27. package/src/endpoints/auth/PatchApiUserEndpoint.ts +35 -33
  28. package/src/endpoints/auth/PatchUserEndpoint.ts +55 -52
  29. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +9 -9
  30. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +8 -8
  31. package/src/endpoints/auth/SignupEndpoint.ts +37 -36
  32. package/src/endpoints/auth/VerifyEmailEndpoint.ts +29 -28
  33. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +33 -33
  34. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +7 -7
  35. package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +37 -37
  36. package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -30
  37. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +13 -13
  38. package/src/endpoints/global/email/GetEmailEndpoint.ts +13 -13
  39. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +16 -16
  40. package/src/endpoints/global/email/PatchEmailEndpoint.ts +25 -25
  41. package/src/endpoints/global/events/GetEventsEndpoint.ts +43 -44
  42. package/src/endpoints/global/events/PatchEventsEndpoint.ts +127 -172
  43. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +49 -50
  44. package/src/endpoints/global/files/GetFileCache.ts +13 -13
  45. package/src/endpoints/global/files/UploadFile.ts +51 -54
  46. package/src/endpoints/global/files/UploadImage.ts +53 -53
  47. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +25 -25
  48. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +24 -23
  49. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +8 -8
  50. package/src/endpoints/global/members/GetMembersEndpoint.ts +105 -102
  51. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +240 -239
  52. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +12 -14
  53. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +32 -33
  54. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +48 -57
  55. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +21 -22
  56. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +28 -28
  57. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +18 -18
  58. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +20 -20
  59. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +17 -17
  60. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +81 -75
  61. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +14 -14
  62. package/src/endpoints/global/platform/GetPlatformEnpoint.ts +11 -11
  63. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +71 -68
  64. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +27 -27
  65. package/src/endpoints/global/registration/GetUserBillingStatusEndpoint.ts +30 -30
  66. package/src/endpoints/global/registration/GetUserDetailedBillingStatusEndpoint.ts +34 -34
  67. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +26 -26
  68. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +12 -12
  69. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +90 -90
  70. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +118 -121
  71. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +362 -350
  72. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +8 -9
  73. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +21 -21
  74. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +65 -65
  75. package/src/endpoints/organization/dashboard/billing/GetOrganizationBillingStatusEndpoint.ts +9 -9
  76. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedBillingStatusEndpoint.ts +14 -14
  77. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +17 -17
  78. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +21 -21
  79. package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +15 -15
  80. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +52 -52
  81. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +37 -37
  82. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +14 -14
  83. package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +113 -112
  84. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +29 -29
  85. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +48 -47
  86. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +22 -21
  87. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +13 -14
  88. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +12 -13
  89. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +24 -24
  90. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +10 -12
  91. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +14 -14
  92. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +13 -13
  93. package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +12 -12
  94. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +120 -124
  95. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +172 -173
  96. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +88 -89
  97. package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +12 -12
  98. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +17 -17
  99. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +8 -8
  100. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +66 -67
  101. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +47 -47
  102. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +93 -91
  103. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +16 -17
  104. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +170 -167
  105. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +25 -24
  106. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +22 -23
  107. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +22 -22
  108. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +17 -18
  109. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +8 -9
  110. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +17 -18
  111. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -15
  112. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +19 -19
  113. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +19 -19
  114. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +14 -14
  115. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +12 -12
  116. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +103 -100
  117. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +11 -12
  118. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +15 -15
  119. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +14 -14
  120. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +14 -14
  121. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +23 -23
  122. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +54 -52
  123. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +84 -81
  124. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +120 -111
  125. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +24 -24
  126. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +18 -18
  127. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +141 -130
  128. package/src/endpoints/organization/shared/GetDocumentHtml.ts +25 -25
  129. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +18 -18
  130. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +36 -37
  131. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +9 -9
  132. package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +11 -11
  133. package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +28 -27
  134. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +20 -20
  135. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +22 -22
  136. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -14
  137. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +57 -56
  138. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +65 -66
  139. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +18 -17
  140. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +124 -128
  141. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +154 -145
  142. package/src/excel-loaders/members.ts +275 -273
  143. package/src/excel-loaders/payments.ts +155 -156
  144. package/src/helpers/AddressValidator.test.ts +32 -32
  145. package/src/helpers/AddressValidator.ts +128 -122
  146. package/src/helpers/AdminPermissionChecker.ts +339 -236
  147. package/src/helpers/AuthenticatedStructures.ts +233 -134
  148. package/src/helpers/BuckarooHelper.ts +134 -134
  149. package/src/helpers/CheckSettlements.ts +94 -88
  150. package/src/helpers/Context.ts +87 -86
  151. package/src/helpers/CookieHelper.ts +23 -22
  152. package/src/helpers/EmailResumer.ts +10 -10
  153. package/src/helpers/FileCache.ts +62 -62
  154. package/src/helpers/ForwardHandler.test.ts +122 -124
  155. package/src/helpers/ForwardHandler.ts +76 -70
  156. package/src/helpers/MemberUserSyncer.ts +101 -96
  157. package/src/helpers/MembershipCharger.ts +69 -69
  158. package/src/helpers/MembershipHelper.ts +11 -12
  159. package/src/helpers/OpenIDConnectHelper.ts +85 -82
  160. package/src/helpers/PeriodHelper.ts +65 -70
  161. package/src/helpers/StripeHelper.ts +146 -137
  162. package/src/helpers/StripePayoutChecker.ts +51 -52
  163. package/src/helpers/ViesHelper.ts +46 -44
  164. package/src/helpers/fetchToAsyncIterator.ts +14 -14
  165. package/src/helpers/xlsxAddressTransformerColumnFactory.ts +58 -60
  166. package/src/middleware/ContextMiddleware.ts +5 -5
  167. package/src/migrations/1646578856-validate-addresses.ts +6 -9
  168. package/src/seeds/0000000000-example.ts +3 -5
  169. package/src/seeds/1715028563-user-permissions.ts +16 -18
  170. package/src/seeds/1722256498-group-update-occupancy.ts +12 -12
  171. package/src/seeds/1722344162-sync-member-users.ts +14 -15
  172. package/src/seeds/1722344162-update-membership.ts +6 -6
  173. package/src/seeds/1726055544-balance-item-paid.ts +4 -4
  174. package/src/seeds/1726055545-balance-item-pending.ts +4 -4
  175. package/src/seeds/1726494419-update-cached-outstanding-balance.ts +16 -16
  176. package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +12 -12
  177. package/src/seeds/1726572303-schedule-stock-updates.ts +12 -12
  178. package/src/seeds/1726847064-setup-steps.ts +16 -0
  179. package/src/sql-filters/balance-item-payments.ts +7 -7
  180. package/src/sql-filters/events.ts +14 -14
  181. package/src/sql-filters/members.ts +96 -96
  182. package/src/sql-filters/organizations.ts +139 -75
  183. package/src/sql-filters/payments.ts +28 -28
  184. package/src/sql-filters/registrations.ts +14 -14
  185. package/src/sql-sorters/events.ts +25 -25
  186. package/src/sql-sorters/members.ts +26 -26
  187. package/src/sql-sorters/organizations.ts +36 -36
  188. package/src/sql-sorters/payments.ts +26 -26
  189. package/tests/e2e/stock.test.ts +616 -621
  190. package/tests/e2e/tickets.test.ts +255 -260
  191. package/tests/helpers/StripeMocker.ts +177 -179
  192. package/tests/helpers/TestServer.ts +9 -9
  193. package/tests/jest.global.setup.ts +14 -13
  194. package/tests/jest.setup.ts +33 -32
  195. package/.eslintrc.js +0 -61
  196. package/jest.config.js +0 -11
  197. package/src/helpers/SetupStepsUpdater.ts +0 -359
  198. package/src/seeds/1724076679-setup-steps.ts +0 -16
@@ -0,0 +1,5 @@
1
+ import stamhoofdEslint from 'eslint-plugin-stamhoofd';
2
+
3
+ export default [
4
+ ...stamhoofdEslint.configs.backend,
5
+ ];
package/index.ts CHANGED
@@ -1,19 +1,21 @@
1
- require('@stamhoofd/backend-env').load()
2
- import { Column, Database, Migration } from "@simonbackx/simple-database";
3
- import { CORSPreflightEndpoint, Router, RouterServer } from "@simonbackx/simple-endpoints";
4
- import { I18n } from "@stamhoofd/backend-i18n";
5
- import { CORSMiddleware, LogMiddleware, VersionMiddleware } from "@stamhoofd/backend-middleware";
6
- import { Email } from "@stamhoofd/email";
7
- import { loadLogger } from "@stamhoofd/logging";
1
+ import backendEnv from '@stamhoofd/backend-env';
2
+ backendEnv.load();
3
+
4
+ import { Column, Database, Migration } from '@simonbackx/simple-database';
5
+ import { CORSPreflightEndpoint, Router, RouterServer } from '@simonbackx/simple-endpoints';
6
+ import { I18n } from '@stamhoofd/backend-i18n';
7
+ import { CORSMiddleware, LogMiddleware, VersionMiddleware } from '@stamhoofd/backend-middleware';
8
+ import { Email } from '@stamhoofd/email';
9
+ import { loadLogger } from '@stamhoofd/logging';
8
10
  import { Version } from '@stamhoofd/structures';
9
- import { sleep } from "@stamhoofd/utility";
11
+ import { sleep } from '@stamhoofd/utility';
10
12
 
11
13
  import { areCronsRunning, crons, stopCronScheduling } from './src/crons';
12
- import { resumeEmails } from "./src/helpers/EmailResumer";
13
- import { ContextMiddleware } from "./src/middleware/ContextMiddleware";
14
+ import { resumeEmails } from './src/helpers/EmailResumer';
15
+ import { ContextMiddleware } from './src/middleware/ContextMiddleware';
14
16
 
15
- process.on("unhandledRejection", (error: Error) => {
16
- console.error("unhandledRejection");
17
+ process.on('unhandledRejection', (error: Error) => {
18
+ console.error('unhandledRejection');
17
19
  console.error(error.message, error.stack);
18
20
  process.exit(1);
19
21
  });
@@ -22,63 +24,64 @@ process.on("unhandledRejection", (error: Error) => {
22
24
  Column.setJSONVersion(Version);
23
25
 
24
26
  // Set timezone!
25
- process.env.TZ = "UTC";
27
+ process.env.TZ = 'UTC';
26
28
 
27
29
  // Quick check
28
- if (new Date().getTimezoneOffset() != 0) {
29
- throw new Error("Process should always run in UTC timezone");
30
+ if (new Date().getTimezoneOffset() !== 0) {
31
+ throw new Error('Process should always run in UTC timezone');
30
32
  }
31
33
 
32
34
  const seeds = async () => {
33
35
  try {
34
36
  // Internal
35
- await Migration.runAll(__dirname + "/src/seeds");
36
- } catch (e) {
37
- console.error("Failed to run seeds:")
38
- console.error(e)
37
+ await Migration.runAll(__dirname + '/src/seeds');
38
+ }
39
+ catch (e) {
40
+ console.error('Failed to run seeds:');
41
+ console.error(e);
39
42
  }
40
43
  };
41
44
 
42
45
  const start = async () => {
43
- console.log('Running server at v' + Version)
46
+ console.log('Running server at v' + Version);
44
47
  loadLogger();
45
- await I18n.load()
48
+ await I18n.load();
46
49
  const router = new Router();
47
- await router.loadAllEndpoints(__dirname + "/src/endpoints/global/*");
48
- await router.loadAllEndpoints(__dirname + "/src/endpoints/admin/*");
49
- await router.loadAllEndpoints(__dirname + "/src/endpoints/auth");
50
- await router.loadAllEndpoints(__dirname + "/src/endpoints/organization/dashboard/*");
51
- await router.loadAllEndpoints(__dirname + "/src/endpoints/organization/registration");
52
- await router.loadAllEndpoints(__dirname + "/src/endpoints/organization/webshops");
53
- await router.loadAllEndpoints(__dirname + "/src/endpoints/organization/shared");
54
- await router.loadAllEndpoints(__dirname + "/src/endpoints/organization/shared/*");
50
+ await router.loadAllEndpoints(__dirname + '/src/endpoints/global/*');
51
+ await router.loadAllEndpoints(__dirname + '/src/endpoints/admin/*');
52
+ await router.loadAllEndpoints(__dirname + '/src/endpoints/auth');
53
+ await router.loadAllEndpoints(__dirname + '/src/endpoints/organization/dashboard/*');
54
+ await router.loadAllEndpoints(__dirname + '/src/endpoints/organization/registration');
55
+ await router.loadAllEndpoints(__dirname + '/src/endpoints/organization/webshops');
56
+ await router.loadAllEndpoints(__dirname + '/src/endpoints/organization/shared');
57
+ await router.loadAllEndpoints(__dirname + '/src/endpoints/organization/shared/*');
55
58
 
56
- router.endpoints.push(new CORSPreflightEndpoint())
59
+ router.endpoints.push(new CORSPreflightEndpoint());
57
60
 
58
61
  const routerServer = new RouterServer(router);
59
- routerServer.verbose = false
60
-
62
+ routerServer.verbose = false;
63
+
61
64
  // Log requests and errors
62
- routerServer.addRequestMiddleware(LogMiddleware)
63
- routerServer.addResponseMiddleware(LogMiddleware)
65
+ routerServer.addRequestMiddleware(LogMiddleware);
66
+ routerServer.addResponseMiddleware(LogMiddleware);
64
67
 
65
68
  // Contexts
66
- routerServer.addRequestMiddleware(ContextMiddleware)
69
+ routerServer.addRequestMiddleware(ContextMiddleware);
67
70
 
68
71
  // Add version headers and minimum version
69
72
  const versionMiddleware = new VersionMiddleware({
70
73
  latestVersions: {
71
74
  android: STAMHOOFD.LATEST_ANDROID_VERSION,
72
75
  ios: STAMHOOFD.LATEST_IOS_VERSION,
73
- web: Version
76
+ web: Version,
74
77
  },
75
- minimumVersion: 331
76
- })
77
- routerServer.addRequestMiddleware(versionMiddleware)
78
- routerServer.addResponseMiddleware(versionMiddleware)
78
+ minimumVersion: 331,
79
+ });
80
+ routerServer.addRequestMiddleware(versionMiddleware);
81
+ routerServer.addResponseMiddleware(versionMiddleware);
79
82
 
80
83
  // Add CORS headers
81
- routerServer.addResponseMiddleware(CORSMiddleware)
84
+ routerServer.addResponseMiddleware(CORSMiddleware);
82
85
 
83
86
  // Register Excel loaders
84
87
  await import('./src/excel-loaders/members');
@@ -99,60 +102,64 @@ const start = async () => {
99
102
  let shuttingDown = false;
100
103
  const shutdown = async () => {
101
104
  if (shuttingDown) {
102
- return
105
+ return;
103
106
  }
104
- shuttingDown = true
105
- console.log("Shutting down...")
107
+ shuttingDown = true;
108
+ console.log('Shutting down...');
106
109
  // Disable keep alive
107
- routerServer.defaultHeaders = Object.assign(routerServer.defaultHeaders, { 'Connection': 'close' })
110
+ routerServer.defaultHeaders = Object.assign(routerServer.defaultHeaders, { Connection: 'close' });
108
111
  if (routerServer.server) {
109
112
  routerServer.server.headersTimeout = 5000;
110
113
  routerServer.server.keepAliveTimeout = 1;
111
114
  }
112
-
115
+
113
116
  stopCronScheduling();
114
- clearInterval(cronInterval)
117
+ clearInterval(cronInterval);
115
118
 
116
119
  if (STAMHOOFD.environment === 'development') {
117
120
  setTimeout(() => {
118
- console.error("Forcing exit after 5 seconds")
121
+ console.error('Forcing exit after 5 seconds');
119
122
  process.exit(1);
120
123
  }, 5000);
121
124
  }
122
125
 
123
126
  try {
124
- await routerServer.close()
125
- console.log("HTTP server stopped");
126
- } catch (err) {
127
- console.error("Failed to stop HTTP server:");
127
+ await routerServer.close();
128
+ console.log('HTTP server stopped');
129
+ }
130
+ catch (err) {
131
+ console.error('Failed to stop HTTP server:');
128
132
  console.error(err);
129
133
  }
130
134
 
131
135
  try {
132
136
  while (areCronsRunning()) {
133
- console.log("Crons are still running. Waiting 2 seconds...")
134
- await sleep(2000)
137
+ console.log('Crons are still running. Waiting 2 seconds...');
138
+ await sleep(2000);
135
139
  }
136
- } catch (err) {
137
- console.error("Failed to wait for crons to finish:");
140
+ }
141
+ catch (err) {
142
+ console.error('Failed to wait for crons to finish:');
138
143
  console.error(err);
139
144
  }
140
145
 
141
146
  try {
142
147
  while (Email.currentQueue.length > 0) {
143
- console.log("Emails still in queue. Waiting 2 seconds...")
144
- await sleep(2000)
148
+ console.log('Emails still in queue. Waiting 2 seconds...');
149
+ await sleep(2000);
145
150
  }
146
- } catch (err) {
147
- console.error("Failed to wait for emails to finish:");
151
+ }
152
+ catch (err) {
153
+ console.error('Failed to wait for emails to finish:');
148
154
  console.error(err);
149
155
  }
150
156
 
151
157
  try {
152
- await Database.end()
153
- console.log("MySQL connections closed");
154
- } catch (err) {
155
- console.error("Failed to close MySQL connection:")
158
+ await Database.end();
159
+ console.log('MySQL connections closed');
160
+ }
161
+ catch (err) {
162
+ console.error('Failed to close MySQL connection:');
156
163
  console.error(err);
157
164
  }
158
165
 
@@ -160,30 +167,30 @@ const start = async () => {
160
167
  process.exit(0);
161
168
  };
162
169
 
163
- process.on("SIGTERM", () => {
164
- console.info("SIGTERM signal received.");
170
+ process.on('SIGTERM', () => {
171
+ console.info('SIGTERM signal received.');
165
172
  shutdown().catch((e) => {
166
- console.error(e)
173
+ console.error(e);
167
174
  process.exit(1);
168
175
  });
169
176
  });
170
177
 
171
- process.on("SIGINT", () => {
172
- console.info("SIGINT signal received.");
178
+ process.on('SIGINT', () => {
179
+ console.info('SIGINT signal received.');
173
180
  shutdown().catch((e) => {
174
- console.error(e)
181
+ console.error(e);
175
182
  process.exit(1);
176
183
  });
177
184
  });
178
185
 
179
186
  const cronInterval = setInterval(() => {
180
- crons().catch(console.error)
187
+ crons().catch(console.error);
181
188
  }, 5 * 60 * 1000);
182
- crons().catch(console.error)
189
+ crons().catch(console.error);
183
190
  seeds().catch(console.error);
184
191
  };
185
192
 
186
- start().catch(error => {
187
- console.error("unhandledRejection", error);
193
+ start().catch((error) => {
194
+ console.error('unhandledRejection', error);
188
195
  process.exit(1);
189
196
  });
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ roots: ['<rootDir>/dist'],
3
+ testEnvironment: 'node',
4
+ setupFilesAfterEnv: [
5
+ 'jest-extended/all',
6
+ './dist/tests/jest.setup.js',
7
+ ],
8
+ globalSetup: './dist/tests/jest.global.setup.js',
9
+ // verbose: true,
10
+ };
package/migrations.ts CHANGED
@@ -1,31 +1,33 @@
1
- require('@stamhoofd/backend-env').load()
2
- import { Column, Migration } from "@simonbackx/simple-database";
3
- import { Version } from "@stamhoofd/structures";
4
- import path from "path";
1
+ import backendEnv from '@stamhoofd/backend-env';
2
+ backendEnv.load();
3
+
4
+ import { Column, Migration } from '@simonbackx/simple-database';
5
+ import { Version } from '@stamhoofd/structures';
6
+ import path from 'path';
5
7
 
6
8
  Column.setJSONVersion(Version);
7
- process.env.TZ = "UTC";
9
+ process.env.TZ = 'UTC';
8
10
 
9
- const emailPath = require.resolve("@stamhoofd/email")
10
- const modelsPath = require.resolve("@stamhoofd/models")
11
+ const emailPath = require.resolve('@stamhoofd/email');
12
+ const modelsPath = require.resolve('@stamhoofd/models');
11
13
 
12
14
  // Validate UTC timezone
13
- if (new Date().getTimezoneOffset() != 0) {
14
- throw new Error("Process should always run in UTC timezone");
15
+ if (new Date().getTimezoneOffset() !== 0) {
16
+ throw new Error('Process should always run in UTC timezone');
15
17
  }
16
18
 
17
19
  const start = async () => {
18
20
  // External migrations
19
- await Migration.runAll(path.dirname(modelsPath) + "/migrations");
20
- await Migration.runAll(path.dirname(emailPath) + "/migrations");
21
+ await Migration.runAll(path.dirname(modelsPath) + '/migrations');
22
+ await Migration.runAll(path.dirname(emailPath) + '/migrations');
21
23
 
22
24
  // Internal
23
- await Migration.runAll(__dirname + "/src/migrations");
25
+ await Migration.runAll(__dirname + '/src/migrations');
24
26
  };
25
27
 
26
28
  start()
27
- .catch(error => {
28
- console.error("unhandledRejection", error);
29
+ .catch((error) => {
30
+ console.error('unhandledRejection', error);
29
31
  process.exit(1);
30
32
  })
31
33
  .finally(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamhoofd/backend",
3
- "version": "2.39.1",
3
+ "version": "2.40.1",
4
4
  "main": "./dist/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -18,7 +18,7 @@
18
18
  "test": "jest --runInBand",
19
19
  "test:reset": "yarn build:full && jest --runInBand",
20
20
  "migrations": "yarn build:full && node ./dist/migrations.js",
21
- "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
21
+ "lint": "eslint"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/cookie": "^0.5.1",
@@ -36,14 +36,14 @@
36
36
  "@simonbackx/simple-encoding": "2.15.1",
37
37
  "@simonbackx/simple-endpoints": "1.14.0",
38
38
  "@simonbackx/simple-logging": "^1.0.1",
39
- "@stamhoofd/backend-i18n": "2.39.1",
40
- "@stamhoofd/backend-middleware": "2.39.1",
41
- "@stamhoofd/email": "2.39.1",
42
- "@stamhoofd/models": "2.39.1",
43
- "@stamhoofd/queues": "2.39.1",
44
- "@stamhoofd/sql": "2.39.1",
45
- "@stamhoofd/structures": "2.39.1",
46
- "@stamhoofd/utility": "2.39.1",
39
+ "@stamhoofd/backend-i18n": "2.40.1",
40
+ "@stamhoofd/backend-middleware": "2.40.1",
41
+ "@stamhoofd/email": "2.40.1",
42
+ "@stamhoofd/models": "2.40.1",
43
+ "@stamhoofd/queues": "2.40.1",
44
+ "@stamhoofd/sql": "2.40.1",
45
+ "@stamhoofd/structures": "2.40.1",
46
+ "@stamhoofd/utility": "2.40.1",
47
47
  "archiver": "^7.0.1",
48
48
  "aws-sdk": "^2.885.0",
49
49
  "axios": "1.6.8",
@@ -60,5 +60,5 @@
60
60
  "postmark": "^4.0.5",
61
61
  "stripe": "^16.6.0"
62
62
  },
63
- "gitHead": "13df02459d27104c9bf92ff3d905920cf81276c4"
63
+ "gitHead": "31eb933097eb608655f755de9df912c21a420963"
64
64
  }
@@ -1,75 +1,73 @@
1
- import { Dirent } from "fs";
2
- import fs from "fs/promises";
3
- import { clearExcelCacheHelper } from "./clear-excel-cache";
1
+ import { Dirent } from 'fs';
2
+ import fs from 'fs/promises';
3
+ import { clearExcelCacheHelper } from './clear-excel-cache';
4
4
 
5
5
  const testPath = '/Users/user/project/backend/app/api/.cache';
6
- jest.mock("fs/promises");
7
- const fsMock = jest.mocked(fs, true)
8
-
9
-
10
- describe("clearExcelCacheHelper", () => {
6
+ jest.mock('fs/promises');
7
+ const fsMock = jest.mocked(fs, true);
11
8
 
9
+ describe('clearExcelCacheHelper', () => {
12
10
  it('should only run between 3 and 6 AM', async () => {
13
- //#region arrange
11
+ // #region arrange
14
12
  const shouldFail = [
15
- new Date(2024,1,1,0,0),
16
- new Date(2024,1,1,2,50),
17
- new Date(2024,1,1,2,59, 59, 999),
18
- new Date(2024,1,1,6,0)
19
- ]
13
+ new Date(2024, 1, 1, 0, 0),
14
+ new Date(2024, 1, 1, 2, 50),
15
+ new Date(2024, 1, 1, 2, 59, 59, 999),
16
+ new Date(2024, 1, 1, 6, 0),
17
+ ];
20
18
 
21
19
  const shouldPass = [
22
- new Date(2024,1,1,3,0),
23
- new Date(2024,1,1,3,55),
24
- new Date(2024,1,1,5,59, 59, 999)
25
- ]
20
+ new Date(2024, 1, 1, 3, 0),
21
+ new Date(2024, 1, 1, 3, 55),
22
+ new Date(2024, 1, 1, 5, 59, 59, 999),
23
+ ];
26
24
 
27
25
  fsMock.readdir.mockReturnValue(Promise.resolve([]));
28
- //#endregion
26
+ // #endregion
29
27
 
30
- //#region act and assert
31
- for(const date of shouldFail) {
28
+ // #region act and assert
29
+ for (const date of shouldFail) {
32
30
  const didClear = await clearExcelCacheHelper({
33
31
  lastExcelClear: null,
34
32
  now: date,
35
33
  cachePath: testPath,
36
- environment: 'production'
37
- })
34
+ environment: 'production',
35
+ });
38
36
 
39
37
  expect(didClear).toBeFalse();
40
38
  }
41
39
 
42
- for(const date of shouldPass) {
40
+ for (const date of shouldPass) {
43
41
  const didClear = await clearExcelCacheHelper({
44
42
  lastExcelClear: null,
45
43
  now: date,
46
44
  cachePath: testPath,
47
- environment: 'production'
48
- })
45
+ environment: 'production',
46
+ });
49
47
 
50
48
  expect(didClear).toBeTrue();
51
49
  }
52
- //#endregion
53
- })
50
+ // #endregion
51
+ });
54
52
 
55
53
  it('should only run once each day', async () => {
56
- //#region arrange
57
- const firstTry = new Date(2024,1,1,3,0);
58
- const secondTry = new Date(2024,1,1,3,5);
59
- const thirdTry = new Date(2024,1,2,3,0);
60
- const fourthTry = new Date(2024,1,2,3,5);
54
+ // #region arrange
55
+ const firstTry = new Date(2024, 1, 1, 3, 0);
56
+ const secondTry = new Date(2024, 1, 1, 3, 5);
57
+ const thirdTry = new Date(2024, 1, 2, 3, 0);
58
+ const fourthTry = new Date(2024, 1, 2, 3, 5);
61
59
 
62
60
  fsMock.readdir.mockReturnValue(Promise.resolve([]));
63
- //#endregion
61
+ // #endregion
64
62
 
65
- //#region act and assert
63
+ // #region act and assert
66
64
 
67
65
  // second try, should fail because 5 min earlier the cache was cleared
68
66
  const didClearSecondTry = await clearExcelCacheHelper({
69
67
  lastExcelClear: firstTry.getTime(),
70
68
  now: secondTry,
71
69
  cachePath: testPath,
72
- environment: 'production'
70
+ environment: 'production',
73
71
  });
74
72
 
75
73
  expect(didClearSecondTry).toBeFalse();
@@ -79,7 +77,7 @@ describe("clearExcelCacheHelper", () => {
79
77
  lastExcelClear: secondTry.getTime(),
80
78
  now: thirdTry,
81
79
  cachePath: testPath,
82
- environment: 'production'
80
+ environment: 'production',
83
81
  });
84
82
 
85
83
  expect(didClearThirdTry).toBeTrue();
@@ -89,16 +87,16 @@ describe("clearExcelCacheHelper", () => {
89
87
  lastExcelClear: thirdTry.getTime(),
90
88
  now: fourthTry,
91
89
  cachePath: testPath,
92
- environment: 'production'
90
+ environment: 'production',
93
91
  });
94
92
 
95
93
  expect(didClearFourthTry).toBeFalse();
96
- //#endregion
97
- })
94
+ // #endregion
95
+ });
98
96
 
99
97
  it('should delete old cache only', async () => {
100
- //#region arrange
101
- const now = new Date(2024,0,5,3,5);
98
+ // #region arrange
99
+ const now = new Date(2024, 0, 5, 3, 5);
102
100
 
103
101
  const dir1 = new Dirent();
104
102
  dir1.name = '2024-01-01';
@@ -128,25 +126,25 @@ describe("clearExcelCacheHelper", () => {
128
126
 
129
127
  const directories = [dir1, dir2, dir3, dir4, dir5, dir6, dir7];
130
128
 
131
- directories.forEach(dir => {
129
+ directories.forEach((dir) => {
132
130
  dir.parentPath = testPath;
133
131
  dir.isDirectory = () => true;
134
- })
132
+ });
135
133
 
136
134
  fsMock.readdir.mockReturnValue(Promise.resolve([...directories, file1]));
137
- //#endregion
135
+ // #endregion
138
136
 
139
137
  // act
140
138
  await clearExcelCacheHelper({
141
139
  lastExcelClear: null,
142
140
  now,
143
141
  cachePath: testPath,
144
- environment: 'production'
142
+ environment: 'production',
145
143
  });
146
144
 
147
145
  // assert
148
146
  expect(fsMock.rm).toHaveBeenCalledTimes(2);
149
- expect(fsMock.rm).toHaveBeenCalledWith("/Users/user/project/backend/app/api/.cache/2024-01-01", { recursive: true, force: true });
150
- expect(fsMock.rm).toHaveBeenCalledWith("/Users/user/project/backend/app/api/.cache/2024-01-02",{ recursive: true, force: true });
151
- })
152
- })
147
+ expect(fsMock.rm).toHaveBeenCalledWith('/Users/user/project/backend/app/api/.cache/2024-01-01', { recursive: true, force: true });
148
+ expect(fsMock.rm).toHaveBeenCalledWith('/Users/user/project/backend/app/api/.cache/2024-01-02', { recursive: true, force: true });
149
+ });
150
+ });
@@ -1,4 +1,4 @@
1
- import fs from "fs/promises";
1
+ import fs from 'fs/promises';
2
2
 
3
3
  const msIn22Hours = 79200000;
4
4
  let lastExcelClear: number | null = null;
@@ -10,29 +10,28 @@ export async function clearExcelCache() {
10
10
  lastExcelClear,
11
11
  now,
12
12
  cachePath: STAMHOOFD.CACHE_PATH,
13
- environment: STAMHOOFD.environment
13
+ environment: STAMHOOFD.environment,
14
14
  });
15
15
 
16
- if(didClear) {
16
+ if (didClear) {
17
17
  lastExcelClear = now.getTime();
18
18
  }
19
19
  }
20
20
 
21
- export async function clearExcelCacheHelper
22
- ({lastExcelClear, now, cachePath, environment}: {lastExcelClear: number | null, now: Date, cachePath: string, environment: "production" | "development" | "staging" | "test"}): Promise<boolean> {
23
- if (environment === "development") {
21
+ export async function clearExcelCacheHelper({ lastExcelClear, now, cachePath, environment }: { lastExcelClear: number | null; now: Date; cachePath: string; environment: 'production' | 'development' | 'staging' | 'test' }): Promise<boolean> {
22
+ if (environment === 'development') {
24
23
  return false;
25
24
  }
26
25
 
27
26
  const hour = now.getHours();
28
27
 
29
28
  // between 3 and 6 AM
30
- if(hour < 3 || hour > 5) {
29
+ if (hour < 3 || hour > 5) {
31
30
  return false;
32
31
  }
33
32
 
34
33
  // only run once each day
35
- if(lastExcelClear !== null && lastExcelClear + msIn22Hours > now.getTime()) {
34
+ if (lastExcelClear !== null && lastExcelClear + msIn22Hours > now.getTime()) {
36
35
  return false;
37
36
  }
38
37
 
@@ -42,9 +41,9 @@ export async function clearExcelCacheHelper
42
41
 
43
42
  const maxDaysInCache = 2;
44
43
 
45
- const dateLimit = new Date(currentYear, currentMonth, currentDay - maxDaysInCache, 0,0,0,0);
44
+ const dateLimit = new Date(currentYear, currentMonth, currentDay - maxDaysInCache, 0, 0, 0, 0);
46
45
 
47
- const files = await fs.readdir(cachePath, {withFileTypes: true});
46
+ const files = await fs.readdir(cachePath, { withFileTypes: true });
48
47
 
49
48
  for (const file of files) {
50
49
  if (file.isDirectory()) {
@@ -52,12 +51,13 @@ export async function clearExcelCacheHelper
52
51
  const date = getDateFromDirectoryName(file.name);
53
52
  const shouldDelete = date < dateLimit;
54
53
 
55
- if(shouldDelete) {
54
+ if (shouldDelete) {
56
55
  const path = file.parentPath + '/' + file.name;
57
- await fs.rm(path, { recursive: true, force: true })
58
- console.log("Removed", path)
56
+ await fs.rm(path, { recursive: true, force: true });
57
+ console.log('Removed', path);
59
58
  }
60
- } catch(error) {
59
+ }
60
+ catch (error) {
61
61
  console.error(error);
62
62
  }
63
63
  }
@@ -69,22 +69,22 @@ export async function clearExcelCacheHelper
69
69
  function getDateFromDirectoryName(file: string): Date {
70
70
  const parts = file.split('-');
71
71
 
72
- if (parts.length != 3) {
72
+ if (parts.length !== 3) {
73
73
  throw new Error(`Invalid directory ${file} in cache.`);
74
74
  }
75
75
 
76
76
  const year = parseInt(parts[0]);
77
- if(isNaN(year)) {
77
+ if (isNaN(year)) {
78
78
  throw new Error(`Year is NAN for directory ${file} in cache.`);
79
79
  }
80
80
 
81
81
  const month = parseInt(parts[1]);
82
- if(isNaN(month)) {
82
+ if (isNaN(month)) {
83
83
  throw new Error(`Month is NAN for directory ${file} in cache.`);
84
84
  }
85
85
 
86
86
  const day = parseInt(parts[2]);
87
- if(isNaN(day)) {
87
+ if (isNaN(day)) {
88
88
  throw new Error(`Day is NAN for directory ${file} in cache.`);
89
89
  }
90
90
 
@@ -1,7 +1,7 @@
1
- import { SetupStepUpdater } from "../helpers/SetupStepsUpdater";
1
+ import { SetupStepUpdater } from '@stamhoofd/models';
2
2
 
3
3
  export async function updateSetupSteps() {
4
- if (STAMHOOFD.userMode !== "platform") {
4
+ if (STAMHOOFD.userMode !== 'platform') {
5
5
  return;
6
6
  }
7
7