fix: signout

This commit is contained in:
Henry
2024-04-22 22:45:12 +01:00
parent 227e76524b
commit 4e344a5ae1
7 changed files with 94 additions and 32 deletions

View File

@@ -2,7 +2,7 @@ import { Fragment } from "react";
import Image from "next/image";
import { Menu, Transition } from "@headlessui/react";
import { useTheme } from "~/providers/theme";
import createClient from "~/utils/supabase/client";
interface UserMenuProps {
imageUrl: string | undefined;
email: string;
@@ -15,6 +15,12 @@ function classNames(...classes: string[]): string {
export default function UserMenu({ imageUrl, email }: UserMenuProps) {
const { theme, switchTheme } = useTheme();
const handleLogout = async () => {
const db = createClient();
await db.auth.signOut();
};
return (
<Menu as="div" className="relative inline-block w-full text-left">
<div>
@@ -103,7 +109,7 @@ export default function UserMenu({ imageUrl, email }: UserMenuProps) {
<div className="light-border-600 border-t-[1px] p-1 dark:border-dark-600">
<Menu.Item>
<button
// onClick={() => signOut({ callbackUrl: "/boards" })}
onClick={handleLogout}
className=" flex w-full items-center rounded-[5px] px-3 py-2 text-left text-xs hover:bg-light-200 dark:hover:bg-dark-400"
>
Logout

View File

@@ -1,12 +1,19 @@
import { Formik, Form, Field } from "formik";
import { api } from "~/utils/api";
import { useRouter } from "next/navigation";
interface FormValues {
email: string;
}
export function Login() {
const login = api.auth.login.useMutation();
const router = useRouter();
const login = api.auth.login.useMutation({
onSuccess: () => {
router.push("/verify");
},
});
return (
<Formik

View File

@@ -12,7 +12,6 @@ import { api } from "~/utils/api";
const jakarta = Plus_Jakarta_Sans({
subsets: ["latin"],
display: "swap",
variable: "--font-plus-jakarta-sans",
});
export const metadata = {
@@ -23,15 +22,22 @@ export const metadata = {
const MyApp: AppType = ({ Component, pageProps }) => {
return (
<main className={`${jakarta.variable} h-screen overflow-hidden font-sans`}>
<ThemeProvider>
<ModalProvider>
<BoardProvider>
<Component {...pageProps} />
</BoardProvider>
</ModalProvider>
</ThemeProvider>
</main>
<>
<style jsx global>{`
html {
font-family: ${jakarta.style.fontFamily};
}
`}</style>
<main className="h-screen overflow-hidden font-sans">
<ThemeProvider>
<ModalProvider>
<BoardProvider>
<Component {...pageProps} />
</BoardProvider>
</ModalProvider>
</ThemeProvider>
</main>
</>
);
};

View File

@@ -1,36 +1,39 @@
import { type EmailOtpType } from '@supabase/supabase-js'
import type { NextApiRequest, NextApiResponse } from 'next'
import { type EmailOtpType } from "@supabase/supabase-js";
import type { NextApiRequest, NextApiResponse } from "next";
import createClient from '~/utils/supabase/api'
import createClient from "~/utils/supabase/api";
function stringOrFirstString(item: string | string[] | undefined) {
return Array.isArray(item) ? item[0] : item
return Array.isArray(item) ? item[0] : item;
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'GET') {
res.status(405).appendHeader('Allow', 'GET').end()
return
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "GET") {
res.status(405).appendHeader("Allow", "GET").end();
return;
}
const queryParams = req.query
const token_hash = stringOrFirstString(queryParams.token_hash)
const type = stringOrFirstString(queryParams.type)
const queryParams = req.query;
const token_hash = stringOrFirstString(queryParams.token_hash);
const type = stringOrFirstString(queryParams.type);
let next = '/error'
let next = "/error";
if (token_hash && type) {
const supabase = createClient(req, res)
const supabase = createClient(req, res);
const { error } = await supabase.auth.verifyOtp({
type: type as EmailOtpType,
token_hash,
})
});
if (error) {
console.error(error)
console.error(error);
} else {
next = stringOrFirstString(queryParams.next) || '/'
next = stringOrFirstString(queryParams.next) ?? "/";
}
}
res.redirect(next)
}
res.redirect(next);
}

View File

@@ -0,0 +1,5 @@
import VerifyView from "~/views/auth/verify";
export default function VerifyPage() {
return <VerifyView />;
}

View File

@@ -4,13 +4,36 @@
*
* We also create a few inference helpers for input and output types.
*/
import { httpBatchLink, loggerLink } from "@trpc/client";
import { httpBatchLink, loggerLink, type TRPCLink } from "@trpc/client";
import { observable } from "@trpc/server/observable";
import { createTRPCNext } from "@trpc/next";
import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server";
import superjson from "superjson";
import { type AppRouter } from "~/server/api/root";
const authLink: TRPCLink<AppRouter> = () => {
return ({ next, op }) => {
return observable((observer) => {
const unsubscribe = next(op).subscribe({
next(value) {
observer.next(value);
},
error(err) {
if (typeof window !== "undefined" && err.message === "UNAUTHORIZED") {
window.location.href = "/login";
}
observer.error(err);
},
complete() {
observer.complete();
},
});
return unsubscribe;
});
};
};
const getBaseUrl = () => {
if (typeof window !== "undefined") return ""; // browser should use relative url
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
@@ -32,6 +55,7 @@ export const api = createTRPCNext<AppRouter>({
process.env.NODE_ENV === "development" ||
(opts.direction === "down" && opts.result instanceof Error),
}),
authLink,
httpBatchLink({
/**
* Transformer used for data de-serialization from the server.

View File

@@ -0,0 +1,11 @@
import { createBrowserClient } from "@supabase/ssr";
import { type Database } from "~/types/database.types";
export default function createClient() {
const supabase = createBrowserClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);
return supabase;
}