dvijaykrishnan commited on
Commit
14b8081
·
1 Parent(s): 694e696

feat: transition to google-only authentication and streamline UI

Browse files
dev.log CHANGED
@@ -2,36 +2,56 @@
2
  > vault@0.1.0 dev
3
  > next dev --turbopack
4
 
5
- ⚠ Mismatching @next/swc version, detected: 15.5.7 while Next.js is on 15.5.11. Please ensure these match
6
  [@sentry/nextjs] DEPRECATION WARNING: automaticVercelMonitors is deprecated and will be removed in a future version. Use webpack.automaticVercelMonitors instead. (Not supported with Turbopack.)
7
  [@sentry/nextjs] DEPRECATION WARNING: reactComponentAnnotation is deprecated and will be removed in a future version. Use webpack.reactComponentAnnotation instead. (Not supported with Turbopack.)
8
- ⚠ Mismatching @next/swc version, detected: 15.5.7 while Next.js is on 15.5.11. Please ensure these match
9
  [@sentry/nextjs] DEPRECATION WARNING: automaticVercelMonitors is deprecated and will be removed in a future version. Use webpack.automaticVercelMonitors instead. (Not supported with Turbopack.)
10
  [@sentry/nextjs] DEPRECATION WARNING: reactComponentAnnotation is deprecated and will be removed in a future version. Use webpack.reactComponentAnnotation instead. (Not supported with Turbopack.)
11
- ▲ Next.js 15.5.11 (Turbopack)
12
  - Local: http://localhost:3000
13
- - Network: http://192.168.1.226:3000
14
  - Environments: .env.local
15
  - Experiments (use with caution):
 
16
  · clientTraceMetadata
17
 
18
  ✓ Starting...
