@stamhoofd/backend 1.0.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/.env.template.json +63 -0
- package/.eslintrc.js +61 -0
- package/README.md +40 -0
- package/index.ts +172 -0
- package/jest.config.js +11 -0
- package/migrations.ts +33 -0
- package/package.json +48 -0
- package/src/crons.ts +845 -0
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +42 -0
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +320 -0
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +171 -0
- package/src/endpoints/auth/CreateAdminEndpoint.ts +137 -0
- package/src/endpoints/auth/CreateTokenEndpoint.test.ts +68 -0
- package/src/endpoints/auth/CreateTokenEndpoint.ts +200 -0
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +31 -0
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +70 -0
- package/src/endpoints/auth/GetUserEndpoint.test.ts +64 -0
- package/src/endpoints/auth/GetUserEndpoint.ts +57 -0
- package/src/endpoints/auth/PatchApiUserEndpoint.ts +90 -0
- package/src/endpoints/auth/PatchUserEndpoint.ts +122 -0
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +37 -0
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +41 -0
- package/src/endpoints/auth/SignupEndpoint.ts +107 -0
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +89 -0
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +95 -0
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +31 -0
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +101 -0
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +53 -0
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +57 -0
- package/src/endpoints/global/files/UploadFile.ts +147 -0
- package/src/endpoints/global/files/UploadImage.ts +119 -0
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +76 -0
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +43 -0
- package/src/endpoints/global/members/GetMembersEndpoint.ts +429 -0
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +734 -0
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +45 -0
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +105 -0
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +146 -0
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +52 -0
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +80 -0
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +49 -0
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +58 -0
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +62 -0
- package/src/endpoints/global/payments/ExchangeSTPaymentEndpoint.ts +153 -0
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +134 -0
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +44 -0
- package/src/endpoints/global/platform/GetPlatformEnpoint.ts +39 -0
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +63 -0
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +68 -0
- package/src/endpoints/global/registration/GetUserBalanceEndpoint.ts +39 -0
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +80 -0
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +41 -0
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +134 -0
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +521 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +37 -0
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +115 -0
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +187 -0
- package/src/endpoints/organization/dashboard/billing/ActivatePackagesEndpoint.ts +424 -0
- package/src/endpoints/organization/dashboard/billing/DeactivatePackageEndpoint.ts +67 -0
- package/src/endpoints/organization/dashboard/billing/GetBillingStatusEndpoint.ts +39 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +57 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +129 -0
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +114 -0
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +234 -0
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +62 -0
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +85 -0
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +80 -0
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +54 -0
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +49 -0
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +63 -0
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +61 -0
- package/src/endpoints/organization/dashboard/organization/ApplyRegisterCodeEndpoint.test.ts +64 -0
- package/src/endpoints/organization/dashboard/organization/ApplyRegisterCodeEndpoint.ts +84 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +43 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +42 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +43 -0
- package/src/endpoints/organization/dashboard/organization/GetRegisterCodeEndpoint.ts +65 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +281 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +338 -0
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +196 -0
- package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +48 -0
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +207 -0
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +202 -0
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +233 -0
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +66 -0
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +210 -0
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +93 -0
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +59 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +78 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +40 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +69 -0
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +52 -0
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +73 -0
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +60 -0
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +47 -0
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +41 -0
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +217 -0
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +51 -0
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +47 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +83 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +68 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +69 -0
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +125 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +204 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +278 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +80 -0
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +60 -0
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +379 -0
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +54 -0
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +45 -0
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +78 -0
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +34 -0
- package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +44 -0
- package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +82 -0
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +59 -0
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +51 -0
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +40 -0
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +124 -0
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +130 -0
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +50 -0
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +450 -0
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +335 -0
- package/src/helpers/AddressValidator.test.ts +40 -0
- package/src/helpers/AddressValidator.ts +256 -0
- package/src/helpers/AdminPermissionChecker.ts +1031 -0
- package/src/helpers/AuthenticatedStructures.ts +158 -0
- package/src/helpers/BuckarooHelper.ts +279 -0
- package/src/helpers/CheckSettlements.ts +215 -0
- package/src/helpers/Context.ts +202 -0
- package/src/helpers/CookieHelper.ts +45 -0
- package/src/helpers/ForwardHandler.test.ts +216 -0
- package/src/helpers/ForwardHandler.ts +140 -0
- package/src/helpers/OpenIDConnectHelper.ts +284 -0
- package/src/helpers/StripeHelper.ts +293 -0
- package/src/helpers/StripePayoutChecker.ts +188 -0
- package/src/middleware/ContextMiddleware.ts +16 -0
- package/src/migrations/1646578856-validate-addresses.ts +60 -0
- package/src/seeds/0000000000-example.ts +13 -0
- package/src/seeds/1715028563-user-permissions.ts +52 -0
- package/tests/e2e/stock.test.ts +2120 -0
- package/tests/e2e/tickets.test.ts +926 -0
- package/tests/helpers/StripeMocker.ts +362 -0
- package/tests/helpers/TestServer.ts +21 -0
- package/tests/jest.global.setup.ts +29 -0
- package/tests/jest.setup.ts +59 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
2
|
+
import { GroupPrivateSettings, OrganizationRegistrationPeriod as OrganizationRegistrationPeriodStruct, PermissionLevel, PermissionsResourceType, ResourcePermissions, Version } from "@stamhoofd/structures";
|
|
3
|
+
|
|
4
|
+
import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } from "@simonbackx/simple-encoding";
|
|
5
|
+
import { Context } from "../../../../helpers/Context";
|
|
6
|
+
import { Group, OrganizationRegistrationPeriod, RegistrationPeriod } from "@stamhoofd/models";
|
|
7
|
+
import { SimpleError } from "@simonbackx/simple-errors";
|
|
8
|
+
|
|
9
|
+
type Params = Record<string, never>;
|
|
10
|
+
type Query = undefined;
|
|
11
|
+
type Body = PatchableArrayAutoEncoder<OrganizationRegistrationPeriodStruct>
|
|
12
|
+
type ResponseBody = OrganizationRegistrationPeriodStruct[]
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* One endpoint to create, patch and delete members and their registrations and payments
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export class PatchRegistrationPeriodsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
19
|
+
bodyDecoder = new PatchableArrayDecoder(OrganizationRegistrationPeriodStruct as Decoder<OrganizationRegistrationPeriodStruct>, OrganizationRegistrationPeriodStruct.patchType() as Decoder<AutoEncoderPatchType<OrganizationRegistrationPeriodStruct>>, StringDecoder)
|
|
20
|
+
|
|
21
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
22
|
+
if (request.method != "PATCH") {
|
|
23
|
+
return [false];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const params = Endpoint.parseParameters(request.url, "/organization/registration-periods", {});
|
|
27
|
+
|
|
28
|
+
if (params) {
|
|
29
|
+
return [true, params as Params];
|
|
30
|
+
}
|
|
31
|
+
return [false];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
35
|
+
const organization = await Context.setOrganizationScope();
|
|
36
|
+
const {user} = await Context.authenticate()
|
|
37
|
+
|
|
38
|
+
if (!await Context.auth.hasFullAccess(organization.id)) {
|
|
39
|
+
throw Context.auth.error()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const structs: OrganizationRegistrationPeriodStruct[] = [];
|
|
43
|
+
|
|
44
|
+
for (const patch of request.body.getPatches()) {
|
|
45
|
+
const organizationPeriod = await OrganizationRegistrationPeriod.getByID(patch.id);
|
|
46
|
+
if (!organizationPeriod || organizationPeriod.organizationId !== organization.id) {
|
|
47
|
+
throw new SimpleError({
|
|
48
|
+
code: "not_found",
|
|
49
|
+
message: "Period not found",
|
|
50
|
+
statusCode: 404
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
let deleteUnreachable = false
|
|
54
|
+
const allowedIds: string[] = []
|
|
55
|
+
|
|
56
|
+
if (await Context.auth.hasFullAccess(organization.id)) {
|
|
57
|
+
if (patch.settings) {
|
|
58
|
+
organizationPeriod.settings.patchOrPut(patch.settings);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
/// Only allow editing category group ids
|
|
62
|
+
if (patch.settings) {
|
|
63
|
+
// Only allow adding groups if we have create permissions in a given category group
|
|
64
|
+
if (patch.settings.categories && !Array.isArray(patch.settings.categories)) {
|
|
65
|
+
for (const pp of patch.settings.categories.getPatches()) {
|
|
66
|
+
const category = organizationPeriod.settings.categories.find(c => c.id === pp.id)
|
|
67
|
+
if (!category) {
|
|
68
|
+
// Fail silently
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!await Context.auth.canCreateGroupInCategory(organization.id, category)) {
|
|
73
|
+
throw Context.auth.error('Je hebt geen toegangsrechten om groepen toe te voegen in deze categorie')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Only process puts
|
|
77
|
+
const ids = pp.groupIds.getPuts().map(p => p.put)
|
|
78
|
+
allowedIds.push(...ids)
|
|
79
|
+
category.groupIds.push(...ids)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (allowedIds.length > 0) {
|
|
83
|
+
deleteUnreachable = true
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await organizationPeriod.save();
|
|
90
|
+
|
|
91
|
+
// Check changes to groups
|
|
92
|
+
const deleteGroups = patch.groups.getDeletes()
|
|
93
|
+
if (deleteGroups.length > 0) {
|
|
94
|
+
for (const id of deleteGroups) {
|
|
95
|
+
const model = await Group.getByID(id)
|
|
96
|
+
if (!model || !await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
|
|
97
|
+
throw Context.auth.error('Je hebt geen toegangsrechten om deze groep te verwijderen')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
model.deletedAt = new Date()
|
|
101
|
+
await model.save()
|
|
102
|
+
deleteUnreachable = true
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (const groupPut of patch.groups.getPuts()) {
|
|
107
|
+
if (!await Context.auth.hasFullAccess(organization.id) && !allowedIds.includes(groupPut.put.id)) {
|
|
108
|
+
throw Context.auth.error('Je hebt geen toegangsrechten om groepen toe te voegen')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const struct = groupPut.put
|
|
112
|
+
const model = new Group()
|
|
113
|
+
model.id = struct.id
|
|
114
|
+
model.organizationId = organization.id
|
|
115
|
+
model.periodId = organizationPeriod.periodId
|
|
116
|
+
model.settings = struct.settings
|
|
117
|
+
model.privateSettings = struct.privateSettings ?? GroupPrivateSettings.create({})
|
|
118
|
+
model.status = struct.status
|
|
119
|
+
|
|
120
|
+
if (!await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
|
|
121
|
+
// Create a temporary permission role for this user
|
|
122
|
+
const organizationPermissions = user.permissions?.organizationPermissions?.get(organization.id)
|
|
123
|
+
if (!organizationPermissions) {
|
|
124
|
+
throw new Error('Unexpected missing permissions')
|
|
125
|
+
}
|
|
126
|
+
const resourcePermissions = ResourcePermissions.create({
|
|
127
|
+
resourceName: model.settings.name,
|
|
128
|
+
level: PermissionLevel.Full
|
|
129
|
+
})
|
|
130
|
+
const patch = resourcePermissions.createInsertPatch(PermissionsResourceType.Groups, model.id, organizationPermissions)
|
|
131
|
+
user.permissions!.organizationPermissions.set(organization.id, organizationPermissions.patch(patch))
|
|
132
|
+
console.log('Automatically granted author full permissions to resource', 'group', model.id, 'user', user.id, 'patch', patch.encode({version: Version}))
|
|
133
|
+
await user.save()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check if current user has permissions to this new group -> else fail with error
|
|
137
|
+
if (!await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
|
|
138
|
+
throw new SimpleError({
|
|
139
|
+
code: "missing_permissions",
|
|
140
|
+
message: "You cannot restrict your own permissions",
|
|
141
|
+
human: "Je kan geen inschrijvingsgroep maken zonder dat je zelf volledige toegang hebt tot de nieuwe groep"
|
|
142
|
+
})
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await model.updateOccupancy()
|
|
147
|
+
await model.save();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const struct of patch.groups.getPatches()) {
|
|
151
|
+
const model = await Group.getByID(struct.id)
|
|
152
|
+
|
|
153
|
+
if (!model || !await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
|
|
154
|
+
throw Context.auth.error('Je hebt geen toegangsrechten om deze groep te wijzigen')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (struct.settings) {
|
|
158
|
+
model.settings.patchOrPut(struct.settings)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (struct.status) {
|
|
162
|
+
model.status = struct.status
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (struct.privateSettings) {
|
|
166
|
+
model.privateSettings.patchOrPut(struct.privateSettings)
|
|
167
|
+
|
|
168
|
+
if (!await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
|
|
169
|
+
throw new SimpleError({
|
|
170
|
+
code: "missing_permissions",
|
|
171
|
+
message: "You cannot restrict your own permissions",
|
|
172
|
+
human: "Je kan je eigen volledige toegang tot deze inschrijvingsgroep niet verwijderen. Vraag aan een hoofdbeheerder om jouw toegang te verwijderen."
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (struct.cycle !== undefined) {
|
|
178
|
+
model.cycle = struct.cycle
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (struct.deletedAt !== undefined) {
|
|
182
|
+
model.deletedAt = struct.deletedAt
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
await model.updateOccupancy()
|
|
186
|
+
await model.save();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const period = await RegistrationPeriod.getByID(organizationPeriod.periodId);
|
|
190
|
+
const groups = await Group.getAll(organization.id, organizationPeriod.periodId)
|
|
191
|
+
|
|
192
|
+
if (deleteUnreachable) {
|
|
193
|
+
// Delete unreachable categories first
|
|
194
|
+
await organization.cleanCategories(groups);
|
|
195
|
+
await Group.deleteUnreachable(organization.id, organizationPeriod, groups)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (period) {
|
|
199
|
+
|
|
200
|
+
structs.push(organizationPeriod.getStructure(period, groups));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return new Response(
|
|
206
|
+
structs
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
|
+
import { StripeAccount } from '@stamhoofd/models';
|
|
5
|
+
import { PermissionLevel, StripeAccount as StripeAccountStruct } from "@stamhoofd/structures";
|
|
6
|
+
import Stripe from 'stripe';
|
|
7
|
+
|
|
8
|
+
import { Context } from '../../../../helpers/Context';
|
|
9
|
+
import { StripeHelper } from '../../../../helpers/StripeHelper';
|
|
10
|
+
type Params = Record<string, never>;
|
|
11
|
+
type Body = undefined;
|
|
12
|
+
type Query = undefined
|
|
13
|
+
type ResponseBody = StripeAccountStruct
|
|
14
|
+
|
|
15
|
+
export class ConnectMollieEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
16
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
17
|
+
if (request.method != "POST") {
|
|
18
|
+
return [false];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const params = Endpoint.parseParameters(request.url, "/stripe/connect", {});
|
|
22
|
+
|
|
23
|
+
if (params) {
|
|
24
|
+
return [true, params as Params];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return [false];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
31
|
+
const organization = await Context.setOrganizationScope();
|
|
32
|
+
await Context.authenticate()
|
|
33
|
+
|
|
34
|
+
// Fast throw first (more in depth checking for patches later)
|
|
35
|
+
if (!await Context.auth.canManagePaymentAccounts(organization.id, PermissionLevel.Full)) {
|
|
36
|
+
throw Context.auth.error()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const models = await StripeAccount.where({ organizationId: organization.id, status: "active" })
|
|
40
|
+
|
|
41
|
+
const canCreateMultipleStripeAccounts = models.every(a => (a.meta.charges_enabled && a.meta.payouts_enabled) || (a.meta.details_submitted))
|
|
42
|
+
if (models.length > 0 && !canCreateMultipleStripeAccounts) {
|
|
43
|
+
throw new SimpleError({
|
|
44
|
+
code: "already_connected",
|
|
45
|
+
message: "Je hebt al een Stripe account gekoppeld"
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const type = 'express'
|
|
50
|
+
|
|
51
|
+
let expressData: Stripe.AccountCreateParams = {
|
|
52
|
+
country: organization.address.country,
|
|
53
|
+
// Problem: we cannot set company or business_type, because then it defaults the structure of the company to one that requires a company number
|
|
54
|
+
capabilities: {
|
|
55
|
+
card_payments: { requested: true },
|
|
56
|
+
transfers: { requested: true },
|
|
57
|
+
bancontact_payments: { requested: true },
|
|
58
|
+
ideal_payments: { requested: true },
|
|
59
|
+
},
|
|
60
|
+
settings: {
|
|
61
|
+
payouts: {
|
|
62
|
+
schedule: {
|
|
63
|
+
delay_days: 'minimum',
|
|
64
|
+
interval: 'weekly',
|
|
65
|
+
weekly_anchor: 'monday',
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (type !== 'express') {
|
|
72
|
+
expressData = {};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Create a new Stripe account
|
|
76
|
+
const stripe = StripeHelper.getInstance()
|
|
77
|
+
const account = await stripe.accounts.create({
|
|
78
|
+
type,
|
|
79
|
+
...expressData
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Save the Stripe account in the database
|
|
83
|
+
const model = new StripeAccount();
|
|
84
|
+
model.organizationId = organization.id;
|
|
85
|
+
model.accountId = account.id;
|
|
86
|
+
model.setMetaFromStripeAccount(account)
|
|
87
|
+
await model.save();
|
|
88
|
+
|
|
89
|
+
// Return information about the Stripe Account
|
|
90
|
+
|
|
91
|
+
return new Response(StripeAccountStruct.create(model));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
|
+
import { StripeAccount } from '@stamhoofd/models';
|
|
5
|
+
import { PermissionLevel } from '@stamhoofd/structures';
|
|
6
|
+
|
|
7
|
+
import { Context } from '../../../../helpers/Context';
|
|
8
|
+
|
|
9
|
+
type Params = { id: string };
|
|
10
|
+
type Body = undefined;
|
|
11
|
+
type Query = undefined
|
|
12
|
+
type ResponseBody = undefined
|
|
13
|
+
|
|
14
|
+
export class DeleteStripeAccountEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
15
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
16
|
+
if (request.method != "DELETE") {
|
|
17
|
+
return [false];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const params = Endpoint.parseParameters(request.url, "/stripe/accounts/@id", {id: String});
|
|
21
|
+
|
|
22
|
+
if (params) {
|
|
23
|
+
return [true, params as Params];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
30
|
+
const organization = await Context.setOrganizationScope();
|
|
31
|
+
await Context.authenticate()
|
|
32
|
+
|
|
33
|
+
// Fast throw first (more in depth checking for patches later)
|
|
34
|
+
if (!await Context.auth.canManagePaymentAccounts(organization.id, PermissionLevel.Full)) {
|
|
35
|
+
throw Context.auth.error()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Search account in database
|
|
39
|
+
const model = await StripeAccount.getByID(request.params.id)
|
|
40
|
+
if (!model || model.organizationId != organization.id || model.status !== "active") {
|
|
41
|
+
throw Context.auth.notFoundOrNoAccess("Account niet gevonden")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// For now we don't delete them in Stripe because this causes issues with data access
|
|
45
|
+
// const stripe = StripeHelper.getInstance()
|
|
46
|
+
//
|
|
47
|
+
// try {
|
|
48
|
+
// await stripe.accounts.del(model.accountId);
|
|
49
|
+
// } catch (e) {
|
|
50
|
+
// console.error('Tried deleting account but failed', e)
|
|
51
|
+
// }
|
|
52
|
+
|
|
53
|
+
// If that succeeded
|
|
54
|
+
model.status = "deleted"
|
|
55
|
+
await model.save()
|
|
56
|
+
|
|
57
|
+
return new Response(undefined);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
|
|
2
|
+
import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
|
|
3
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
4
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
5
|
+
import { StripeAccount } from '@stamhoofd/models';
|
|
6
|
+
import { PermissionLevel } from '@stamhoofd/structures';
|
|
7
|
+
|
|
8
|
+
import { Context } from '../../../../helpers/Context';
|
|
9
|
+
import { StripeHelper } from '../../../../helpers/StripeHelper';
|
|
10
|
+
|
|
11
|
+
type Params = Record<string, never>;
|
|
12
|
+
class Body extends AutoEncoder {
|
|
13
|
+
/**
|
|
14
|
+
* The account id (internal id, not the stripe id)
|
|
15
|
+
*/
|
|
16
|
+
@field({ decoder: StringDecoder })
|
|
17
|
+
accountId: string
|
|
18
|
+
|
|
19
|
+
@field({ decoder: StringDecoder })
|
|
20
|
+
returnUrl: string
|
|
21
|
+
|
|
22
|
+
@field({ decoder: StringDecoder })
|
|
23
|
+
refreshUrl: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type Query = undefined
|
|
27
|
+
class ResponseBody extends AutoEncoder {
|
|
28
|
+
@field({ decoder: StringDecoder })
|
|
29
|
+
url: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class GetStripeAccountLinkEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
33
|
+
bodyDecoder = Body as Decoder<Body>
|
|
34
|
+
|
|
35
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
36
|
+
if (request.method != "POST") {
|
|
37
|
+
return [false];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const params = Endpoint.parseParameters(request.url, "/stripe/account-link", {});
|
|
41
|
+
|
|
42
|
+
if (params) {
|
|
43
|
+
return [true, params as Params];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return [false];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
50
|
+
const organization = await Context.setOrganizationScope();
|
|
51
|
+
await Context.authenticate()
|
|
52
|
+
|
|
53
|
+
// Fast throw first (more in depth checking for patches later)
|
|
54
|
+
if (!await Context.auth.canManagePaymentAccounts(organization.id, PermissionLevel.Full)) {
|
|
55
|
+
throw Context.auth.error()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Search account in database
|
|
59
|
+
const model = await StripeAccount.getByID(request.body.accountId)
|
|
60
|
+
if (!model || model.organizationId != organization.id || model.status !== 'active') {
|
|
61
|
+
throw Context.auth.notFoundOrNoAccess("Account niet gevonden")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Get account
|
|
65
|
+
const stripe = StripeHelper.getInstance()
|
|
66
|
+
const accountLink = await stripe.accountLinks.create({
|
|
67
|
+
account: model.accountId,
|
|
68
|
+
refresh_url: request.body.refreshUrl,
|
|
69
|
+
return_url: request.body.returnUrl,
|
|
70
|
+
type: 'account_onboarding',
|
|
71
|
+
collect: model.meta.type === 'express' ? 'eventually_due' : undefined, // Collect all at the start
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return new Response(ResponseBody.create({
|
|
75
|
+
url: accountLink.url
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { StripeAccount } from '@stamhoofd/models';
|
|
4
|
+
import { PermissionLevel, StripeAccount as StripeAccountStruct } from '@stamhoofd/structures';
|
|
5
|
+
|
|
6
|
+
import { Context } from '../../../../helpers/Context';
|
|
7
|
+
|
|
8
|
+
type Params = Record<string, never>;
|
|
9
|
+
type Body = undefined;
|
|
10
|
+
type Query = undefined
|
|
11
|
+
type ResponseBody = StripeAccountStruct[]
|
|
12
|
+
|
|
13
|
+
export class GetStripeAccountLinkEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
14
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
15
|
+
if (request.method != "GET") {
|
|
16
|
+
return [false];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const params = Endpoint.parseParameters(request.url, "/stripe/accounts", {});
|
|
20
|
+
|
|
21
|
+
if (params) {
|
|
22
|
+
return [true, params as Params];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return [false];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
29
|
+
const organization = await Context.setOrganizationScope();
|
|
30
|
+
await Context.authenticate()
|
|
31
|
+
|
|
32
|
+
// Fast throw first (more in depth checking for patches later)
|
|
33
|
+
if (!await Context.auth.canManagePaymentAccounts(organization.id, PermissionLevel.Read)) {
|
|
34
|
+
throw Context.auth.error()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const models = await StripeAccount.where({ organizationId: organization.id, status: 'active' })
|
|
38
|
+
return new Response(models.map(m => StripeAccountStruct.create(m)));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
|
|
3
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
4
|
+
import { SimpleError } from '@simonbackx/simple-errors';
|
|
5
|
+
import { StripeAccount, Token } from '@stamhoofd/models';
|
|
6
|
+
import { PermissionLevel } from '@stamhoofd/structures';
|
|
7
|
+
|
|
8
|
+
import { Context } from '../../../../helpers/Context';
|
|
9
|
+
import { StripeHelper } from '../../../../helpers/StripeHelper';
|
|
10
|
+
|
|
11
|
+
type Params = Record<string, never>;
|
|
12
|
+
class Body extends AutoEncoder {
|
|
13
|
+
/**
|
|
14
|
+
* The account id (internal id, not the stripe id)
|
|
15
|
+
*/
|
|
16
|
+
@field({ decoder: StringDecoder })
|
|
17
|
+
accountId: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type Query = undefined
|
|
21
|
+
class ResponseBody extends AutoEncoder {
|
|
22
|
+
@field({ decoder: StringDecoder })
|
|
23
|
+
url: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class GetStripeLoginLinkEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
27
|
+
bodyDecoder = Body as Decoder<Body>
|
|
28
|
+
|
|
29
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
30
|
+
if (request.method != "POST") {
|
|
31
|
+
return [false];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const params = Endpoint.parseParameters(request.url, "/stripe/login-link", {});
|
|
35
|
+
|
|
36
|
+
if (params) {
|
|
37
|
+
return [true, params as Params];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return [false];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
44
|
+
const organization = await Context.setOrganizationScope();
|
|
45
|
+
await Context.authenticate()
|
|
46
|
+
|
|
47
|
+
// Fast throw first (more in depth checking for patches later)
|
|
48
|
+
if (!await Context.auth.canManagePaymentAccounts(organization.id, PermissionLevel.Full)) {
|
|
49
|
+
throw Context.auth.error()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Search account in database
|
|
53
|
+
const model = await StripeAccount.getByID(request.body.accountId)
|
|
54
|
+
if (!model || model.organizationId != organization.id || model.status !== 'active') {
|
|
55
|
+
throw new SimpleError({
|
|
56
|
+
code: "not_found",
|
|
57
|
+
message: "Account niet gevonden",
|
|
58
|
+
statusCode: 400
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const stripe = StripeHelper.getInstance()
|
|
63
|
+
const accountLink = await stripe.accounts.createLoginLink(model.accountId);
|
|
64
|
+
|
|
65
|
+
return new Response(ResponseBody.create({
|
|
66
|
+
url: accountLink.url
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { StripeAccount } from '@stamhoofd/models';
|
|
4
|
+
import { PermissionLevel, StripeAccount as StripeAccountStruct } from '@stamhoofd/structures';
|
|
5
|
+
|
|
6
|
+
import { Context } from '../../../../helpers/Context';
|
|
7
|
+
import { StripeHelper } from '../../../../helpers/StripeHelper';
|
|
8
|
+
|
|
9
|
+
type Params = { id: string };
|
|
10
|
+
type Body = undefined;
|
|
11
|
+
type Query = undefined
|
|
12
|
+
type ResponseBody = StripeAccountStruct
|
|
13
|
+
|
|
14
|
+
export class UpdateStripeAccountEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
15
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
16
|
+
if (request.method != "POST") {
|
|
17
|
+
return [false];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const params = Endpoint.parseParameters(request.url, "/stripe/accounts/@id", {id: String});
|
|
21
|
+
|
|
22
|
+
if (params) {
|
|
23
|
+
return [true, params as Params];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
30
|
+
const organization = await Context.setOrganizationScope();
|
|
31
|
+
await Context.authenticate()
|
|
32
|
+
|
|
33
|
+
// Fast throw first (more in depth checking for patches later)
|
|
34
|
+
if (!await Context.auth.canManagePaymentAccounts(organization.id, PermissionLevel.Read)) {
|
|
35
|
+
throw Context.auth.error()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Search account in database
|
|
39
|
+
const model = await StripeAccount.getByID(request.params.id)
|
|
40
|
+
if (!model || model.organizationId != organization.id) {
|
|
41
|
+
throw Context.auth.notFoundOrNoAccess("Account niet gevonden")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Get account
|
|
45
|
+
const stripe = StripeHelper.getInstance()
|
|
46
|
+
const account = await stripe.accounts.retrieve(model.accountId);
|
|
47
|
+
model.setMetaFromStripeAccount(account)
|
|
48
|
+
await model.save()
|
|
49
|
+
|
|
50
|
+
return new Response(StripeAccountStruct.create(model));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
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 { Token, User } from '@stamhoofd/models';
|
|
5
|
+
import { ApiUser, ApiUserWithToken, UserPermissions } from "@stamhoofd/structures";
|
|
6
|
+
|
|
7
|
+
import { Context } from '../../../../helpers/Context';
|
|
8
|
+
type Params = Record<string, never>;
|
|
9
|
+
type Query = undefined;
|
|
10
|
+
type Body = ApiUser
|
|
11
|
+
type ResponseBody = ApiUser
|
|
12
|
+
|
|
13
|
+
export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
14
|
+
bodyDecoder = ApiUser as Decoder<ApiUser>
|
|
15
|
+
|
|
16
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
17
|
+
if (request.method != "POST") {
|
|
18
|
+
return [false];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const params = Endpoint.parseParameters(request.url, "/api-keys", {});
|
|
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
|
+
const organization = await Context.setOrganizationScope();
|
|
31
|
+
await Context.authenticate()
|
|
32
|
+
|
|
33
|
+
// Fast throw first (more in depth checking for patches later)
|
|
34
|
+
if (!await Context.auth.canManageAdmins(organization.id)) {
|
|
35
|
+
throw Context.auth.error()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const admin = new User();
|
|
39
|
+
admin.organizationId = organization.id;
|
|
40
|
+
admin.firstName = request.body.name;
|
|
41
|
+
admin.lastName = null
|
|
42
|
+
admin.email = 'creating.api'
|
|
43
|
+
admin.verified = true
|
|
44
|
+
|
|
45
|
+
if (!admin.isApiUser) {
|
|
46
|
+
throw new Error('Unexpectedly created normal user while trying to create API-user')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Merge permissions
|
|
50
|
+
if (!request.body.permissions) {
|
|
51
|
+
throw new SimpleError({
|
|
52
|
+
code: 'missing_field',
|
|
53
|
+
message: 'When creating API-users, you are required to specify permissions in the request',
|
|
54
|
+
field: 'permissions'
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
admin.permissions = UserPermissions.limitedAdd(null, request.body.permissions, organization.id)
|
|
59
|
+
await admin.save();
|
|
60
|
+
|
|
61
|
+
// Set id
|
|
62
|
+
admin.email = admin.id + '.api'
|
|
63
|
+
await admin.save();
|
|
64
|
+
|
|
65
|
+
const createdToken = await Token.createApiToken(admin);
|
|
66
|
+
|
|
67
|
+
return new Response(ApiUserWithToken.create({
|
|
68
|
+
...(await Token.getAPIUserWithToken(admin)),
|
|
69
|
+
token: createdToken.accessToken,
|
|
70
|
+
expiresAt: createdToken.accessTokenValidUntil
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
}
|