@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.
- package/eslint.config.mjs +5 -0
- package/index.ts +81 -74
- package/jest.config.cjs +10 -0
- package/migrations.ts +16 -14
- package/package.json +11 -11
- package/src/crons/clear-excel-cache.test.ts +48 -50
- package/src/crons/clear-excel-cache.ts +18 -18
- package/src/crons/setup-steps.ts +2 -2
- package/src/crons.ts +325 -306
- package/src/decoders/StringArrayDecoder.ts +7 -7
- package/src/decoders/StringNullableDecoder.ts +1 -2
- package/src/email-recipient-loaders/members.ts +22 -22
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +8 -9
- package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +39 -40
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +8 -8
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +44 -45
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +58 -57
- package/src/endpoints/auth/CreateAdminEndpoint.ts +48 -45
- package/src/endpoints/auth/CreateTokenEndpoint.test.ts +31 -31
- package/src/endpoints/auth/CreateTokenEndpoint.ts +146 -147
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +7 -7
- package/src/endpoints/auth/DeleteUserEndpoint.ts +15 -15
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +17 -18
- package/src/endpoints/auth/GetOtherUserEndpoint.ts +9 -10
- package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -35
- package/src/endpoints/auth/GetUserEndpoint.ts +5 -6
- package/src/endpoints/auth/PatchApiUserEndpoint.ts +35 -33
- package/src/endpoints/auth/PatchUserEndpoint.ts +55 -52
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +9 -9
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +8 -8
- package/src/endpoints/auth/SignupEndpoint.ts +37 -36
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +29 -28
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +33 -33
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +7 -7
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +37 -37
- package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -30
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +13 -13
- package/src/endpoints/global/email/GetEmailEndpoint.ts +13 -13
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +16 -16
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +25 -25
- package/src/endpoints/global/events/GetEventsEndpoint.ts +43 -44
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +127 -172
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +49 -50
- package/src/endpoints/global/files/GetFileCache.ts +13 -13
- package/src/endpoints/global/files/UploadFile.ts +51 -54
- package/src/endpoints/global/files/UploadImage.ts +53 -53
- package/src/endpoints/global/groups/GetGroupsEndpoint.ts +25 -25
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +24 -23
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +8 -8
- package/src/endpoints/global/members/GetMembersEndpoint.ts +105 -102
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +240 -239
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +12 -14
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +32 -33
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +48 -57
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +21 -22
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +28 -28
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +18 -18
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +20 -20
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +17 -17
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +81 -75
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +14 -14
- package/src/endpoints/global/platform/GetPlatformEnpoint.ts +11 -11
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +71 -68
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +27 -27
- package/src/endpoints/global/registration/GetUserBillingStatusEndpoint.ts +30 -30
- package/src/endpoints/global/registration/GetUserDetailedBillingStatusEndpoint.ts +34 -34
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +26 -26
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +12 -12
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +90 -90
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +118 -121
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +362 -350
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +8 -9
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +21 -21
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +65 -65
- package/src/endpoints/organization/dashboard/billing/GetOrganizationBillingStatusEndpoint.ts +9 -9
- package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedBillingStatusEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +17 -17
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +21 -21
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +15 -15
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +52 -52
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +37 -37
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +113 -112
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +29 -29
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +48 -47
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +22 -21
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +13 -14
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +12 -13
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +24 -24
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +10 -12
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +14 -14
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +13 -13
- package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +12 -12
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +120 -124
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +172 -173
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +88 -89
- package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +12 -12
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +17 -17
- package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +66 -67
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +47 -47
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +93 -91
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +16 -17
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +170 -167
- package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +25 -24
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +22 -23
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +22 -22
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +17 -18
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +8 -9
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +17 -18
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -15
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +19 -19
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +19 -19
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +12 -12
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +103 -100
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +11 -12
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +15 -15
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +14 -14
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +23 -23
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +54 -52
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +84 -81
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +120 -111
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +24 -24
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +18 -18
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +141 -130
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +25 -25
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +18 -18
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +36 -37
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +9 -9
- package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +11 -11
- package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +28 -27
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +20 -20
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +22 -22
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -14
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +57 -56
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +65 -66
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +18 -17
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +124 -128
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +154 -145
- package/src/excel-loaders/members.ts +275 -273
- package/src/excel-loaders/payments.ts +155 -156
- package/src/helpers/AddressValidator.test.ts +32 -32
- package/src/helpers/AddressValidator.ts +128 -122
- package/src/helpers/AdminPermissionChecker.ts +339 -236
- package/src/helpers/AuthenticatedStructures.ts +233 -134
- package/src/helpers/BuckarooHelper.ts +134 -134
- package/src/helpers/CheckSettlements.ts +94 -88
- package/src/helpers/Context.ts +87 -86
- package/src/helpers/CookieHelper.ts +23 -22
- package/src/helpers/EmailResumer.ts +10 -10
- package/src/helpers/FileCache.ts +62 -62
- package/src/helpers/ForwardHandler.test.ts +122 -124
- package/src/helpers/ForwardHandler.ts +76 -70
- package/src/helpers/MemberUserSyncer.ts +101 -96
- package/src/helpers/MembershipCharger.ts +69 -69
- package/src/helpers/MembershipHelper.ts +11 -12
- package/src/helpers/OpenIDConnectHelper.ts +85 -82
- package/src/helpers/PeriodHelper.ts +65 -70
- package/src/helpers/StripeHelper.ts +146 -137
- package/src/helpers/StripePayoutChecker.ts +51 -52
- package/src/helpers/ViesHelper.ts +46 -44
- package/src/helpers/fetchToAsyncIterator.ts +14 -14
- package/src/helpers/xlsxAddressTransformerColumnFactory.ts +58 -60
- package/src/middleware/ContextMiddleware.ts +5 -5
- package/src/migrations/1646578856-validate-addresses.ts +6 -9
- package/src/seeds/0000000000-example.ts +3 -5
- package/src/seeds/1715028563-user-permissions.ts +16 -18
- package/src/seeds/1722256498-group-update-occupancy.ts +12 -12
- package/src/seeds/1722344162-sync-member-users.ts +14 -15
- package/src/seeds/1722344162-update-membership.ts +6 -6
- package/src/seeds/1726055544-balance-item-paid.ts +4 -4
- package/src/seeds/1726055545-balance-item-pending.ts +4 -4
- package/src/seeds/1726494419-update-cached-outstanding-balance.ts +16 -16
- package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +12 -12
- package/src/seeds/1726572303-schedule-stock-updates.ts +12 -12
- package/src/seeds/1726847064-setup-steps.ts +16 -0
- package/src/sql-filters/balance-item-payments.ts +7 -7
- package/src/sql-filters/events.ts +14 -14
- package/src/sql-filters/members.ts +96 -96
- package/src/sql-filters/organizations.ts +139 -75
- package/src/sql-filters/payments.ts +28 -28
- package/src/sql-filters/registrations.ts +14 -14
- package/src/sql-sorters/events.ts +25 -25
- package/src/sql-sorters/members.ts +26 -26
- package/src/sql-sorters/organizations.ts +36 -36
- package/src/sql-sorters/payments.ts +26 -26
- package/tests/e2e/stock.test.ts +616 -621
- package/tests/e2e/tickets.test.ts +255 -260
- package/tests/helpers/StripeMocker.ts +177 -179
- package/tests/helpers/TestServer.ts +9 -9
- package/tests/jest.global.setup.ts +14 -13
- package/tests/jest.setup.ts +33 -32
- package/.eslintrc.js +0 -61
- package/jest.config.js +0 -11
- package/src/helpers/SetupStepsUpdater.ts +0 -359
- package/src/seeds/1724076679-setup-steps.ts +0 -16
package/index.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
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
|
|
11
|
+
import { sleep } from '@stamhoofd/utility';
|
|
10
12
|
|
|
11
13
|
import { areCronsRunning, crons, stopCronScheduling } from './src/crons';
|
|
12
|
-
import { resumeEmails } from
|
|
13
|
-
import { ContextMiddleware } from
|
|
14
|
+
import { resumeEmails } from './src/helpers/EmailResumer';
|
|
15
|
+
import { ContextMiddleware } from './src/middleware/ContextMiddleware';
|
|
14
16
|
|
|
15
|
-
process.on(
|
|
16
|
-
console.error(
|
|
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 =
|
|
27
|
+
process.env.TZ = 'UTC';
|
|
26
28
|
|
|
27
29
|
// Quick check
|
|
28
|
-
if (new Date().getTimezoneOffset()
|
|
29
|
-
throw new Error(
|
|
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 +
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
console.error(
|
|
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 +
|
|
48
|
-
await router.loadAllEndpoints(__dirname +
|
|
49
|
-
await router.loadAllEndpoints(__dirname +
|
|
50
|
-
await router.loadAllEndpoints(__dirname +
|
|
51
|
-
await router.loadAllEndpoints(__dirname +
|
|
52
|
-
await router.loadAllEndpoints(__dirname +
|
|
53
|
-
await router.loadAllEndpoints(__dirname +
|
|
54
|
-
await router.loadAllEndpoints(__dirname +
|
|
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(
|
|
107
|
+
shuttingDown = true;
|
|
108
|
+
console.log('Shutting down...');
|
|
106
109
|
// Disable keep alive
|
|
107
|
-
routerServer.defaultHeaders = Object.assign(routerServer.defaultHeaders, {
|
|
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(
|
|
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(
|
|
126
|
-
}
|
|
127
|
-
|
|
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(
|
|
134
|
-
await sleep(2000)
|
|
137
|
+
console.log('Crons are still running. Waiting 2 seconds...');
|
|
138
|
+
await sleep(2000);
|
|
135
139
|
}
|
|
136
|
-
}
|
|
137
|
-
|
|
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(
|
|
144
|
-
await sleep(2000)
|
|
148
|
+
console.log('Emails still in queue. Waiting 2 seconds...');
|
|
149
|
+
await sleep(2000);
|
|
145
150
|
}
|
|
146
|
-
}
|
|
147
|
-
|
|
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(
|
|
154
|
-
}
|
|
155
|
-
|
|
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(
|
|
164
|
-
console.info(
|
|
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(
|
|
172
|
-
console.info(
|
|
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(
|
|
193
|
+
start().catch((error) => {
|
|
194
|
+
console.error('unhandledRejection', error);
|
|
188
195
|
process.exit(1);
|
|
189
196
|
});
|
package/jest.config.cjs
ADDED
package/migrations.ts
CHANGED
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import
|
|
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 =
|
|
9
|
+
process.env.TZ = 'UTC';
|
|
8
10
|
|
|
9
|
-
const emailPath = require.resolve(
|
|
10
|
-
const modelsPath = require.resolve(
|
|
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()
|
|
14
|
-
throw new Error(
|
|
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) +
|
|
20
|
-
await Migration.runAll(path.dirname(emailPath) +
|
|
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 +
|
|
25
|
+
await Migration.runAll(__dirname + '/src/migrations');
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
start()
|
|
27
|
-
.catch(error => {
|
|
28
|
-
console.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.
|
|
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
|
|
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.
|
|
40
|
-
"@stamhoofd/backend-middleware": "2.
|
|
41
|
-
"@stamhoofd/email": "2.
|
|
42
|
-
"@stamhoofd/models": "2.
|
|
43
|
-
"@stamhoofd/queues": "2.
|
|
44
|
-
"@stamhoofd/sql": "2.
|
|
45
|
-
"@stamhoofd/structures": "2.
|
|
46
|
-
"@stamhoofd/utility": "2.
|
|
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": "
|
|
63
|
+
"gitHead": "31eb933097eb608655f755de9df912c21a420963"
|
|
64
64
|
}
|
|
@@ -1,75 +1,73 @@
|
|
|
1
|
-
import { Dirent } from
|
|
2
|
-
import fs from
|
|
3
|
-
import { clearExcelCacheHelper } from
|
|
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(
|
|
7
|
-
const fsMock =
|
|
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
|
-
|
|
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
|
-
|
|
26
|
+
// #endregion
|
|
29
27
|
|
|
30
|
-
|
|
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
|
-
|
|
53
|
-
})
|
|
50
|
+
// #endregion
|
|
51
|
+
});
|
|
54
52
|
|
|
55
53
|
it('should only run once each day', async () => {
|
|
56
|
-
|
|
57
|
-
const firstTry
|
|
58
|
-
const secondTry = new Date(2024,1,1,3,5);
|
|
59
|
-
const thirdTry
|
|
60
|
-
const fourthTry
|
|
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
|
-
|
|
61
|
+
// #endregion
|
|
64
62
|
|
|
65
|
-
|
|
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
|
-
|
|
97
|
-
})
|
|
94
|
+
// #endregion
|
|
95
|
+
});
|
|
98
96
|
|
|
99
97
|
it('should delete old cache only', async () => {
|
|
100
|
-
|
|
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
|
-
|
|
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(
|
|
150
|
-
expect(fsMock.rm).toHaveBeenCalledWith(
|
|
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
|
|
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
|
-
(
|
|
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(
|
|
56
|
+
await fs.rm(path, { recursive: true, force: true });
|
|
57
|
+
console.log('Removed', path);
|
|
59
58
|
}
|
|
60
|
-
}
|
|
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
|
|
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
|
|
package/src/crons/setup-steps.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { SetupStepUpdater } from
|
|
1
|
+
import { SetupStepUpdater } from '@stamhoofd/models';
|
|
2
2
|
|
|
3
3
|
export async function updateSetupSteps() {
|
|
4
|
-
if (STAMHOOFD.userMode !==
|
|
4
|
+
if (STAMHOOFD.userMode !== 'platform') {
|
|
5
5
|
return;
|
|
6
6
|
}
|
|
7
7
|
|