@stamhoofd/backend 2.39.0 → 2.40.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/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 +102 -103
- 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 +50 -52
- 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
|
@@ -1,46 +1,46 @@
|
|
|
1
1
|
import { Decoder } from '@simonbackx/simple-encoding';
|
|
2
|
-
import { DecodedRequest, Endpoint, Request, Response } from
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { I18n } from '@stamhoofd/backend-i18n';
|
|
5
5
|
import { Email } from '@stamhoofd/email';
|
|
6
|
-
import { getEmailBuilder,RateLimiter } from '@stamhoofd/models';
|
|
7
|
-
import { EmailRequest, Recipient } from
|
|
6
|
+
import { getEmailBuilder, RateLimiter } from '@stamhoofd/models';
|
|
7
|
+
import { EmailRequest, Recipient } from '@stamhoofd/structures';
|
|
8
8
|
|
|
9
9
|
import { Context } from '../../../../helpers/Context';
|
|
10
10
|
|
|
11
11
|
type Params = Record<string, never>;
|
|
12
12
|
type Query = undefined;
|
|
13
|
-
type Body = EmailRequest
|
|
13
|
+
type Body = EmailRequest;
|
|
14
14
|
type ResponseBody = undefined;
|
|
15
15
|
|
|
16
16
|
export const paidEmailRateLimiter = new RateLimiter({
|
|
17
17
|
limits: [
|
|
18
|
-
{
|
|
18
|
+
{
|
|
19
19
|
// Max 5.000 emails a day
|
|
20
20
|
limit: 5000,
|
|
21
|
-
duration: 24 * 60 * 1000 * 60
|
|
21
|
+
duration: 24 * 60 * 1000 * 60,
|
|
22
22
|
},
|
|
23
|
-
{
|
|
23
|
+
{
|
|
24
24
|
// 10.000 requests per week
|
|
25
25
|
limit: 10000,
|
|
26
|
-
duration: 24 * 60 * 1000 * 60 * 7
|
|
27
|
-
}
|
|
28
|
-
]
|
|
26
|
+
duration: 24 * 60 * 1000 * 60 * 7,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
export const freeEmailRateLimiter = new RateLimiter({
|
|
32
32
|
limits: [
|
|
33
|
-
{
|
|
33
|
+
{
|
|
34
34
|
// Max 100 a day
|
|
35
35
|
limit: 100,
|
|
36
|
-
duration: 24 * 60 * 1000 * 60
|
|
36
|
+
duration: 24 * 60 * 1000 * 60,
|
|
37
37
|
},
|
|
38
|
-
{
|
|
38
|
+
{
|
|
39
39
|
// Max 200 a week
|
|
40
40
|
limit: 200,
|
|
41
|
-
duration: 7 * 24 * 60 * 1000 * 60
|
|
42
|
-
}
|
|
43
|
-
]
|
|
41
|
+
duration: 7 * 24 * 60 * 1000 * 60,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -48,14 +48,14 @@ export const freeEmailRateLimiter = new RateLimiter({
|
|
|
48
48
|
*/
|
|
49
49
|
|
|
50
50
|
export class EmailEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
51
|
-
bodyDecoder = EmailRequest as Decoder<EmailRequest
|
|
51
|
+
bodyDecoder = EmailRequest as Decoder<EmailRequest>;
|
|
52
52
|
|
|
53
53
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
54
|
-
if (request.method
|
|
54
|
+
if (request.method !== 'POST') {
|
|
55
55
|
return [false];
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const params = Endpoint.parseParameters(request.url,
|
|
58
|
+
const params = Endpoint.parseParameters(request.url, '/email/legacy', {});
|
|
59
59
|
|
|
60
60
|
if (params) {
|
|
61
61
|
return [true, params as Params];
|
|
@@ -65,152 +65,153 @@ export class EmailEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
|
65
65
|
|
|
66
66
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
67
67
|
const organization = await Context.setOrganizationScope();
|
|
68
|
-
const {user} = await Context.authenticate()
|
|
68
|
+
const { user } = await Context.authenticate();
|
|
69
69
|
|
|
70
70
|
if (!Context.auth.canSendEmails()) {
|
|
71
|
-
throw Context.auth.error()
|
|
72
|
-
}
|
|
71
|
+
throw Context.auth.error();
|
|
72
|
+
}
|
|
73
73
|
|
|
74
74
|
if (request.body.recipients.length > 5000) {
|
|
75
75
|
throw new SimpleError({
|
|
76
|
-
code:
|
|
77
|
-
message:
|
|
78
|
-
human:
|
|
79
|
-
field:
|
|
80
|
-
})
|
|
76
|
+
code: 'too_many_recipients',
|
|
77
|
+
message: 'Too many recipients',
|
|
78
|
+
human: 'Je kan maar een mail naar maximaal 5000 personen tergelijk versturen. Contacteer ons om deze limiet te verhogen indien dit nodig is.',
|
|
79
|
+
field: 'recipients',
|
|
80
|
+
});
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// For non paid organizations, the limit is 10
|
|
84
84
|
if (request.body.recipients.length > 10 && !organization.meta.packages.isPaid) {
|
|
85
85
|
throw new SimpleError({
|
|
86
|
-
code:
|
|
87
|
-
message:
|
|
88
|
-
human:
|
|
89
|
-
field:
|
|
90
|
-
})
|
|
86
|
+
code: 'too_many_emails',
|
|
87
|
+
message: 'Too many e-mails',
|
|
88
|
+
human: 'Zolang je de demo versie van Stamhoofd gebruikt kan je maar maximaal een email sturen naar 10 emailadressen. Als je het pakket aankoopt zal deze limiet er niet zijn. Dit is om misbruik te voorkomen met spammers die spam email versturen via Stamhoofd.',
|
|
89
|
+
field: 'recipients',
|
|
90
|
+
});
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
const limiter = organization.meta.packages.isPaid ? paidEmailRateLimiter : freeEmailRateLimiter
|
|
93
|
+
const limiter = organization.meta.packages.isPaid ? paidEmailRateLimiter : freeEmailRateLimiter;
|
|
94
94
|
|
|
95
95
|
try {
|
|
96
96
|
limiter.track(organization.id, request.body.recipients.length);
|
|
97
|
-
}
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
98
99
|
Email.sendWebmaster({
|
|
99
|
-
subject:
|
|
100
|
-
text:
|
|
101
|
-
})
|
|
100
|
+
subject: '[Limiet] Limiet bereikt voor aantal e-mails',
|
|
101
|
+
text: 'Beste, \nDe limiet werd bereikt voor het aantal e-mails per dag. \nVereniging: ' + organization.id + ' (' + organization.name + ')' + '\n\n' + e.message + '\n\nStamhoofd',
|
|
102
|
+
});
|
|
102
103
|
|
|
103
104
|
throw new SimpleError({
|
|
104
|
-
code:
|
|
105
|
-
message:
|
|
106
|
-
human:
|
|
107
|
-
field:
|
|
108
|
-
})
|
|
105
|
+
code: 'too_many_emails_period',
|
|
106
|
+
message: 'Too many e-mails limited',
|
|
107
|
+
human: 'Oeps! Om spam te voorkomen limiteren we het aantal emails die je per dag/week kan versturen. Neem contact met ons op om deze limiet te verhogen.',
|
|
108
|
+
field: 'recipients',
|
|
109
|
+
});
|
|
109
110
|
}
|
|
110
|
-
|
|
111
111
|
|
|
112
112
|
// Validate email
|
|
113
|
-
const sender = organization.privateMeta.emails.find(e => e.id == request.body.emailId)
|
|
113
|
+
const sender = organization.privateMeta.emails.find(e => e.id == request.body.emailId);
|
|
114
114
|
if (!sender) {
|
|
115
115
|
throw new SimpleError({
|
|
116
|
-
code:
|
|
117
|
-
message:
|
|
118
|
-
human:
|
|
119
|
-
field:
|
|
120
|
-
})
|
|
116
|
+
code: 'invalid_field',
|
|
117
|
+
message: 'Invalid emailId',
|
|
118
|
+
human: 'Het e-mailadres waarvan je wilt versturen bestaat niet (meer). Kijk je het na?',
|
|
119
|
+
field: 'emailId',
|
|
120
|
+
});
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// Validate attachments
|
|
124
124
|
const size = request.body.attachments.reduce((value: number, attachment) => {
|
|
125
|
-
return value + attachment.content.length
|
|
126
|
-
}, 0)
|
|
127
|
-
|
|
128
|
-
if (size > 9.5*1024*1024) {
|
|
125
|
+
return value + attachment.content.length;
|
|
126
|
+
}, 0);
|
|
127
|
+
|
|
128
|
+
if (size > 9.5 * 1024 * 1024) {
|
|
129
129
|
throw new SimpleError({
|
|
130
|
-
code:
|
|
131
|
-
message:
|
|
132
|
-
human:
|
|
133
|
-
field:
|
|
134
|
-
})
|
|
130
|
+
code: 'too_big_attachments',
|
|
131
|
+
message: 'Too big attachments',
|
|
132
|
+
human: 'Jouw bericht is te groot. Grote bijlages verstuur je beter niet via e-mail, je plaatst dan best een link naar de locatie in bv. Google Drive. De maximale grootte van een e-mail is 10MB, inclusief het bericht. Als je grote bestanden verstuurt kan je ze ook proberen te verkleinen.',
|
|
133
|
+
field: 'attachments',
|
|
134
|
+
});
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
const safeContentTypes = [
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
]
|
|
138
|
+
'application/msword',
|
|
139
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
140
|
+
'application/vnd.ms-excel',
|
|
141
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
142
|
+
'application/pdf',
|
|
143
|
+
'image/jpeg',
|
|
144
|
+
'image/png',
|
|
145
|
+
'image/gif',
|
|
146
|
+
];
|
|
147
147
|
|
|
148
148
|
for (const attachment of request.body.attachments) {
|
|
149
149
|
if (attachment.contentType && !safeContentTypes.includes(attachment.contentType)) {
|
|
150
150
|
throw new SimpleError({
|
|
151
|
-
code:
|
|
152
|
-
message:
|
|
153
|
-
human:
|
|
154
|
-
field:
|
|
155
|
-
})
|
|
151
|
+
code: 'content_type_not_supported',
|
|
152
|
+
message: 'Content-Type not supported',
|
|
153
|
+
human: 'Het bestandstype van jouw bijlage wordt niet ondersteund of is onveilig om in een e-mail te plaatsen. Overweeg om je bestand op bv. Google Drive te zetten en de link in jouw e-mail te zetten.',
|
|
154
|
+
field: 'attachments',
|
|
155
|
+
});
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
const attachments = request.body.attachments.map((attachment, index) => {
|
|
160
|
-
let filename =
|
|
161
|
-
|
|
162
|
-
if (attachment.contentType ==
|
|
160
|
+
let filename = 'bijlage-' + index;
|
|
161
|
+
|
|
162
|
+
if (attachment.contentType == 'application/pdf') {
|
|
163
163
|
// tmp solution for pdf only
|
|
164
|
-
filename +=
|
|
164
|
+
filename += '.pdf';
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
// Correct file name if needed
|
|
168
168
|
if (attachment.filename) {
|
|
169
|
-
filename = attachment.filename.toLowerCase().replace(/[^a-z0-9.]+/g,
|
|
169
|
+
filename = attachment.filename.toLowerCase().replace(/[^a-z0-9.]+/g, '-').replace(/^-+/, '').replace(/-+$/, '');
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
return {
|
|
173
173
|
filename: filename,
|
|
174
174
|
content: attachment.content,
|
|
175
175
|
contentType: attachment.contentType ?? undefined,
|
|
176
|
-
encoding:
|
|
177
|
-
}
|
|
178
|
-
})
|
|
176
|
+
encoding: 'base64',
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
179
|
|
|
180
180
|
let from = organization.getDefaultFrom(request.i18n, false, 'broadcast');
|
|
181
181
|
let replyTo: string | undefined = sender.email;
|
|
182
182
|
|
|
183
183
|
// Can we send from this e-mail or reply-to?
|
|
184
|
-
if (organization.privateMeta.mailDomain && organization.privateMeta.mailDomainActive && sender.email.endsWith(
|
|
185
|
-
from = sender.email
|
|
184
|
+
if (organization.privateMeta.mailDomain && organization.privateMeta.mailDomainActive && sender.email.endsWith('@' + organization.privateMeta.mailDomain)) {
|
|
185
|
+
from = sender.email;
|
|
186
186
|
replyTo = undefined;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
// Include name in form field
|
|
190
190
|
if (sender.name) {
|
|
191
|
-
from = '"'+sender.name.replaceAll("
|
|
192
|
-
}
|
|
193
|
-
|
|
191
|
+
from = '"' + sender.name.replaceAll('"', '\\"') + '" <' + from + '>';
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
from = '"' + organization.name.replaceAll('"', '\\"') + '" <' + from + '>';
|
|
194
195
|
}
|
|
195
196
|
|
|
196
|
-
const email = request.body
|
|
197
|
+
const email = request.body;
|
|
197
198
|
|
|
198
199
|
if (!email.html) {
|
|
199
200
|
throw new SimpleError({
|
|
200
|
-
code:
|
|
201
|
-
message:
|
|
202
|
-
human:
|
|
203
|
-
field:
|
|
204
|
-
})
|
|
201
|
+
code: 'missing_field',
|
|
202
|
+
message: 'Missing html',
|
|
203
|
+
human: 'Je hebt geen inhoud ingevuld voor je e-mail. Vul een bericht in en probeer opnieuw.',
|
|
204
|
+
field: 'html',
|
|
205
|
+
});
|
|
205
206
|
}
|
|
206
207
|
|
|
207
208
|
if (!email.subject) {
|
|
208
209
|
throw new SimpleError({
|
|
209
|
-
code:
|
|
210
|
-
message:
|
|
211
|
-
human:
|
|
212
|
-
field:
|
|
213
|
-
})
|
|
210
|
+
code: 'missing_field',
|
|
211
|
+
message: 'Missing subject',
|
|
212
|
+
human: 'Je hebt geen onderwerp ingevuld voor je e-mail. Vul een onderwerp in en probeer opnieuw.',
|
|
213
|
+
field: 'subject',
|
|
214
|
+
});
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
// Create e-mail builder
|
|
@@ -221,32 +222,32 @@ export class EmailEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
|
221
222
|
from,
|
|
222
223
|
replyTo,
|
|
223
224
|
attachments,
|
|
224
|
-
defaultReplacements: request.body.defaultReplacements ?? []
|
|
225
|
-
})
|
|
225
|
+
defaultReplacements: request.body.defaultReplacements ?? [],
|
|
226
|
+
});
|
|
226
227
|
|
|
227
|
-
Email.schedule(builder)
|
|
228
|
+
Email.schedule(builder);
|
|
228
229
|
|
|
229
230
|
// Also send a copy
|
|
230
|
-
const recipient = Recipient.create(email.recipients[0])
|
|
231
|
-
recipient.email = sender.email
|
|
232
|
-
recipient.firstName = sender.name ?? null
|
|
233
|
-
recipient.lastName = null
|
|
234
|
-
recipient.userId = null
|
|
235
|
-
|
|
236
|
-
const prefix =
|
|
231
|
+
const recipient = Recipient.create(email.recipients[0]);
|
|
232
|
+
recipient.email = sender.email;
|
|
233
|
+
recipient.firstName = sender.name ?? null;
|
|
234
|
+
recipient.lastName = null;
|
|
235
|
+
recipient.userId = null;
|
|
236
|
+
|
|
237
|
+
const prefix = '<p><i>Kopie e-mail verzonden door ' + user.firstName + ' ' + user.lastName + '</i><br /><br /></p>';
|
|
237
238
|
const builder2 = await getEmailBuilder(organization, {
|
|
238
239
|
...email,
|
|
239
|
-
subject:
|
|
240
|
-
html: email.html.replace(
|
|
240
|
+
subject: '[KOPIE] ' + email.subject,
|
|
241
|
+
html: email.html.replace('<body>', '<body>' + prefix),
|
|
241
242
|
recipients: [
|
|
242
|
-
recipient
|
|
243
|
+
recipient,
|
|
243
244
|
],
|
|
244
245
|
from,
|
|
245
246
|
replyTo,
|
|
246
|
-
attachments
|
|
247
|
-
})
|
|
247
|
+
attachments,
|
|
248
|
+
});
|
|
248
249
|
|
|
249
|
-
Email.schedule(builder2)
|
|
250
|
+
Email.schedule(builder2);
|
|
250
251
|
|
|
251
252
|
return new Response(undefined);
|
|
252
253
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutoEncoder, Data, Decoder, EnumDecoder, field, StringDecoder } from '@simonbackx/simple-encoding';
|
|
2
|
-
import { DecodedRequest, Endpoint, Request, Response } from
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { EmailTemplate } from '@stamhoofd/models';
|
|
4
4
|
import { EmailTemplate as EmailTemplateStruct, EmailTemplateType } from '@stamhoofd/structures';
|
|
5
5
|
|
|
@@ -12,13 +12,13 @@ type Body = undefined;
|
|
|
12
12
|
|
|
13
13
|
class Query extends AutoEncoder {
|
|
14
14
|
@field({ decoder: new StringNullableDecoder(StringDecoder), optional: true, nullable: true })
|
|
15
|
-
webshopId: string|null = null
|
|
15
|
+
webshopId: string | null = null;
|
|
16
16
|
|
|
17
|
-
@field({ decoder: new StringNullableDecoder(new StringArrayDecoder(StringDecoder)), optional: true, nullable: true})
|
|
18
|
-
groupIds: string[]|null = null
|
|
17
|
+
@field({ decoder: new StringNullableDecoder(new StringArrayDecoder(StringDecoder)), optional: true, nullable: true })
|
|
18
|
+
groupIds: string[] | null = null;
|
|
19
19
|
|
|
20
|
-
@field({ decoder: new StringNullableDecoder(new StringArrayDecoder(new EnumDecoder(EmailTemplateType))), optional: true, nullable: true})
|
|
21
|
-
types: EmailTemplateType[]|null = null
|
|
20
|
+
@field({ decoder: new StringNullableDecoder(new StringArrayDecoder(new EnumDecoder(EmailTemplateType))), optional: true, nullable: true })
|
|
21
|
+
types: EmailTemplateType[] | null = null;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
type ResponseBody = EmailTemplateStruct[];
|
|
@@ -27,11 +27,11 @@ export class GetEmailTemplatesEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
27
27
|
queryDecoder = Query as Decoder<Query>;
|
|
28
28
|
|
|
29
29
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
30
|
-
if (request.method
|
|
30
|
+
if (request.method !== 'GET') {
|
|
31
31
|
return [false];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const params = Endpoint.parseParameters(request.url,
|
|
34
|
+
const params = Endpoint.parseParameters(request.url, '/email-templates', {});
|
|
35
35
|
|
|
36
36
|
if (params) {
|
|
37
37
|
return [true, params as Params];
|
|
@@ -41,37 +41,37 @@ export class GetEmailTemplatesEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
41
41
|
|
|
42
42
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
43
43
|
const organization = await Context.setOptionalOrganizationScope();
|
|
44
|
-
await Context.authenticate()
|
|
44
|
+
await Context.authenticate();
|
|
45
45
|
|
|
46
46
|
if (organization) {
|
|
47
47
|
if (!await Context.auth.canReadEmailTemplates(organization.id)) {
|
|
48
|
-
throw Context.auth.error()
|
|
49
|
-
}
|
|
50
|
-
}
|
|
48
|
+
throw Context.auth.error();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
51
52
|
if (!Context.auth.hasPlatformFullAccess()) {
|
|
52
|
-
throw Context.auth.error()
|
|
53
|
-
}
|
|
53
|
+
throw Context.auth.error();
|
|
54
|
+
}
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
const types = (request.query.types ?? [...Object.values(EmailTemplateType)]).filter(type => {
|
|
57
|
+
const types = (request.query.types ?? [...Object.values(EmailTemplateType)]).filter((type) => {
|
|
57
58
|
if (!organization) {
|
|
58
|
-
return EmailTemplateStruct.allowPlatformLevel(type)
|
|
59
|
+
return EmailTemplateStruct.allowPlatformLevel(type);
|
|
59
60
|
}
|
|
60
|
-
return EmailTemplateStruct.allowOrganizationLevel(type)
|
|
61
|
-
})
|
|
61
|
+
return EmailTemplateStruct.allowOrganizationLevel(type);
|
|
62
|
+
});
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
)
|
|
64
|
+
const templates = organization
|
|
65
|
+
? (
|
|
66
|
+
await EmailTemplate.where({ organizationId: organization.id, webshopId: request.query.webshopId ?? null, groupId: request.query.groupIds ? { sign: 'IN', value: request.query.groupIds } : null, type: { sign: 'IN', value: types } })
|
|
67
|
+
)
|
|
68
68
|
: (
|
|
69
69
|
// Required for event emails when logged in as the platform admin
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const defaultTemplates = await EmailTemplate.where({ organizationId: null, type: {sign: 'IN', value: types} });
|
|
75
|
-
return new Response([...templates, ...defaultTemplates].map(template => EmailTemplateStruct.create(template)))
|
|
70
|
+
(request.query.webshopId || request.query.groupIds)
|
|
71
|
+
? await EmailTemplate.where({ webshopId: request.query.webshopId ?? null, groupId: request.query.groupIds ? { sign: 'IN', value: request.query.groupIds } : null, type: { sign: 'IN', value: types } })
|
|
72
|
+
: []
|
|
73
|
+
);
|
|
74
|
+
const defaultTemplates = await EmailTemplate.where({ organizationId: null, type: { sign: 'IN', value: types } });
|
|
75
|
+
return new Response([...templates, ...defaultTemplates].map(template => EmailTemplateStruct.create(template)));
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } from '@simonbackx/simple-encoding';
|
|
2
|
-
import { DecodedRequest, Endpoint, Request, Response } from
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { EmailTemplate, Group, Webshop } from '@stamhoofd/models';
|
|
4
4
|
import { EmailTemplate as EmailTemplateStruct, PermissionLevel } from '@stamhoofd/structures';
|
|
5
5
|
|
|
@@ -13,14 +13,14 @@ type Query = undefined;
|
|
|
13
13
|
type ResponseBody = EmailTemplateStruct[];
|
|
14
14
|
|
|
15
15
|
export class PatchEmailTemplatesEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
16
|
-
bodyDecoder = new PatchableArrayDecoder(EmailTemplateStruct as Decoder<EmailTemplateStruct>, EmailTemplateStruct.patchType() as Decoder<AutoEncoderPatchType<EmailTemplateStruct>>, StringDecoder)
|
|
16
|
+
bodyDecoder = new PatchableArrayDecoder(EmailTemplateStruct as Decoder<EmailTemplateStruct>, EmailTemplateStruct.patchType() as Decoder<AutoEncoderPatchType<EmailTemplateStruct>>, StringDecoder);
|
|
17
17
|
|
|
18
18
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
19
|
-
if (request.method
|
|
19
|
+
if (request.method !== 'PATCH') {
|
|
20
20
|
return [false];
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const params = Endpoint.parseParameters(request.url,
|
|
23
|
+
const params = Endpoint.parseParameters(request.url, '/email-templates', {});
|
|
24
24
|
|
|
25
25
|
if (params) {
|
|
26
26
|
return [true, params as Params];
|
|
@@ -30,39 +30,40 @@ export class PatchEmailTemplatesEndpoint extends Endpoint<Params, Query, Body, R
|
|
|
30
30
|
|
|
31
31
|
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
32
32
|
const organization = await Context.setOptionalOrganizationScope();
|
|
33
|
-
await Context.authenticate()
|
|
33
|
+
await Context.authenticate();
|
|
34
34
|
|
|
35
35
|
if (organization) {
|
|
36
36
|
if (!await Context.auth.canReadEmailTemplates(organization.id)) {
|
|
37
|
-
throw Context.auth.error()
|
|
38
|
-
}
|
|
39
|
-
}
|
|
37
|
+
throw Context.auth.error();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
40
41
|
if (!Context.auth.hasPlatformFullAccess()) {
|
|
41
|
-
throw Context.auth.error()
|
|
42
|
-
}
|
|
42
|
+
throw Context.auth.error();
|
|
43
|
+
}
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
const templates: EmailTemplate[] = []
|
|
46
|
+
const templates: EmailTemplate[] = [];
|
|
46
47
|
|
|
47
48
|
// Get all patches
|
|
48
49
|
for (const patch of request.body.getPatches()) {
|
|
49
|
-
const template = await EmailTemplate.getByID(patch.id)
|
|
50
|
+
const template = await EmailTemplate.getByID(patch.id);
|
|
50
51
|
if (!template || !(await Context.auth.canAccessEmailTemplate(template, PermissionLevel.Write))) {
|
|
51
|
-
throw Context.auth.notFoundOrNoAccess(
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
template.html = patch.html ?? template.html
|
|
55
|
-
template.subject = patch.subject ?? template.subject
|
|
56
|
-
template.text = patch.text ?? template.text
|
|
57
|
-
template.json = patch.json ?? template.json
|
|
52
|
+
throw Context.auth.notFoundOrNoAccess('Je hebt geen toegang om deze emailtemplate te bewerken');
|
|
53
|
+
}
|
|
58
54
|
|
|
59
|
-
|
|
55
|
+
template.html = patch.html ?? template.html;
|
|
56
|
+
template.subject = patch.subject ?? template.subject;
|
|
57
|
+
template.text = patch.text ?? template.text;
|
|
58
|
+
template.json = patch.json ?? template.json;
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
await template.save();
|
|
61
|
+
|
|
62
|
+
templates.push(template);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
for (const put of request.body.getPuts()) {
|
|
65
|
-
const struct = put.put
|
|
66
|
+
const struct = put.put;
|
|
66
67
|
|
|
67
68
|
if (!EmailTemplateStruct.allowOrganizationLevel(struct.type) && organization) {
|
|
68
69
|
throw Context.auth.error();
|
|
@@ -72,54 +73,54 @@ export class PatchEmailTemplatesEndpoint extends Endpoint<Params, Query, Body, R
|
|
|
72
73
|
throw Context.auth.error();
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
const template = new EmailTemplate()
|
|
76
|
-
template.id = struct.id
|
|
77
|
-
template.organizationId = organization?.id ?? null
|
|
78
|
-
template.webshopId = struct.webshopId
|
|
79
|
-
template.groupId = struct.groupId
|
|
76
|
+
const template = new EmailTemplate();
|
|
77
|
+
template.id = struct.id;
|
|
78
|
+
template.organizationId = organization?.id ?? null;
|
|
79
|
+
template.webshopId = struct.webshopId;
|
|
80
|
+
template.groupId = struct.groupId;
|
|
80
81
|
|
|
81
82
|
if (struct.groupId) {
|
|
82
|
-
const group = await Group.getByID(struct.groupId)
|
|
83
|
+
const group = await Group.getByID(struct.groupId);
|
|
83
84
|
if (!group || !await Context.auth.canAccessGroup(group, PermissionLevel.Full)) {
|
|
84
85
|
throw Context.auth.error();
|
|
85
86
|
}
|
|
86
|
-
template.organizationId = group.organizationId
|
|
87
|
+
template.organizationId = group.organizationId;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
if (struct.webshopId) {
|
|
90
|
-
const webshop = await Webshop.getByID(struct.webshopId)
|
|
91
|
+
const webshop = await Webshop.getByID(struct.webshopId);
|
|
91
92
|
if (!webshop || !await Context.auth.canAccessWebshop(webshop, PermissionLevel.Full)) {
|
|
92
93
|
throw Context.auth.error();
|
|
93
94
|
}
|
|
94
|
-
template.organizationId = webshop.organizationId
|
|
95
|
+
template.organizationId = webshop.organizationId;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
template.html = struct.html
|
|
98
|
-
template.subject = struct.subject
|
|
99
|
-
template.text = struct.text
|
|
100
|
-
template.json = struct.json
|
|
98
|
+
template.html = struct.html;
|
|
99
|
+
template.subject = struct.subject;
|
|
100
|
+
template.text = struct.text;
|
|
101
|
+
template.json = struct.json;
|
|
101
102
|
|
|
102
|
-
template.type = struct.type
|
|
103
|
+
template.type = struct.type;
|
|
103
104
|
|
|
104
105
|
// Check if valid + write permissions
|
|
105
106
|
if (!(await Context.auth.canAccessEmailTemplate(template, PermissionLevel.Write))) {
|
|
106
|
-
throw Context.auth.error(
|
|
107
|
-
}
|
|
107
|
+
throw Context.auth.error('Je hebt geen toegang om deze emailtemplate te maken');
|
|
108
|
+
}
|
|
108
109
|
|
|
109
|
-
await template.save()
|
|
110
|
+
await template.save();
|
|
110
111
|
|
|
111
|
-
templates.push(template)
|
|
112
|
+
templates.push(template);
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
for (const id of request.body.getDeletes()) {
|
|
115
|
-
const template = await EmailTemplate.getByID(id)
|
|
116
|
+
const template = await EmailTemplate.getByID(id);
|
|
116
117
|
if (!template || !(await Context.auth.canAccessEmailTemplate(template, PermissionLevel.Write))) {
|
|
117
|
-
throw Context.auth.notFoundOrNoAccess(
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
await template.delete()
|
|
118
|
+
throw Context.auth.notFoundOrNoAccess('Je hebt geen toegang om deze emailtemplate te verwijderen');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await template.delete();
|
|
121
122
|
}
|
|
122
|
-
|
|
123
|
-
return new Response(templates.map(template => EmailTemplateStruct.create(template)))
|
|
123
|
+
|
|
124
|
+
return new Response(templates.map(template => EmailTemplateStruct.create(template)));
|
|
124
125
|
}
|
|
125
126
|
}
|