@stamhoofd/backend 2.73.3 → 2.75.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/index.ts +7 -2
- package/package.json +13 -13
- package/src/audit-logs/MemberPlatformMembershipLogger.ts +1 -1
- package/src/crons/update-cached-balances.ts +1 -2
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +2 -2
- package/src/endpoints/auth/CreateAdminEndpoint.ts +4 -15
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +2 -2
- package/src/endpoints/global/events/GetEventNotificationsCountEndpoint.ts +43 -0
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +181 -0
- package/src/endpoints/global/events/GetEventsEndpoint.ts +2 -2
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +288 -0
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +2 -2
- package/src/endpoints/global/files/UploadFile.ts +56 -4
- package/src/endpoints/global/files/UploadImage.ts +9 -3
- package/src/endpoints/global/members/GetMembersEndpoint.ts +2 -2
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +14 -5
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +1 -5
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +7 -0
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +1 -1
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +1756 -164
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +2 -2
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +48 -2
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +8 -0
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +3 -3
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +2 -2
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +1 -2
- package/src/helpers/AdminPermissionChecker.ts +80 -2
- package/src/helpers/AuthenticatedStructures.ts +88 -2
- package/src/helpers/FlagMomentCleanup.ts +1 -8
- package/src/helpers/GlobalHelper.ts +15 -0
- package/src/helpers/MembershipCharger.ts +2 -1
- package/src/seeds-temporary/README.md +1 -0
- package/src/services/EventNotificationService.ts +201 -0
- package/src/services/FileSignService.ts +227 -0
- package/src/services/PlatformMembershipService.ts +38 -14
- package/src/sql-filters/event-notifications.ts +39 -0
- package/src/sql-filters/organizations.ts +1 -1
- package/src/sql-sorters/event-notifications.ts +96 -0
- package/src/sql-sorters/events.ts +2 -2
- package/src/sql-sorters/organizations.ts +2 -2
- package/tests/e2e/private-files.test.ts +497 -0
- package/tests/e2e/register.test.ts +762 -0
- package/tests/helpers/TestServer.ts +3 -0
- package/tests/jest.setup.ts +15 -2
- package/tsconfig.json +1 -0
- /package/src/{seeds → seeds-temporary}/1732117645-move-rrn.ts +0 -0
- /package/src/{seeds → seeds-temporary}/1736266448-recall-balance-item-price-paid.ts +0 -0
package/index.ts
CHANGED
|
@@ -3,7 +3,6 @@ backendEnv.load();
|
|
|
3
3
|
|
|
4
4
|
import { Column, Database, Migration } from '@simonbackx/simple-database';
|
|
5
5
|
import { CORSPreflightEndpoint, Router, RouterServer } from '@simonbackx/simple-endpoints';
|
|
6
|
-
import { I18n } from '@stamhoofd/backend-i18n';
|
|
7
6
|
import { CORSMiddleware, LogMiddleware, VersionMiddleware } from '@stamhoofd/backend-middleware';
|
|
8
7
|
import { Email } from '@stamhoofd/email';
|
|
9
8
|
import { loadLogger } from '@stamhoofd/logging';
|
|
@@ -13,10 +12,12 @@ import { sleep } from '@stamhoofd/utility';
|
|
|
13
12
|
import { startCrons, stopCrons, waitForCrons } from '@stamhoofd/crons';
|
|
14
13
|
import { Platform } from '@stamhoofd/models';
|
|
15
14
|
import { resumeEmails } from './src/helpers/EmailResumer';
|
|
15
|
+
import { GlobalHelper } from './src/helpers/GlobalHelper';
|
|
16
16
|
import { SetupStepUpdater } from './src/helpers/SetupStepUpdater';
|
|
17
17
|
import { ContextMiddleware } from './src/middleware/ContextMiddleware';
|
|
18
18
|
import { AuditLogService } from './src/services/AuditLogService';
|
|
19
19
|
import { DocumentService } from './src/services/DocumentService';
|
|
20
|
+
import { FileSignService } from './src/services/FileSignService';
|
|
20
21
|
import { PlatformMembershipService } from './src/services/PlatformMembershipService';
|
|
21
22
|
|
|
22
23
|
process.on('unhandledRejection', (error: Error) => {
|
|
@@ -52,7 +53,8 @@ const seeds = async () => {
|
|
|
52
53
|
const start = async () => {
|
|
53
54
|
console.log('Running server at v' + Version);
|
|
54
55
|
loadLogger();
|
|
55
|
-
await
|
|
56
|
+
await GlobalHelper.load();
|
|
57
|
+
|
|
56
58
|
const router = new Router();
|
|
57
59
|
await router.loadAllEndpoints(__dirname + '/src/endpoints/global/*');
|
|
58
60
|
await router.loadAllEndpoints(__dirname + '/src/endpoints/admin/*');
|
|
@@ -72,6 +74,9 @@ const start = async () => {
|
|
|
72
74
|
routerServer.addRequestMiddleware(LogMiddleware);
|
|
73
75
|
routerServer.addResponseMiddleware(LogMiddleware);
|
|
74
76
|
|
|
77
|
+
routerServer.addResponseMiddleware(FileSignService);
|
|
78
|
+
routerServer.addRequestMiddleware(FileSignService);
|
|
79
|
+
|
|
75
80
|
// Contexts
|
|
76
81
|
routerServer.addRequestMiddleware(ContextMiddleware);
|
|
77
82
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/backend",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.75.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -33,18 +33,18 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@bwip-js/node": "^4.5.1",
|
|
35
35
|
"@mollie/api-client": "3.7.0",
|
|
36
|
-
"@simonbackx/simple-database": "1.
|
|
37
|
-
"@simonbackx/simple-encoding": "2.
|
|
38
|
-
"@simonbackx/simple-endpoints": "1.
|
|
36
|
+
"@simonbackx/simple-database": "1.29.0",
|
|
37
|
+
"@simonbackx/simple-encoding": "2.20.0",
|
|
38
|
+
"@simonbackx/simple-endpoints": "1.19.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.75.0",
|
|
41
|
+
"@stamhoofd/backend-middleware": "2.75.0",
|
|
42
|
+
"@stamhoofd/email": "2.75.0",
|
|
43
|
+
"@stamhoofd/models": "2.75.0",
|
|
44
|
+
"@stamhoofd/queues": "2.75.0",
|
|
45
|
+
"@stamhoofd/sql": "2.75.0",
|
|
46
|
+
"@stamhoofd/structures": "2.75.0",
|
|
47
|
+
"@stamhoofd/utility": "2.75.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": "4710a9386f9f489356e04518622cc83c5faa2bf1"
|
|
68
68
|
}
|
|
@@ -10,7 +10,7 @@ const defaultGenerator = getDefaultGenerator({
|
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
export const MemberPlatformMembershipLogger = new ModelLogger(MemberPlatformMembership, {
|
|
13
|
-
skipKeys: ['balanceItemId'],
|
|
13
|
+
skipKeys: ['balanceItemId', 'priceWithoutDiscount', 'maximumFreeAmount', 'generated'],
|
|
14
14
|
async optionsGenerator(event) {
|
|
15
15
|
const result = await defaultGenerator(event);
|
|
16
16
|
|
|
@@ -5,8 +5,7 @@ registerCron('updateCachedBalances', updateCachedBalances);
|
|
|
5
5
|
|
|
6
6
|
async function updateCachedBalances() {
|
|
7
7
|
// Check if between 3 - 6 AM
|
|
8
|
-
if ((new Date().getHours() > 6 || new Date().getHours() < 3)
|
|
9
|
-
console.log('Not between 3 and 6 AM, skipping.');
|
|
8
|
+
if ((new Date().getHours() > 6 || new Date().getHours() < 3)) {
|
|
10
9
|
return;
|
|
11
10
|
}
|
|
12
11
|
|
|
@@ -2,7 +2,7 @@ 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
4
|
import { Organization } from '@stamhoofd/models';
|
|
5
|
-
import { SQL, compileToSQLFilter,
|
|
5
|
+
import { SQL, compileToSQLFilter, applySQLSorter } from '@stamhoofd/sql';
|
|
6
6
|
import { CountFilteredRequest, LimitedFilteredRequest, Organization as OrganizationStruct, PaginatedResponse, PermissionLevel, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
7
7
|
|
|
8
8
|
import { SQLResultNamespacedRow } from '@simonbackx/simple-database';
|
|
@@ -90,7 +90,7 @@ export class GetOrganizationsEndpoint extends Endpoint<Params, Query, Body, Resp
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
q.sort = assertSort(q.sort, [{ key: 'id' }]);
|
|
93
|
-
query
|
|
93
|
+
applySQLSorter(query, q.sort, sorters);
|
|
94
94
|
query.limit(q.limit);
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -74,15 +74,10 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
if (organization) {
|
|
77
|
-
admin.permissions = UserPermissions.
|
|
77
|
+
admin.permissions = UserPermissions.limitedAdd(admin.permissions, request.body.permissions, organization.id);
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
80
|
-
|
|
81
|
-
admin.permissions.patchOrPut(request.body.permissions);
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
admin.permissions = request.body.permissions.isPut() ? request.body.permissions : null;
|
|
85
|
-
}
|
|
80
|
+
admin.permissions = UserPermissions.add(admin.permissions, request.body.permissions);
|
|
86
81
|
}
|
|
87
82
|
|
|
88
83
|
if (!admin.firstName && request.body.firstName) {
|
|
@@ -114,12 +109,10 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
114
109
|
await sendEmailTemplate(organization, {
|
|
115
110
|
recipients: [
|
|
116
111
|
Recipient.create({
|
|
112
|
+
firstName: admin.firstName,
|
|
113
|
+
lastName: admin.lastName,
|
|
117
114
|
email,
|
|
118
115
|
replacements: [
|
|
119
|
-
Replacement.create({
|
|
120
|
-
token: 'greeting',
|
|
121
|
-
value: admin.firstName ? `Dag ${admin.firstName},` : 'Hallo!',
|
|
122
|
-
}),
|
|
123
116
|
Replacement.create({
|
|
124
117
|
token: 'resetUrl',
|
|
125
118
|
value: recoveryUrl,
|
|
@@ -136,10 +129,6 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
136
129
|
token: 'validUntil',
|
|
137
130
|
value: dateTime,
|
|
138
131
|
}),
|
|
139
|
-
Replacement.create({
|
|
140
|
-
token: 'email',
|
|
141
|
-
value: admin.email,
|
|
142
|
-
}),
|
|
143
132
|
],
|
|
144
133
|
}),
|
|
145
134
|
],
|
|
@@ -2,7 +2,7 @@ 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
4
|
import { AuditLog } from '@stamhoofd/models';
|
|
5
|
-
import { SQL, SQLFilterDefinitions, SQLSortDefinitions, compileToSQLFilter,
|
|
5
|
+
import { SQL, SQLFilterDefinitions, SQLSortDefinitions, compileToSQLFilter, applySQLSorter } from '@stamhoofd/sql';
|
|
6
6
|
import { AuditLog as AuditLogStruct, CountFilteredRequest, LimitedFilteredRequest, PaginatedResponse, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
7
7
|
|
|
8
8
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
@@ -82,7 +82,7 @@ export class GetAuditLogsEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
q.sort = assertSort(q.sort, [{ key: 'id' }]);
|
|
85
|
-
query
|
|
85
|
+
applySQLSorter(query, q.sort, sorters);
|
|
86
86
|
query.limit(q.limit);
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { CountFilteredRequest, CountResponse } from '@stamhoofd/structures';
|
|
4
|
+
|
|
5
|
+
import { Context } from '../../../helpers/Context';
|
|
6
|
+
import { GetEventNotificationsEndpoint } from './GetEventNotificationsEndpoint';
|
|
7
|
+
|
|
8
|
+
type Params = Record<string, never>;
|
|
9
|
+
type Query = CountFilteredRequest;
|
|
10
|
+
type Body = undefined;
|
|
11
|
+
type ResponseBody = CountResponse;
|
|
12
|
+
|
|
13
|
+
export class GetEventNotificationsCountEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
14
|
+
queryDecoder = CountFilteredRequest as Decoder<CountFilteredRequest>;
|
|
15
|
+
|
|
16
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
17
|
+
if (request.method !== 'GET') {
|
|
18
|
+
return [false];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const params = Endpoint.parseParameters(request.url, '/event-notifications/count', {});
|
|
22
|
+
|
|
23
|
+
if (params) {
|
|
24
|
+
return [true, params as Params];
|
|
25
|
+
}
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
30
|
+
await Context.setOptionalOrganizationScope();
|
|
31
|
+
await Context.authenticate();
|
|
32
|
+
const query = await GetEventNotificationsEndpoint.buildQuery(request.query);
|
|
33
|
+
|
|
34
|
+
const count = await query
|
|
35
|
+
.count();
|
|
36
|
+
|
|
37
|
+
return new Response(
|
|
38
|
+
CountResponse.create({
|
|
39
|
+
count,
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
|
+
import { EventNotification } from '@stamhoofd/models';
|
|
5
|
+
import { SQL, SQLFilterDefinitions, SQLSortDefinitions, applySQLSorter, compileToSQLFilter } from '@stamhoofd/sql';
|
|
6
|
+
import { AccessRight, CountFilteredRequest, EventNotification as EventNotificationStruct, LimitedFilteredRequest, PaginatedResponse, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
7
|
+
|
|
8
|
+
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
9
|
+
import { Context } from '../../../helpers/Context';
|
|
10
|
+
import { eventNotificationsFilterCompilers } from '../../../sql-filters/event-notifications';
|
|
11
|
+
import { eventNotificationsSorters } from '../../../sql-sorters/event-notifications';
|
|
12
|
+
import { SQLResultNamespacedRow } from '@simonbackx/simple-database';
|
|
13
|
+
|
|
14
|
+
type Params = Record<string, never>;
|
|
15
|
+
type Query = LimitedFilteredRequest;
|
|
16
|
+
type Body = undefined;
|
|
17
|
+
type ResponseBody = PaginatedResponse<EventNotificationStruct[], LimitedFilteredRequest>;
|
|
18
|
+
|
|
19
|
+
const filterCompilers: SQLFilterDefinitions = eventNotificationsFilterCompilers;
|
|
20
|
+
const sorters: SQLSortDefinitions<SQLResultNamespacedRow> = eventNotificationsSorters;
|
|
21
|
+
|
|
22
|
+
export class GetEventNotificationsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
23
|
+
queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest>;
|
|
24
|
+
|
|
25
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
26
|
+
if (request.method !== 'GET') {
|
|
27
|
+
return [false];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const params = Endpoint.parseParameters(request.url, '/event-notifications', {});
|
|
31
|
+
|
|
32
|
+
if (params) {
|
|
33
|
+
return [true, params as Params];
|
|
34
|
+
}
|
|
35
|
+
return [false];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static async buildQuery(q: CountFilteredRequest | LimitedFilteredRequest) {
|
|
39
|
+
const organization = Context.organization;
|
|
40
|
+
let scopeFilter: StamhoofdFilter | undefined = undefined;
|
|
41
|
+
|
|
42
|
+
// todo: add proper scoping and permission checking
|
|
43
|
+
if (organization) {
|
|
44
|
+
scopeFilter = {
|
|
45
|
+
organizationId: organization.id,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!organization) {
|
|
50
|
+
// Get all tags
|
|
51
|
+
const tags = Context.auth.getOrganizationTagsWithAccessRight(AccessRight.OrganizationEventNotificationReviewer);
|
|
52
|
+
|
|
53
|
+
if (tags !== 'all') {
|
|
54
|
+
if (tags.length === 0) {
|
|
55
|
+
throw Context.auth.error();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
scopeFilter = {
|
|
59
|
+
organization: {
|
|
60
|
+
$elemMatch: {
|
|
61
|
+
tags: {
|
|
62
|
+
$in: tags,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const query = SQL
|
|
71
|
+
.select(
|
|
72
|
+
SQL.wildcard(EventNotification.table),
|
|
73
|
+
)
|
|
74
|
+
.from(
|
|
75
|
+
SQL.table(EventNotification.table),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (scopeFilter) {
|
|
79
|
+
query.where(await compileToSQLFilter(scopeFilter, filterCompilers));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (q.filter) {
|
|
83
|
+
query.where(await compileToSQLFilter(q.filter, filterCompilers));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (q.search) {
|
|
87
|
+
let searchFilter: StamhoofdFilter | null = null;
|
|
88
|
+
|
|
89
|
+
searchFilter = {
|
|
90
|
+
events: {
|
|
91
|
+
$elemMatch: {
|
|
92
|
+
name: {
|
|
93
|
+
$contains: q.search,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
if (searchFilter) {
|
|
100
|
+
query.where(await compileToSQLFilter(searchFilter, filterCompilers));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (q instanceof LimitedFilteredRequest) {
|
|
105
|
+
if (q.pageFilter) {
|
|
106
|
+
query.where(await compileToSQLFilter(q.pageFilter, filterCompilers));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
q.sort = assertSort(q.sort, [{ key: 'id' }]);
|
|
110
|
+
applySQLSorter(query, q.sort, sorters);
|
|
111
|
+
query.limit(q.limit);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return query;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static async buildData(requestQuery: LimitedFilteredRequest) {
|
|
118
|
+
const query = await GetEventNotificationsEndpoint.buildQuery(requestQuery);
|
|
119
|
+
const data = await query.fetch();
|
|
120
|
+
|
|
121
|
+
const notifications = EventNotification.fromRows(data, EventNotification.table);
|
|
122
|
+
|
|
123
|
+
let next: LimitedFilteredRequest | undefined;
|
|
124
|
+
|
|
125
|
+
if (notifications.length >= requestQuery.limit) {
|
|
126
|
+
const lastObject = data[data.length - 1];
|
|
127
|
+
const nextFilter = getSortFilter(lastObject, sorters, requestQuery.sort);
|
|
128
|
+
|
|
129
|
+
next = new LimitedFilteredRequest({
|
|
130
|
+
filter: requestQuery.filter,
|
|
131
|
+
pageFilter: nextFilter,
|
|
132
|
+
sort: requestQuery.sort,
|
|
133
|
+
limit: requestQuery.limit,
|
|
134
|
+
search: requestQuery.search,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (JSON.stringify(nextFilter) === JSON.stringify(requestQuery.pageFilter)) {
|
|
138
|
+
console.error('Found infinite loading loop for', requestQuery);
|
|
139
|
+
next = undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const notification of notifications) {
|
|
144
|
+
if (!await Context.auth.canAccessEventNotification(notification)) {
|
|
145
|
+
throw Context.auth.error('Je hebt geen toegang om deze melding te bekijken');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return new PaginatedResponse<EventNotificationStruct[], LimitedFilteredRequest>({
|
|
150
|
+
results: await AuthenticatedStructures.eventNotifications(notifications),
|
|
151
|
+
next,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
156
|
+
await Context.setOptionalOrganizationScope();
|
|
157
|
+
await Context.authenticate();
|
|
158
|
+
|
|
159
|
+
const maxLimit = Context.auth.hasSomePlatformAccess() ? 1000 : 100;
|
|
160
|
+
|
|
161
|
+
if (request.query.limit > maxLimit) {
|
|
162
|
+
throw new SimpleError({
|
|
163
|
+
code: 'invalid_field',
|
|
164
|
+
field: 'limit',
|
|
165
|
+
message: 'Limit can not be more than ' + maxLimit,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (request.query.limit < 1) {
|
|
170
|
+
throw new SimpleError({
|
|
171
|
+
code: 'invalid_field',
|
|
172
|
+
field: 'limit',
|
|
173
|
+
message: 'Limit can not be less than 1',
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return new Response(
|
|
178
|
+
await GetEventNotificationsEndpoint.buildData(request.query),
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -2,7 +2,7 @@ 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
4
|
import { Event } from '@stamhoofd/models';
|
|
5
|
-
import { SQL, SQLFilterDefinitions, SQLSortDefinitions, compileToSQLFilter,
|
|
5
|
+
import { SQL, SQLFilterDefinitions, SQLSortDefinitions, compileToSQLFilter, applySQLSorter } from '@stamhoofd/sql';
|
|
6
6
|
import { CountFilteredRequest, Event as EventStruct, LimitedFilteredRequest, PaginatedResponse, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
|
|
7
7
|
|
|
8
8
|
import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
|
|
@@ -88,7 +88,7 @@ export class GetEventsEndpoint extends Endpoint<Params, Query, Body, ResponseBod
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
q.sort = assertSort(q.sort, [{ key: 'id' }]);
|
|
91
|
-
query
|
|
91
|
+
applySQLSorter(query, q.sort, sorters);
|
|
92
92
|
query.limit(q.limit);
|
|
93
93
|
}
|
|
94
94
|
|