mirror of
https://github.com/fergalmoran/opengifame.git
synced 2025-12-24 02:28:36 +00:00
Fix session handling and update auth pages
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"workbench.colorTheme": "City Lights"
|
"workbench.colorTheme": "Tinacious Design (High Contrast)"
|
||||||
}
|
}
|
||||||
42
package.json
42
package.json
@@ -14,8 +14,8 @@
|
|||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/drizzle-adapter": "^1.1.0",
|
"@auth/drizzle-adapter": "^1.4.2",
|
||||||
"@headlessui/react": "^2.1.4",
|
"@headlessui/react": "^2.1.5",
|
||||||
"@hookform/resolvers": "^3.9.0",
|
"@hookform/resolvers": "^3.9.0",
|
||||||
"@radix-ui/react-accordion": "^1.2.0",
|
"@radix-ui/react-accordion": "^1.2.0",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.1",
|
"@radix-ui/react-alert-dialog": "^1.1.1",
|
||||||
@@ -45,8 +45,8 @@
|
|||||||
"@radix-ui/react-toggle": "^1.1.0",
|
"@radix-ui/react-toggle": "^1.1.0",
|
||||||
"@radix-ui/react-toggle-group": "^1.1.0",
|
"@radix-ui/react-toggle-group": "^1.1.0",
|
||||||
"@radix-ui/react-tooltip": "^1.1.2",
|
"@radix-ui/react-tooltip": "^1.1.2",
|
||||||
"@t3-oss/env-nextjs": "^0.10.1",
|
"@t3-oss/env-nextjs": "^0.11.1",
|
||||||
"@tanstack/react-query": "^5.50.0",
|
"@tanstack/react-query": "^5.54.1",
|
||||||
"@trpc/client": "^11.0.0-rc.446",
|
"@trpc/client": "^11.0.0-rc.446",
|
||||||
"@trpc/react-query": "^11.0.0-rc.446",
|
"@trpc/react-query": "^11.0.0-rc.446",
|
||||||
"@trpc/server": "^11.0.0-rc.446",
|
"@trpc/server": "^11.0.0-rc.446",
|
||||||
@@ -62,19 +62,19 @@
|
|||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"drizzle-orm": "^0.33.0",
|
"drizzle-orm": "^0.33.0",
|
||||||
"embla-carousel-react": "^8.2.1",
|
"embla-carousel-react": "^8.2.1",
|
||||||
"geist": "^1.3.0",
|
"geist": "^1.3.1",
|
||||||
"input-otp": "^1.2.4",
|
"input-otp": "^1.2.4",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"loglevel": "^1.9.1",
|
"loglevel": "^1.9.1",
|
||||||
"loglevel-plugin-prefix": "^0.8.4",
|
"loglevel-plugin-prefix": "^0.8.4",
|
||||||
"lucide-react": "^0.438.0",
|
"lucide-react": "^0.438.0",
|
||||||
"next": "^14.2.4",
|
"next": "^14.2.8",
|
||||||
"next-auth": "^4.24.7",
|
"next-auth": "^4.24.7",
|
||||||
"next-themes": "^0.3.0",
|
"next-themes": "^0.3.0",
|
||||||
"postgres": "^3.4.4",
|
"postgres": "^3.4.4",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-copy-to-clipboard": "^5.1.0",
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
"react-day-picker": "8.10.1",
|
"react-day-picker": "9.0.8",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hook-form": "^7.53.0",
|
"react-hook-form": "^7.53.0",
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
@@ -85,25 +85,25 @@
|
|||||||
"superjson": "^2.2.1",
|
"superjson": "^2.2.1",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"vaul": "^0.9.1",
|
"vaul": "^0.9.2",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/eslint": "^8.56.10",
|
"@types/eslint": "^9.6.1",
|
||||||
"@types/node": "^20.14.10",
|
"@types/node": "^22.5.4",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.5",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
"@typescript-eslint/eslint-plugin": "^8.4.0",
|
||||||
"@typescript-eslint/parser": "^8.1.0",
|
"@typescript-eslint/parser": "^8.4.0",
|
||||||
"drizzle-kit": "^0.24.0",
|
"drizzle-kit": "^0.24.2",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^9.9.1",
|
||||||
"eslint-config-next": "^14.2.4",
|
"eslint-config-next": "^14.2.8",
|
||||||
"eslint-plugin-drizzle": "^0.2.3",
|
"eslint-plugin-drizzle": "^0.2.3",
|
||||||
"postcss": "^8.4.39",
|
"postcss": "^8.4.45",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.5",
|
"prettier-plugin-tailwindcss": "^0.6.6",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.10",
|
||||||
"typescript": "^5.5.3"
|
"typescript": "^5.5.4"
|
||||||
},
|
},
|
||||||
"ct3aMetadata": {
|
"ct3aMetadata": {
|
||||||
"initVersion": "7.37.0"
|
"initVersion": "7.37.0"
|
||||||
|
|||||||
@@ -1,7 +1,30 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { Icons } from "@/components/icons";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
interface AuthLayoutProps {
|
interface AuthLayoutProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AuthLayout({ children }: AuthLayoutProps) {
|
export default function AuthLayout({ children }: AuthLayoutProps) {
|
||||||
return <div className="min-h-screen">{children}</div>;
|
return (
|
||||||
|
<div className="min-h-screen">
|
||||||
|
<div className="container flex h-screen w-screen flex-col items-center justify-center">
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "ghost" }),
|
||||||
|
"absolute left-4 top-4 md:left-8 md:top-8",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<Icons.chevronLeft className="mr-2 h-4 w-4" />
|
||||||
|
Back
|
||||||
|
</>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,40 +10,22 @@ import { buttonVariants } from "@/components/ui/button";
|
|||||||
|
|
||||||
const RegisterPage: React.FC = () => {
|
const RegisterPage: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="container flex h-screen w-screen flex-col items-center justify-center">
|
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
|
||||||
<Link
|
<div className="flex flex-col space-y-2 text-center">
|
||||||
href="/"
|
<Icons.logo className="mx-auto h-6 w-6" />
|
||||||
className={cn(
|
<h1 className="text-2xl font-semibold tracking-tight">Welcome</h1>
|
||||||
buttonVariants({ variant: "ghost" }),
|
<p className="text-sm text-muted-foreground">Register for an account</p>
|
||||||
"absolute left-4 top-4 md:left-8 md:top-8",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
<Icons.chevronLeft className="mr-2 h-4 w-4" />
|
|
||||||
Back
|
|
||||||
</>
|
|
||||||
</Link>
|
|
||||||
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
|
|
||||||
<div className="flex flex-col space-y-2 text-center">
|
|
||||||
<Icons.logo className="mx-auto h-6 w-6" />
|
|
||||||
<h1 className="text-2xl font-semibold tracking-tight">
|
|
||||||
Welcome back
|
|
||||||
</h1>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Enter your email to sign in to your account
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<RegistrationForm />
|
|
||||||
<SocialLogin />
|
|
||||||
<p className="px-8 text-center text-sm text-muted-foreground">
|
|
||||||
<Link
|
|
||||||
href="/register"
|
|
||||||
className="hover:text-brand underline underline-offset-4"
|
|
||||||
>
|
|
||||||
Don't have an account? Sign Up
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<RegistrationForm />
|
||||||
|
<SocialLogin />
|
||||||
|
<p className="px-8 text-center text-sm text-muted-foreground">
|
||||||
|
<Link
|
||||||
|
href="/signin"
|
||||||
|
className="hover:text-brand underline underline-offset-4"
|
||||||
|
>
|
||||||
|
Already have an account? Login?
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,121 +2,31 @@
|
|||||||
import SocialLogin from "@/components/widgets/login/SocialLogin";
|
import SocialLogin from "@/components/widgets/login/SocialLogin";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { logger } from "@/lib/logger";
|
import { Icons } from "@/components/icons";
|
||||||
import { signIn } from "next-auth/react";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
|
import SignInForm from "@/components/forms/auth/SignInForm";
|
||||||
|
|
||||||
const SignInPage = () => {
|
const SignInPage = () => {
|
||||||
const [userInfo, setUserInfo] = React.useState({
|
|
||||||
email: "fergal.moran+opengifame@gmail.com",
|
|
||||||
password: "secret",
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-full flex-col justify-center py-1 sm:px-6 lg:px-8">
|
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
|
||||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
<div className="flex flex-col space-y-2 text-center">
|
||||||
<h2 className="mt-2 text-center text-3xl font-extrabold">
|
<Icons.logo className="mx-auto h-6 w-6" />
|
||||||
Sign in to your account
|
<h1 className="text-2xl font-semibold tracking-tight">Welcome back</h1>
|
||||||
</h2>
|
<p className="text-sm text-muted-foreground">
|
||||||
<p className="mt-2 text-center text-sm">
|
Enter your email to sign in to your account
|
||||||
Or
|
|
||||||
<Link href="/register" className="font-medium">
|
|
||||||
create a new account
|
|
||||||
</Link>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<SignInForm />
|
||||||
<div className="mt-2 sm:mx-auto sm:w-full sm:max-w-md">
|
<SocialLogin />
|
||||||
<div className="px-4 py-8 shadow sm:rounded-lg sm:px-10">
|
<p className="px-8 text-center text-sm text-muted-foreground">
|
||||||
<form
|
<Link
|
||||||
className="space-y-6"
|
href="/register"
|
||||||
onSubmit={async (e) => {
|
className="hover:text-brand underline underline-offset-4"
|
||||||
e.preventDefault();
|
>
|
||||||
logger.debug("signin", "using", userInfo);
|
Don't have an account? Sign Up
|
||||||
const result = await signIn("credentials", {
|
</Link>
|
||||||
redirect: false,
|
</p>
|
||||||
email: userInfo.email,
|
|
||||||
password: userInfo.password,
|
|
||||||
});
|
|
||||||
logger.debug("signin", "result", result);
|
|
||||||
}}
|
|
||||||
method="post"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="email" className="block text-sm font-medium">
|
|
||||||
Email address
|
|
||||||
</label>
|
|
||||||
<div className="mt-1">
|
|
||||||
<input
|
|
||||||
id="email"
|
|
||||||
type="email"
|
|
||||||
autoComplete="email"
|
|
||||||
required
|
|
||||||
value={userInfo.email}
|
|
||||||
onChange={({ target }) =>
|
|
||||||
setUserInfo({ ...userInfo, email: target.value })
|
|
||||||
}
|
|
||||||
className="input input-bordered w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label htmlFor="password" className="block text-sm font-medium">
|
|
||||||
Password
|
|
||||||
</label>
|
|
||||||
<div className="mt-1">
|
|
||||||
<input
|
|
||||||
id="password"
|
|
||||||
type="password"
|
|
||||||
autoComplete="current-password"
|
|
||||||
required
|
|
||||||
value={userInfo.password}
|
|
||||||
onChange={({ target }) =>
|
|
||||||
setUserInfo({ ...userInfo, password: target.value })
|
|
||||||
}
|
|
||||||
className="input input-bordered w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<input
|
|
||||||
id="remember-me"
|
|
||||||
name="remember-me"
|
|
||||||
type="checkbox"
|
|
||||||
className="h-4 w-4"
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="remember-me"
|
|
||||||
className="text-accent ml-2 block text-sm"
|
|
||||||
>
|
|
||||||
Remember me
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-sm">
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
className="text-info hover:text-primary/50 font-medium"
|
|
||||||
>
|
|
||||||
Forgot your password?
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button type="submit" className="btn btn-primary w-full">
|
|
||||||
Sign in
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div className="mt-6">
|
|
||||||
<SocialLogin />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export default async function Home() {
|
|||||||
<div>
|
<div>
|
||||||
<a href="/signin">Sign In</a>
|
<a href="/signin">Sign In</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>{JSON.stringify(session, null, 2)}</div>
|
||||||
{session?.user && <TrendingImages />}
|
{session?.user && <TrendingImages />}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
128
src/components/forms/auth/SignInForm.tsx
Normal file
128
src/components/forms/auth/SignInForm.tsx
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
// src/components/RegistrationForm.tsx
|
||||||
|
import React from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { Button, buttonVariants } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { api } from "@/trpc/react";
|
||||||
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||||
|
import { logger } from "@/lib/logger";
|
||||||
|
import { Icons } from "@/components/icons";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { signIn } from "next-auth/react";
|
||||||
|
|
||||||
|
const signInSchema = z.object({
|
||||||
|
email: z.string().email({ message: "Invalid email address" }),
|
||||||
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(5, { message: "Password must be at least 5 characters long" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
type SignInFormValues = z.infer<typeof signInSchema>;
|
||||||
|
|
||||||
|
const SignInForm: React.FC = () => {
|
||||||
|
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||||
|
const router = useRouter();
|
||||||
|
const form = useForm<SignInFormValues>({
|
||||||
|
resolver: zodResolver(signInSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (data: SignInFormValues) => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await signIn("credentials", {
|
||||||
|
redirect: false,
|
||||||
|
email: data.email,
|
||||||
|
password: data.password,
|
||||||
|
});
|
||||||
|
logger.debug("signin", "result", result);
|
||||||
|
if (result?.status === 200) {
|
||||||
|
router.push("/");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("SignInForm", "error", error);
|
||||||
|
toast("Failed to signin user");
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<div className="grid gap-1">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
defaultValue={"fergal.moran+opengifame@gmail.com"}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input type="email" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
{form.formState.errors.email && (
|
||||||
|
<FormMessage>
|
||||||
|
{form.formState.errors.email.message}
|
||||||
|
</FormMessage>
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="password"
|
||||||
|
defaultValue={"secret"}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Password</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input type="password" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
{form.formState.errors.password && (
|
||||||
|
<FormMessage>
|
||||||
|
{form.formState.errors.password.message}
|
||||||
|
</FormMessage>
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{form.formState.errors && false && (
|
||||||
|
<Alert>
|
||||||
|
<Icons.terminal className="h-4 w-4" />
|
||||||
|
<AlertTitle>Heads up!</AlertTitle>
|
||||||
|
<AlertDescription>
|
||||||
|
{JSON.stringify(form.formState.errors)}
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className={cn(buttonVariants())}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{isLoading && (
|
||||||
|
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
)}
|
||||||
|
Sign in
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignInForm;
|
||||||
@@ -16,18 +16,4 @@ export const authRouter = createTRPCRouter({
|
|||||||
return user;
|
return user;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
login: publicProcedure
|
|
||||||
.input(z.object({ email: z.string().email(), password: z.string().min(5) }))
|
|
||||||
.query(async ({ ctx, input }) => {
|
|
||||||
const hashedPassword = await bcrypt.hash(input.password, 10);
|
|
||||||
const user = await ctx.db
|
|
||||||
.select()
|
|
||||||
.from(users)
|
|
||||||
.where(
|
|
||||||
and(eq(users.email, input.email), eq(users.password, hashedPassword)),
|
|
||||||
)
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
return user[0];
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { DrizzleAdapter } from "@auth/drizzle-adapter";
|
import { DrizzleAdapter } from "@auth/drizzle-adapter";
|
||||||
import CredentialsProvider from "next-auth/providers/credentials";
|
|
||||||
import {
|
import {
|
||||||
getServerSession,
|
getServerSession,
|
||||||
type DefaultSession,
|
type DefaultSession,
|
||||||
type NextAuthOptions,
|
type NextAuthOptions,
|
||||||
RequestInternal,
|
|
||||||
} from "next-auth";
|
} from "next-auth";
|
||||||
import { type Adapter } from "next-auth/adapters";
|
import { type Adapter } from "next-auth/adapters";
|
||||||
import {
|
import {
|
||||||
@@ -13,11 +11,12 @@ import {
|
|||||||
users,
|
users,
|
||||||
verificationTokens,
|
verificationTokens,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { api } from "@/trpc/server";
|
|
||||||
import { env } from "@/env";
|
import { env } from "@/env";
|
||||||
import { db } from "@/server/db";
|
import { db } from "@/server/db";
|
||||||
|
|
||||||
import Credentials from "next-auth/providers/credentials";
|
import Credentials from "next-auth/providers/credentials";
|
||||||
|
import bcrypt from "bcrypt";
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
|
|
||||||
declare module "next-auth" {
|
declare module "next-auth" {
|
||||||
interface Session extends DefaultSession {
|
interface Session extends DefaultSession {
|
||||||
@@ -29,13 +28,18 @@ declare module "next-auth" {
|
|||||||
|
|
||||||
export const authOptions: NextAuthOptions = {
|
export const authOptions: NextAuthOptions = {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
session: ({ session, user }) => ({
|
session: ({ session, user }) => {
|
||||||
...session,
|
const s = {
|
||||||
user: {
|
...session,
|
||||||
...session.user,
|
user: {
|
||||||
id: user.id,
|
...session.user
|
||||||
},
|
},
|
||||||
}),
|
};
|
||||||
|
return s;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
strategy: "jwt",
|
||||||
},
|
},
|
||||||
adapter: DrizzleAdapter(db, {
|
adapter: DrizzleAdapter(db, {
|
||||||
usersTable: users,
|
usersTable: users,
|
||||||
@@ -54,14 +58,24 @@ export const authOptions: NextAuthOptions = {
|
|||||||
if (!credentials) {
|
if (!credentials) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const result = await api.auth.login({
|
const user = await db
|
||||||
email: credentials.email,
|
.select()
|
||||||
password: credentials.password,
|
.from(users)
|
||||||
});
|
.where(and(eq(users.email, credentials.email)))
|
||||||
if (!result) {
|
.limit(1);
|
||||||
|
|
||||||
|
if (!user || user.length < 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return { id: result.id, email: result.email };
|
if (
|
||||||
|
!(await bcrypt.compare(
|
||||||
|
credentials.password,
|
||||||
|
user[0]!.password as string,
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { id: user[0]!.id, email: user[0]!.email };
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user