Merge pull request #7 from Nezumi-2711/feat/auth-states

Feature/ Implement auth states
This commit is contained in:
2025-07-19 16:26:40 +07:00
committed by GitHub
10 changed files with 136 additions and 65 deletions
+13 -4
View File
@@ -1,7 +1,16 @@
import { SignInView } from "@/modules/auth/ui/views/sign-in-view";
import { redirect } from 'next/navigation';
const Page = () => {
return <SignInView />
}
import { caller } from '@/trpc/server';
import { SignInView } from '@/modules/auth/ui/views/sign-in-view';
const Page = async () => {
const session = await caller.auth.session();
if (session.user) {
redirect('/');
}
return <SignInView />;
};
export default Page;
+13 -4
View File
@@ -1,7 +1,16 @@
import { SignUpView } from "@/modules/auth/ui/views/sign-up-view";
import { redirect } from 'next/navigation';
const Page = () => {
return <SignUpView />
}
import { caller } from '@/trpc/server';
import { SignUpView } from '@/modules/auth/ui/views/sign-up-view';
const Page = async () => {
const session = await caller.auth.session();
if (session.user) {
redirect('/');
}
return <SignUpView />;
};
export default Page;
+53 -26
View File
@@ -5,9 +5,11 @@ import Link from 'next/link';
import { Poppins } from 'next/font/google';
import { MenuIcon } from 'lucide-react';
import { usePathname } from 'next/navigation';
import { useQuery } from '@tanstack/react-query';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { useTRPC } from '@/trpc/client';
import NavbarSidebar from './navbar-sidebar';
@@ -64,9 +66,12 @@ const Navbar = () => {
const pathname = usePathname();
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
return (
<nav className="h-20 flex border-b justify-between font-medium bg-white">
<Link href="/" className="pl-6 flex items-center">
const trpc = useTRPC();
const session = useQuery(trpc.auth.session.queryOptions());
return (
<nav className="h-20 flex border-b justify-between font-medium bg-white">
<Link href="/" className="pl-6 flex items-center">
<span className={cn('text-5xl font-semibold', poppins.className)}>
funroad
</span>
@@ -90,31 +95,53 @@ const Navbar = () => {
))}
</div>
<div className="hidden lg:flex">
<Button
asChild
variant="secondary"
className="border-l border-t-0 border-b-0 border-r-0 px-12 h-full rounded-none bg-white hover:bg-pink-400 transition-colors text-lg"
>
<Link prefetch href="/sign-in">Log in</Link>
</Button>
{session.data?.user ? (
<div className='hidden lg:flex'>
<Button
asChild
variant="secondary"
className="border-l border-t-0 border-b-0 border-r-0 px-12 h-full rounded-none bg-black text-white hover:bg-pink-400 hover:text-black transition-colors text-lg"
>
<Link href="/admin">
Dashboard
</Link>
</Button>
</div>
) : (
<div className="hidden lg:flex">
<Button
asChild
variant="secondary"
className="border-l border-t-0 border-b-0 border-r-0 px-12 h-full rounded-none bg-white hover:bg-pink-400 transition-colors text-lg"
>
<Link prefetch href="/sign-in">
Log in
</Link>
</Button>
<Button
asChild
variant="secondary"
className="border-l border-t-0 border-b-0 border-r-0 px-12 h-full rounded-none bg-black text-white hover:bg-pink-400 hover:text-black transition-colors text-lg"
>
<Link prefetch href="/sign-up">Start Selling</Link>
</Button>
</div>
<Button
asChild
variant="secondary"
className="border-l border-t-0 border-b-0 border-r-0 px-12 h-full rounded-none bg-black text-white hover:bg-pink-400 hover:text-black transition-colors text-lg"
>
<Link prefetch href="/sign-up">
Start Selling
</Link>
</Button>
</div>
)}
<div className='flex lg:hidden items-center justify-center'>
<Button variant="ghost" className="size-12 border-transparent bg-white" onClick={() => setIsSidebarOpen(true)}>
<MenuIcon/>
</Button>
</div>
</nav>
);
<div className="flex lg:hidden items-center justify-center">
<Button
variant="ghost"
className="size-12 border-transparent bg-white"
onClick={() => setIsSidebarOpen(true)}
>
<MenuIcon />
</Button>
</div>
</nav>
);
};
export default Navbar;
@@ -1,10 +1,13 @@
'use client';
import { useState } from 'react';
import { ListFilterIcon, SearchIcon } from 'lucide-react';
import Link from 'next/link';
import { useQuery } from '@tanstack/react-query';
import { BookmarkCheckIcon, ListFilterIcon, SearchIcon } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { useTRPC } from '@/trpc/client';
import CategoriesSidebar from './categories-sidebar';
@@ -15,6 +18,9 @@ interface SearchInputProps {
const SearchInput = ({ disabled }: SearchInputProps) => {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const trpc = useTRPC();
const session = useQuery(trpc.auth.session.queryOptions());
return (
<div>
<div className="flex items-center gap-2 w-full">
@@ -40,6 +46,15 @@ const SearchInput = ({ disabled }: SearchInputProps) => {
>
<ListFilterIcon />
</Button>
{session.data?.user && (
<Button asChild variant="elevated">
<Link href="/library">
<BookmarkCheckIcon />
Library
</Link>
</Button>
)}
</div>
</div>
);
-1
View File
@@ -1 +0,0 @@
export const AUTH_COOKIE = 'payload-token';
+9 -19
View File
@@ -1,21 +1,17 @@
import { headers as getHeaders, cookies as getCookies } from "next/headers";
import { headers as getHeaders } from "next/headers";
import { TRPCError } from "@trpc/server";
import { AUTH_COOKIE } from "@/modules/auth/constants";
import { loginSchema, registerSchema } from "@/modules/auth/schemas";
import { baseProcedure, createTRPCRouter } from "@/trpc/init";
import { generateAuthCookie } from "../utils";
export const authRouter = createTRPCRouter({
session: baseProcedure.query(async ({ ctx }) => {
const headers = await getHeaders();
return await ctx.db.auth({ headers });
}),
logout: baseProcedure.mutation((async () => {
const cookies = await getCookies();
cookies.delete(AUTH_COOKIE);
})),
register: baseProcedure
.input(
registerSchema
@@ -62,13 +58,10 @@ export const authRouter = createTRPCRouter({
})
}
const cookies = await getCookies();
cookies.set({
name: AUTH_COOKIE,
await generateAuthCookie({
prefix: ctx.db.config.cookiePrefix,
value: data.token,
httpOnly: true,
path: '/',
});
})
}),
login: baseProcedure
.input(loginSchema)
@@ -88,13 +81,10 @@ export const authRouter = createTRPCRouter({
})
}
const cookies = await getCookies();
cookies.set({
name: AUTH_COOKIE,
await generateAuthCookie({
prefix: ctx.db.config.cookiePrefix,
value: data.token,
httpOnly: true,
path: '/',
});
})
return data;
})
+12 -9
View File
@@ -1,18 +1,19 @@
'use client';
import { toast } from "sonner";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { Poppins } from "next/font/google";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { zodResolver } from "@hookform/resolvers/zod";
import { useTRPC } from "@/trpc/client";
import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";
import { LoginSchema, loginSchema } from "@/modules/auth/schemas";
import { useTRPC } from "@/trpc/client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import { Poppins } from "next/font/google";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
const poppins = Poppins({
subsets: ['latin'],
@@ -22,11 +23,13 @@ const poppins = Poppins({
export const SignInView = () => {
const router = useRouter();
const trpc = useTRPC();
const queryClient = useQueryClient();
const login = useMutation(trpc.auth.login.mutationOptions({
onError: (error) => {
toast.error(error.message)
},
onSuccess: () => {
onSuccess: async () => {
await queryClient.invalidateQueries(trpc.auth.session.queryFilter())
router.push('/');
}
}));
+17
View File
@@ -0,0 +1,17 @@
import { cookies as getCookies } from "next/headers";
interface Props {
prefix: string;
value: string;
}
export const generateAuthCookie = async ({prefix, value}: Props) => {
const cookies = await getCookies();
cookies.set({
name: `${prefix}-token`,
value,
httpOnly: true,
path: '/',
})
}
+1
View File
@@ -21,6 +21,7 @@ export default buildConfig({
baseDir: path.resolve(dirname),
},
},
cookiePrefix: 'funroad',
collections: [Users, Media, Categories],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',
+2 -1
View File
@@ -11,4 +11,5 @@ export const trpc = createTRPCOptionsProxy({
ctx: createTRPCContext,
router: appRouter,
queryClient: getQueryClient,
});
});
export const caller = appRouter.createCaller(createTRPCContext);