@augmenting-integrations/auth 8.4.1 → 8.5.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/dist/client.cjs +2 -9
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +0 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -9
- package/dist/client.js.map +1 -1
- package/dist/server/createAuth.d.ts +31 -2
- package/dist/server/createAuth.d.ts.map +1 -1
- package/dist/server/invitations.d.ts +82 -0
- package/dist/server/invitations.d.ts.map +1 -0
- package/dist/server/settings.d.ts +64 -0
- package/dist/server/settings.d.ts.map +1 -0
- package/dist/server.cjs +285 -93
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.ts +2 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +282 -88
- package/dist/server.js.map +1 -1
- package/package.json +5 -3
- package/dist/client/tenant.cjs +0 -64
- package/dist/client/tenant.cjs.map +0 -1
- package/dist/client/tenant.d.ts +0 -9
- package/dist/client/tenant.d.ts.map +0 -1
- package/dist/client/tenant.js +0 -30
- package/dist/client/tenant.js.map +0 -1
- package/dist/server/tenant.d.ts +0 -26
- package/dist/server/tenant.d.ts.map +0 -1
- package/dist/tenant-types.cjs +0 -29
- package/dist/tenant-types.cjs.map +0 -1
- package/dist/tenant-types.d.ts +0 -63
- package/dist/tenant-types.d.ts.map +0 -1
- package/dist/tenant-types.js +0 -5
- package/dist/tenant-types.js.map +0 -1
package/dist/server.js
CHANGED
|
@@ -63,6 +63,9 @@ function createAuth(opts) {
|
|
|
63
63
|
appDomain: tenant.appDomain,
|
|
64
64
|
apex: tenant.apex
|
|
65
65
|
});
|
|
66
|
+
const requiredIdentityGroups = opts.appAccess?.requiredIdentityGroups ?? [];
|
|
67
|
+
const hasAccessPolicy = requiredIdentityGroups.length > 0;
|
|
68
|
+
const forbiddenPage = opts.appAccess?.forbiddenPage ?? (tenant.appDomain === tenant.apex ? `/login?error=app_forbidden&app=${encodeURIComponent(tenant.appSlug ?? "apex")}` : `https://${tenant.apex}/login?error=app_forbidden&app=${encodeURIComponent(tenant.appSlug ?? "")}`);
|
|
66
69
|
const config = {
|
|
67
70
|
secret: authSecret,
|
|
68
71
|
cookies: cookieDomain ? {
|
|
@@ -109,7 +112,7 @@ function createAuth(opts) {
|
|
|
109
112
|
],
|
|
110
113
|
session: { strategy: "jwt" },
|
|
111
114
|
callbacks: {
|
|
112
|
-
jwt: ({ token, user, profile }) => {
|
|
115
|
+
jwt: ({ token, user, profile, account }) => {
|
|
113
116
|
if (user) {
|
|
114
117
|
token.sub ??= user.id ?? void 0;
|
|
115
118
|
token.email ??= user.email ?? void 0;
|
|
@@ -127,11 +130,18 @@ function createAuth(opts) {
|
|
|
127
130
|
token["cognito:groups"] = groups;
|
|
128
131
|
}
|
|
129
132
|
}
|
|
133
|
+
if (account?.access_token) {
|
|
134
|
+
token["accessToken"] = account.access_token;
|
|
135
|
+
}
|
|
130
136
|
return token;
|
|
131
137
|
},
|
|
132
138
|
session: ({ session, token }) => {
|
|
133
139
|
const groups = token["cognito:groups"] ?? [];
|
|
134
140
|
session.user.groups = groups;
|
|
141
|
+
const accessToken = token["accessToken"];
|
|
142
|
+
if (typeof accessToken === "string") {
|
|
143
|
+
session.accessToken = accessToken;
|
|
144
|
+
}
|
|
135
145
|
return session;
|
|
136
146
|
},
|
|
137
147
|
authorized: ({ auth: session, request: { nextUrl } }) => {
|
|
@@ -147,6 +157,16 @@ function createAuth(opts) {
|
|
|
147
157
|
}
|
|
148
158
|
return false;
|
|
149
159
|
}
|
|
160
|
+
if (session && isAuthedRoute && hasAccessPolicy) {
|
|
161
|
+
const userGroups = (session.user?.groups ?? []).map((g) => g.toLowerCase());
|
|
162
|
+
const allowed = requiredIdentityGroups.some(
|
|
163
|
+
(g) => userGroups.includes(g.toLowerCase())
|
|
164
|
+
);
|
|
165
|
+
if (!allowed) {
|
|
166
|
+
const target = forbiddenPage.startsWith("http") ? new URL(forbiddenPage) : new URL(forbiddenPage, nextUrl.origin);
|
|
167
|
+
return Response.redirect(target.toString(), 302);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
150
170
|
return true;
|
|
151
171
|
},
|
|
152
172
|
redirect: buildRedirectCallback(tenant.parentDomain)
|
|
@@ -293,89 +313,6 @@ function createGetOrCreateAppUser(opts) {
|
|
|
293
313
|
};
|
|
294
314
|
}
|
|
295
315
|
|
|
296
|
-
// src/server/tenant.ts
|
|
297
|
-
import "server-only";
|
|
298
|
-
import * as React from "react";
|
|
299
|
-
|
|
300
|
-
// src/tenant-types.ts
|
|
301
|
-
var TENANT_GLOBAL_KEY = "__TENANT__";
|
|
302
|
-
|
|
303
|
-
// src/server/tenant.ts
|
|
304
|
-
function loadTenantConfig(opts) {
|
|
305
|
-
const env = process.env;
|
|
306
|
-
const o = opts.overrides ?? {};
|
|
307
|
-
const parentDomainRaw = o.parentDomain ?? env.AUTH_ALLOWED_PARENT_DOMAIN;
|
|
308
|
-
const apexFallback = parentDomainRaw?.replace(/^\./, "");
|
|
309
|
-
const draft = {
|
|
310
|
-
role: opts.role,
|
|
311
|
-
apex: o.apex ?? env.APEX_DOMAIN ?? apexFallback,
|
|
312
|
-
cookieDomain: o.cookieDomain ?? env.AUTH_COOKIE_DOMAIN,
|
|
313
|
-
parentDomain: parentDomainRaw,
|
|
314
|
-
region: o.region ?? env.AWS_REGION ?? "us-east-1",
|
|
315
|
-
appSlug: o.appSlug ?? env.APP_SLUG,
|
|
316
|
-
appDomain: o.appDomain ?? env.APP_DOMAIN,
|
|
317
|
-
authSecretArn: o.authSecretArn ?? env.AUTH_SECRET_ARN,
|
|
318
|
-
registryTable: o.registryTable ?? env.APP_REGISTRY_TABLE,
|
|
319
|
-
authCognitoSecretArn: o.authCognitoSecretArn ?? env.AUTH_COGNITO_SECRET_ARN,
|
|
320
|
-
cognitoIssuer: o.cognitoIssuer ?? env.AUTH_COGNITO_ISSUER,
|
|
321
|
-
cognitoClientId: o.cognitoClientId ?? env.AUTH_COGNITO_ID,
|
|
322
|
-
adminEmails: o.adminEmails ?? env.ADMIN_EMAILS,
|
|
323
|
-
dbSecretArn: o.dbSecretArn ?? env.DB_SECRET_ARN,
|
|
324
|
-
dbHost: o.dbHost ?? env.DB_HOST,
|
|
325
|
-
dbName: o.dbName ?? env.DB_NAME,
|
|
326
|
-
stripeSecretArn: o.stripeSecretArn ?? env.STRIPE_SECRET_ARN,
|
|
327
|
-
stripeWebhookSecretArn: o.stripeWebhookSecretArn ?? env.STRIPE_WEBHOOK_SECRET_ARN
|
|
328
|
-
};
|
|
329
|
-
if (opts.role === "apex" && !draft.appDomain) {
|
|
330
|
-
draft.appDomain = draft.apex;
|
|
331
|
-
}
|
|
332
|
-
const required = [
|
|
333
|
-
{ key: "apex", env: "APEX_DOMAIN (or derived from AUTH_ALLOWED_PARENT_DOMAIN)" },
|
|
334
|
-
{ key: "cookieDomain", env: "AUTH_COOKIE_DOMAIN" },
|
|
335
|
-
{ key: "parentDomain", env: "AUTH_ALLOWED_PARENT_DOMAIN" },
|
|
336
|
-
{ key: "authSecretArn", env: "AUTH_SECRET_ARN" },
|
|
337
|
-
{ key: "appDomain", env: "APP_DOMAIN" }
|
|
338
|
-
];
|
|
339
|
-
if (opts.role === "apex") {
|
|
340
|
-
required.push(
|
|
341
|
-
{ key: "authCognitoSecretArn", env: "AUTH_COGNITO_SECRET_ARN" },
|
|
342
|
-
{ key: "cognitoIssuer", env: "AUTH_COGNITO_ISSUER" },
|
|
343
|
-
{ key: "cognitoClientId", env: "AUTH_COGNITO_ID" }
|
|
344
|
-
);
|
|
345
|
-
} else {
|
|
346
|
-
required.push({ key: "appSlug", env: "APP_SLUG" });
|
|
347
|
-
}
|
|
348
|
-
if (process.env.NEXT_PHASE === "phase-production-build" || !process.env.AWS_LAMBDA_FUNCTION_NAME) {
|
|
349
|
-
return draft;
|
|
350
|
-
}
|
|
351
|
-
const missing = required.filter((r) => !draft[r.key]).map((r) => r.env);
|
|
352
|
-
if (missing.length > 0) {
|
|
353
|
-
throw new Error(
|
|
354
|
-
`loadTenantConfig(${opts.role}): missing required env vars: ${missing.join(", ")}`
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
return draft;
|
|
358
|
-
}
|
|
359
|
-
function publicSubset(config) {
|
|
360
|
-
return {
|
|
361
|
-
apex: config.apex,
|
|
362
|
-
cookieDomain: config.cookieDomain,
|
|
363
|
-
parentDomain: config.parentDomain,
|
|
364
|
-
region: config.region,
|
|
365
|
-
appSlug: config.appSlug,
|
|
366
|
-
appDomain: config.appDomain,
|
|
367
|
-
role: config.role
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
var INNER_HTML_PROP = "dangerouslySetInnerHTML";
|
|
371
|
-
function TenantBootScript({ config }) {
|
|
372
|
-
const payload = JSON.stringify(config).replace(/</g, "\\u003c");
|
|
373
|
-
const body = `window.${TENANT_GLOBAL_KEY}=${payload};`;
|
|
374
|
-
const props = {};
|
|
375
|
-
props[INNER_HTML_PROP] = { __html: body };
|
|
376
|
-
return React.createElement("script", props);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
316
|
// src/server/handlers.ts
|
|
380
317
|
import "server-only";
|
|
381
318
|
import { NextResponse } from "next/server";
|
|
@@ -603,22 +540,279 @@ function createInvitationHandlers(opts) {
|
|
|
603
540
|
}
|
|
604
541
|
};
|
|
605
542
|
}
|
|
543
|
+
|
|
544
|
+
// src/server/settings.ts
|
|
545
|
+
import "server-only";
|
|
546
|
+
import { NextResponse as NextResponse2 } from "next/server";
|
|
547
|
+
function getAccessToken(session) {
|
|
548
|
+
if (!session) return null;
|
|
549
|
+
const fromTop = session.accessToken;
|
|
550
|
+
if (typeof fromTop === "string" && fromTop.length > 0) return fromTop;
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
function accessTokenMissingResponse() {
|
|
554
|
+
return NextResponse2.json(
|
|
555
|
+
{
|
|
556
|
+
error: "Cognito access token not present on the session. The user must sign in fresh on the apex.",
|
|
557
|
+
code: "access_token_missing"
|
|
558
|
+
},
|
|
559
|
+
{ status: 401 }
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
function jsonValidation(message) {
|
|
563
|
+
return NextResponse2.json({ error: message, code: "validation" }, { status: 400 });
|
|
564
|
+
}
|
|
565
|
+
function createSettingsHandlers(opts) {
|
|
566
|
+
const passwordChange = {
|
|
567
|
+
POST: async (request) => {
|
|
568
|
+
const session = await opts.auth();
|
|
569
|
+
if (!session?.user) {
|
|
570
|
+
return NextResponse2.json({ error: "unauthorized" }, { status: 401 });
|
|
571
|
+
}
|
|
572
|
+
const accessToken = getAccessToken(session);
|
|
573
|
+
if (!accessToken) return accessTokenMissingResponse();
|
|
574
|
+
let body;
|
|
575
|
+
try {
|
|
576
|
+
body = await request.json();
|
|
577
|
+
} catch {
|
|
578
|
+
return jsonValidation("invalid JSON body");
|
|
579
|
+
}
|
|
580
|
+
const currentPassword = typeof body.currentPassword === "string" ? body.currentPassword : "";
|
|
581
|
+
const newPassword = typeof body.newPassword === "string" ? body.newPassword : "";
|
|
582
|
+
if (!currentPassword) return jsonValidation("currentPassword required");
|
|
583
|
+
if (newPassword.length < 8)
|
|
584
|
+
return jsonValidation("newPassword must be at least 8 chars");
|
|
585
|
+
try {
|
|
586
|
+
await opts.cognito.changePassword({
|
|
587
|
+
accessToken,
|
|
588
|
+
previousPassword: currentPassword,
|
|
589
|
+
proposedPassword: newPassword
|
|
590
|
+
});
|
|
591
|
+
const appUser = await opts.getOrCreateAppUser(session);
|
|
592
|
+
const db = await opts.getDb();
|
|
593
|
+
await db.user.update({
|
|
594
|
+
where: { id: appUser.id },
|
|
595
|
+
data: { must_change_password: false }
|
|
596
|
+
});
|
|
597
|
+
return NextResponse2.json({ ok: true });
|
|
598
|
+
} catch (err) {
|
|
599
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
600
|
+
return NextResponse2.json({ error: message, code: "cognito" }, { status: 400 });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
const twoFactorSetup = {
|
|
605
|
+
POST: async () => {
|
|
606
|
+
const session = await opts.auth();
|
|
607
|
+
if (!session?.user) {
|
|
608
|
+
return NextResponse2.json({ error: "unauthorized" }, { status: 401 });
|
|
609
|
+
}
|
|
610
|
+
const accessToken = getAccessToken(session);
|
|
611
|
+
if (!accessToken) return accessTokenMissingResponse();
|
|
612
|
+
try {
|
|
613
|
+
const { secretCode } = await opts.cognito.associateSoftwareToken({
|
|
614
|
+
accessToken
|
|
615
|
+
});
|
|
616
|
+
const accountName = session.user.email ?? "user";
|
|
617
|
+
const otpAuthUri = opts.cognito.buildOtpAuthUri({
|
|
618
|
+
secret: secretCode,
|
|
619
|
+
accountName,
|
|
620
|
+
issuer: opts.appDisplayName
|
|
621
|
+
});
|
|
622
|
+
const qrDataUrl = opts.generateQrDataUrl ? await opts.generateQrDataUrl(otpAuthUri).catch(() => "") : "";
|
|
623
|
+
return NextResponse2.json({ qrDataUrl, otpAuthUri, secret: secretCode });
|
|
624
|
+
} catch (err) {
|
|
625
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
626
|
+
return NextResponse2.json({ error: message, code: "cognito" }, { status: 500 });
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
const twoFactorVerify = {
|
|
631
|
+
POST: async (request) => {
|
|
632
|
+
const session = await opts.auth();
|
|
633
|
+
if (!session?.user) {
|
|
634
|
+
return NextResponse2.json({ error: "unauthorized" }, { status: 401 });
|
|
635
|
+
}
|
|
636
|
+
const accessToken = getAccessToken(session);
|
|
637
|
+
if (!accessToken) return accessTokenMissingResponse();
|
|
638
|
+
let body;
|
|
639
|
+
try {
|
|
640
|
+
body = await request.json();
|
|
641
|
+
} catch {
|
|
642
|
+
return jsonValidation("invalid JSON body");
|
|
643
|
+
}
|
|
644
|
+
const code = typeof body.code === "string" ? body.code : "";
|
|
645
|
+
if (!/^[0-9]{6}$/.test(code)) return jsonValidation("code must be 6 digits");
|
|
646
|
+
try {
|
|
647
|
+
const result = await opts.cognito.verifySoftwareToken({ accessToken, code });
|
|
648
|
+
if (result.status !== "SUCCESS") {
|
|
649
|
+
return NextResponse2.json(
|
|
650
|
+
{ error: `verify failed: ${result.status}`, code: "verify_failed" },
|
|
651
|
+
{ status: 400 }
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
await opts.cognito.setUserMfaPreference({ accessToken, enabled: true });
|
|
655
|
+
const appUser = await opts.getOrCreateAppUser(session);
|
|
656
|
+
const db = await opts.getDb();
|
|
657
|
+
await db.user.update({
|
|
658
|
+
where: { id: appUser.id },
|
|
659
|
+
data: { two_factor_confirmed_at: /* @__PURE__ */ new Date() }
|
|
660
|
+
});
|
|
661
|
+
return NextResponse2.json({ ok: true });
|
|
662
|
+
} catch (err) {
|
|
663
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
664
|
+
return NextResponse2.json({ error: message, code: "cognito" }, { status: 500 });
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
const twoFactorDisable = {
|
|
669
|
+
POST: async () => {
|
|
670
|
+
const session = await opts.auth();
|
|
671
|
+
if (!session?.user) {
|
|
672
|
+
return NextResponse2.json({ error: "unauthorized" }, { status: 401 });
|
|
673
|
+
}
|
|
674
|
+
const accessToken = getAccessToken(session);
|
|
675
|
+
if (!accessToken) return accessTokenMissingResponse();
|
|
676
|
+
try {
|
|
677
|
+
await opts.cognito.setUserMfaPreference({ accessToken, enabled: false });
|
|
678
|
+
const appUser = await opts.getOrCreateAppUser(session);
|
|
679
|
+
const db = await opts.getDb();
|
|
680
|
+
await db.user.update({
|
|
681
|
+
where: { id: appUser.id },
|
|
682
|
+
data: { two_factor_confirmed_at: null }
|
|
683
|
+
});
|
|
684
|
+
return NextResponse2.json({ ok: true });
|
|
685
|
+
} catch (err) {
|
|
686
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
687
|
+
return NextResponse2.json({ error: message, code: "cognito" }, { status: 500 });
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
return { passwordChange, twoFactorSetup, twoFactorVerify, twoFactorDisable };
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/server/invitations.ts
|
|
695
|
+
import "server-only";
|
|
696
|
+
import { NextResponse as NextResponse3 } from "next/server";
|
|
697
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
698
|
+
var ROLE_RE = /^[a-z_]+$/;
|
|
699
|
+
var PARENT_RE = /^\d+$/;
|
|
700
|
+
function jsonError(status, error, code, extra) {
|
|
701
|
+
return NextResponse3.json({ error, code, ...extra ?? {} }, { status });
|
|
702
|
+
}
|
|
703
|
+
function createInvitationSendHandlers(opts) {
|
|
704
|
+
const send = {
|
|
705
|
+
POST: async (request) => {
|
|
706
|
+
const session = await opts.auth();
|
|
707
|
+
if (!session?.user) return jsonError(401, "unauthorized", "unauthorized");
|
|
708
|
+
const caller = await opts.getOrCreateAppUser(session);
|
|
709
|
+
if (!opts.canInvite(caller.role)) {
|
|
710
|
+
return jsonError(403, "forbidden", "forbidden");
|
|
711
|
+
}
|
|
712
|
+
let body;
|
|
713
|
+
try {
|
|
714
|
+
body = await request.json();
|
|
715
|
+
} catch {
|
|
716
|
+
return jsonError(400, "invalid JSON body", "validation");
|
|
717
|
+
}
|
|
718
|
+
const email = typeof body.email === "string" ? body.email.trim().toLowerCase() : "";
|
|
719
|
+
const role = typeof body.role === "string" ? body.role.trim() : "";
|
|
720
|
+
const parentIdRaw = body.parent_id;
|
|
721
|
+
if (!EMAIL_RE.test(email) || email.length > 255) {
|
|
722
|
+
return jsonError(400, "invalid email", "validation");
|
|
723
|
+
}
|
|
724
|
+
if (!role || role.length > 30 || !ROLE_RE.test(role)) {
|
|
725
|
+
return jsonError(400, "role must be snake_case lowercase", "validation");
|
|
726
|
+
}
|
|
727
|
+
if (opts.allowedRoles && !opts.allowedRoles.includes(role)) {
|
|
728
|
+
return jsonError(
|
|
729
|
+
400,
|
|
730
|
+
`role must be one of: ${opts.allowedRoles.join(", ")}`,
|
|
731
|
+
"validation"
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
let parentOverride = null;
|
|
735
|
+
if (parentIdRaw !== void 0 && parentIdRaw !== null) {
|
|
736
|
+
if (typeof parentIdRaw !== "string" || !PARENT_RE.test(parentIdRaw)) {
|
|
737
|
+
return jsonError(
|
|
738
|
+
400,
|
|
739
|
+
"parent_id must be a stringified BigInt or null",
|
|
740
|
+
"validation"
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
parentOverride = BigInt(parentIdRaw);
|
|
744
|
+
}
|
|
745
|
+
const parent_id = opts.resolveInviteScope(caller, parentOverride);
|
|
746
|
+
const db = await opts.getDb();
|
|
747
|
+
const existing = await db.user.findUnique({ where: { email } });
|
|
748
|
+
if (existing) {
|
|
749
|
+
return jsonError(409, "user with that email already exists", "conflict");
|
|
750
|
+
}
|
|
751
|
+
const token = opts.generateInvitationToken();
|
|
752
|
+
const expiresAt = opts.invitationExpiresAt();
|
|
753
|
+
const invitation = await db.invitation.create({
|
|
754
|
+
data: {
|
|
755
|
+
token,
|
|
756
|
+
email,
|
|
757
|
+
inviter_id: caller.id,
|
|
758
|
+
intended_role: role,
|
|
759
|
+
parent_id,
|
|
760
|
+
expires_at: expiresAt
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
const origin = opts.appDomainEnv ? `https://${opts.appDomainEnv}` : new URL(request.url).origin;
|
|
764
|
+
const invitationUrl = opts.buildInvitationUrl(token, origin);
|
|
765
|
+
const rendered = opts.renderInvitationEmail({
|
|
766
|
+
inviterName: caller.name,
|
|
767
|
+
inviteeEmail: email,
|
|
768
|
+
intendedRole: role,
|
|
769
|
+
invitationUrl,
|
|
770
|
+
expiresAt,
|
|
771
|
+
appDisplayName: opts.appDisplayName
|
|
772
|
+
});
|
|
773
|
+
try {
|
|
774
|
+
await opts.sendEmail({
|
|
775
|
+
to: email,
|
|
776
|
+
subject: rendered.subject,
|
|
777
|
+
html: rendered.html,
|
|
778
|
+
text: rendered.text
|
|
779
|
+
});
|
|
780
|
+
} catch (err) {
|
|
781
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
782
|
+
return NextResponse3.json(
|
|
783
|
+
{
|
|
784
|
+
invitationId: invitation.id.toString(),
|
|
785
|
+
expiresAt: invitation.expires_at.toISOString(),
|
|
786
|
+
warning: `invitation saved but email send failed: ${message}`
|
|
787
|
+
},
|
|
788
|
+
{ status: 202 }
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
return NextResponse3.json(
|
|
792
|
+
{
|
|
793
|
+
invitationId: invitation.id.toString(),
|
|
794
|
+
expiresAt: invitation.expires_at.toISOString()
|
|
795
|
+
},
|
|
796
|
+
{ status: 201 }
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
return { send };
|
|
801
|
+
}
|
|
606
802
|
export {
|
|
607
803
|
AuthError,
|
|
608
804
|
IMPERSONATE_COOKIE_NAME,
|
|
609
805
|
IMPERSONATE_TTL_SECONDS,
|
|
610
|
-
TENANT_GLOBAL_KEY,
|
|
611
|
-
TenantBootScript,
|
|
612
806
|
createAuth,
|
|
613
807
|
createGetOrCreateAppUser,
|
|
614
808
|
createImpersonateHandlers,
|
|
615
809
|
createInvitationHandlers,
|
|
810
|
+
createInvitationSendHandlers,
|
|
616
811
|
createMeHandler,
|
|
812
|
+
createSettingsHandlers,
|
|
617
813
|
getUserGroups,
|
|
618
814
|
hasGroup,
|
|
619
|
-
loadTenantConfig,
|
|
620
815
|
mintImpersonationToken,
|
|
621
|
-
publicSubset,
|
|
622
816
|
requireGroup,
|
|
623
817
|
verifyImpersonationToken
|
|
624
818
|
};
|