create-better-t-stack 2.46.2 → 2.46.3-canary.62efbc44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-NOw0j6Z9.js → src-DHpq-szu.js} +132 -35
- package/package.json +3 -2
- package/templates/auth/better-auth/convex/backend/convex/auth.config.ts.hbs +8 -0
- package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +48 -0
- package/templates/auth/better-auth/convex/backend/convex/convex.config.ts.hbs +7 -0
- package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +12 -0
- package/templates/auth/better-auth/convex/backend/convex/privateData.ts.hbs +16 -0
- package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +3 -0
- package/templates/auth/better-auth/convex/web/react/next/src/app/dashboard/page.tsx.hbs +40 -0
- package/templates/auth/better-auth/convex/web/react/next/src/components/sign-in-form.tsx.hbs +129 -0
- package/templates/auth/better-auth/convex/web/react/next/src/components/sign-up-form.tsx.hbs +154 -0
- package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +48 -0
- package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-client.ts.hbs +6 -0
- package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +6 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/sign-in-form.tsx.hbs +133 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/sign-up-form.tsx.hbs +158 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +50 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/lib/auth-client.ts.hbs +10 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +43 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/sign-in-form.tsx.hbs +133 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/sign-up-form.tsx.hbs +158 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +50 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-client.ts.hbs +6 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +5 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +11 -0
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +43 -0
- package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +21 -2
- package/templates/frontend/react/next/next.config.ts.hbs +4 -1
- package/templates/frontend/react/next/src/components/providers.tsx.hbs +8 -0
- package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +8 -1
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +47 -0
- package/templates/frontend/react/tanstack-start/src/routes/index.tsx.hbs +2 -2
- package/templates/frontend/react/web-base/src/components/header.tsx.hbs +2 -2
- package/templates/auth/better-auth/web/react/next/src/components/theme-provider.tsx.hbs +0 -11
- /package/templates/frontend/react/web-base/src/components/{loader.tsx → loader.tsx.hbs} +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { authClient } from "@/lib/auth-client";
|
|
2
|
+
import { useForm } from "@tanstack/react-form";
|
|
3
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
4
|
+
import { toast } from "sonner";
|
|
5
|
+
import z from "zod";
|
|
6
|
+
import { Button } from "./ui/button";
|
|
7
|
+
import { Input } from "./ui/input";
|
|
8
|
+
import { Label } from "./ui/label";
|
|
9
|
+
|
|
10
|
+
export default function SignInForm({
|
|
11
|
+
onSwitchToSignUp,
|
|
12
|
+
}: {
|
|
13
|
+
onSwitchToSignUp: () => void;
|
|
14
|
+
}) {
|
|
15
|
+
const navigate = useNavigate({
|
|
16
|
+
from: "/",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const form = useForm({
|
|
20
|
+
defaultValues: {
|
|
21
|
+
email: "",
|
|
22
|
+
password: "",
|
|
23
|
+
},
|
|
24
|
+
onSubmit: async ({ value }) => {
|
|
25
|
+
await authClient.signIn.email(
|
|
26
|
+
{
|
|
27
|
+
email: value.email,
|
|
28
|
+
password: value.password,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
onSuccess: () => {
|
|
32
|
+
navigate({
|
|
33
|
+
to: "/dashboard",
|
|
34
|
+
});
|
|
35
|
+
toast.success("Sign in successful");
|
|
36
|
+
},
|
|
37
|
+
onError: (error) => {
|
|
38
|
+
toast.error(error.error.message || error.error.statusText);
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
},
|
|
43
|
+
validators: {
|
|
44
|
+
onSubmit: z.object({
|
|
45
|
+
email: z.email("Invalid email address"),
|
|
46
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="mx-auto w-full mt-10 max-w-md p-6">
|
|
53
|
+
<h1 className="mb-6 text-center text-3xl font-bold">Welcome Back</h1>
|
|
54
|
+
|
|
55
|
+
<form
|
|
56
|
+
onSubmit={(e) => {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
e.stopPropagation();
|
|
59
|
+
form.handleSubmit();
|
|
60
|
+
}}
|
|
61
|
+
className="space-y-4"
|
|
62
|
+
>
|
|
63
|
+
<div>
|
|
64
|
+
<form.Field name="email">
|
|
65
|
+
{(field) => (
|
|
66
|
+
<div className="space-y-2">
|
|
67
|
+
<Label htmlFor={field.name}>Email</Label>
|
|
68
|
+
<Input
|
|
69
|
+
id={field.name}
|
|
70
|
+
name={field.name}
|
|
71
|
+
type="email"
|
|
72
|
+
value={field.state.value}
|
|
73
|
+
onBlur={field.handleBlur}
|
|
74
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
75
|
+
/>
|
|
76
|
+
{field.state.meta.errors.map((error) => (
|
|
77
|
+
<p key={error?.message} className="text-red-500">
|
|
78
|
+
{error?.message}
|
|
79
|
+
</p>
|
|
80
|
+
))}
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
</form.Field>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div>
|
|
87
|
+
<form.Field name="password">
|
|
88
|
+
{(field) => (
|
|
89
|
+
<div className="space-y-2">
|
|
90
|
+
<Label htmlFor={field.name}>Password</Label>
|
|
91
|
+
<Input
|
|
92
|
+
id={field.name}
|
|
93
|
+
name={field.name}
|
|
94
|
+
type="password"
|
|
95
|
+
value={field.state.value}
|
|
96
|
+
onBlur={field.handleBlur}
|
|
97
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
98
|
+
/>
|
|
99
|
+
{field.state.meta.errors.map((error) => (
|
|
100
|
+
<p key={error?.message} className="text-red-500">
|
|
101
|
+
{error?.message}
|
|
102
|
+
</p>
|
|
103
|
+
))}
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
106
|
+
</form.Field>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<form.Subscribe>
|
|
110
|
+
{(state) => (
|
|
111
|
+
<Button
|
|
112
|
+
type="submit"
|
|
113
|
+
className="w-full"
|
|
114
|
+
disabled={!state.canSubmit || state.isSubmitting}
|
|
115
|
+
>
|
|
116
|
+
{state.isSubmitting ? "Submitting..." : "Sign In"}
|
|
117
|
+
</Button>
|
|
118
|
+
)}
|
|
119
|
+
</form.Subscribe>
|
|
120
|
+
</form>
|
|
121
|
+
|
|
122
|
+
<div className="mt-4 text-center">
|
|
123
|
+
<Button
|
|
124
|
+
variant="link"
|
|
125
|
+
onClick={onSwitchToSignUp}
|
|
126
|
+
className="text-indigo-600 hover:text-indigo-800"
|
|
127
|
+
>
|
|
128
|
+
Need an account? Sign Up
|
|
129
|
+
</Button>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { authClient } from "@/lib/auth-client";
|
|
2
|
+
import { useForm } from "@tanstack/react-form";
|
|
3
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
4
|
+
import { toast } from "sonner";
|
|
5
|
+
import z from "zod";
|
|
6
|
+
import { Button } from "./ui/button";
|
|
7
|
+
import { Input } from "./ui/input";
|
|
8
|
+
import { Label } from "./ui/label";
|
|
9
|
+
|
|
10
|
+
export default function SignUpForm({
|
|
11
|
+
onSwitchToSignIn,
|
|
12
|
+
}: {
|
|
13
|
+
onSwitchToSignIn: () => void;
|
|
14
|
+
}) {
|
|
15
|
+
const navigate = useNavigate({
|
|
16
|
+
from: "/",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const form = useForm({
|
|
20
|
+
defaultValues: {
|
|
21
|
+
email: "",
|
|
22
|
+
password: "",
|
|
23
|
+
name: "",
|
|
24
|
+
},
|
|
25
|
+
onSubmit: async ({ value }) => {
|
|
26
|
+
await authClient.signUp.email(
|
|
27
|
+
{
|
|
28
|
+
email: value.email,
|
|
29
|
+
password: value.password,
|
|
30
|
+
name: value.name,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
onSuccess: () => {
|
|
34
|
+
navigate({
|
|
35
|
+
to: "/dashboard",
|
|
36
|
+
});
|
|
37
|
+
toast.success("Sign up successful");
|
|
38
|
+
},
|
|
39
|
+
onError: (error) => {
|
|
40
|
+
toast.error(error.error.message || error.error.statusText);
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
},
|
|
45
|
+
validators: {
|
|
46
|
+
onSubmit: z.object({
|
|
47
|
+
name: z.string().min(2, "Name must be at least 2 characters"),
|
|
48
|
+
email: z.email("Invalid email address"),
|
|
49
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="mx-auto w-full mt-10 max-w-md p-6">
|
|
56
|
+
<h1 className="mb-6 text-center text-3xl font-bold">Create Account</h1>
|
|
57
|
+
|
|
58
|
+
<form
|
|
59
|
+
onSubmit={(e) => {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
e.stopPropagation();
|
|
62
|
+
form.handleSubmit();
|
|
63
|
+
}}
|
|
64
|
+
className="space-y-4"
|
|
65
|
+
>
|
|
66
|
+
<div>
|
|
67
|
+
<form.Field name="name">
|
|
68
|
+
{(field) => (
|
|
69
|
+
<div className="space-y-2">
|
|
70
|
+
<Label htmlFor={field.name}>Name</Label>
|
|
71
|
+
<Input
|
|
72
|
+
id={field.name}
|
|
73
|
+
name={field.name}
|
|
74
|
+
value={field.state.value}
|
|
75
|
+
onBlur={field.handleBlur}
|
|
76
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
77
|
+
/>
|
|
78
|
+
{field.state.meta.errors.map((error) => (
|
|
79
|
+
<p key={error?.message} className="text-red-500">
|
|
80
|
+
{error?.message}
|
|
81
|
+
</p>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
</form.Field>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div>
|
|
89
|
+
<form.Field name="email">
|
|
90
|
+
{(field) => (
|
|
91
|
+
<div className="space-y-2">
|
|
92
|
+
<Label htmlFor={field.name}>Email</Label>
|
|
93
|
+
<Input
|
|
94
|
+
id={field.name}
|
|
95
|
+
name={field.name}
|
|
96
|
+
type="email"
|
|
97
|
+
value={field.state.value}
|
|
98
|
+
onBlur={field.handleBlur}
|
|
99
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
100
|
+
/>
|
|
101
|
+
{field.state.meta.errors.map((error) => (
|
|
102
|
+
<p key={error?.message} className="text-red-500">
|
|
103
|
+
{error?.message}
|
|
104
|
+
</p>
|
|
105
|
+
))}
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
</form.Field>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div>
|
|
112
|
+
<form.Field name="password">
|
|
113
|
+
{(field) => (
|
|
114
|
+
<div className="space-y-2">
|
|
115
|
+
<Label htmlFor={field.name}>Password</Label>
|
|
116
|
+
<Input
|
|
117
|
+
id={field.name}
|
|
118
|
+
name={field.name}
|
|
119
|
+
type="password"
|
|
120
|
+
value={field.state.value}
|
|
121
|
+
onBlur={field.handleBlur}
|
|
122
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
123
|
+
/>
|
|
124
|
+
{field.state.meta.errors.map((error) => (
|
|
125
|
+
<p key={error?.message} className="text-red-500">
|
|
126
|
+
{error?.message}
|
|
127
|
+
</p>
|
|
128
|
+
))}
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
</form.Field>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<form.Subscribe>
|
|
135
|
+
{(state) => (
|
|
136
|
+
<Button
|
|
137
|
+
type="submit"
|
|
138
|
+
className="w-full"
|
|
139
|
+
disabled={!state.canSubmit || state.isSubmitting}
|
|
140
|
+
>
|
|
141
|
+
{state.isSubmitting ? "Submitting..." : "Sign Up"}
|
|
142
|
+
</Button>
|
|
143
|
+
)}
|
|
144
|
+
</form.Subscribe>
|
|
145
|
+
</form>
|
|
146
|
+
|
|
147
|
+
<div className="mt-4 text-center">
|
|
148
|
+
<Button
|
|
149
|
+
variant="link"
|
|
150
|
+
onClick={onSwitchToSignIn}
|
|
151
|
+
className="text-indigo-600 hover:text-indigo-800"
|
|
152
|
+
>
|
|
153
|
+
Already have an account? Sign In
|
|
154
|
+
</Button>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
}
|
package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DropdownMenu,
|
|
3
|
+
DropdownMenuContent,
|
|
4
|
+
DropdownMenuItem,
|
|
5
|
+
DropdownMenuLabel,
|
|
6
|
+
DropdownMenuSeparator,
|
|
7
|
+
DropdownMenuTrigger,
|
|
8
|
+
} from "@/components/ui/dropdown-menu";
|
|
9
|
+
import { authClient } from "@/lib/auth-client";
|
|
10
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
11
|
+
import { Button } from "./ui/button";
|
|
12
|
+
import { useQuery } from "convex/react";
|
|
13
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
14
|
+
|
|
15
|
+
export default function UserMenu() {
|
|
16
|
+
const navigate = useNavigate();
|
|
17
|
+
const user = useQuery(api.auth.getCurrentUser)
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<DropdownMenu>
|
|
21
|
+
<DropdownMenuTrigger asChild>
|
|
22
|
+
<Button variant="outline">{user?.name}</Button>
|
|
23
|
+
</DropdownMenuTrigger>
|
|
24
|
+
<DropdownMenuContent className="bg-card">
|
|
25
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
26
|
+
<DropdownMenuSeparator />
|
|
27
|
+
<DropdownMenuItem>{user?.email}</DropdownMenuItem>
|
|
28
|
+
<DropdownMenuItem asChild>
|
|
29
|
+
<Button
|
|
30
|
+
variant="destructive"
|
|
31
|
+
className="w-full"
|
|
32
|
+
onClick={() => {
|
|
33
|
+
authClient.signOut({
|
|
34
|
+
fetchOptions: {
|
|
35
|
+
onSuccess: () => {
|
|
36
|
+
navigate({
|
|
37
|
+
to: "/dashboard",
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
Sign Out
|
|
45
|
+
</Button>
|
|
46
|
+
</DropdownMenuItem>
|
|
47
|
+
</DropdownMenuContent>
|
|
48
|
+
</DropdownMenu>
|
|
49
|
+
);
|
|
50
|
+
}
|
package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { reactStartHandler } from '@convex-dev/better-auth/react-start'
|
|
2
|
+
import { createServerFileRoute } from '@tanstack/react-start/server'
|
|
3
|
+
|
|
4
|
+
export const ServerRoute = createServerFileRoute('/api/auth/$').methods({
|
|
5
|
+
GET: ({ request }) => {
|
|
6
|
+
return reactStartHandler(request)
|
|
7
|
+
},
|
|
8
|
+
POST: ({ request }) => {
|
|
9
|
+
return reactStartHandler(request)
|
|
10
|
+
},
|
|
11
|
+
})
|
package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import SignInForm from "@/components/sign-in-form";
|
|
2
|
+
import SignUpForm from "@/components/sign-up-form";
|
|
3
|
+
import UserMenu from "@/components/user-menu";
|
|
4
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
5
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
6
|
+
import {
|
|
7
|
+
Authenticated,
|
|
8
|
+
AuthLoading,
|
|
9
|
+
Unauthenticated,
|
|
10
|
+
useQuery,
|
|
11
|
+
} from "convex/react";
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
|
|
14
|
+
export const Route = createFileRoute("/dashboard")({
|
|
15
|
+
component: RouteComponent,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
function RouteComponent() {
|
|
19
|
+
const [showSignIn, setShowSignIn] = useState(false);
|
|
20
|
+
const privateData = useQuery(api.privateData.get);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<Authenticated>
|
|
25
|
+
<div>
|
|
26
|
+
<h1>Dashboard</h1>
|
|
27
|
+
<p>privateData: {privateData?.message}</p>
|
|
28
|
+
<UserMenu />
|
|
29
|
+
</div>
|
|
30
|
+
</Authenticated>
|
|
31
|
+
<Unauthenticated>
|
|
32
|
+
{showSignIn ? (
|
|
33
|
+
<SignInForm onSwitchToSignUp={() => setShowSignIn(false)} />
|
|
34
|
+
) : (
|
|
35
|
+
<SignUpForm onSwitchToSignIn={() => setShowSignIn(true)} />
|
|
36
|
+
)}
|
|
37
|
+
</Unauthenticated>
|
|
38
|
+
<AuthLoading>
|
|
39
|
+
<div>Loading...</div>
|
|
40
|
+
</AuthLoading>
|
|
41
|
+
</>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -3,11 +3,30 @@
|
|
|
3
3
|
import { useChat } from "@ai-sdk/react";
|
|
4
4
|
import { DefaultChatTransport } from "ai";
|
|
5
5
|
import { Send } from "lucide-react";
|
|
6
|
-
|
|
6
|
+
{{#if (or (eq webDeploy "wrangler") (eq webDeploy "alchemy"))}}
|
|
7
|
+
import dynamic from "next/dynamic";
|
|
8
|
+
{{else}}
|
|
7
9
|
import { Response } from "@/components/response";
|
|
10
|
+
{{/if}}
|
|
11
|
+
import { useEffect, useRef, useState } from "react";
|
|
8
12
|
import { Button } from "@/components/ui/button";
|
|
9
13
|
import { Input } from "@/components/ui/input";
|
|
10
14
|
|
|
15
|
+
{{#if (or (eq webDeploy "wrangler") (eq webDeploy "alchemy"))}}
|
|
16
|
+
const Response = dynamic(
|
|
17
|
+
() =>
|
|
18
|
+
import("@/components/response").then((mod) => ({ default: mod.Response })),
|
|
19
|
+
{
|
|
20
|
+
loading: () => (
|
|
21
|
+
<div className="flex h-full items-center justify-center">
|
|
22
|
+
<div className="text-muted-foreground">Loading response...</div>
|
|
23
|
+
</div>
|
|
24
|
+
),
|
|
25
|
+
ssr: false,
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
{{/if}}
|
|
29
|
+
|
|
11
30
|
export default function AIPage() {
|
|
12
31
|
const [input, setInput] = useState("");
|
|
13
32
|
const { messages, sendMessage } = useChat({
|
|
@@ -81,4 +100,4 @@ export default function AIPage() {
|
|
|
81
100
|
</form>
|
|
82
101
|
</div>
|
|
83
102
|
);
|
|
84
|
-
}
|
|
103
|
+
}
|
|
@@ -5,10 +5,13 @@ import type { NextConfig } from "next";
|
|
|
5
5
|
|
|
6
6
|
const nextConfig: NextConfig = {
|
|
7
7
|
typedRoutes: true,
|
|
8
|
+
{{#if (includes examples "ai")}}
|
|
9
|
+
transpilePackages: ["shiki"],
|
|
10
|
+
{{/if}}
|
|
8
11
|
};
|
|
9
12
|
|
|
10
13
|
export default nextConfig;
|
|
11
14
|
|
|
12
15
|
{{#if (or (eq webDeploy "alchemy") (eq webDeploy "wrangler"))}}
|
|
13
16
|
initOpenNextCloudflareForDev();
|
|
14
|
-
{{/if}}
|
|
17
|
+
{{/if}}
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
import { useAuth } from "@clerk/nextjs";
|
|
6
6
|
import { ConvexReactClient } from "convex/react";
|
|
7
7
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
8
|
+
{{else if (eq auth "better-auth")}}
|
|
9
|
+
import { ConvexProvider, ConvexReactClient } from "convex/react";
|
|
10
|
+
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
|
|
11
|
+
import { authClient } from "@/lib/auth-client";
|
|
8
12
|
{{else}}
|
|
9
13
|
import { ConvexProvider, ConvexReactClient } from "convex/react";
|
|
10
14
|
{{/if}}
|
|
@@ -44,6 +48,10 @@ export default function Providers({
|
|
|
44
48
|
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
|
|
45
49
|
{children}
|
|
46
50
|
</ConvexProviderWithClerk>
|
|
51
|
+
{{else if (eq auth "better-auth")}}
|
|
52
|
+
<ConvexBetterAuthProvider client={convex} authClient={authClient}>
|
|
53
|
+
{children}
|
|
54
|
+
</ConvexBetterAuthProvider>
|
|
47
55
|
{{else}}
|
|
48
56
|
<ConvexProvider client={convex}>{children}</ConvexProvider>
|
|
49
57
|
{{/if}}
|
|
@@ -16,10 +16,15 @@ import { routeTree } from "./routeTree.gen";
|
|
|
16
16
|
{{#if (eq auth "clerk")}}
|
|
17
17
|
import { ClerkProvider, useAuth } from "@clerk/clerk-react";
|
|
18
18
|
import { ConvexProviderWithClerk } from "convex/react-clerk";
|
|
19
|
+
{{else if (eq auth "better-auth")}}
|
|
20
|
+
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
|
|
21
|
+
import { authClient } from "@/lib/auth-client";
|
|
19
22
|
{{else}}
|
|
20
23
|
import { ConvexProvider } from "convex/react";
|
|
21
24
|
{{/if}}
|
|
22
|
-
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string)
|
|
25
|
+
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string{{#if (eq auth "better-auth")}}, {
|
|
26
|
+
expectAuth: true,
|
|
27
|
+
}{{/if}});
|
|
23
28
|
{{/if}}
|
|
24
29
|
|
|
25
30
|
const router = createRouter({
|
|
@@ -57,6 +62,8 @@ const router = createRouter({
|
|
|
57
62
|
</ConvexProviderWithClerk>
|
|
58
63
|
</ClerkProvider>
|
|
59
64
|
);
|
|
65
|
+
{{else if (eq auth "better-auth")}}
|
|
66
|
+
return <ConvexBetterAuthProvider client={convex} authClient={authClient}>{children}</ConvexBetterAuthProvider>;
|
|
60
67
|
{{else}}
|
|
61
68
|
return <ConvexProvider client={convex}>{children}</ConvexProvider>;
|
|
62
69
|
{{/if}}
|
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
Scripts,
|
|
9
9
|
createRootRouteWithContext,
|
|
10
10
|
useRouterState,
|
|
11
|
+
{{#if (and (eq backend "convex") (or (eq auth "clerk") (eq auth "better-auth")))}}
|
|
11
12
|
useRouteContext,
|
|
13
|
+
{{/if}}
|
|
12
14
|
} from "@tanstack/react-router";
|
|
13
15
|
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
|
14
16
|
import Header from "../components/header";
|
|
@@ -36,6 +38,23 @@ const fetchClerkAuth = createServerFn({ method: "GET" }).handler(async () => {
|
|
|
36
38
|
const token = await auth.getToken({ template: "convex" });
|
|
37
39
|
return { userId: auth.userId, token };
|
|
38
40
|
});
|
|
41
|
+
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
42
|
+
import { createServerFn } from "@tanstack/react-start";
|
|
43
|
+
import { getWebRequest, getCookie } from "@tanstack/react-start/server";
|
|
44
|
+
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
|
|
45
|
+
import { fetchSession, getCookieName } from "@convex-dev/better-auth/react-start";
|
|
46
|
+
import { authClient } from "@/lib/auth-client";
|
|
47
|
+
import { createAuth } from "@{{projectName}}/backend/convex/auth";
|
|
48
|
+
|
|
49
|
+
const fetchAuth = createServerFn({ method: "GET" }).handler(async () => {
|
|
50
|
+
const { session } = await fetchSession(getWebRequest());
|
|
51
|
+
const sessionCookieName = getCookieName(createAuth);
|
|
52
|
+
const token = getCookie(sessionCookieName);
|
|
53
|
+
return {
|
|
54
|
+
userId: session?.user.id,
|
|
55
|
+
token,
|
|
56
|
+
};
|
|
57
|
+
});
|
|
39
58
|
{{/if}}
|
|
40
59
|
|
|
41
60
|
{{#if (eq backend "convex")}}
|
|
@@ -95,6 +114,14 @@ export const Route = createRootRouteWithContext<RouterAppContext>()({
|
|
|
95
114
|
}
|
|
96
115
|
return { userId, token };
|
|
97
116
|
},
|
|
117
|
+
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
118
|
+
beforeLoad: async (ctx) => {
|
|
119
|
+
const { userId, token } = await fetchAuth();
|
|
120
|
+
if (token) {
|
|
121
|
+
ctx.context.convexQueryClient.serverHttpClient?.setAuth(token);
|
|
122
|
+
}
|
|
123
|
+
return { userId, token };
|
|
124
|
+
},
|
|
98
125
|
{{/if}}
|
|
99
126
|
});
|
|
100
127
|
|
|
@@ -122,6 +149,26 @@ function RootDocument() {
|
|
|
122
149
|
</ConvexProviderWithClerk>
|
|
123
150
|
</ClerkProvider>
|
|
124
151
|
);
|
|
152
|
+
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
153
|
+
const context = useRouteContext({ from: Route.id });
|
|
154
|
+
return (
|
|
155
|
+
<ConvexBetterAuthProvider client={context.convexClient} authClient={authClient}>
|
|
156
|
+
<html lang="en" className="dark">
|
|
157
|
+
<head>
|
|
158
|
+
<HeadContent />
|
|
159
|
+
</head>
|
|
160
|
+
<body>
|
|
161
|
+
<div className="grid h-svh grid-rows-[auto_1fr]">
|
|
162
|
+
<Header />
|
|
163
|
+
{isFetching ? <Loader /> : <Outlet />}
|
|
164
|
+
</div>
|
|
165
|
+
<Toaster richColors />
|
|
166
|
+
<TanStackRouterDevtools position="bottom-left" />
|
|
167
|
+
<Scripts />
|
|
168
|
+
</body>
|
|
169
|
+
</html>
|
|
170
|
+
</ConvexBetterAuthProvider>
|
|
171
|
+
);
|
|
125
172
|
{{else}}
|
|
126
173
|
return (
|
|
127
174
|
<html lang="en" className="dark">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
2
|
{{#if (eq backend "convex")}}
|
|
3
3
|
import { convexQuery } from "@convex-dev/react-query";
|
|
4
|
-
import {
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
5
|
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
6
6
|
{{else if (or (eq api "trpc") (eq api "orpc"))}}
|
|
7
7
|
import { useQuery } from "@tanstack/react-query";
|
|
@@ -35,7 +35,7 @@ const TITLE_TEXT = `
|
|
|
35
35
|
|
|
36
36
|
function HomeComponent() {
|
|
37
37
|
{{#if (eq backend "convex")}}
|
|
38
|
-
const healthCheck =
|
|
38
|
+
const healthCheck = useQuery(convexQuery(api.healthCheck.get, {}));
|
|
39
39
|
{{else if (eq api "trpc")}}
|
|
40
40
|
const trpc = useTRPC();
|
|
41
41
|
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
|
@@ -9,7 +9,7 @@ import { Link } from "@tanstack/react-router";
|
|
|
9
9
|
{{#unless (includes frontend "tanstack-start")}}
|
|
10
10
|
import { ModeToggle } from "./mode-toggle";
|
|
11
11
|
{{/unless}}
|
|
12
|
-
{{#if (eq auth "better-auth")}}
|
|
12
|
+
{{#if (and (eq auth "better-auth") (ne backend "convex"))}}
|
|
13
13
|
import UserMenu from "./user-menu";
|
|
14
14
|
{{/if}}
|
|
15
15
|
|
|
@@ -67,7 +67,7 @@ export default function Header() {
|
|
|
67
67
|
{{#unless (includes frontend "tanstack-start")}}
|
|
68
68
|
<ModeToggle />
|
|
69
69
|
{{/unless}}
|
|
70
|
-
{{#if (eq auth "better-auth")}}
|
|
70
|
+
{{#if (and (eq auth "better-auth") (ne backend "convex"))}}
|
|
71
71
|
<UserMenu />
|
|
72
72
|
{{/if}}
|
|
73
73
|
</div>
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { ThemeProvider as NextThemesProvider } from "next-themes"
|
|
5
|
-
|
|
6
|
-
export function ThemeProvider({
|
|
7
|
-
children,
|
|
8
|
-
...props
|
|
9
|
-
}: React.ComponentProps<typeof NextThemesProvider>) {
|
|
10
|
-
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
|
|
11
|
-
}
|
|
File without changes
|