@stamhoofd/backend 2.96.1 → 2.96.3
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.96.
|
|
3
|
+
"version": "2.96.3",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"@simonbackx/simple-encoding": "2.22.0",
|
|
46
46
|
"@simonbackx/simple-endpoints": "1.20.1",
|
|
47
47
|
"@simonbackx/simple-logging": "^1.0.1",
|
|
48
|
-
"@stamhoofd/backend-i18n": "2.96.
|
|
49
|
-
"@stamhoofd/backend-middleware": "2.96.
|
|
50
|
-
"@stamhoofd/email": "2.96.
|
|
51
|
-
"@stamhoofd/models": "2.96.
|
|
52
|
-
"@stamhoofd/queues": "2.96.
|
|
53
|
-
"@stamhoofd/sql": "2.96.
|
|
54
|
-
"@stamhoofd/structures": "2.96.
|
|
55
|
-
"@stamhoofd/utility": "2.96.
|
|
48
|
+
"@stamhoofd/backend-i18n": "2.96.3",
|
|
49
|
+
"@stamhoofd/backend-middleware": "2.96.3",
|
|
50
|
+
"@stamhoofd/email": "2.96.3",
|
|
51
|
+
"@stamhoofd/models": "2.96.3",
|
|
52
|
+
"@stamhoofd/queues": "2.96.3",
|
|
53
|
+
"@stamhoofd/sql": "2.96.3",
|
|
54
|
+
"@stamhoofd/structures": "2.96.3",
|
|
55
|
+
"@stamhoofd/utility": "2.96.3",
|
|
56
56
|
"archiver": "^7.0.1",
|
|
57
57
|
"axios": "^1.8.2",
|
|
58
58
|
"cookie": "^0.7.0",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "9da200d4958b6959f604def8861ac00595a668cf"
|
|
74
74
|
}
|
|
@@ -21,7 +21,7 @@ async function balanceEmails() {
|
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
if ((new Date().getHours() >
|
|
24
|
+
if ((new Date().getHours() > 18 || new Date().getHours() < 6) && STAMHOOFD.environment !== 'development') {
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
|
-
import { Group, Member, Platform } from '@stamhoofd/models';
|
|
5
|
-
import { SQL, SQLSortDefinitions, applySQLSorter, compileToSQLFilter } from '@stamhoofd/sql';
|
|
4
|
+
import { Group, Member, Platform, Registration } from '@stamhoofd/models';
|
|
5
|
+
import { SQL, SQLExpression, SQLSelect, SQLSortDefinitions, applySQLSorter, compileToSQLFilter } from '@stamhoofd/sql';
|
|
6
6
|
import { CountFilteredRequest, GroupType, LimitedFilteredRequest, PaginatedResponse, PermissionLevel, StamhoofdFilter, assertSort } from '@stamhoofd/structures';
|
|
7
7
|
|
|
8
8
|
import { SQLResultNamespacedRow } from '@simonbackx/simple-database';
|
|
@@ -11,7 +11,7 @@ import { RegistrationWithMemberBlob } from '@stamhoofd/structures/dist/src/membe
|
|
|
11
11
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
12
12
|
import { Context } from '../../../helpers/Context';
|
|
13
13
|
import { LimitedFilteredRequestHelper } from '../../../helpers/LimitedFilteredRequestHelper';
|
|
14
|
-
import { registrationFilterCompilers } from '../../../sql-filters/registrations';
|
|
14
|
+
import { groupJoin, registrationFilterCompilers } from '../../../sql-filters/registrations';
|
|
15
15
|
import { registrationSorters } from '../../../sql-sorters/registrations';
|
|
16
16
|
import { GetMembersEndpoint } from '../members/GetMembersEndpoint';
|
|
17
17
|
import { validateGroupFilter } from '../members/helpers/validateGroupFilter';
|
|
@@ -40,6 +40,29 @@ export class GetRegistrationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
40
40
|
return [false];
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
static selectRegistrationWithGroup(...columns: (SQLExpression | string)[]): SQLSelect<Registration & { group: Group }> {
|
|
44
|
+
const transformer = (row: SQLResultNamespacedRow): Registration & { group: Group } => {
|
|
45
|
+
const d = Registration.fromRow(row[Registration.table]);
|
|
46
|
+
|
|
47
|
+
if (!d) {
|
|
48
|
+
console.error('Could not transform row', row, 'into model', Registration.table, 'check if the primary key is returned in the query');
|
|
49
|
+
throw new Error('Missing data for model ' + Registration.table);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const g = Group.fromRow(row[Group.table]);
|
|
53
|
+
if (!g) {
|
|
54
|
+
console.error('Could not transform row', row, 'into model', Group.table, 'check if the primary key is returned in the query');
|
|
55
|
+
throw new Error('Missing data for model ' + Group.table);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return d.setRelation(Registration.group, g);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const select = new SQLSelect(transformer, ...(columns.length === 0 ? [SQL.wildcard(), SQL.wildcard(Group.table)] : columns));
|
|
62
|
+
select.join(groupJoin);
|
|
63
|
+
return select.from(SQL.table(Registration.table));
|
|
64
|
+
}
|
|
65
|
+
|
|
43
66
|
static async buildQuery(q: CountFilteredRequest | LimitedFilteredRequest, permissionLevel: PermissionLevel = PermissionLevel.Read) {
|
|
44
67
|
const organization = Context.organization;
|
|
45
68
|
let scopeFilter: StamhoofdFilter | undefined = undefined;
|
|
@@ -115,15 +138,8 @@ export class GetRegistrationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
115
138
|
}
|
|
116
139
|
}
|
|
117
140
|
|
|
118
|
-
const query =
|
|
119
|
-
.select(
|
|
120
|
-
SQL.column('registrations', 'id'),
|
|
121
|
-
SQL.column('registrations', 'memberId'),
|
|
122
|
-
)
|
|
141
|
+
const query = this.selectRegistrationWithGroup()
|
|
123
142
|
.setMaxExecutionTime(15 * 1000)
|
|
124
|
-
.from(
|
|
125
|
-
SQL.table('registrations'),
|
|
126
|
-
)
|
|
127
143
|
.where('registeredAt', '!=', null);
|
|
128
144
|
|
|
129
145
|
if (scopeFilter) {
|
|
@@ -161,7 +177,9 @@ export class GetRegistrationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
161
177
|
|
|
162
178
|
static async buildData(requestQuery: LimitedFilteredRequest, permissionLevel = PermissionLevel.Read) {
|
|
163
179
|
const query = await GetRegistrationsEndpoint.buildQuery(requestQuery, permissionLevel);
|
|
164
|
-
let data:
|
|
180
|
+
let data: (Registration & {
|
|
181
|
+
group: Group;
|
|
182
|
+
})[];
|
|
165
183
|
|
|
166
184
|
try {
|
|
167
185
|
data = await query.fetch();
|
|
@@ -177,14 +195,13 @@ export class GetRegistrationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
177
195
|
throw error;
|
|
178
196
|
}
|
|
179
197
|
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
|
|
198
|
+
for (const registration of data) {
|
|
199
|
+
if (!await Context.auth.canAccessRegistration(registration, permissionLevel)) {
|
|
200
|
+
throw Context.auth.error();
|
|
183
201
|
}
|
|
184
|
-
|
|
185
|
-
});
|
|
202
|
+
}
|
|
186
203
|
|
|
187
|
-
const members = await Member.getBlobByIds(...
|
|
204
|
+
const members = await Member.getBlobByIds(...data.map(r => r.memberId));
|
|
188
205
|
|
|
189
206
|
for (const member of members) {
|
|
190
207
|
if (!await Context.auth.canAccessMember(member, permissionLevel)) {
|
|
@@ -192,7 +209,7 @@ export class GetRegistrationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
192
209
|
}
|
|
193
210
|
}
|
|
194
211
|
|
|
195
|
-
const registrationsBlob = await AuthenticatedStructures.registrationsBlob(
|
|
212
|
+
const registrationsBlob = await AuthenticatedStructures.registrationsBlob(data, members);
|
|
196
213
|
|
|
197
214
|
const next = LimitedFilteredRequestHelper.fixInfiniteLoadingLoop({
|
|
198
215
|
request: requestQuery,
|
|
@@ -456,6 +456,9 @@ export class AuthenticatedStructures {
|
|
|
456
456
|
|
|
457
457
|
const memberBlobs: MemberWithRegistrationsBlob[] = [];
|
|
458
458
|
for (const member of members) {
|
|
459
|
+
const filtered: (Registration & {
|
|
460
|
+
group: Group;
|
|
461
|
+
})[] = [];
|
|
459
462
|
for (const registration of member.registrations) {
|
|
460
463
|
if (includeContextOrganization || registration.organizationId !== Context.auth.organization?.id) {
|
|
461
464
|
const found = organizations.get(registration.id);
|
|
@@ -464,8 +467,11 @@ export class AuthenticatedStructures {
|
|
|
464
467
|
organizations.set(organization.id, organization);
|
|
465
468
|
}
|
|
466
469
|
}
|
|
470
|
+
if (organizations.get(registration.organizationId)?.active || (Context.auth.organization && Context.auth.organization.active && registration.organizationId === Context.auth.organization.id) || await Context.auth.hasFullAccess(registration.organizationId)) {
|
|
471
|
+
filtered.push(registration);
|
|
472
|
+
}
|
|
467
473
|
}
|
|
468
|
-
member.registrations =
|
|
474
|
+
member.registrations = filtered;
|
|
469
475
|
const balancesPermission = await Context.auth.hasFinancialMemberAccess(member, PermissionLevel.Read, Context.organization?.id);
|
|
470
476
|
|
|
471
477
|
let memberBalances: GenericBalance[] = [];
|
|
@@ -521,7 +527,13 @@ export class AuthenticatedStructures {
|
|
|
521
527
|
}
|
|
522
528
|
}
|
|
523
529
|
|
|
524
|
-
const activeOrganizations = [
|
|
530
|
+
const activeOrganizations: Organization[] = [];
|
|
531
|
+
|
|
532
|
+
for (const organization of organizations.values()) {
|
|
533
|
+
if (organization.active || await Context.auth.hasFullAccess(organization.id)) {
|
|
534
|
+
activeOrganizations.push(organization);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
525
537
|
const organizationStructs = await this.organizations(activeOrganizations);
|
|
526
538
|
|
|
527
539
|
// Load missing groups
|
|
@@ -588,30 +600,44 @@ export class AuthenticatedStructures {
|
|
|
588
600
|
return (await this.eventNotifications([eventNotification]))[0];
|
|
589
601
|
}
|
|
590
602
|
|
|
591
|
-
static async registrationsBlob(
|
|
592
|
-
memberId: string;
|
|
593
|
-
id: string;
|
|
594
|
-
}[], members: MemberWithRegistrations[], includeContextOrganization = false, includeUser?: User): Promise<RegistrationsBlob> {
|
|
603
|
+
static async registrationsBlob(registrations: (Registration & { group: Group })[], members: MemberWithRegistrations[], includeContextOrganization = false, includeUser?: User): Promise<RegistrationsBlob> {
|
|
595
604
|
const membersBlob = await this.membersBlob(members, includeContextOrganization, includeUser);
|
|
596
605
|
|
|
597
606
|
const memberBlobs = membersBlob.members;
|
|
598
607
|
|
|
599
|
-
const registrationWithMemberBlobs =
|
|
600
|
-
const memberBlob = memberBlobs.find(m => m.id === memberId);
|
|
608
|
+
const registrationWithMemberBlobs = await Promise.all(registrations.map(async (registration) => {
|
|
609
|
+
const memberBlob = memberBlobs.find(m => m.id === registration.memberId);
|
|
601
610
|
if (!memberBlob) {
|
|
602
611
|
throw new Error('Member not found');
|
|
603
612
|
}
|
|
604
613
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
614
|
+
let r = memberBlob.registrations.find(r => r.id === registration.id);
|
|
615
|
+
|
|
616
|
+
if (!r) {
|
|
617
|
+
const member = members.find(m => m.id === registration.memberId);
|
|
618
|
+
const balancesPermission = member ? (await Context.auth.hasFinancialMemberAccess(member, PermissionLevel.Read, Context.organization?.id)) : false;
|
|
619
|
+
r = registration.getStructure();
|
|
620
|
+
r.balances = balancesPermission
|
|
621
|
+
? ((await CachedBalance.getForObjects([registration.id], null)).map((b) => {
|
|
622
|
+
return GenericBalance.create(b);
|
|
623
|
+
}))
|
|
624
|
+
: [];
|
|
625
|
+
r.group = await this.group(registration.group);
|
|
626
|
+
|
|
627
|
+
memberBlob.registrations.push(r);
|
|
628
|
+
|
|
629
|
+
// Add organization if missing
|
|
630
|
+
if (!membersBlob.organizations.find(o => o.id === r!.organizationId)) {
|
|
631
|
+
const organization = await Context.auth.getOrganization(r!.organizationId);
|
|
632
|
+
membersBlob.organizations.push(organization.getBaseStructure());
|
|
633
|
+
}
|
|
608
634
|
}
|
|
609
635
|
|
|
610
636
|
return RegistrationWithMemberBlob.create({
|
|
611
|
-
...
|
|
637
|
+
...r,
|
|
612
638
|
member: memberBlob,
|
|
613
639
|
});
|
|
614
|
-
});
|
|
640
|
+
}));
|
|
615
641
|
|
|
616
642
|
return RegistrationsBlob.create({
|
|
617
643
|
registrations: registrationWithMemberBlobs,
|
|
@@ -5,7 +5,7 @@ import { memberFilterCompilers } from './members';
|
|
|
5
5
|
|
|
6
6
|
export const memberJoin = SQL.join(Member.table).where(SQL.column(Member.table, 'id'), SQL.column(Registration.table, 'memberId'));
|
|
7
7
|
|
|
8
|
-
const groupJoin = SQL.join(Group.table).where(SQL.column(Group.table, 'id'), SQL.column(Registration.table, 'groupId'));
|
|
8
|
+
export const groupJoin = SQL.join(Group.table).where(SQL.column(Group.table, 'id'), SQL.column(Registration.table, 'groupId'));
|
|
9
9
|
|
|
10
10
|
export const registrationFilterCompilers: SQLFilterDefinitions = {
|
|
11
11
|
...baseSQLFilterCompilers,
|