better-ts-stack 0.1.1 → 1.1.0
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/README.md +12 -6
- package/dist/builder/configGenerator.d.ts.map +1 -1
- package/dist/builder/configGenerator.js +30 -24
- package/dist/builder/configGenerator.js.map +1 -1
- package/dist/builder/dependencyInstaller.d.ts.map +1 -1
- package/dist/builder/dependencyInstaller.js +1 -10
- package/dist/builder/dependencyInstaller.js.map +1 -1
- package/dist/builder/index.d.ts.map +1 -1
- package/dist/builder/index.js +3 -7
- package/dist/builder/index.js.map +1 -1
- package/dist/builder/moduleSelector.d.ts.map +1 -1
- package/dist/builder/moduleSelector.js +1 -1
- package/dist/builder/moduleSelector.js.map +1 -1
- package/dist/builder/templateContext.d.ts +2 -2
- package/dist/builder/templateContext.d.ts.map +1 -1
- package/dist/builder/templateContext.js +1 -3
- package/dist/builder/templateContext.js.map +1 -1
- package/dist/modules/registry.d.ts.map +1 -1
- package/dist/modules/registry.js +2 -5
- package/dist/modules/registry.js.map +1 -1
- package/dist/output/nextSteps.d.ts.map +1 -1
- package/dist/output/nextSteps.js +7 -34
- package/dist/output/nextSteps.js.map +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +3 -14
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/stack.d.ts +3 -0
- package/dist/prompts/stack.d.ts.map +1 -0
- package/dist/prompts/stack.js +77 -0
- package/dist/prompts/stack.js.map +1 -0
- package/dist/types/index.d.ts +12 -10
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +6 -10
- package/dist/types/index.js.map +1 -1
- package/dist/validators/index.d.ts +1 -1
- package/dist/validators/index.d.ts.map +1 -1
- package/dist/validators/index.js +1 -1
- package/package.json +12 -12
- package/templates/backend/express/config.json +44 -42
- package/templates/backend/express/eslint.config.mjs +40 -31
- package/templates/backend/express/src/index.ts +61 -46
- package/templates/backend/express/src/routes/health.ts +12 -12
- package/templates/backend/express/tsconfig.json +25 -23
- package/templates/frontend/nextjs/config.json +33 -27
- package/templates/frontend/nextjs/eslint.config.mjs +11 -11
- package/templates/frontend/nextjs/proxy.ts.hbs +9 -8
- package/templates/frontend/nextjs/tsconfig.json +34 -21
- package/templates/frontend/tanstack/components.json +18 -0
- package/templates/frontend/tanstack/config.json +49 -0
- package/templates/frontend/tanstack/eslint.config.mjs +23 -0
- package/templates/frontend/tanstack/gitignore +38 -0
- package/templates/frontend/tanstack/public/favicon.svg +7 -0
- package/templates/frontend/tanstack/src/components/ui/button.tsx +51 -0
- package/templates/frontend/tanstack/src/components/ui/card.tsx +60 -0
- package/templates/frontend/tanstack/src/components/ui/field.tsx +67 -0
- package/templates/frontend/tanstack/src/components/ui/input.tsx +18 -0
- package/templates/frontend/tanstack/src/lib/utils.ts +6 -0
- package/templates/frontend/tanstack/src/routeTree.gen.ts +68 -0
- package/templates/frontend/tanstack/src/router.tsx +32 -0
- package/templates/frontend/tanstack/src/routes/__root.tsx +70 -0
- package/templates/frontend/tanstack/src/routes/index.tsx.hbs +102 -0
- package/templates/frontend/tanstack/src/styles/app.css +85 -0
- package/templates/frontend/tanstack/tsconfig.json +22 -0
- package/templates/frontend/tanstack/vite.config.ts +14 -0
- package/templates/modules/auth/express/config.json +26 -1
- package/templates/modules/auth/express/src/routes/auth.ts.hbs +1 -1
- package/templates/modules/auth/express/src/services/userStore.ts.hbs +95 -107
- package/templates/modules/auth/nextjs/config.json +6 -6
- package/templates/modules/auth/nextjs/lib/auth.ts.hbs +34 -32
- package/templates/modules/auth/tanstack/config.json +33 -0
- package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +135 -0
- package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +49 -0
- package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +153 -0
- package/templates/modules/auth/tanstack/src/lib/auth-client.ts +3 -0
- package/templates/modules/auth/tanstack/src/lib/auth-functions.ts +12 -0
- package/templates/modules/auth/tanstack/src/lib/auth-schema.ts +39 -0
- package/templates/modules/auth/tanstack/src/lib/auth.ts.hbs +38 -0
- package/templates/modules/auth/tanstack/src/routeTree.gen.ts +140 -0
- package/templates/modules/auth/tanstack/src/routes/api/auth/$.ts +16 -0
- package/templates/modules/auth/tanstack/src/routes/dashboard.tsx +111 -0
- package/templates/modules/auth/tanstack/src/routes/sign-in.tsx +37 -0
- package/templates/modules/auth/tanstack/src/routes/sign-up.tsx +37 -0
- package/templates/modules/docker/.dockerignore +14 -13
- package/templates/modules/docker/Dockerfile.hbs +64 -9
- package/templates/modules/docker/config.json +19 -16
- package/templates/modules/docker/docker-compose.yml.hbs +9 -9
- package/templates/modules/drizzle/express/config.json +27 -0
- package/templates/modules/drizzle/express/drizzle.config.ts +13 -0
- package/templates/modules/drizzle/express/src/lib/db.ts +13 -0
- package/templates/modules/drizzle/express/src/lib/schema.ts +10 -0
- package/templates/modules/drizzle/nextjs/config.json +27 -19
- package/templates/modules/drizzle/nextjs/lib/db.ts +3 -1
- package/templates/modules/drizzle/nextjs/lib/schema.ts.hbs +84 -84
- package/templates/modules/drizzle/tanstack/config.json +26 -0
- package/templates/modules/drizzle/tanstack/drizzle.config.ts +13 -0
- package/templates/modules/drizzle/tanstack/src/lib/db.ts +13 -0
- package/templates/modules/drizzle/tanstack/src/lib/schema.ts.hbs +84 -0
- package/templates/modules/mongoose/config.json +16 -16
- package/templates/modules/mongoose/src/lib/db.ts.hbs +0 -13
- package/templates/modules/mongoose/src/models/User.ts +48 -47
- package/templates/modules/prisma/express/config.json +27 -21
- package/templates/modules/prisma/express/prisma/schema.prisma +3 -2
- package/templates/modules/prisma/express/prisma.config.ts +12 -13
- package/templates/modules/prisma/express/src/lib/prisma.ts +24 -37
- package/templates/modules/prisma/nextjs/config.json +28 -23
- package/templates/modules/prisma/nextjs/prisma/schema.prisma.hbs +91 -90
- package/templates/modules/prisma/tanstack/config.json +28 -0
- package/templates/modules/prisma/tanstack/prisma/schema.prisma.hbs +91 -0
- package/templates/modules/prisma/tanstack/prisma.config.ts +12 -0
- package/templates/modules/prisma/tanstack/src/lib/prisma.ts +18 -0
- package/dist/prompts/backend/index.d.ts +0 -3
- package/dist/prompts/backend/index.d.ts.map +0 -1
- package/dist/prompts/backend/index.js +0 -128
- package/dist/prompts/backend/index.js.map +0 -1
- package/dist/prompts/frontend/index.d.ts +0 -3
- package/dist/prompts/frontend/index.d.ts.map +0 -1
- package/dist/prompts/frontend/index.js +0 -111
- package/dist/prompts/frontend/index.js.map +0 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Link, useNavigate } from "@tanstack/react-router";
|
|
4
|
+
import { useState, useTransition } from "react";
|
|
5
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
6
|
+
import { LoaderCircle } from "lucide-react";
|
|
7
|
+
import { useForm } from "react-hook-form";
|
|
8
|
+
|
|
9
|
+
import { authClient } from "~/lib/auth-client";
|
|
10
|
+
import {
|
|
11
|
+
getAuthErrorMessage,
|
|
12
|
+
signInSchema,
|
|
13
|
+
type SignInValues,
|
|
14
|
+
} from "~/lib/auth-schema";
|
|
15
|
+
import { Button } from "~/components/ui/button";
|
|
16
|
+
import {
|
|
17
|
+
Card,
|
|
18
|
+
CardContent,
|
|
19
|
+
CardDescription,
|
|
20
|
+
CardFooter,
|
|
21
|
+
CardHeader,
|
|
22
|
+
CardTitle,
|
|
23
|
+
} from "~/components/ui/card";
|
|
24
|
+
import {
|
|
25
|
+
Field,
|
|
26
|
+
FieldDescription,
|
|
27
|
+
FieldError,
|
|
28
|
+
FieldGroup,
|
|
29
|
+
FieldLabel,
|
|
30
|
+
} from "~/components/ui/field";
|
|
31
|
+
import { Input } from "~/components/ui/input";
|
|
32
|
+
|
|
33
|
+
export function SignInForm() {
|
|
34
|
+
const navigate = useNavigate();
|
|
35
|
+
const [isPending, startTransition] = useTransition();
|
|
36
|
+
const [authError, setAuthError] = useState<string | null>(null);
|
|
37
|
+
const form = useForm<SignInValues>({
|
|
38
|
+
resolver: zodResolver(signInSchema),
|
|
39
|
+
defaultValues: {
|
|
40
|
+
email: "",
|
|
41
|
+
password: "",
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const onSubmit = form.handleSubmit((values) => {
|
|
46
|
+
setAuthError(null);
|
|
47
|
+
|
|
48
|
+
startTransition(async () => {
|
|
49
|
+
const result = await authClient.signIn.email({
|
|
50
|
+
email: values.email,
|
|
51
|
+
password: values.password,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (result.error) {
|
|
55
|
+
setAuthError(getAuthErrorMessage(result.error));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
navigate({ to: "/dashboard" });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Card className="border-white/80 bg-white/90 shadow-2xl shadow-slate-200/70 backdrop-blur">
|
|
65
|
+
<CardHeader className="space-y-2">
|
|
66
|
+
<CardTitle>Login to your account</CardTitle>
|
|
67
|
+
<CardDescription>
|
|
68
|
+
Sign in with your email and password to continue to your dashboard.
|
|
69
|
+
</CardDescription>
|
|
70
|
+
</CardHeader>
|
|
71
|
+
<CardContent>
|
|
72
|
+
<form className="space-y-6" onSubmit={onSubmit} noValidate>
|
|
73
|
+
<FieldGroup>
|
|
74
|
+
<Field data-invalid={Boolean(form.formState.errors.email)}>
|
|
75
|
+
<FieldLabel htmlFor="email">Email</FieldLabel>
|
|
76
|
+
<Input
|
|
77
|
+
id="email"
|
|
78
|
+
type="email"
|
|
79
|
+
autoComplete="email"
|
|
80
|
+
aria-invalid={Boolean(form.formState.errors.email)}
|
|
81
|
+
placeholder="you@example.com"
|
|
82
|
+
{...form.register("email")}
|
|
83
|
+
/>
|
|
84
|
+
<FieldDescription>
|
|
85
|
+
Use the email you registered with.
|
|
86
|
+
</FieldDescription>
|
|
87
|
+
<FieldError errors={[form.formState.errors.email]} />
|
|
88
|
+
</Field>
|
|
89
|
+
|
|
90
|
+
<Field data-invalid={Boolean(form.formState.errors.password)}>
|
|
91
|
+
<FieldLabel htmlFor="password">Password</FieldLabel>
|
|
92
|
+
<Input
|
|
93
|
+
id="password"
|
|
94
|
+
type="password"
|
|
95
|
+
autoComplete="current-password"
|
|
96
|
+
aria-invalid={Boolean(form.formState.errors.password)}
|
|
97
|
+
placeholder="Enter your password"
|
|
98
|
+
{...form.register("password")}
|
|
99
|
+
/>
|
|
100
|
+
<FieldError errors={[form.formState.errors.password]} />
|
|
101
|
+
</Field>
|
|
102
|
+
</FieldGroup>
|
|
103
|
+
|
|
104
|
+
{authError ? (
|
|
105
|
+
<div className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700">
|
|
106
|
+
{authError}
|
|
107
|
+
</div>
|
|
108
|
+
) : null}
|
|
109
|
+
|
|
110
|
+
<Button className="w-full" type="submit" disabled={isPending}>
|
|
111
|
+
{isPending ? (
|
|
112
|
+
<>
|
|
113
|
+
<LoaderCircle className="mr-2 size-4 animate-spin" />
|
|
114
|
+
Signing in
|
|
115
|
+
</>
|
|
116
|
+
) : (
|
|
117
|
+
"Login"
|
|
118
|
+
)}
|
|
119
|
+
</Button>
|
|
120
|
+
</form>
|
|
121
|
+
</CardContent>
|
|
122
|
+
<CardFooter className="justify-center">
|
|
123
|
+
<p className="text-sm text-slate-600">
|
|
124
|
+
Need an account?{" "}
|
|
125
|
+
<Link
|
|
126
|
+
className="font-medium text-sky-700 hover:text-sky-800"
|
|
127
|
+
to="/sign-up"
|
|
128
|
+
>
|
|
129
|
+
Signup
|
|
130
|
+
</Link>
|
|
131
|
+
</p>
|
|
132
|
+
</CardFooter>
|
|
133
|
+
</Card>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
4
|
+
import { useState, useTransition } from "react";
|
|
5
|
+
import { LoaderCircle, LogOut } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
import { authClient } from "~/lib/auth-client";
|
|
8
|
+
import { Button } from "~/components/ui/button";
|
|
9
|
+
|
|
10
|
+
export function SignOutButton() {
|
|
11
|
+
const navigate = useNavigate();
|
|
12
|
+
const [isPending, startTransition] = useTransition();
|
|
13
|
+
const [error, setError] = useState<string | null>(null);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className="space-y-2">
|
|
17
|
+
<Button
|
|
18
|
+
variant="outline"
|
|
19
|
+
onClick={() => {
|
|
20
|
+
setError(null);
|
|
21
|
+
startTransition(async () => {
|
|
22
|
+
const result = await authClient.signOut();
|
|
23
|
+
|
|
24
|
+
if (result.error) {
|
|
25
|
+
setError("Unable to sign out right now. Please try again.");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
navigate({ to: "/sign-in" });
|
|
30
|
+
});
|
|
31
|
+
}}
|
|
32
|
+
disabled={isPending}
|
|
33
|
+
>
|
|
34
|
+
{isPending ? (
|
|
35
|
+
<>
|
|
36
|
+
<LoaderCircle className="mr-2 size-4 animate-spin" />
|
|
37
|
+
Signing out
|
|
38
|
+
</>
|
|
39
|
+
) : (
|
|
40
|
+
<>
|
|
41
|
+
<LogOut className="mr-2 size-4" />
|
|
42
|
+
Sign out
|
|
43
|
+
</>
|
|
44
|
+
)}
|
|
45
|
+
</Button>
|
|
46
|
+
{error ? <p className="text-sm text-rose-600">{error}</p> : null}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Link, useNavigate } from "@tanstack/react-router";
|
|
4
|
+
import { useState, useTransition } from "react";
|
|
5
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
6
|
+
import { LoaderCircle } from "lucide-react";
|
|
7
|
+
import { useForm } from "react-hook-form";
|
|
8
|
+
|
|
9
|
+
import { authClient } from "~/lib/auth-client";
|
|
10
|
+
import {
|
|
11
|
+
getAuthErrorMessage,
|
|
12
|
+
signUpSchema,
|
|
13
|
+
type SignUpValues,
|
|
14
|
+
} from "~/lib/auth-schema";
|
|
15
|
+
import { Button } from "~/components/ui/button";
|
|
16
|
+
import {
|
|
17
|
+
Card,
|
|
18
|
+
CardContent,
|
|
19
|
+
CardDescription,
|
|
20
|
+
CardFooter,
|
|
21
|
+
CardHeader,
|
|
22
|
+
CardTitle,
|
|
23
|
+
} from "~/components/ui/card";
|
|
24
|
+
import {
|
|
25
|
+
Field,
|
|
26
|
+
FieldDescription,
|
|
27
|
+
FieldError,
|
|
28
|
+
FieldGroup,
|
|
29
|
+
FieldLabel,
|
|
30
|
+
} from "~/components/ui/field";
|
|
31
|
+
import { Input } from "~/components/ui/input";
|
|
32
|
+
|
|
33
|
+
export function SignUpForm() {
|
|
34
|
+
const navigate = useNavigate();
|
|
35
|
+
const [isPending, startTransition] = useTransition();
|
|
36
|
+
const [authError, setAuthError] = useState<string | null>(null);
|
|
37
|
+
const form = useForm<SignUpValues>({
|
|
38
|
+
resolver: zodResolver(signUpSchema),
|
|
39
|
+
defaultValues: {
|
|
40
|
+
name: "",
|
|
41
|
+
email: "",
|
|
42
|
+
password: "",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const onSubmit = form.handleSubmit((values) => {
|
|
47
|
+
setAuthError(null);
|
|
48
|
+
|
|
49
|
+
startTransition(async () => {
|
|
50
|
+
const result = await authClient.signUp.email({
|
|
51
|
+
name: values.name,
|
|
52
|
+
email: values.email,
|
|
53
|
+
password: values.password,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (result.error) {
|
|
57
|
+
setAuthError(getAuthErrorMessage(result.error));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
navigate({ to: "/dashboard" });
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Card className="border-white/80 bg-white/90 shadow-2xl shadow-slate-200/70 backdrop-blur">
|
|
67
|
+
<CardHeader className="space-y-2">
|
|
68
|
+
<CardTitle>Create your account</CardTitle>
|
|
69
|
+
<CardDescription>
|
|
70
|
+
Start with a simple email and password sign-up flow powered by Better
|
|
71
|
+
Auth.
|
|
72
|
+
</CardDescription>
|
|
73
|
+
</CardHeader>
|
|
74
|
+
<CardContent>
|
|
75
|
+
<form className="space-y-6" onSubmit={onSubmit} noValidate>
|
|
76
|
+
<FieldGroup>
|
|
77
|
+
<Field data-invalid={Boolean(form.formState.errors.name)}>
|
|
78
|
+
<FieldLabel htmlFor="name">Full name</FieldLabel>
|
|
79
|
+
<Input
|
|
80
|
+
id="name"
|
|
81
|
+
autoComplete="name"
|
|
82
|
+
aria-invalid={Boolean(form.formState.errors.name)}
|
|
83
|
+
placeholder="Ada Lovelace"
|
|
84
|
+
{...form.register("name")}
|
|
85
|
+
/>
|
|
86
|
+
<FieldError errors={[form.formState.errors.name]} />
|
|
87
|
+
</Field>
|
|
88
|
+
|
|
89
|
+
<Field data-invalid={Boolean(form.formState.errors.email)}>
|
|
90
|
+
<FieldLabel htmlFor="email">Email</FieldLabel>
|
|
91
|
+
<Input
|
|
92
|
+
id="email"
|
|
93
|
+
type="email"
|
|
94
|
+
autoComplete="email"
|
|
95
|
+
aria-invalid={Boolean(form.formState.errors.email)}
|
|
96
|
+
placeholder="you@example.com"
|
|
97
|
+
{...form.register("email")}
|
|
98
|
+
/>
|
|
99
|
+
<FieldDescription>
|
|
100
|
+
This email will be used for future sign-ins.
|
|
101
|
+
</FieldDescription>
|
|
102
|
+
<FieldError errors={[form.formState.errors.email]} />
|
|
103
|
+
</Field>
|
|
104
|
+
|
|
105
|
+
<Field data-invalid={Boolean(form.formState.errors.password)}>
|
|
106
|
+
<FieldLabel htmlFor="password">Password</FieldLabel>
|
|
107
|
+
<Input
|
|
108
|
+
id="password"
|
|
109
|
+
type="password"
|
|
110
|
+
autoComplete="new-password"
|
|
111
|
+
aria-invalid={Boolean(form.formState.errors.password)}
|
|
112
|
+
placeholder="Create a strong password"
|
|
113
|
+
{...form.register("password")}
|
|
114
|
+
/>
|
|
115
|
+
<FieldDescription>
|
|
116
|
+
Use at least 8 characters for the generated starter flow.
|
|
117
|
+
</FieldDescription>
|
|
118
|
+
<FieldError errors={[form.formState.errors.password]} />
|
|
119
|
+
</Field>
|
|
120
|
+
</FieldGroup>
|
|
121
|
+
|
|
122
|
+
{authError ? (
|
|
123
|
+
<div className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700">
|
|
124
|
+
{authError}
|
|
125
|
+
</div>
|
|
126
|
+
) : null}
|
|
127
|
+
|
|
128
|
+
<Button className="w-full" type="submit" disabled={isPending}>
|
|
129
|
+
{isPending ? (
|
|
130
|
+
<>
|
|
131
|
+
<LoaderCircle className="mr-2 size-4 animate-spin" />
|
|
132
|
+
Creating account
|
|
133
|
+
</>
|
|
134
|
+
) : (
|
|
135
|
+
"Signup"
|
|
136
|
+
)}
|
|
137
|
+
</Button>
|
|
138
|
+
</form>
|
|
139
|
+
</CardContent>
|
|
140
|
+
<CardFooter className="justify-center">
|
|
141
|
+
<p className="text-sm text-slate-600">
|
|
142
|
+
Already have an account?{" "}
|
|
143
|
+
<Link
|
|
144
|
+
className="font-medium text-sky-700 hover:text-sky-800"
|
|
145
|
+
to="/sign-in"
|
|
146
|
+
>
|
|
147
|
+
Login
|
|
148
|
+
</Link>
|
|
149
|
+
</p>
|
|
150
|
+
</CardFooter>
|
|
151
|
+
</Card>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createServerFn } from "@tanstack/react-start";
|
|
2
|
+
import { getRequestHeaders } from "@tanstack/react-start/server";
|
|
3
|
+
|
|
4
|
+
import { auth } from "./auth";
|
|
5
|
+
|
|
6
|
+
export const getSession = createServerFn({ method: "GET" }).handler(async () => {
|
|
7
|
+
const session = await auth.api.getSession({
|
|
8
|
+
headers: getRequestHeaders(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
return session;
|
|
12
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const signInSchema = z.object({
|
|
4
|
+
email: z.string().email("Enter a valid email address."),
|
|
5
|
+
password: z.string().min(8, "Password must be at least 8 characters."),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const signUpSchema = signInSchema.extend({
|
|
9
|
+
name: z
|
|
10
|
+
.string()
|
|
11
|
+
.trim()
|
|
12
|
+
.min(2, "Name must be at least 2 characters.")
|
|
13
|
+
.max(50, "Name must be 50 characters or fewer."),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type SignInValues = z.infer<typeof signInSchema>;
|
|
17
|
+
export type SignUpValues = z.infer<typeof signUpSchema>;
|
|
18
|
+
|
|
19
|
+
export function getAuthErrorMessage(error: unknown) {
|
|
20
|
+
if (!error) {
|
|
21
|
+
return "Something went wrong. Please try again.";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (typeof error === "string") {
|
|
25
|
+
return error;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof error === "object") {
|
|
29
|
+
if ("message" in error && typeof error.message === "string") {
|
|
30
|
+
return error.message;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if ("statusText" in error && typeof error.statusText === "string") {
|
|
34
|
+
return error.statusText;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return "Something went wrong. Please try again.";
|
|
39
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { betterAuth } from "better-auth";
|
|
3
|
+
import { tanstackStartCookies } from "better-auth/tanstack-start";
|
|
4
|
+
{{#if (eq database 'prisma')}}
|
|
5
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
6
|
+
import client from "./prisma";
|
|
7
|
+
{{/if}}
|
|
8
|
+
{{#if (eq database 'drizzle')}}
|
|
9
|
+
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
10
|
+
import { db } from "./db";
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if (eq database 'mongoose')}}
|
|
13
|
+
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
14
|
+
import { MongoClient } from "mongodb";
|
|
15
|
+
|
|
16
|
+
const client = new MongoClient(process.env.MONGODB_URI!);
|
|
17
|
+
const db = client.db();
|
|
18
|
+
{{/if}}
|
|
19
|
+
|
|
20
|
+
export const auth = betterAuth({
|
|
21
|
+
{{#if (eq database 'prisma')}}
|
|
22
|
+
database: prismaAdapter(client, { provider: "postgresql" }),
|
|
23
|
+
{{/if}}
|
|
24
|
+
{{#if (eq database 'drizzle')}}
|
|
25
|
+
database: drizzleAdapter(db, { provider: "pg" }),
|
|
26
|
+
{{/if}}
|
|
27
|
+
{{#if (eq database 'mongoose')}}
|
|
28
|
+
// Transactions require a replica set. Create user/session/account/verification
|
|
29
|
+
// collections before the first sign-up; see the database setup documentation.
|
|
30
|
+
database: mongodbAdapter(db, { client }),
|
|
31
|
+
{{/if}}
|
|
32
|
+
emailAndPassword: {
|
|
33
|
+
enabled: true,
|
|
34
|
+
},
|
|
35
|
+
plugins: [tanstackStartCookies()],
|
|
36
|
+
secret: process.env.BETTER_AUTH_SECRET,
|
|
37
|
+
baseURL: process.env.BETTER_AUTH_URL,
|
|
38
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
|
|
5
|
+
// noinspection JSUnusedGlobalSymbols
|
|
6
|
+
|
|
7
|
+
// This file was automatically generated by TanStack Router.
|
|
8
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
9
|
+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
|
10
|
+
|
|
11
|
+
import { Route as rootRouteImport } from './routes/__root'
|
|
12
|
+
import { Route as IndexRouteImport } from './routes/index'
|
|
13
|
+
import { Route as DashboardRouteImport } from './routes/dashboard'
|
|
14
|
+
import { Route as SignInRouteImport } from './routes/sign-in'
|
|
15
|
+
import { Route as SignUpRouteImport } from './routes/sign-up'
|
|
16
|
+
import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$'
|
|
17
|
+
|
|
18
|
+
const IndexRoute = IndexRouteImport.update({
|
|
19
|
+
id: '/',
|
|
20
|
+
path: '/',
|
|
21
|
+
getParentRoute: () => rootRouteImport,
|
|
22
|
+
} as any)
|
|
23
|
+
const DashboardRoute = DashboardRouteImport.update({
|
|
24
|
+
id: '/dashboard',
|
|
25
|
+
path: '/dashboard',
|
|
26
|
+
getParentRoute: () => rootRouteImport,
|
|
27
|
+
} as any)
|
|
28
|
+
const SignInRoute = SignInRouteImport.update({
|
|
29
|
+
id: '/sign-in',
|
|
30
|
+
path: '/sign-in',
|
|
31
|
+
getParentRoute: () => rootRouteImport,
|
|
32
|
+
} as any)
|
|
33
|
+
const SignUpRoute = SignUpRouteImport.update({
|
|
34
|
+
id: '/sign-up',
|
|
35
|
+
path: '/sign-up',
|
|
36
|
+
getParentRoute: () => rootRouteImport,
|
|
37
|
+
} as any)
|
|
38
|
+
const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({
|
|
39
|
+
id: '/api/auth/$',
|
|
40
|
+
path: '/api/auth/$',
|
|
41
|
+
getParentRoute: () => rootRouteImport,
|
|
42
|
+
} as any)
|
|
43
|
+
|
|
44
|
+
export interface FileRoutesByFullPath {
|
|
45
|
+
'/': typeof IndexRoute
|
|
46
|
+
'/dashboard': typeof DashboardRoute
|
|
47
|
+
'/sign-in': typeof SignInRoute
|
|
48
|
+
'/sign-up': typeof SignUpRoute
|
|
49
|
+
'/api/auth/$': typeof ApiAuthSplatRoute
|
|
50
|
+
}
|
|
51
|
+
export interface FileRoutesByTo {
|
|
52
|
+
'/': typeof IndexRoute
|
|
53
|
+
'/dashboard': typeof DashboardRoute
|
|
54
|
+
'/sign-in': typeof SignInRoute
|
|
55
|
+
'/sign-up': typeof SignUpRoute
|
|
56
|
+
'/api/auth/$': typeof ApiAuthSplatRoute
|
|
57
|
+
}
|
|
58
|
+
export interface FileRoutesById {
|
|
59
|
+
__root__: typeof rootRouteImport
|
|
60
|
+
'/': typeof IndexRoute
|
|
61
|
+
'/dashboard': typeof DashboardRoute
|
|
62
|
+
'/sign-in': typeof SignInRoute
|
|
63
|
+
'/sign-up': typeof SignUpRoute
|
|
64
|
+
'/api/auth/$': typeof ApiAuthSplatRoute
|
|
65
|
+
}
|
|
66
|
+
export interface FileRouteTypes {
|
|
67
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
68
|
+
fullPaths: '/' | '/dashboard' | '/sign-in' | '/sign-up' | '/api/auth/$'
|
|
69
|
+
fileRoutesByTo: FileRoutesByTo
|
|
70
|
+
to: '/' | '/dashboard' | '/sign-in' | '/sign-up' | '/api/auth/$'
|
|
71
|
+
id: '__root__' | '/' | '/dashboard' | '/sign-in' | '/sign-up' | '/api/auth/$'
|
|
72
|
+
fileRoutesById: FileRoutesById
|
|
73
|
+
}
|
|
74
|
+
export interface RootRouteChildren {
|
|
75
|
+
IndexRoute: typeof IndexRoute
|
|
76
|
+
DashboardRoute: typeof DashboardRoute
|
|
77
|
+
SignInRoute: typeof SignInRoute
|
|
78
|
+
SignUpRoute: typeof SignUpRoute
|
|
79
|
+
ApiAuthSplatRoute: typeof ApiAuthSplatRoute
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
declare module '@tanstack/react-router' {
|
|
83
|
+
interface FileRoutesByPath {
|
|
84
|
+
'/': {
|
|
85
|
+
id: '/'
|
|
86
|
+
path: '/'
|
|
87
|
+
fullPath: '/'
|
|
88
|
+
preLoaderRoute: typeof IndexRouteImport
|
|
89
|
+
parentRoute: typeof rootRouteImport
|
|
90
|
+
}
|
|
91
|
+
'/dashboard': {
|
|
92
|
+
id: '/dashboard'
|
|
93
|
+
path: '/dashboard'
|
|
94
|
+
fullPath: '/dashboard'
|
|
95
|
+
preLoaderRoute: typeof DashboardRouteImport
|
|
96
|
+
parentRoute: typeof rootRouteImport
|
|
97
|
+
}
|
|
98
|
+
'/sign-in': {
|
|
99
|
+
id: '/sign-in'
|
|
100
|
+
path: '/sign-in'
|
|
101
|
+
fullPath: '/sign-in'
|
|
102
|
+
preLoaderRoute: typeof SignInRouteImport
|
|
103
|
+
parentRoute: typeof rootRouteImport
|
|
104
|
+
}
|
|
105
|
+
'/sign-up': {
|
|
106
|
+
id: '/sign-up'
|
|
107
|
+
path: '/sign-up'
|
|
108
|
+
fullPath: '/sign-up'
|
|
109
|
+
preLoaderRoute: typeof SignUpRouteImport
|
|
110
|
+
parentRoute: typeof rootRouteImport
|
|
111
|
+
}
|
|
112
|
+
'/api/auth/$': {
|
|
113
|
+
id: '/api/auth/$'
|
|
114
|
+
path: '/api/auth/$'
|
|
115
|
+
fullPath: '/api/auth/$'
|
|
116
|
+
preLoaderRoute: typeof ApiAuthSplatRouteImport
|
|
117
|
+
parentRoute: typeof rootRouteImport
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const rootRouteChildren: RootRouteChildren = {
|
|
123
|
+
IndexRoute: IndexRoute,
|
|
124
|
+
DashboardRoute: DashboardRoute,
|
|
125
|
+
SignInRoute: SignInRoute,
|
|
126
|
+
SignUpRoute: SignUpRoute,
|
|
127
|
+
ApiAuthSplatRoute: ApiAuthSplatRoute,
|
|
128
|
+
}
|
|
129
|
+
export const routeTree = rootRouteImport
|
|
130
|
+
._addFileChildren(rootRouteChildren)
|
|
131
|
+
._addFileTypes<FileRouteTypes>()
|
|
132
|
+
|
|
133
|
+
import type { getRouter } from './router.tsx'
|
|
134
|
+
import type { createStart } from '@tanstack/react-start'
|
|
135
|
+
declare module '@tanstack/react-start' {
|
|
136
|
+
interface Register {
|
|
137
|
+
ssr: true
|
|
138
|
+
router: Awaited<ReturnType<typeof getRouter>>
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
+
|
|
3
|
+
import { auth } from "~/lib/auth";
|
|
4
|
+
|
|
5
|
+
export const Route = createFileRoute("/api/auth/$")({
|
|
6
|
+
server: {
|
|
7
|
+
handlers: {
|
|
8
|
+
GET: async ({ request }: { request: Request }) => {
|
|
9
|
+
return await auth.handler(request);
|
|
10
|
+
},
|
|
11
|
+
POST: async ({ request }: { request: Request }) => {
|
|
12
|
+
return await auth.handler(request);
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|