fix: signout
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
5
src/pages/verify/index.tsx
Normal file
5
src/pages/verify/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import VerifyView from "~/views/auth/verify";
|
||||
|
||||
export default function VerifyPage() {
|
||||
return <VerifyView />;
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
11
src/utils/supabase/client.ts
Normal file
11
src/utils/supabase/client.ts
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user