19
- Compiling instrumentation Node.js ...
20
- ✓ Compiled instrumentation Node.js in 750ms
21
- ✓ Compiled instrumentation Edge in 203ms
22
- Compiled middleware in 112ms
23
- Ready in 2.4s
24
- Compiling /register ...
25
- Compiled /register in 1870ms
26
- HEAD /register Reload env: Reload e ✓ Compiled instrumentation Node.js in 29ms
27
- Compiled instrumentation Edge in 4ms
28
- [Error: ENOENT: no such file or directory, open '/Users/vijaykrishnan/Dev/Vault/.next/static/development/_buildManifest.js.tmp.88n5sr8e3hb'] {
29
- errno: -2,
30
- code: 'ENOENT',
31
- syscall: 'open',
32
- path: '/Users/vijaykrishnan/Dev/Vault/.next/static/development/_buildManifest.js.tmp.88n5sr8e3hb'
33
- }
34
- [?25h
35
- mx'
36
- }
37
- [?25h
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  > vault@0.1.0 dev
3
  > next dev --turbopack
4
 
 
5
  [@sentry/nextjs] DEPRECATION WARNING: automaticVercelMonitors is deprecated and will be removed in a future version. Use webpack.automaticVercelMonitors instead. (Not supported with Turbopack.)
6
  [@sentry/nextjs] DEPRECATION WARNING: reactComponentAnnotation is deprecated and will be removed in a future version. Use webpack.reactComponentAnnotation instead. (Not supported with Turbopack.)
 
7
  [@sentry/nextjs] DEPRECATION WARNING: automaticVercelMonitors is deprecated and will be removed in a future version. Use webpack.automaticVercelMonitors instead. (Not supported with Turbopack.)
8
  [@sentry/nextjs] DEPRECATION WARNING: reactComponentAnnotation is deprecated and will be removed in a future version. Use webpack.reactComponentAnnotation instead. (Not supported with Turbopack.)
9
+ ▲ Next.js 15.5.12 (Turbopack)
10
  - Local: http://localhost:3000
11
+ - Network: http://192.168.1.225:3000
12
  - Environments: .env.local
13
  - Experiments (use with caution):
14
+ · serverActions
15
  · clientTraceMetadata
16
 
17
  ✓ Starting...
18
+ Compiled instrumentation Node.js in 473ms
19
+ ✓ Compiled instrumentation Edge in 151ms
20
+ ✓ Compiled middleware in 80ms
21
+ Ready in 1849ms
22
+ Compiling /login ...
23
+ Compiled /login in 2.3s
24
+ GET /login 200 in 2700ms
25
+ ✓ Compiled /favicon.ico in 166ms
26
+ GET /favicon.ico?favicon.0b3bf435.ico 200 in 428ms
27
+ Compiling /api/auth/[...all] ...
28
+ ✓ Compiled /api/auth/[...all] in 715ms
29
+ POST /api/auth/sign-in/social 200 in 2621ms
30
+ GET /api/auth/callback/google?state=1ytLvxWE8WL44ljrBpjooa5ktCWdMu0l&code=4/0ASc3gC0LWU5y8kD2WHo1zGejy6ln-RUjUSLcGVGrYUEC6xo84ddALNF_6LzH2IEXKsm2Yg&scope=email%20profile%20https://www.googleapis.com/auth/youtube.readonly%20https://www.googleapis.com/auth/userinfo.profile%20https://www.googleapis.com/auth/userinfo.email%20openid&authuser=0&prompt=consent 302 in 2140ms
31
+ GET /api/auth/get-session 200 in 634ms
32
+ ○ Compiling /dashboard ...
33
+ ✓ Compiled /dashboard in 990ms
34
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
35
+ GET /dashboard 200 in 3728ms
36
+ GET /favicon.ico?favicon.0b3bf435.ico 200 in 234ms
37
+ GET /api/auth/get-session 200 in 619ms
38
+ GET /api/auth/get-session 200 in 1383ms
39
+ POST /dashboard 200 in 1568ms
40
+ GET /api/auth/get-session 200 in 587ms
41
+ POST /dashboard 200 in 604ms
42
+ ✓ Compiled in 76ms
43
+ GET /api/auth/get-session 200 in 1479ms
44
+ ✓ Compiled /dashboard in 29ms
45
+ GET /api/auth/get-session 200 in 653ms
46
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
47
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
48
+ GET /dashboard 200 in 2634ms
49
+ GET /dashboard 200 in 2544ms
50
+ ✓ Compiled in 171ms
51
+ GET /api/auth/get-session 200 in 599ms
52
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
53
+ GET /dashboard 200 in 769ms
54
+ ✓ Compiled in 259ms
55
+ GET /api/auth/get-session 200 in 557ms
56
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
57
+ GET /dashboard 200 in 820ms
src/app/dashboard/page.tsx CHANGED
@@ -14,6 +14,8 @@ import { KeyMetricsRow } from "@/features/analytics/components/key-metrics-row";
14
  import { TopEvergreenVideos } from "@/features/analytics/components/top-evergreen-videos";
15
  import { QuickAnalyzeTool } from "@/features/vault/components/quick-analyze-tool";
16
  import { UserNav } from "@/components/auth/user-nav";
 
 
17
 
18
  export default async function DashboardPage({
19
  searchParams,
@@ -130,6 +132,9 @@ export default async function DashboardPage({
130
 
131
  return (
132
  <div className="max-w-7xl mx-auto px-4 py-8 space-y-8 animate-in fade-in duration-500">
 
 
 
133
  <div className="flex justify-between items-start">
134
  <div className="space-y-1">
135
  <Link href="/" className="inline-block text-primary font-bold text-xl tracking-tight mb-1">Vault</Link>
 
14
  import { TopEvergreenVideos } from "@/features/analytics/components/top-evergreen-videos";
15
  import { QuickAnalyzeTool } from "@/features/vault/components/quick-analyze-tool";
16
  import { UserNav } from "@/components/auth/user-nav";
17
+ import { AutoClaimer } from "@/components/auth/auto-claimer";
18
+ import { Suspense } from "react";
19
 
20
  export default async function DashboardPage({
21
  searchParams,
 
132
 
133
  return (
134
  <div className="max-w-7xl mx-auto px-4 py-8 space-y-8 animate-in fade-in duration-500">
135
+ <Suspense fallback={null}>
136
+ <AutoClaimer />
137
+ </Suspense>
138
  <div className="flex justify-between items-start">
139
  <div className="space-y-1">
140
  <Link href="/" className="inline-block text-primary font-bold text-xl tracking-tight mb-1">Vault</Link>
src/app/login/page.tsx CHANGED
@@ -9,7 +9,7 @@ export default function LoginPage() {
9
  <div className="space-y-2 text-center">
10
  <Link href="/" className="inline-block text-primary font-bold text-xl tracking-tight mb-2">Vault</Link>
11
  <h1 className="text-3xl font-bold">Login</h1>
12
- <p className="text-gray-500 dark:text-gray-400">Enter your email below to login to your account</p>
13
  </div>
14
  <Suspense fallback={<div className="h-64 flex items-center justify-center">Loading...</div>}>
15
  <SignInForm />
 
9
  <div className="space-y-2 text-center">
10
  <Link href="/" className="inline-block text-primary font-bold text-xl tracking-tight mb-2">Vault</Link>
11
  <h1 className="text-3xl font-bold">Login</h1>
12
+ <p className="text-gray-500 dark:text-gray-400">Continue with your Google account to access your vault</p>
13
  </div>
14
  <Suspense fallback={<div className="h-64 flex items-center justify-center">Loading...</div>}>
15
  <SignInForm />
src/app/register/page.tsx CHANGED
@@ -9,7 +9,7 @@ export default function RegisterPage() {
9
  <div className="space-y-2 text-center">
10
  <Link href="/" className="inline-block text-primary font-bold text-xl tracking-tight mb-2">Vault</Link>
11
  <h1 className="text-3xl font-bold">Sign Up</h1>
12
- <p className="text-gray-500 dark:text-gray-400">Enter your information to create an account</p>
13
  </div>
14
  <Suspense fallback={<div className="h-64 flex items-center justify-center">Loading...</div>}>
15
  <SignUpForm />
 
9
  <div className="space-y-2 text-center">
10
  <Link href="/" className="inline-block text-primary font-bold text-xl tracking-tight mb-2">Vault</Link>
11
  <h1 className="text-3xl font-bold">Sign Up</h1>
12
+ <p className="text-gray-500 dark:text-gray-400">Sign up with your Google account to start your vault</p>
13
  </div>
14
  <Suspense fallback={<div className="h-64 flex items-center justify-center">Loading...</div>}>
15
  <SignUpForm />
src/components/auth/auto-claimer.tsx ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { useSearchParams } from "next/navigation";
5
+ import { claimVideo } from "@/actions/claim-video";
6
+ import { toast } from "sonner";
7
+
8
+ export function AutoClaimer() {
9
+ const searchParams = useSearchParams();
10
+ const videoId = searchParams.get("videoId");
11
+ const [processed, setProcessed] = useState(false);
12
+
13
+ useEffect(() => {
14
+ if (videoId && !processed) {
15
+ console.log(`[AutoClaimer] Found videoId ${videoId} in URL, attempting to claim...`);
16
+ setProcessed(true);
17
+
18
+ const performClaim = async () => {
19
+ try {
20
+ const result = await claimVideo(videoId);
21
+ if (result.success) {
22
+ toast.success("Video successfully claimed!");
23
+ } else {
24
+ console.error("[AutoClaimer] Claim failed:", result.error);
25
+ toast.error(result.error || "Failed to claim video");
26
+ }
27
+ } catch (err) {
28
+ console.error("[AutoClaimer] Error claiming video:", err);
29
+ toast.error("An unexpected error occurred while claiming your video");
30
+ }
31
+ };
32
+
33
+ performClaim();
34
+ }
35
+ }, [videoId, processed]);
36
+
37
+ return null;
38
+ }
src/components/auth/sign-in-form.tsx CHANGED
@@ -3,48 +3,25 @@
3
  import { useState } from "react";
4
  import { signIn } from "@/lib/auth-client";
5
  import { Button } from "@/components/ui/button";
6
- import { Input } from "@/components/ui/input";
7
- import { Label } from "@/components/ui/label";
8
- import { useRouter } from "next/navigation";
9
  import { Loader2 } from "lucide-react";
10
  import { useSearchParams } from "next/navigation";
11
- import { claimVideo } from "@/actions/claim-video";
12
 
13
  export function SignInForm() {
14
- const [email, setEmail] = useState("");
15
- const [password, setPassword] = useState("");
16
  const [loading, setLoading] = useState(false);
17
  const [error, setError] = useState("");
18
- const router = useRouter();
19
  const searchParams = useSearchParams();
20
  const videoId = searchParams.get("videoId");
21
 
22
- const handleSubmit = async (e: React.FormEvent) => {
23
- e.preventDefault();
24
  setLoading(true);
25
  setError("");
26
- await signIn.email({
27
- email,
28
- password,
29
- callbackURL: "/dashboard",
30
  }, {
31
  onRequest: () => {
32
  setLoading(true);
33
  },
34
- onSuccess: async () => {
35
- console.log("[SignInForm] Login success");
36
- if (videoId) {
37
- console.log(`[SignInForm] Found videoId ${videoId}, claiming...`);
38
- try {
39
- const result = await claimVideo(videoId);
40
- console.log(`[SignInForm] Claim result:`, result);
41
- } catch (err) {
42
- console.error("[SignInForm] Claim failed:", err);
43
- }
44
- }
45
- setLoading(false);
46
- router.push("/dashboard");
47
- },
48
  onError: (ctx) => {
49
  setLoading(false);
50
  setError(ctx.error.message);
@@ -54,41 +31,28 @@ export function SignInForm() {
54
 
55
  return (
56
  <div className="grid gap-6">
57
- <form onSubmit={handleSubmit}>
58
- <div className="grid gap-4">
59
- <div className="grid gap-2">
60
- <Label htmlFor="email">Email</Label>
61
- <Input
62
- id="email"
63
- placeholder="name@example.com"
64
- type="email"
65
- autoCapitalize="none"
66
- autoComplete="email"
67
- autoCorrect="off"
68
- required
69
- value={email}
70
- onChange={(e) => setEmail(e.target.value)}
71
- />
72
- </div>
73
- <div className="grid gap-2">
74
- <Label htmlFor="password">Password</Label>
75
- <Input
76
- id="password"
77
- type="password"
78
- required
79
- value={password}
80
- onChange={(e) => setPassword(e.target.value)}
81
- />
82
- </div>
83
- {error && <p className="text-sm text-red-500">{error}</p>}
84
- <Button disabled={loading}>
85
- {loading && (
86
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
87
- )}
88
- Sign In
89
- </Button>
90
  </div>
91
- </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  </div>
93
  );
94
  }
 
3
  import { useState } from "react";
4
  import { signIn } from "@/lib/auth-client";
5
  import { Button } from "@/components/ui/button";
 
 
 
6
  import { Loader2 } from "lucide-react";
7
  import { useSearchParams } from "next/navigation";
 
8
 
9
  export function SignInForm() {
 
 
10
  const [loading, setLoading] = useState(false);
11
  const [error, setError] = useState("");
 
12
  const searchParams = useSearchParams();
13
  const videoId = searchParams.get("videoId");
14
 
15
+ const handleGoogleSignIn = async () => {
 
16
  setLoading(true);
17
  setError("");
18
+ await signIn.social({
19
+ provider: "google",
20
+ callbackURL: videoId ? `/dashboard?videoId=${videoId}` : "/dashboard",
 
21
  }, {
22
  onRequest: () => {
23
  setLoading(true);
24
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  onError: (ctx) => {
26
  setLoading(false);
27
  setError(ctx.error.message);
 
31
 
32
  return (
33
  <div className="grid gap-6">
34
+ {error && (
35
+ <div className="bg-destructive/15 text-destructive text-sm p-3 rounded-lg border border-destructive/20 mb-2">
36
+ {error}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  </div>
38
+ )}
39
+
40
+ <Button
41
+ variant="outline"
42
+ type="button"
43
+ disabled={loading}
44
+ onClick={handleGoogleSignIn}
45
+ className="w-full h-11 text-base glass border-white/10 hover:bg-white/5 transition-all duration-300"
46
+ >
47
+ {loading ? (
48
+ <Loader2 className="mr-2 h-5 w-5 animate-spin" />
49
+ ) : (
50
+ <svg className="mr-2 h-5 w-5" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="google" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 488 512">
51
+ <path fill="currentColor" d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"></path>
52
+ </svg>
53
+ )}
54
+ Continue with Google
55
+ </Button>
56
  </div>
57
  );
58
  }
src/components/auth/sign-up-form.tsx CHANGED
@@ -1,51 +1,27 @@
1
  "use client";
2
 
3
  import { useState } from "react";
4
- import { useRouter, useSearchParams } from "next/navigation";
5
- import { signUp } from "@/lib/auth-client";
6
  import { Button } from "@/components/ui/button";
7
- import { Input } from "@/components/ui/input";
8
- import { Label } from "@/components/ui/label";
9
  import { Loader2 } from "lucide-react";
 
10
 
11
  export function SignUpForm() {
12
- const [name, setName] = useState("");
13
- const [email, setEmail] = useState("");
14
- const [password, setPassword] = useState("");
15
  const [loading, setLoading] = useState(false);
16
  const [error, setError] = useState("");
17
- const router = useRouter();
18
  const searchParams = useSearchParams();
19
  const videoId = searchParams.get("videoId");
20
 
21
- console.log(`[SignUpForm] Rendered. videoId from URL: ${videoId}`);
22
-
23
- const handleSubmit = async (e: React.FormEvent) => {
24
- e.preventDefault();
25
  setLoading(true);
26
  setError("");
27
- await signUp.email({
28
- email,
29
- password,
30
- name,
31
  }, {
32
  onRequest: () => {
33
  setLoading(true);
34
  },
35
- onSuccess: async () => {
36
- if (videoId) {
37
- console.log(`[SignUpForm] Success. Triggering claim for videoId: ${videoId}`);
38
- try {
39
- const { claimVideo } = await import("@/actions/claim-video");
40
- const result = await claimVideo(videoId);
41
- console.log(`[SignUpForm] Claim result:`, result);
42
- } catch (err) {
43
- console.error("Failed to claim video:", err);
44
- }
45
- }
46
- setLoading(false);
47
- router.push("/dashboard");
48
- },
49
  onError: (ctx) => {
50
  setLoading(false);
51
  setError(ctx.error.message);
@@ -55,51 +31,28 @@ export function SignUpForm() {
55
 
56
  return (
57
  <div className="grid gap-6">
58
- <form onSubmit={handleSubmit}>
59
- <div className="grid gap-4">
60
- <div className="grid gap-2">
61
- <Label htmlFor="name">Name</Label>
62
- <Input
63
- id="name"
64
- placeholder="John Doe"
65
- required
66
- value={name}
67
- onChange={(e) => setName(e.target.value)}
68
- />
69
- </div>
70
- <div className="grid gap-2">
71
- <Label htmlFor="email">Email</Label>
72
- <Input
73
- id="email"
74
- placeholder="name@example.com"
75
- type="email"
76
- autoCapitalize="none"
77
- autoComplete="email"
78
- autoCorrect="off"
79
- required
80
- value={email}
81
- onChange={(e) => setEmail(e.target.value)}
82
- />
83
- </div>
84
- <div className="grid gap-2">
85
- <Label htmlFor="password">Password</Label>
86
- <Input
87
- id="password"
88
- type="password"
89
- required
90
- value={password}
91
- onChange={(e) => setPassword(e.target.value)}
92
- />
93
- </div>
94
- {error && <p className="text-sm text-red-500">{error}</p>}
95
- <Button disabled={loading}>
96
- {loading && (
97
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
98
- )}
99
- Sign Up
100
- </Button>
101
  </div>
102
- </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  </div>
104
  );
105
  }
 
1
  "use client";
2
 
3
  import { useState } from "react";
4
+ import { signIn } from "@/lib/auth-client";
 
5
  import { Button } from "@/components/ui/button";
 
 
6
  import { Loader2 } from "lucide-react";
7
+ import { useSearchParams } from "next/navigation";
8
 
9
  export function SignUpForm() {
 
 
 
10
  const [loading, setLoading] = useState(false);
11
  const [error, setError] = useState("");
 
12
  const searchParams = useSearchParams();
13
  const videoId = searchParams.get("videoId");
14
 
15
+ const handleGoogleSignIn = async () => {
 
 
 
16
  setLoading(true);
17
  setError("");
18
+ await signIn.social({
19
+ provider: "google",
20
+ callbackURL: videoId ? `/dashboard?videoId=${videoId}` : "/dashboard",
 
21
  }, {
22
  onRequest: () => {
23
  setLoading(true);
24
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  onError: (ctx) => {
26
  setLoading(false);
27
  setError(ctx.error.message);
 
31
 
32
  return (
33
  <div className="grid gap-6">
34
+ {error && (
35
+ <div className="bg-destructive/15 text-destructive text-sm p-3 rounded-lg border border-destructive/20 mb-2">
36
+ {error}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  </div>
38
+ )}
39
+
40
+ <Button
41
+ variant="outline"
42
+ type="button"
43
+ disabled={loading}
44
+ onClick={handleGoogleSignIn}
45
+ className="w-full h-11 text-base glass border-white/10 hover:bg-white/5 transition-all duration-300"
46
+ >
47
+ {loading ? (
48
+ <Loader2 className="mr-2 h-5 w-5 animate-spin" />
49
+ ) : (
50
+ <svg className="mr-2 h-5 w-5" aria-hidden="true" focusable="false" data-prefix="fab" data-icon="google" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 488 512">
51
+ <path fill="currentColor" d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"></path>
52
+ </svg>
53
+ )}
54
+ Continue with Google
55
+ </Button>
56
  </div>
57
  );
58
  }
src/lib/auth.ts CHANGED
@@ -1,11 +1,11 @@
1
  import { betterAuth } from "better-auth";
2
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
3
- import { db } from "./db"; // Adjust path if necessary
4
  import * as schema from "./db/schema";
5
 
6
  export const auth = betterAuth({
7
  database: drizzleAdapter(db, {
8
- provider: "pg", // or "mysql", "sqlite"
9
  schema: {
10
  user: schema.users,
11
  session: schema.sessions,
@@ -14,10 +14,15 @@ export const auth = betterAuth({
14
  },
15
  }),
16
  account: {
 
 
 
 
17
  encryptOAuthTokens: false,
18
  },
 
19
  emailAndPassword: {
20
- enabled: true,
21
  },
22
  socialProviders: {
23
  google: {
@@ -31,5 +36,10 @@ export const auth = betterAuth({
31
  }
32
  }
33
  }
34
- }
 
 
 
 
 
35
  });
 
1
  import { betterAuth } from "better-auth";
2
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
3
+ import { db } from "./db";
4
  import * as schema from "./db/schema";
5
 
6
  export const auth = betterAuth({
7
  database: drizzleAdapter(db, {
8
+ provider: "pg",
9
  schema: {
10
  user: schema.users,
11
  session: schema.sessions,
 
14
  },
15
  }),
16
  account: {
17
+ accountLinking: {
18
+ enabled: true,
19
+ trustedProviders: ["google"],
20
+ },
21
  encryptOAuthTokens: false,
22
  },
23
+ // Transitioning to Google-only authentication
24
  emailAndPassword: {
25
+ enabled: false,
26
  },
27
  socialProviders: {
28
  google: {
 
36
  }
37
  }
38
  }
39
+ },
40
+ advanced: {
41
+ // Help resolve state mismatch on localhost
42
+ useSecureCookies: false, // Ensure cookies work on HTTP localhost
43
+ },
44
+ trustedOrigins: ["http://localhost:3000", "http://127.0.0.1:3000"]
45
  });