@stamhoofd/backend 2.57.1 → 2.58.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.
- package/package.json +11 -11
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +12 -1
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +7 -14
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +20 -1
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +13 -12
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +5 -0
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +8 -1
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +8 -1
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -2
- package/src/helpers/PeriodHelper.ts +30 -25
- package/src/services/AuditLogService.ts +105 -20
- package/src/services/explainPatch.ts +452 -309
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.58.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -35,16 +35,16 @@
|
|
|
35
35
|
"@mollie/api-client": "3.7.0",
|
|
36
36
|
"@simonbackx/simple-database": "1.25.0",
|
|
37
37
|
"@simonbackx/simple-encoding": "2.17.0",
|
|
38
|
-
"@simonbackx/simple-endpoints": "1.
|
|
38
|
+
"@simonbackx/simple-endpoints": "1.15.0",
|
|
39
39
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
40
|
-
"@stamhoofd/backend-i18n": "2.
|
|
41
|
-
"@stamhoofd/backend-middleware": "2.
|
|
42
|
-
"@stamhoofd/email": "2.
|
|
43
|
-
"@stamhoofd/models": "2.
|
|
44
|
-
"@stamhoofd/queues": "2.
|
|
45
|
-
"@stamhoofd/sql": "2.
|
|
46
|
-
"@stamhoofd/structures": "2.
|
|
47
|
-
"@stamhoofd/utility": "2.
|
|
40
|
+
"@stamhoofd/backend-i18n": "2.58.0",
|
|
41
|
+
"@stamhoofd/backend-middleware": "2.58.0",
|
|
42
|
+
"@stamhoofd/email": "2.58.0",
|
|
43
|
+
"@stamhoofd/models": "2.58.0",
|
|
44
|
+
"@stamhoofd/queues": "2.58.0",
|
|
45
|
+
"@stamhoofd/sql": "2.58.0",
|
|
46
|
+
"@stamhoofd/structures": "2.58.0",
|
|
47
|
+
"@stamhoofd/utility": "2.58.0",
|
|
48
48
|
"archiver": "^7.0.1",
|
|
49
49
|
"aws-sdk": "^2.885.0",
|
|
50
50
|
"axios": "1.6.8",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"publishConfig": {
|
|
65
65
|
"access": "public"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "4159f0bc858d301854c4e38a3e315053aec08399"
|
|
68
68
|
}
|
|
@@ -7,6 +7,8 @@ import { isDebouncedError, QueueHandler } from '@stamhoofd/queues';
|
|
|
7
7
|
import { StripeHelper } from '../../../helpers/StripeHelper';
|
|
8
8
|
import { ExchangePaymentEndpoint } from '../../organization/shared/ExchangePaymentEndpoint';
|
|
9
9
|
import { PaymentService } from '../../../services/PaymentService';
|
|
10
|
+
import { AuditLogService } from '../../../services/AuditLogService';
|
|
11
|
+
import { AuditLogType } from '@stamhoofd/structures';
|
|
10
12
|
|
|
11
13
|
type Params = Record<string, never>;
|
|
12
14
|
class Body extends AutoEncoder {
|
|
@@ -85,8 +87,17 @@ export class StripeWebookEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
85
87
|
const id = account.id as string;
|
|
86
88
|
const [model] = await StripeAccount.where({ accountId: id }, { limit: 1 });
|
|
87
89
|
if (model) {
|
|
90
|
+
const beforeMeta = model.meta.clone();
|
|
88
91
|
model.setMetaFromStripeAccount(account);
|
|
89
|
-
await model.save()
|
|
92
|
+
if (await model.save()) {
|
|
93
|
+
// Track audit log
|
|
94
|
+
await AuditLogService.log({
|
|
95
|
+
type: AuditLogType.StripeAccountEdited,
|
|
96
|
+
stripeAccount: model,
|
|
97
|
+
oldData: beforeMeta,
|
|
98
|
+
patch: model.meta,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
90
101
|
}
|
|
91
102
|
else {
|
|
92
103
|
console.warn('Could not find stripe account with id', id);
|
|
@@ -49,10 +49,9 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const platform = await Platform.getShared();
|
|
52
|
+
const initialStruct = (await Platform.getSharedPrivateStruct()).clone();
|
|
52
53
|
|
|
53
54
|
if (request.body.privateConfig) {
|
|
54
|
-
const oldConfig = platform.privateConfig.clone();
|
|
55
|
-
|
|
56
55
|
// Did we patch roles?
|
|
57
56
|
if (request.body.privateConfig.roles) {
|
|
58
57
|
if (!Context.auth.canManagePlatformAdmins()) {
|
|
@@ -77,12 +76,6 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
77
76
|
request.body.privateConfig.emails,
|
|
78
77
|
);
|
|
79
78
|
}
|
|
80
|
-
|
|
81
|
-
await AuditLogService.log({
|
|
82
|
-
type: AuditLogType.PlatformSettingsChanged,
|
|
83
|
-
oldConfig,
|
|
84
|
-
patch: request.body.privateConfig,
|
|
85
|
-
});
|
|
86
79
|
}
|
|
87
80
|
|
|
88
81
|
let shouldUpdateSetupSteps = false;
|
|
@@ -116,12 +109,6 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
116
109
|
else {
|
|
117
110
|
platform.config = patchObject(platform.config, newConfig);
|
|
118
111
|
}
|
|
119
|
-
|
|
120
|
-
await AuditLogService.log({
|
|
121
|
-
type: AuditLogType.PlatformSettingsChanged,
|
|
122
|
-
oldConfig,
|
|
123
|
-
patch: newConfig,
|
|
124
|
-
});
|
|
125
112
|
}
|
|
126
113
|
|
|
127
114
|
if (newConfig.tags && isPatchableArray(newConfig.tags) && newConfig.tags.changes.length > 0) {
|
|
@@ -236,6 +223,12 @@ export class PatchPlatformEndpoint extends Endpoint<
|
|
|
236
223
|
SetupStepUpdater.updateSetupStepsForAllOrganizationsInCurrentPeriod().catch(console.error);
|
|
237
224
|
}
|
|
238
225
|
|
|
226
|
+
await AuditLogService.log({
|
|
227
|
+
type: AuditLogType.PlatformSettingsChanged,
|
|
228
|
+
oldData: initialStruct,
|
|
229
|
+
patch: request.body,
|
|
230
|
+
});
|
|
231
|
+
|
|
239
232
|
return new Response(await Platform.getSharedPrivateStruct());
|
|
240
233
|
}
|
|
241
234
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ConvertArrayToPatchableArray, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder, patchObject } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
-
import { RegistrationPeriod as RegistrationPeriodStruct } from '@stamhoofd/structures';
|
|
3
|
+
import { AuditLogType, RegistrationPeriod as RegistrationPeriodStruct } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
5
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
6
6
|
import { Platform, RegistrationPeriod } from '@stamhoofd/models';
|
|
7
7
|
import { Context } from '../../../helpers/Context';
|
|
8
8
|
import { PeriodHelper } from '../../../helpers/PeriodHelper';
|
|
9
|
+
import { AuditLogService } from '../../../services/AuditLogService';
|
|
9
10
|
|
|
10
11
|
type Params = Record<string, never>;
|
|
11
12
|
type Query = undefined;
|
|
@@ -62,6 +63,11 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
|
|
|
62
63
|
|
|
63
64
|
await period.save();
|
|
64
65
|
periods.push(period);
|
|
66
|
+
|
|
67
|
+
await AuditLogService.log({
|
|
68
|
+
type: AuditLogType.RegistrationPeriodAdded,
|
|
69
|
+
period,
|
|
70
|
+
});
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
for (const patch of request.body.getPatches()) {
|
|
@@ -74,6 +80,7 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
|
|
|
74
80
|
message: 'Registration period not found',
|
|
75
81
|
});
|
|
76
82
|
}
|
|
83
|
+
const initialStructure = model.getStructure().clone();
|
|
77
84
|
|
|
78
85
|
if (patch.startDate !== undefined) {
|
|
79
86
|
model.startDate = patch.startDate;
|
|
@@ -95,6 +102,13 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
|
|
|
95
102
|
|
|
96
103
|
// Schedule patch of all groups in this period
|
|
97
104
|
PeriodHelper.updateGroupsInPeriod(model).catch(console.error);
|
|
105
|
+
|
|
106
|
+
await AuditLogService.log({
|
|
107
|
+
type: AuditLogType.RegistrationPeriodEdited,
|
|
108
|
+
period: model,
|
|
109
|
+
patch,
|
|
110
|
+
oldData: initialStructure,
|
|
111
|
+
});
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
for (const id of request.body.getDeletes()) {
|
|
@@ -109,6 +123,11 @@ export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Bo
|
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
await model.delete();
|
|
126
|
+
|
|
127
|
+
await AuditLogService.log({
|
|
128
|
+
type: AuditLogType.RegistrationPeriodDeleted,
|
|
129
|
+
period: model,
|
|
130
|
+
});
|
|
112
131
|
}
|
|
113
132
|
|
|
114
133
|
// Clear platform cache
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AutoEncoderPatchType, Decoder, isPatchableArray, ObjectData, PatchableArrayAutoEncoder, patchObject } from '@simonbackx/simple-encoding';
|
|
1
|
+
import { AutoEncoderPatchType, cloneObject, Decoder, isPatchableArray, ObjectData, PatchableArrayAutoEncoder, patchObject } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError, SimpleErrors } from '@simonbackx/simple-errors';
|
|
4
4
|
import { Organization, OrganizationRegistrationPeriod, PayconiqPayment, Platform, RegistrationPeriod, SetupStepUpdater, StripeAccount, Webshop } from '@stamhoofd/models';
|
|
@@ -63,6 +63,8 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
const initialStruct = (await AuthenticatedStructures.organization(organization)).clone();
|
|
67
|
+
|
|
66
68
|
const errors = new SimpleErrors();
|
|
67
69
|
let shouldUpdateSetupSteps = false;
|
|
68
70
|
let updateTags = false;
|
|
@@ -136,7 +138,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
136
138
|
|
|
137
139
|
// Apply payconiq patch
|
|
138
140
|
if (request.body.privateMeta.payconiqAccounts !== undefined) {
|
|
139
|
-
organization.privateMeta.payconiqAccounts = patchObject(organization.privateMeta.payconiqAccounts, request.body.privateMeta.payconiqAccounts);
|
|
141
|
+
organization.privateMeta.payconiqAccounts = patchObject(organization.privateMeta.payconiqAccounts, cloneObject(request.body.privateMeta.payconiqAccounts as any));
|
|
140
142
|
|
|
141
143
|
for (const account of organization.privateMeta.payconiqAccounts) {
|
|
142
144
|
if (account.merchantId === null) {
|
|
@@ -214,8 +216,6 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
214
216
|
await this.validateCompanies(organization, request.body.meta.companies);
|
|
215
217
|
shouldUpdateSetupSteps = true;
|
|
216
218
|
}
|
|
217
|
-
const oldMeta = organization.meta.clone();
|
|
218
|
-
|
|
219
219
|
const savedPackages = organization.meta.packages;
|
|
220
220
|
organization.meta.patchOrPut(request.body.meta);
|
|
221
221
|
organization.meta.packages = savedPackages;
|
|
@@ -283,13 +283,6 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
283
283
|
|
|
284
284
|
updateTags = true;
|
|
285
285
|
}
|
|
286
|
-
|
|
287
|
-
await AuditLogService.log({
|
|
288
|
-
type: AuditLogType.OrganizationSettingsChanged,
|
|
289
|
-
organization,
|
|
290
|
-
oldMeta,
|
|
291
|
-
patch: request.body.meta,
|
|
292
|
-
});
|
|
293
286
|
}
|
|
294
287
|
|
|
295
288
|
if (request.body.active !== undefined) {
|
|
@@ -393,8 +386,16 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
393
386
|
if (updateTags) {
|
|
394
387
|
await TagHelper.updateOrganizations();
|
|
395
388
|
}
|
|
389
|
+
const struct = await AuthenticatedStructures.organization(organization);
|
|
390
|
+
|
|
391
|
+
await AuditLogService.log({
|
|
392
|
+
type: AuditLogType.OrganizationSettingsChanged,
|
|
393
|
+
organization,
|
|
394
|
+
oldData: initialStruct,
|
|
395
|
+
patch: struct,
|
|
396
|
+
});
|
|
396
397
|
|
|
397
|
-
return new Response(
|
|
398
|
+
return new Response(struct);
|
|
398
399
|
}
|
|
399
400
|
|
|
400
401
|
async validateCompanies(organization: Organization, companies: PatchableArrayAutoEncoder<Company> | Company[]) {
|
|
@@ -425,6 +425,11 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
|
|
|
425
425
|
Member.updateMembershipsForGroupId(model.id);
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
+
if (Object.keys(struct).length === 1 && struct.id) {
|
|
429
|
+
// Nothing changed
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
428
433
|
await AuditLogService.log({
|
|
429
434
|
type: AuditLogType.GroupEdited,
|
|
430
435
|
group: model,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
3
3
|
import { StripeAccount } from '@stamhoofd/models';
|
|
4
|
-
import { PermissionLevel, StripeAccount as StripeAccountStruct } from '@stamhoofd/structures';
|
|
4
|
+
import { AuditLogType, PermissionLevel, StripeAccount as StripeAccountStruct } from '@stamhoofd/structures';
|
|
5
5
|
import Stripe from 'stripe';
|
|
6
6
|
|
|
7
7
|
import { Context } from '../../../../helpers/Context';
|
|
8
8
|
import { StripeHelper } from '../../../../helpers/StripeHelper';
|
|
9
|
+
import { AuditLogService } from '../../../../services/AuditLogService';
|
|
9
10
|
type Params = Record<string, never>;
|
|
10
11
|
type Body = undefined;
|
|
11
12
|
type Query = undefined;
|
|
@@ -91,6 +92,12 @@ export class ConnectMollieEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
91
92
|
model.setMetaFromStripeAccount(account);
|
|
92
93
|
await model.save();
|
|
93
94
|
|
|
95
|
+
// Track audit log
|
|
96
|
+
await AuditLogService.log({
|
|
97
|
+
type: AuditLogType.StripeAccountAdded,
|
|
98
|
+
stripeAccount: model,
|
|
99
|
+
});
|
|
100
|
+
|
|
94
101
|
// Return information about the Stripe Account
|
|
95
102
|
|
|
96
103
|
return new Response(StripeAccountStruct.create(model));
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { StripeAccount } from '@stamhoofd/models';
|
|
3
|
-
import { PermissionLevel } from '@stamhoofd/structures';
|
|
3
|
+
import { AuditLogType, PermissionLevel } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
5
|
import { Context } from '../../../../helpers/Context';
|
|
6
6
|
import { StripeHelper } from '../../../../helpers/StripeHelper';
|
|
7
7
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
8
|
+
import { AuditLogService } from '../../../../services/AuditLogService';
|
|
8
9
|
|
|
9
10
|
type Params = { id: string };
|
|
10
11
|
type Body = undefined;
|
|
@@ -63,6 +64,12 @@ export class DeleteStripeAccountEndpoint extends Endpoint<Params, Query, Body, R
|
|
|
63
64
|
model.status = 'deleted';
|
|
64
65
|
await model.save();
|
|
65
66
|
|
|
67
|
+
// Track audit log
|
|
68
|
+
await AuditLogService.log({
|
|
69
|
+
type: AuditLogType.StripeAccountDeleted,
|
|
70
|
+
stripeAccount: model,
|
|
71
|
+
});
|
|
72
|
+
|
|
66
73
|
return new Response(undefined);
|
|
67
74
|
}
|
|
68
75
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { StripeAccount } from '@stamhoofd/models';
|
|
3
|
-
import { PermissionLevel, StripeAccount as StripeAccountStruct } from '@stamhoofd/structures';
|
|
3
|
+
import { AuditLogType, PermissionLevel, StripeAccount as StripeAccountStruct } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
5
|
import { Context } from '../../../../helpers/Context';
|
|
6
6
|
import { StripeHelper } from '../../../../helpers/StripeHelper';
|
|
7
|
+
import { AuditLogService } from '../../../../services/AuditLogService';
|
|
8
|
+
import { Model } from '@simonbackx/simple-database';
|
|
7
9
|
|
|
8
10
|
type Params = { id: string };
|
|
9
11
|
type Body = undefined;
|
|
@@ -43,8 +45,18 @@ export class UpdateStripeAccountEndpoint extends Endpoint<Params, Query, Body, R
|
|
|
43
45
|
// Get account
|
|
44
46
|
const stripe = StripeHelper.getInstance();
|
|
45
47
|
const account = await stripe.accounts.retrieve(model.accountId);
|
|
48
|
+
const beforeMeta = model.meta;
|
|
46
49
|
model.setMetaFromStripeAccount(account);
|
|
47
|
-
|
|
50
|
+
|
|
51
|
+
if (await model.save()) {
|
|
52
|
+
// Track audit log
|
|
53
|
+
await AuditLogService.log({
|
|
54
|
+
type: AuditLogType.StripeAccountEdited,
|
|
55
|
+
stripeAccount: model,
|
|
56
|
+
oldData: beforeMeta,
|
|
57
|
+
patch: model.meta,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
48
60
|
|
|
49
61
|
return new Response(StripeAccountStruct.create(model));
|
|
50
62
|
}
|
|
@@ -5,14 +5,17 @@ import { Group as GroupStruct, PermissionLevel } from '@stamhoofd/structures';
|
|
|
5
5
|
import { PatchOrganizationRegistrationPeriodsEndpoint } from '../endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint';
|
|
6
6
|
import { AuthenticatedStructures } from './AuthenticatedStructures';
|
|
7
7
|
import { MemberUserSyncer } from './MemberUserSyncer';
|
|
8
|
+
import { AuditLogService } from '../services/AuditLogService';
|
|
8
9
|
|
|
9
10
|
export class PeriodHelper {
|
|
10
11
|
static async moveOrganizationToPeriod(organization: Organization, period: RegistrationPeriod) {
|
|
11
|
-
|
|
12
|
+
await AuditLogService.disable(async () => {
|
|
13
|
+
console.log('moveOrganizationToPeriod', organization.id, period.id);
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
await this.createOrganizationPeriodForPeriod(organization, period);
|
|
16
|
+
organization.periodId = period.id;
|
|
17
|
+
await organization.save();
|
|
18
|
+
});
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
static async stopAllResponsibilities() {
|
|
@@ -169,29 +172,31 @@ export class PeriodHelper {
|
|
|
169
172
|
|
|
170
173
|
const batchSize = 100;
|
|
171
174
|
await QueueHandler.schedule(tag, async () => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
175
|
+
await AuditLogService.disable(async () => {
|
|
176
|
+
let lastId = '';
|
|
177
|
+
|
|
178
|
+
while (true) {
|
|
179
|
+
const groups = await Group.where(
|
|
180
|
+
{
|
|
181
|
+
id: { sign: '>', value: lastId },
|
|
182
|
+
periodId: period.id,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
limit: batchSize,
|
|
186
|
+
sort: ['id'],
|
|
187
|
+
},
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
for (const group of groups) {
|
|
191
|
+
await PatchOrganizationRegistrationPeriodsEndpoint.patchGroup(GroupStruct.patch({ id: group.id }), period);
|
|
192
|
+
lastId = group.id;
|
|
193
|
+
}
|
|
190
194
|
|
|
191
|
-
|
|
192
|
-
|
|
195
|
+
if (groups.length < batchSize) {
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
193
198
|
}
|
|
194
|
-
}
|
|
199
|
+
});
|
|
195
200
|
});
|
|
196
201
|
}
|
|
197
202
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { AutoEncoder, AutoEncoderPatchType } from '@simonbackx/simple-encoding';
|
|
2
|
-
import { AuditLog, Group, Member, Organization, Registration, Event } from '@stamhoofd/models';
|
|
2
|
+
import { AuditLog, Group, Member, Organization, Registration, Event, RegistrationPeriod, StripeAccount } from '@stamhoofd/models';
|
|
3
3
|
import { AuditLogReplacement, AuditLogReplacementType, AuditLogType, GroupType, MemberDetails, OrganizationMetaData, OrganizationPrivateMetaData, PlatformConfig, PlatformPrivateConfig } from '@stamhoofd/structures';
|
|
4
4
|
import { Context } from '../helpers/Context';
|
|
5
5
|
import { explainPatch } from './explainPatch';
|
|
6
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
6
7
|
|
|
7
8
|
export type MemberAddedAuditOptions = {
|
|
8
9
|
type: AuditLogType.MemberAdded;
|
|
@@ -25,24 +26,16 @@ export type MemberRegisteredAuditOptions = {
|
|
|
25
26
|
|
|
26
27
|
export type PlatformConfigChangeAuditOptions = {
|
|
27
28
|
type: AuditLogType.PlatformSettingsChanged;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} | {
|
|
32
|
-
oldConfig: PlatformConfig;
|
|
33
|
-
patch: PlatformConfig | AutoEncoderPatchType<PlatformConfig>;
|
|
34
|
-
});
|
|
29
|
+
oldData?: AutoEncoder;
|
|
30
|
+
patch?: AutoEncoder | AutoEncoderPatchType<AutoEncoder>;
|
|
31
|
+
};
|
|
35
32
|
|
|
36
33
|
export type OrganizationConfigChangeAuditOptions = {
|
|
37
34
|
type: AuditLogType.OrganizationSettingsChanged;
|
|
38
35
|
organization: Organization;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
} | {
|
|
43
|
-
oldMeta: OrganizationPrivateMetaData;
|
|
44
|
-
patch: OrganizationPrivateMetaData | AutoEncoderPatchType<OrganizationPrivateMetaData>;
|
|
45
|
-
});
|
|
36
|
+
oldData?: AutoEncoder;
|
|
37
|
+
patch?: AutoEncoder | AutoEncoderPatchType<AutoEncoder>;
|
|
38
|
+
};
|
|
46
39
|
|
|
47
40
|
export type EventAuditOptions = {
|
|
48
41
|
type: AuditLogType.EventAdded | AuditLogType.EventEdited | AuditLogType.EventDeleted;
|
|
@@ -58,11 +51,46 @@ export type GroupAuditOptions = {
|
|
|
58
51
|
patch?: AutoEncoder | AutoEncoderPatchType<AutoEncoder>;
|
|
59
52
|
};
|
|
60
53
|
|
|
61
|
-
export type
|
|
54
|
+
export type PeriodAuditOptions = {
|
|
55
|
+
type: AuditLogType.RegistrationPeriodAdded | AuditLogType.RegistrationPeriodEdited | AuditLogType.RegistrationPeriodDeleted;
|
|
56
|
+
period: RegistrationPeriod;
|
|
57
|
+
oldData?: AutoEncoder;
|
|
58
|
+
patch?: AutoEncoder | AutoEncoderPatchType<AutoEncoder>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type StripeAccountAuditOptions = {
|
|
62
|
+
type: AuditLogType.StripeAccountAdded | AuditLogType.StripeAccountEdited | AuditLogType.StripeAccountDeleted;
|
|
63
|
+
stripeAccount: StripeAccount;
|
|
64
|
+
oldData?: AutoEncoder;
|
|
65
|
+
patch?: AutoEncoder | AutoEncoderPatchType<AutoEncoder>;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type AuditLogOptions = StripeAccountAuditOptions | PeriodAuditOptions | GroupAuditOptions | EventAuditOptions | MemberAddedAuditOptions | MemberEditedAuditOptions | MemberRegisteredAuditOptions | PlatformConfigChangeAuditOptions | OrganizationConfigChangeAuditOptions;
|
|
62
69
|
|
|
63
70
|
export const AuditLogService = {
|
|
71
|
+
disableLocalStore: new AsyncLocalStorage<boolean>(),
|
|
72
|
+
|
|
73
|
+
disable<T extends Promise<void> | void>(run: () => T): T {
|
|
74
|
+
return this.disableLocalStore.run(true, () => {
|
|
75
|
+
return run();
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
isDisabled(): boolean {
|
|
80
|
+
const c = this.disableLocalStore.getStore();
|
|
81
|
+
|
|
82
|
+
if (!c) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return true;
|
|
87
|
+
},
|
|
88
|
+
|
|
64
89
|
async log(options: AuditLogOptions) {
|
|
65
90
|
try {
|
|
91
|
+
if (this.isDisabled()) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
66
94
|
const userId = Context.optionalAuth?.user?.id ?? null;
|
|
67
95
|
const organizationId = Context.organization?.id ?? null;
|
|
68
96
|
|
|
@@ -96,11 +124,18 @@ export const AuditLogService = {
|
|
|
96
124
|
else if (options.type === AuditLogType.GroupAdded || options.type === AuditLogType.GroupEdited || options.type === AuditLogType.GroupDeleted) {
|
|
97
125
|
this.fillForGroup(model, options);
|
|
98
126
|
}
|
|
127
|
+
else if (options.type === AuditLogType.RegistrationPeriodAdded || options.type === AuditLogType.RegistrationPeriodEdited || options.type === AuditLogType.RegistrationPeriodDeleted) {
|
|
128
|
+
this.fillForPeriod(model, options);
|
|
129
|
+
}
|
|
130
|
+
else if (options.type === AuditLogType.StripeAccountAdded || options.type === AuditLogType.StripeAccountEdited || options.type === AuditLogType.StripeAccountDeleted) {
|
|
131
|
+
if (this.fillForStripeAccount(model, options) === false) {
|
|
132
|
+
// do not save
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
99
136
|
|
|
100
137
|
// In the future we might group these saves together in one query to improve performance
|
|
101
138
|
await model.save();
|
|
102
|
-
|
|
103
|
-
console.log('Audit log', model.id, options);
|
|
104
139
|
}
|
|
105
140
|
catch (e) {
|
|
106
141
|
console.error('Failed to save log', options, e);
|
|
@@ -162,7 +197,10 @@ export const AuditLogService = {
|
|
|
162
197
|
model.objectId = null;
|
|
163
198
|
|
|
164
199
|
// Generate changes list
|
|
165
|
-
|
|
200
|
+
if (options.patch) {
|
|
201
|
+
// Generate changes list
|
|
202
|
+
model.patchList = explainPatch(options.oldData ?? null, options.patch);
|
|
203
|
+
}
|
|
166
204
|
},
|
|
167
205
|
|
|
168
206
|
fillForOrganizationConfig(model: AuditLog, options: OrganizationConfigChangeAuditOptions) {
|
|
@@ -178,7 +216,10 @@ export const AuditLogService = {
|
|
|
178
216
|
]);
|
|
179
217
|
|
|
180
218
|
// Generate changes list
|
|
181
|
-
|
|
219
|
+
if (options.patch) {
|
|
220
|
+
// Generate changes list
|
|
221
|
+
model.patchList = explainPatch(options.oldData ?? null, options.patch);
|
|
222
|
+
}
|
|
182
223
|
},
|
|
183
224
|
|
|
184
225
|
fillForEvent(model: AuditLog, options: EventAuditOptions) {
|
|
@@ -229,4 +270,48 @@ export const AuditLogService = {
|
|
|
229
270
|
})],
|
|
230
271
|
]);
|
|
231
272
|
},
|
|
273
|
+
|
|
274
|
+
fillForPeriod(model: AuditLog, options: PeriodAuditOptions) {
|
|
275
|
+
model.objectId = options.period.id;
|
|
276
|
+
|
|
277
|
+
if (options.patch) {
|
|
278
|
+
// Generate changes list
|
|
279
|
+
model.patchList = explainPatch(options.oldData ?? null, options.patch);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
model.replacements = new Map([
|
|
283
|
+
['p', AuditLogReplacement.create({
|
|
284
|
+
id: options.period.id,
|
|
285
|
+
value: options.period.getStructure().nameShort,
|
|
286
|
+
type: AuditLogReplacementType.RegistrationPeriod,
|
|
287
|
+
})],
|
|
288
|
+
]);
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
fillForStripeAccount(model: AuditLog, options: StripeAccountAuditOptions) {
|
|
292
|
+
model.objectId = options.stripeAccount.id;
|
|
293
|
+
|
|
294
|
+
if (options.patch) {
|
|
295
|
+
// Generate changes list
|
|
296
|
+
model.patchList = explainPatch(options.oldData ?? null, options.patch);
|
|
297
|
+
|
|
298
|
+
if (model.patchList.length === 0) {
|
|
299
|
+
// No changes, ignore (only for stripe)
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (options.type === AuditLogType.StripeAccountEdited) {
|
|
305
|
+
// Never caused by a user
|
|
306
|
+
model.userId = null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
model.replacements = new Map([
|
|
310
|
+
['a', AuditLogReplacement.create({
|
|
311
|
+
id: options.stripeAccount.id,
|
|
312
|
+
value: options.stripeAccount.accountId,
|
|
313
|
+
type: AuditLogReplacementType.StripeAccount,
|
|
314
|
+
})],
|
|
315
|
+
]);
|
|
316
|
+
},
|
|
232
317
|
};
|