@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,85 @@
|
|
|
1
|
+
import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArrayDecoder, StringDecoder } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
3
|
+
import { EmailTemplate } from '@stamhoofd/models';
|
|
4
|
+
import { EmailTemplate as EmailTemplateStruct, PermissionLevel } from '@stamhoofd/structures';
|
|
5
|
+
|
|
6
|
+
import { Context } from '../../../../helpers/Context';
|
|
7
|
+
|
|
8
|
+
type Params = Record<string, never>;
|
|
9
|
+
type Body = PatchableArrayAutoEncoder<EmailTemplateStruct>;
|
|
10
|
+
type Query = undefined;
|
|
11
|
+
|
|
12
|
+
type ResponseBody = EmailTemplateStruct[];
|
|
13
|
+
|
|
14
|
+
export class PatchEmailTemplatesEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
15
|
+
bodyDecoder = new PatchableArrayDecoder(EmailTemplateStruct as Decoder<EmailTemplateStruct>, EmailTemplateStruct.patchType() as Decoder<AutoEncoderPatchType<EmailTemplateStruct>>, StringDecoder)
|
|
16
|
+
|
|
17
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
18
|
+
if (request.method != "PATCH") {
|
|
19
|
+
return [false];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const params = Endpoint.parseParameters(request.url, "/email-templates", {});
|
|
23
|
+
|
|
24
|
+
if (params) {
|
|
25
|
+
return [true, params as Params];
|
|
26
|
+
}
|
|
27
|
+
return [false];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async handle(request: 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.canReadEmailTemplates(organization.id)) {
|
|
36
|
+
throw Context.auth.error()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const templates: EmailTemplate[] = []
|
|
40
|
+
|
|
41
|
+
// Get all patches
|
|
42
|
+
for (const patch of request.body.getPatches()) {
|
|
43
|
+
const template = await EmailTemplate.getByID(patch.id)
|
|
44
|
+
if (!template || !(await Context.auth.canAccessEmailTemplate(template, PermissionLevel.Write))) {
|
|
45
|
+
throw Context.auth.notFoundOrNoAccess("Je hebt geen toegang om deze emailtemplate te bewerken")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
template.html = patch.html ?? template.html
|
|
49
|
+
template.subject = patch.subject ?? template.subject
|
|
50
|
+
template.text = patch.text ?? template.text
|
|
51
|
+
template.json = patch.json ?? template.json
|
|
52
|
+
|
|
53
|
+
await template.save()
|
|
54
|
+
|
|
55
|
+
templates.push(template)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const put of request.body.getPuts()) {
|
|
59
|
+
const struct = put.put
|
|
60
|
+
const template = new EmailTemplate()
|
|
61
|
+
template.id = struct.id
|
|
62
|
+
template.organizationId = organization.id
|
|
63
|
+
template.webshopId = struct.webshopId
|
|
64
|
+
template.groupId = struct.groupId
|
|
65
|
+
|
|
66
|
+
template.html = struct.html
|
|
67
|
+
template.subject = struct.subject
|
|
68
|
+
template.text = struct.text
|
|
69
|
+
template.json = struct.json
|
|
70
|
+
|
|
71
|
+
template.type = struct.type
|
|
72
|
+
|
|
73
|
+
// Check if valid + write permissions
|
|
74
|
+
if (!(await Context.auth.canAccessEmailTemplate(template, PermissionLevel.Write))) {
|
|
75
|
+
throw Context.auth.error("Je hebt geen toegang om deze emailtemplate te maken")
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await template.save()
|
|
79
|
+
|
|
80
|
+
templates.push(template)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return new Response(templates.map(template => EmailTemplateStruct.create(template)))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { MollieToken } from '@stamhoofd/models';
|
|
3
|
+
import { CheckMollieResponse, Organization as OrganizationStruct, PermissionLevel } from "@stamhoofd/structures";
|
|
4
|
+
|
|
5
|
+
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
6
|
+
import { Context } from '../../../../helpers/Context';
|
|
7
|
+
|
|
8
|
+
type Params = Record<string, never>;
|
|
9
|
+
type Body = undefined
|
|
10
|
+
type Query = undefined
|
|
11
|
+
type ResponseBody = OrganizationStruct|CheckMollieResponse
|
|
12
|
+
|
|
13
|
+
export class CheckMollieEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
14
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
15
|
+
if (request.method != "POST") {
|
|
16
|
+
return [false];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const params = Endpoint.parseParameters(request.url, "/mollie/check", {});
|
|
20
|
+
|
|
21
|
+
if (params) {
|
|
22
|
+
return [true, params as Params];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return [false];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async handle(request: 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.Full)) {
|
|
34
|
+
throw Context.auth.error()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const mollie = await MollieToken.getTokenFor(organization.id)
|
|
38
|
+
|
|
39
|
+
if (!mollie) {
|
|
40
|
+
organization.privateMeta.mollieOnboarding = null
|
|
41
|
+
organization.privateMeta.mollieProfile = null
|
|
42
|
+
await organization.save()
|
|
43
|
+
|
|
44
|
+
if (request.request.getVersion() < 200) {
|
|
45
|
+
return new Response(await AuthenticatedStructures.organization(organization));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return new Response(CheckMollieResponse.create({
|
|
49
|
+
organization: await AuthenticatedStructures.organization(organization),
|
|
50
|
+
profiles: []
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
const profiles = await mollie.getProfiles();
|
|
54
|
+
|
|
55
|
+
const status = await mollie.getOnboardingStatus();
|
|
56
|
+
organization.privateMeta.mollieOnboarding = status;
|
|
57
|
+
|
|
58
|
+
// Check profile is still valid
|
|
59
|
+
if (organization.privateMeta.mollieProfile) {
|
|
60
|
+
const s = organization.privateMeta.mollieProfile.id
|
|
61
|
+
const profile = profiles.find(p => p.id === s)
|
|
62
|
+
if (!profile) {
|
|
63
|
+
organization.privateMeta.mollieProfile = null
|
|
64
|
+
} else {
|
|
65
|
+
organization.privateMeta.mollieProfile = profile
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await organization.save()
|
|
70
|
+
|
|
71
|
+
if (request.request.getVersion() < 200) {
|
|
72
|
+
return new Response(await AuthenticatedStructures.organization(organization));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return new Response(CheckMollieResponse.create({
|
|
76
|
+
organization: await AuthenticatedStructures.organization(organization),
|
|
77
|
+
profiles
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
|
|
3
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
4
|
+
import { MollieToken } from '@stamhoofd/models';
|
|
5
|
+
import { Organization as OrganizationStruct, PermissionLevel } from "@stamhoofd/structures";
|
|
6
|
+
|
|
7
|
+
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
8
|
+
import { checkMollieSettlementsFor } from '../../../../helpers/CheckSettlements';
|
|
9
|
+
import { Context } from '../../../../helpers/Context';
|
|
10
|
+
|
|
11
|
+
type Params = Record<string, never>;
|
|
12
|
+
|
|
13
|
+
class Body extends AutoEncoder {
|
|
14
|
+
@field({ decoder: StringDecoder })
|
|
15
|
+
code: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Query = undefined
|
|
19
|
+
type ResponseBody = OrganizationStruct
|
|
20
|
+
|
|
21
|
+
export class ConnectMollieEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
22
|
+
bodyDecoder = Body as Decoder<Body>
|
|
23
|
+
|
|
24
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
25
|
+
if (request.method != "POST") {
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const params = Endpoint.parseParameters(request.url, "/mollie/connect", {});
|
|
30
|
+
|
|
31
|
+
if (params) {
|
|
32
|
+
return [true, params as Params];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return [false];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
39
|
+
const organization = await Context.setOrganizationScope();
|
|
40
|
+
await Context.authenticate()
|
|
41
|
+
|
|
42
|
+
// Fast throw first (more in depth checking for patches later)
|
|
43
|
+
if (!await Context.auth.canManagePaymentAccounts(organization.id, PermissionLevel.Full)) {
|
|
44
|
+
throw Context.auth.error()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const mollieToken = await MollieToken.create(organization, request.body.code)
|
|
48
|
+
|
|
49
|
+
// Check settlements after linking (shouldn't block)
|
|
50
|
+
checkMollieSettlementsFor(mollieToken.accessToken, true).catch(console.error)
|
|
51
|
+
|
|
52
|
+
return new Response(await AuthenticatedStructures.organization(organization));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { MollieToken } from '@stamhoofd/models';
|
|
4
|
+
import { Organization as OrganizationStruct, PermissionLevel } from "@stamhoofd/structures";
|
|
5
|
+
|
|
6
|
+
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
7
|
+
import { Context } from '../../../../helpers/Context';
|
|
8
|
+
|
|
9
|
+
type Params = Record<string, never>;
|
|
10
|
+
type Body = undefined
|
|
11
|
+
type Query = undefined
|
|
12
|
+
type ResponseBody = OrganizationStruct
|
|
13
|
+
|
|
14
|
+
export class DisonnectMollieEndpoint 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, "/mollie/disconnect", {});
|
|
21
|
+
|
|
22
|
+
if (params) {
|
|
23
|
+
return [true, params as Params];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async handle(_: 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
|
+
const mollieToken = await MollieToken.getTokenFor(organization.id)
|
|
39
|
+
await mollieToken?.revoke();
|
|
40
|
+
organization.privateMeta.mollieOnboarding = null;
|
|
41
|
+
organization.privateMeta.mollieProfile = null;
|
|
42
|
+
|
|
43
|
+
await organization.save()
|
|
44
|
+
|
|
45
|
+
// TODO: disable all payment methods that use this method
|
|
46
|
+
|
|
47
|
+
return new Response(await AuthenticatedStructures.organization(organization));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import { isSimpleError, isSimpleErrors,SimpleError } from '@simonbackx/simple-errors';
|
|
4
|
+
import { MollieToken } from '@stamhoofd/models';
|
|
5
|
+
import { PermissionLevel } from '@stamhoofd/structures';
|
|
6
|
+
|
|
7
|
+
import { Context } from '../../../../helpers/Context';
|
|
8
|
+
|
|
9
|
+
type Params = Record<string, never>;
|
|
10
|
+
type Body = undefined
|
|
11
|
+
type Query = undefined
|
|
12
|
+
type ResponseBody = string
|
|
13
|
+
|
|
14
|
+
export class GetMollieDashboardEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
15
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
16
|
+
if (request.method != "GET") {
|
|
17
|
+
return [false];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const params = Endpoint.parseParameters(request.url, "/mollie/dashboard", {});
|
|
21
|
+
|
|
22
|
+
if (params) {
|
|
23
|
+
return [true, params as Params];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async handle(_: 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
|
+
const mollie = await MollieToken.getTokenFor(organization.id)
|
|
39
|
+
if (!mollie) {
|
|
40
|
+
throw new SimpleError({
|
|
41
|
+
code: "not_yet_linked",
|
|
42
|
+
message: "Mollie is nog niet gekoppeld. Koppel Mollie eerst voor je de gegevens aanvult"
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const url = await mollie.getOnboardingLink() as string
|
|
48
|
+
|
|
49
|
+
const response = new Response(url)
|
|
50
|
+
response.headers['Content-Type'] = "text/plain"
|
|
51
|
+
return response
|
|
52
|
+
} catch (e) {
|
|
53
|
+
if (isSimpleErrors(e) || isSimpleError(e)) {
|
|
54
|
+
throw e;
|
|
55
|
+
}
|
|
56
|
+
await mollie.delete()
|
|
57
|
+
throw new SimpleError({
|
|
58
|
+
code: "not_yet_linked",
|
|
59
|
+
message: "Mollie is nog niet gekoppeld. Koppel Mollie eerst voor je de gegevens aanvult"
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AutoEncoder, field, StringDecoder } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
|
+
import jwt from 'jsonwebtoken';
|
|
4
|
+
|
|
5
|
+
import { Context } from '../../../../helpers/Context';
|
|
6
|
+
|
|
7
|
+
type Params = Record<string, never>;
|
|
8
|
+
type Query = undefined;
|
|
9
|
+
type Body = undefined;
|
|
10
|
+
class ResponseBody extends AutoEncoder {
|
|
11
|
+
@field({ decoder: StringDecoder })
|
|
12
|
+
jwt: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class CreateNoltTokenEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
16
|
+
|
|
17
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
18
|
+
if (request.method != "POST") {
|
|
19
|
+
return [false];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!STAMHOOFD.NOLT_SSO_SECRET_KEY) {
|
|
23
|
+
return [false];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const params = Endpoint.parseParameters(request.url, "/nolt/create-token", {});
|
|
27
|
+
|
|
28
|
+
if (params) {
|
|
29
|
+
return [true, params as Params];
|
|
30
|
+
}
|
|
31
|
+
return [false];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
35
|
+
const organization = await Context.setOrganizationScope();
|
|
36
|
+
const {user} = await Context.authenticate()
|
|
37
|
+
|
|
38
|
+
// Fast throw first (more in depth checking for patches later)
|
|
39
|
+
if (!await Context.auth.hasSomeAccess(organization.id)) {
|
|
40
|
+
throw Context.auth.error()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Create token
|
|
44
|
+
const payload = {
|
|
45
|
+
// The ID that you use in your app for this user
|
|
46
|
+
id: user.id,
|
|
47
|
+
// The user's email address that
|
|
48
|
+
// Nolt should use for notifications
|
|
49
|
+
email: user.email,
|
|
50
|
+
// The display name for this user
|
|
51
|
+
name: user.firstName+" "+user.lastName,
|
|
52
|
+
|
|
53
|
+
// Optional: The URL to the user's avatar picture
|
|
54
|
+
imageUrl: organization.meta.squareLogo?.getPublicPath() ?? organization.meta.horizontalLogo?.getPublicPath() ?? undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const str = jwt.sign(payload, STAMHOOFD.NOLT_SSO_SECRET_KEY, { algorithm: 'HS256' });
|
|
58
|
+
return new Response(ResponseBody.create({ "jwt": str }));
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
import { Request } from "@simonbackx/simple-endpoints";
|
|
3
|
+
import { OrganizationFactory, RegisterCodeFactory, STCredit, Token, UserFactory } from "@stamhoofd/models";
|
|
4
|
+
import { PermissionLevel, Permissions } from "@stamhoofd/structures";
|
|
5
|
+
|
|
6
|
+
import { testServer } from "../../../../../tests/helpers/TestServer";
|
|
7
|
+
import { ApplyRegisterCodeEndpoint } from "./ApplyRegisterCodeEndpoint";
|
|
8
|
+
|
|
9
|
+
describe("Endpoint.ApplyRegisterCodeEndpoint", () => {
|
|
10
|
+
// Test endpoint
|
|
11
|
+
const endpoint = new ApplyRegisterCodeEndpoint();
|
|
12
|
+
|
|
13
|
+
test("Cannot apply a register code if not platform admin", async () => {
|
|
14
|
+
const otherOrganization = await new OrganizationFactory({}).create();
|
|
15
|
+
const code = await new RegisterCodeFactory({organization: otherOrganization}).create();
|
|
16
|
+
|
|
17
|
+
const organization = await new OrganizationFactory({}).create();
|
|
18
|
+
const user = await new UserFactory({ organization, permissions: Permissions.create({ level: PermissionLevel.Full }) }).create()
|
|
19
|
+
const token = await Token.createToken(user)
|
|
20
|
+
|
|
21
|
+
const r = Request.buildJson(
|
|
22
|
+
"POST",
|
|
23
|
+
"/organization/register-code",
|
|
24
|
+
organization.getApiHost(),
|
|
25
|
+
{
|
|
26
|
+
registerCode: code.code,
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
r.headers.authorization = "Bearer "+token.accessToken
|
|
30
|
+
|
|
31
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow("You do not have permissions for this action");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("Can apply a register code and apply the discount", async () => {
|
|
35
|
+
const otherOrganization = await new OrganizationFactory({}).create();
|
|
36
|
+
const code = await new RegisterCodeFactory({organization: otherOrganization}).create();
|
|
37
|
+
|
|
38
|
+
const organization = await new OrganizationFactory({}).create();
|
|
39
|
+
const user = await new UserFactory({
|
|
40
|
+
organization,
|
|
41
|
+
globalPermissions: Permissions.create({ level: PermissionLevel.Full }),
|
|
42
|
+
email: 'admin@stamhoofd.be'
|
|
43
|
+
}).create()
|
|
44
|
+
const token = await Token.createToken(user)
|
|
45
|
+
|
|
46
|
+
const r = Request.buildJson(
|
|
47
|
+
"POST",
|
|
48
|
+
"/organization/register-code",
|
|
49
|
+
organization.getApiHost(),
|
|
50
|
+
{
|
|
51
|
+
registerCode: code.code,
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
r.headers.authorization = "Bearer "+token.accessToken
|
|
55
|
+
|
|
56
|
+
const response = await testServer.test(endpoint, r);
|
|
57
|
+
expect(response.body).toBeUndefined();
|
|
58
|
+
|
|
59
|
+
// Check if this organization has an open register code
|
|
60
|
+
const credits = await STCredit.getForOrganization(organization.id);
|
|
61
|
+
expect(credits.length).toBe(1);
|
|
62
|
+
expect(credits[0].change).toBe(code.value);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { AutoEncoder, Decoder, field, StringDecoder } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
3
|
+
import { Email } from '@stamhoofd/email';
|
|
4
|
+
import { RegisterCode, UsedRegisterCode } from '@stamhoofd/models';
|
|
5
|
+
|
|
6
|
+
import { Context } from '../../../../helpers/Context';
|
|
7
|
+
|
|
8
|
+
type Params = Record<string, never>;
|
|
9
|
+
type Query = undefined;
|
|
10
|
+
type ResponseBody = undefined;
|
|
11
|
+
|
|
12
|
+
class Body extends AutoEncoder {
|
|
13
|
+
@field({ decoder: StringDecoder })
|
|
14
|
+
registerCode: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One endpoint to create, patch and delete groups. Usefull because on organization setup, we need to create multiple groups at once. Also, sometimes we need to link values and update multiple groups at once
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export class ApplyRegisterCodeEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
22
|
+
bodyDecoder = Body as Decoder<Body>
|
|
23
|
+
|
|
24
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
25
|
+
if (request.method != "POST") {
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const params = Endpoint.parseParameters(request.url, "/organization/register-code", {});
|
|
30
|
+
|
|
31
|
+
if (params) {
|
|
32
|
+
return [true, params as Params];
|
|
33
|
+
}
|
|
34
|
+
return [false];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
38
|
+
const organization = await Context.setOrganizationScope();
|
|
39
|
+
await Context.authenticate()
|
|
40
|
+
|
|
41
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
42
|
+
throw Context.auth.error()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let code = request.body.registerCode;
|
|
46
|
+
|
|
47
|
+
if (code.startsWith('https:')) {
|
|
48
|
+
try {
|
|
49
|
+
const url = new URL(code);
|
|
50
|
+
const codeParam = url.searchParams.get('code');
|
|
51
|
+
if (codeParam) {
|
|
52
|
+
console.log('Parsed code from URL', codeParam)
|
|
53
|
+
code = codeParam;
|
|
54
|
+
}
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.error('Tried parsing code as URL but failed', code)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const {models, emails} = await RegisterCode.applyRegisterCode(organization, code)
|
|
61
|
+
|
|
62
|
+
for (const model of models) {
|
|
63
|
+
await model.save();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const email of emails) {
|
|
67
|
+
Email.sendInternal(email, organization.i18n)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (organization.meta.packages.isPaid) {
|
|
71
|
+
// Already bought something: apply credit to other organization immediately
|
|
72
|
+
const code = await UsedRegisterCode.getFor(organization.id)
|
|
73
|
+
if (code && !code.creditId) {
|
|
74
|
+
console.log("Rewarding code "+code.id+" for payment")
|
|
75
|
+
|
|
76
|
+
// Deze code werd nog niet beloond
|
|
77
|
+
await code.reward()
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return new Response(undefined);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
2
|
+
import { SimpleError } from "@simonbackx/simple-errors";
|
|
3
|
+
import { Group, Token } from '@stamhoofd/models';
|
|
4
|
+
import { Group as GroupStruct, GroupStatus } from "@stamhoofd/structures";
|
|
5
|
+
|
|
6
|
+
import { AuthenticatedStructures } from "../../../../helpers/AuthenticatedStructures";
|
|
7
|
+
import { Context } from "../../../../helpers/Context";
|
|
8
|
+
type Params = Record<string, never>;
|
|
9
|
+
type Query = undefined;
|
|
10
|
+
type Body = undefined
|
|
11
|
+
type ResponseBody = GroupStruct[]
|
|
12
|
+
|
|
13
|
+
export class GetOrganizationArchivedEndpoint 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, "/organization/archived-groups", {});
|
|
20
|
+
|
|
21
|
+
if (params) {
|
|
22
|
+
return [true, params as Params];
|
|
23
|
+
}
|
|
24
|
+
return [false];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
28
|
+
const organization = await Context.setOrganizationScope();
|
|
29
|
+
await Context.authenticate()
|
|
30
|
+
|
|
31
|
+
if (!await Context.auth.canAccessArchivedGroups(organization.id)) {
|
|
32
|
+
throw Context.auth.error()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Get all admins
|
|
36
|
+
const groups = await Group.where({ organizationId: organization.id, status: GroupStatus.Archived, deletedAt: null })
|
|
37
|
+
const structures: GroupStruct[] = []
|
|
38
|
+
for (const g of groups) {
|
|
39
|
+
structures.push(await AuthenticatedStructures.group(g))
|
|
40
|
+
}
|
|
41
|
+
return new Response(structures);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
2
|
+
import { Group } from '@stamhoofd/models';
|
|
3
|
+
import { Group as GroupStruct } from "@stamhoofd/structures";
|
|
4
|
+
|
|
5
|
+
import { AuthenticatedStructures } from "../../../../helpers/AuthenticatedStructures";
|
|
6
|
+
import { Context } from "../../../../helpers/Context";
|
|
7
|
+
type Params = Record<string, never>;
|
|
8
|
+
type Query = undefined;
|
|
9
|
+
type Body = undefined
|
|
10
|
+
type ResponseBody = GroupStruct[]
|
|
11
|
+
|
|
12
|
+
export class GetOrganizationAdminsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
13
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
14
|
+
if (request.method != "GET") {
|
|
15
|
+
return [false];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const params = Endpoint.parseParameters(request.url, "/organization/deleted-groups", {});
|
|
19
|
+
|
|
20
|
+
if (params) {
|
|
21
|
+
return [true, params as Params];
|
|
22
|
+
}
|
|
23
|
+
return [false];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
27
|
+
const organization = await Context.setOrganizationScope();
|
|
28
|
+
await Context.authenticate()
|
|
29
|
+
|
|
30
|
+
if (!await Context.auth.canAccessArchivedGroups(organization.id)) {
|
|
31
|
+
throw Context.auth.error()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Get all admins
|
|
35
|
+
const groups = await Group.where({ organizationId: organization.id, deletedAt: { sign: '!=', value: null } })
|
|
36
|
+
const structures: GroupStruct[] = []
|
|
37
|
+
for (const g of groups) {
|
|
38
|
+
structures.push(await AuthenticatedStructures.group(g))
|
|
39
|
+
}
|
|
40
|
+
return new Response(structures);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
2
|
+
import { OpenIDClientConfiguration } from "@stamhoofd/structures";
|
|
3
|
+
|
|
4
|
+
import { Context } from "../../../../helpers/Context";
|
|
5
|
+
|
|
6
|
+
type Params = Record<string, never>;
|
|
7
|
+
type Query = undefined;
|
|
8
|
+
type Body = undefined;
|
|
9
|
+
type ResponseBody = OpenIDClientConfiguration
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* One endpoint to create, patch and delete groups. Usefull because on organization setup, we need to create multiple groups at once. Also, sometimes we need to link values and update multiple groups at once
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export class GetOrganizationSSOEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
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, "/organization/sso", {});
|
|
22
|
+
|
|
23
|
+
if (params) {
|
|
24
|
+
return [true, params as Params];
|
|
25
|
+
}
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
30
|
+
const organization = await Context.setOrganizationScope();
|
|
31
|
+
await Context.authenticate()
|
|
32
|
+
|
|
33
|
+
if (!await Context.auth.canManageSSOSettings(organization.id)) {
|
|
34
|
+
throw Context.auth.error()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return new Response(organization.serverMeta.ssoConfiguration ?? OpenIDClientConfiguration.create({
|
|
38
|
+
clientId: "",
|
|
39
|
+
clientSecret: "",
|
|
40
|
+
issuer: ""
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
}
|