Openstatus www.openstatus.dev

fix: invite callback redirect (#1719)

authored by

Maximilian Kaske and committed by
GitHub
c5f89042 252bded3

+35 -8
+3 -3
apps/dashboard/src/app/(dashboard)/invite/client.tsx
··· 59 59 <SectionHeader> 60 60 <SectionTitle>Invitation</SectionTitle> 61 61 <SectionDescription> 62 - You&apos;ve been invited to join the workspace 62 + You&apos;ve been invited to join the workspace{" "} 63 63 {invitation.workspace.name ? ( 64 - <span className="font-semibold">{` ${invitation.workspace.name}`}</span> 64 + <span className="font-semibold">{invitation.workspace.name}</span> 65 65 ) : ( 66 - "" 66 + <span className="font-mono">{invitation.workspace.slug}</span> 67 67 )} 68 68 . 69 69 </SectionDescription>
+15 -1
apps/dashboard/src/app/(dashboard)/onboarding/client.tsx
··· 31 31 import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; 32 32 import { ArrowUpRight } from "lucide-react"; 33 33 import Link from "next/link"; 34 + import { useRouter } from "next/navigation"; 34 35 import { useQueryStates } from "nuqs"; 36 + import { useEffect } from "react"; 35 37 import { searchParamsParsers } from "./search-params"; 36 38 37 39 const moreActions = [ ··· 86 88 ]; 87 89 88 90 export function Client() { 89 - const [{ step }, setSearchParams] = useQueryStates(searchParamsParsers); 91 + const [{ step, callbackUrl }, setSearchParams] = 92 + useQueryStates(searchParamsParsers); 93 + const router = useRouter(); 90 94 const trpc = useTRPC(); 91 95 const queryClient = useQueryClient(); 92 96 const { data: workspace, refetch } = useQuery( ··· 123 127 const createFeedbackMutation = useMutation( 124 128 trpc.feedback.submit.mutationOptions({}), 125 129 ); 130 + 131 + useEffect(() => { 132 + if (callbackUrl) { 133 + const callbackUrlObj = new URL(callbackUrl); 134 + const redirectTo = callbackUrlObj.searchParams.get("redirectTo"); 135 + if (redirectTo?.startsWith("/invite")) { 136 + router.push(redirectTo); 137 + } 138 + } 139 + }, [callbackUrl, router]); 126 140 127 141 return ( 128 142 <SectionGroup>
+6 -1
apps/dashboard/src/app/(dashboard)/onboarding/search-params.ts
··· 1 - import { createSearchParamsCache, parseAsStringLiteral } from "nuqs/server"; 1 + import { 2 + createSearchParamsCache, 3 + parseAsString, 4 + parseAsStringLiteral, 5 + } from "nuqs/server"; 2 6 3 7 const STEPS = ["1", "2", "next"] as const; 4 8 5 9 export const searchParamsParsers = { 6 10 step: parseAsStringLiteral(STEPS).withDefault("1"), 11 + callbackUrl: parseAsString, 7 12 }; 8 13 9 14 export const searchParamsCache = createSearchParamsCache(searchParamsParsers);
+8
apps/dashboard/src/proxy.ts
··· 32 32 return NextResponse.redirect(newURL); 33 33 } 34 34 35 + if (req.auth && url.pathname === "/login") { 36 + const redirectTo = url.searchParams.get("redirectTo"); 37 + if (redirectTo) { 38 + const redirectToUrl = new URL(redirectTo, req.url); 39 + return NextResponse.redirect(redirectToUrl); 40 + } 41 + } 42 + 35 43 const hasWorkspaceSlug = req.cookies.has("workspace-slug"); 36 44 37 45 if (req.auth?.user?.id && !hasWorkspaceSlug) {
+2 -2
packages/api/src/router/invitation.ts
··· 62 62 63 63 if (process.env.NODE_ENV === "development") { 64 64 console.log( 65 - `>>>> Invitation token: http://localhost:3000/app/invite?token=${token} <<<< `, 65 + `>>>> Invitation token: http://localhost:3000/invite?token=${token} <<<< `, 66 66 ); 67 67 } 68 68 ··· 108 108 }), 109 109 110 110 /** 111 - * REMINDER: we are not using a protected procedure here of the `/app/invite` url 111 + * REMINDER: we are not using a protected procedure here of the `/invite` url 112 112 * instead of `/app/workspace-slug/invite` as the user is not allowed to it yet. 113 113 * We validate the auth token in the `acceptInvitation` procedure 114 114 */
+1 -1
packages/emails/emails/team-invitation.tsx
··· 13 13 import { Layout } from "./_components/layout"; 14 14 import { styles } from "./_components/styles"; 15 15 16 - const BASE_URL = "https://openstatus.dev/app/invite"; 16 + const BASE_URL = "https://app.openstatus.dev/invite"; 17 17 18 18 export const TeamInvitationSchema = z.object({ 19 19 invitedBy: z.string(),