Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 30 additions & 14 deletions apps/start/src/routes/_login.login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { useCookieStore } from '@/hooks/use-cookie-store';
import { createTitle, PAGE_TITLES } from '@/utils/title';

const validateSearch = z.object({
error: z.string().optional(),
correlationId: z.string().optional(),
inviteId: z.string().optional(),
});
export const Route = createFileRoute('/_login/login')({
component: LoginPage,
head: () => ({
Expand All @@ -17,15 +22,20 @@ export const Route = createFileRoute('/_login/login')({
{ name: 'robots', content: 'noindex, follow' },
],
}),
validateSearch: z.object({
error: z.string().optional(),
correlationId: z.string().optional(),
inviteId: z.string().optional(),
}),
validateSearch,
loader: ({ context, location }) => {
const search = validateSearch.safeParse(location.search);
return context.queryClient.ensureQueryData(
context.trpc.auth.isRegistrationAllowed.queryOptions({
inviteId: search.success ? search.data.inviteId : undefined,
})
);
},
});

function LoginPage() {
const { error, correlationId, inviteId } = Route.useSearch();
const isRegistrationAllowed = Route.useLoaderData();
const [lastProvider] = useCookieStore<null | string>(
'last-auth-provider',
null
Expand All @@ -35,15 +45,21 @@ function LoginPage() {
<div className="col w-full gap-8 text-left">
<div>
<h1 className="mb-2 font-bold text-3xl text-foreground">Sign in</h1>
<p className="text-muted-foreground">
Don't have an account?{' '}
<a
className="font-medium text-foreground underline"
href="/onboarding"
>
Create one today
</a>
</p>
{isRegistrationAllowed && (
<p className="text-muted-foreground">
Don't have an account?{' '}
<a
className="font-medium text-foreground underline"
href={
inviteId
? `/onboarding?inviteId=${encodeURIComponent(inviteId)}`
: '/onboarding'
}
>
Create one today
</a>
</p>
)}
</div>
{error && (
<Alert
Expand Down
32 changes: 32 additions & 0 deletions apps/start/src/routes/_public.onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@ export const Route = createFileRoute('/_public/onboarding')({
})
);
}
return context.queryClient.ensureQueryData(
context.trpc.auth.isRegistrationAllowed.queryOptions({
inviteId: search.success ? search.data.inviteId : undefined,
})
);
},
pendingComponent: FullPageLoadingState,
});

function Component() {
const { inviteId } = Route.useSearch();
const isRegistrationAllowed = Route.useLoaderData();
const trpc = useTRPC();
const { data: invite } = useQuery(
trpc.organization.getInvite.queryOptions(
Expand All @@ -53,6 +59,32 @@ function Component() {
}
)
);

if (!isRegistrationAllowed) {
return (
<div className="col w-full gap-8 py-4 text-left">
<div>
<h1 className="mb-2 font-bold text-3xl text-foreground">
{inviteId
? 'Registration is unavailable'
: 'Registration is disabled'}
</h1>
<p className="text-muted-foreground">
{inviteId
? 'This invitation no longer exists, has already been used, or invitations are disabled on this instance. Ask an administrator for help.'
: "New accounts can't be created on this instance. Ask an administrator to invite you."}
</p>
<p className="mt-3 text-muted-foreground">
Already have an account?{' '}
<a className="font-medium text-foreground underline" href="/login">
Sign in
</a>
</p>
</div>
</div>
);
}

return (
<div className="col w-full gap-8 py-4 text-left">
<div>
Expand Down
5 changes: 5 additions & 0 deletions packages/trpc/src/routers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,11 @@ export const authRouter = createTRPCRouter({
return ctx.session;
}),

// Lets the login/onboarding pages hide sign-up when it would be rejected.
isRegistrationAllowed: publicProcedure
.input(z.object({ inviteId: z.string().nullish() }))
.query(({ input }) => getIsRegistrationAllowed(input.inviteId)),
Comment thread
coderabbitai[bot] marked this conversation as resolved.

extendSession: publicProcedure.mutation(async ({ ctx }) => {
if (!(ctx.session.session && ctx.cookies.session)) {
return { extended: false };
Expand Down