2.18.0: New Invitation flow, refactored Database Webhooks, new ShadCN UI Components (#384)

* Streamlined invitations flow
* Removed web hooks in favor of handling logic directly in server actions
* Added new Shadcn UI Components
This commit is contained in:
Giancarlo Buomprisco
2025-10-05 17:54:16 +08:00
committed by GitHub
parent 195cf41680
commit 2e20d3e76f
60 changed files with 3760 additions and 1009 deletions

View File

@@ -65,7 +65,13 @@ async function DocumentationPage({ params }: DocumentationPageProps) {
<section
className={'flex flex-col gap-y-1 border-b border-dashed pb-4'}
>
<h1 className={'text-foreground text-3xl'}>{page.title}</h1>
<h1
className={
'text-foreground text-3xl font-semibold tracking-tighter'
}
>
{page.title}
</h1>
<h2 className={'text-secondary-foreground/80 text-lg'}>
{description}

View File

@@ -26,11 +26,7 @@ export const generateMetadata = async () => {
};
async function SignInPage({ searchParams }: SignInPageProps) {
const { invite_token: inviteToken, next } = await searchParams;
const signUpPath =
pathsConfig.auth.signUp +
(inviteToken ? `?invite_token=${inviteToken}` : '');
const { next } = await searchParams;
const paths = {
callback: pathsConfig.auth.callback,
@@ -50,15 +46,11 @@ async function SignInPage({ searchParams }: SignInPageProps) {
</p>
</div>
<SignInMethodsContainer
inviteToken={inviteToken}
paths={paths}
providers={authConfig.providers}
/>
<SignInMethodsContainer paths={paths} providers={authConfig.providers} />
<div className={'flex justify-center'}>
<Button asChild variant={'link'} size={'sm'}>
<Link href={signUpPath} prefetch={true}>
<Link href={pathsConfig.auth.signUp} prefetch={true}>
<Trans i18nKey={'auth:doNotHaveAccountYet'} />
</Link>
</Button>

View File

@@ -18,24 +18,12 @@ export const generateMetadata = async () => {
};
};
interface Props {
searchParams: Promise<{
invite_token?: string;
}>;
}
const paths = {
callback: pathsConfig.auth.callback,
appHome: pathsConfig.app.home,
};
async function SignUpPage({ searchParams }: Props) {
const inviteToken = (await searchParams).invite_token;
const signInPath =
pathsConfig.auth.signIn +
(inviteToken ? `?invite_token=${inviteToken}` : '');
async function SignUpPage() {
return (
<>
<div className={'flex flex-col items-center gap-1'}>
@@ -51,13 +39,12 @@ async function SignUpPage({ searchParams }: Props) {
<SignUpMethodsContainer
providers={authConfig.providers}
displayTermsCheckbox={authConfig.displayTermsCheckbox}
inviteToken={inviteToken}
paths={paths}
/>
<div className={'flex justify-center'}>
<Button asChild variant={'link'} size={'sm'}>
<Link href={signInPath} prefetch={true}>
<Link href={pathsConfig.auth.signIn} prefetch={true}>
<Trans i18nKey={'auth:alreadyHaveAnAccount'} />
</Link>
</Button>

View File

@@ -56,12 +56,12 @@ async function JoinTeamAccountPage(props: JoinTeamAccountPageProps) {
const verifyMfaUrl = `${pathsConfig.auth.verifyMfa}?${urlParams.toString()}`;
// if the user needs to verify MFA, redirect them to the MFA verification page
// if the user needs to verify MFA
// redirect them to the MFA verification page
redirect(verifyMfaUrl);
} else {
const urlParams = new URLSearchParams({
invite_token: token,
email: searchParams.email ?? '',
});
const nextUrl = `${pathsConfig.auth.signUp}?${urlParams.toString()}`;
@@ -78,8 +78,10 @@ async function JoinTeamAccountPage(props: JoinTeamAccountPageProps) {
// the user is logged in, we can now check if the token is valid
const invitation = await api.getInvitation(adminClient, token);
// the invitation is not found or expired
if (!invitation) {
// the invitation is not found or expired or the email is not the same as the user's email
const isInvitationValid = invitation?.email === auth.data.email;
if (!isInvitationValid) {
return (
<AuthLayoutShell Logo={AppLogo}>
<InviteNotFoundOrExpired />

View File

@@ -135,5 +135,6 @@
"accountUnlinked": "Account successfully unlinked",
"linkEmailPassword": "Email & Password",
"linkEmailPasswordDescription": "Add an email and password to your account for additional sign-in options",
"noAccountsAvailable": "No additional accounts available to link"
"noAccountsAvailable": "No additional accounts available to link",
"linkAccountDescription": "Link account to sign in with {{provider}}"
}

View File

@@ -5,19 +5,6 @@
-- In production, you should manually create webhooks in the Supabase dashboard (or create a migration to do so).
-- We don't do it because you'll need to manually add your webhook URL and secret key.
-- this webhook will be triggered after deleting an account
create trigger "accounts_teardown"
after delete
on "public"."accounts"
for each row
execute function "supabase_functions"."http_request"(
'http://host.docker.internal:3000/api/db/webhook',
'POST',
'{"Content-Type":"application/json", "X-Supabase-Event-Signature":"WEBHOOKSECRET"}',
'{}',
'5000'
);
-- this webhook will be triggered after a delete on the subscriptions table
-- which should happen when a user deletes their account (and all their subscriptions)
create trigger "subscriptions_delete"
@@ -32,21 +19,6 @@ execute function "supabase_functions"."http_request"(
'5000'
);
-- this webhook will be triggered after every insert on the invitations table
-- which should happen when a user invites someone to their account
create trigger "invitations_insert"
after insert
on "public"."invitations"
for each row
execute function "supabase_functions"."http_request"(
'http://host.docker.internal:3000/api/db/webhook',
'POST',
'{"Content-Type":"application/json", "X-Supabase-Event-Signature":"WEBHOOKSECRET"}',
'{}',
'5000'
);
-- DATA SEED
-- This is a data dump for testing purposes. It should be used to seed the database with data for testing.