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,70 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
import {
|
|
3
|
+
HeadContent,
|
|
4
|
+
Outlet,
|
|
5
|
+
Scripts,
|
|
6
|
+
createRootRouteWithContext,
|
|
7
|
+
} from "@tanstack/react-router";
|
|
8
|
+
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
|
9
|
+
import type { QueryClient } from "@tanstack/react-query";
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
|
|
12
|
+
import appCss from "~/styles/app.css?url";
|
|
13
|
+
|
|
14
|
+
export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
|
|
15
|
+
head: () => ({
|
|
16
|
+
meta: [
|
|
17
|
+
{
|
|
18
|
+
charSet: "utf-8",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "viewport",
|
|
22
|
+
content: "width=device-width, initial-scale=1",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
links: [
|
|
26
|
+
{
|
|
27
|
+
rel: "icon",
|
|
28
|
+
type: "image/svg+xml",
|
|
29
|
+
href: "/favicon.svg",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
}),
|
|
33
|
+
component: RootComponent,
|
|
34
|
+
notFoundComponent: () => <NotFound />,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
function RootComponent() {
|
|
38
|
+
return (
|
|
39
|
+
<RootDocument>
|
|
40
|
+
<Outlet />
|
|
41
|
+
</RootDocument>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function RootDocument({ children }: { children: React.ReactNode }) {
|
|
46
|
+
return (
|
|
47
|
+
<html lang="en">
|
|
48
|
+
<head>
|
|
49
|
+
<HeadContent />
|
|
50
|
+
<link rel="stylesheet" href={appCss} />
|
|
51
|
+
</head>
|
|
52
|
+
<body>
|
|
53
|
+
{children}
|
|
54
|
+
<TanStackRouterDevtools />
|
|
55
|
+
<Scripts />
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function NotFound() {
|
|
62
|
+
return (
|
|
63
|
+
<div className="flex min-h-[50vh] items-center justify-center">
|
|
64
|
+
<div className="text-center">
|
|
65
|
+
<h1 className="text-4xl font-bold">404</h1>
|
|
66
|
+
<p className="mt-2 text-muted-foreground">Page not found</p>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createFileRoute{{#if useAuth}}, Link{{/if}} } from "@tanstack/react-router";
|
|
2
|
+
import { ArrowRight } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import { buttonVariants } from "~/components/ui/button";
|
|
5
|
+
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
|
6
|
+
import { cn } from "~/lib/utils";
|
|
7
|
+
|
|
8
|
+
export const Route = createFileRoute("/")({
|
|
9
|
+
component: Home,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const featureList = [
|
|
13
|
+
"TanStack Start SSR",
|
|
14
|
+
"TanStack Router and Query",
|
|
15
|
+
"React 19 and TypeScript",
|
|
16
|
+
"Tailwind CSS v4",
|
|
17
|
+
{{#if useAuth}}"Better Auth-ready session flow",{{/if}}
|
|
18
|
+
{{#if (eq database "prisma")}}"Prisma database tooling",{{/if}}
|
|
19
|
+
{{#if (eq database "drizzle")}}"Drizzle database tooling",{{/if}}
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
function Home() {
|
|
23
|
+
return (
|
|
24
|
+
<main className="min-h-screen bg-background text-foreground">
|
|
25
|
+
<div className="mx-auto flex min-h-screen max-w-6xl flex-col justify-center gap-12 px-6 py-16 sm:px-10 lg:px-12">
|
|
26
|
+
<section className="grid gap-10 lg:grid-cols-[1.2fr_0.8fr] lg:items-center">
|
|
27
|
+
<div className="space-y-6">
|
|
28
|
+
<span className="inline-flex w-fit items-center rounded-full border border-border bg-card px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-muted-foreground shadow-sm">
|
|
29
|
+
Created with better-ts-stack
|
|
30
|
+
</span>
|
|
31
|
+
<div className="space-y-4">
|
|
32
|
+
<h1 className="max-w-3xl text-4xl font-semibold tracking-tight sm:text-5xl">
|
|
33
|
+
Ship your stack faster with a polished TypeScript foundation.
|
|
34
|
+
</h1>
|
|
35
|
+
<p className="max-w-2xl text-base leading-7 text-muted-foreground sm:text-lg">
|
|
36
|
+
Your project already includes the core app shell, database wiring,
|
|
37
|
+
and a shadcn-compatible UI foundation so you can keep building
|
|
38
|
+
instead of setting up boilerplate.
|
|
39
|
+
</p>
|
|
40
|
+
</div>
|
|
41
|
+
<div className="flex flex-col gap-3 sm:flex-row">
|
|
42
|
+
{{#if useAuth}}
|
|
43
|
+
<Link
|
|
44
|
+
to="/sign-up"
|
|
45
|
+
className={cn(buttonVariants({ size: "lg" }), "gap-2")}
|
|
46
|
+
>
|
|
47
|
+
Create an account
|
|
48
|
+
<ArrowRight className="size-4" />
|
|
49
|
+
</Link>
|
|
50
|
+
<Link
|
|
51
|
+
to="/sign-in"
|
|
52
|
+
className={buttonVariants({ variant: "outline", size: "lg" })}
|
|
53
|
+
>
|
|
54
|
+
Login
|
|
55
|
+
</Link>
|
|
56
|
+
{{else}}
|
|
57
|
+
<a
|
|
58
|
+
href="https://github.com/Abdullah-dev0/better-ts-stack"
|
|
59
|
+
target="_blank"
|
|
60
|
+
rel="noreferrer"
|
|
61
|
+
className={cn(buttonVariants({ size: "lg" }), "gap-2")}
|
|
62
|
+
>
|
|
63
|
+
Explore the docs
|
|
64
|
+
<ArrowRight className="size-4" />
|
|
65
|
+
</a>
|
|
66
|
+
<a
|
|
67
|
+
href="https://tanstack.com/start/latest"
|
|
68
|
+
target="_blank"
|
|
69
|
+
rel="noreferrer"
|
|
70
|
+
className={buttonVariants({ variant: "outline", size: "lg" })}
|
|
71
|
+
>
|
|
72
|
+
TanStack guide
|
|
73
|
+
</a>
|
|
74
|
+
{{/if}}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<Card className="border-border bg-card shadow-sm">
|
|
79
|
+
<CardHeader>
|
|
80
|
+
<CardTitle>Included in this starter</CardTitle>
|
|
81
|
+
</CardHeader>
|
|
82
|
+
<CardContent className="space-y-4">
|
|
83
|
+
<ul className="space-y-3 text-sm text-muted-foreground">
|
|
84
|
+
{featureList.map((feature) => (
|
|
85
|
+
<li key={feature} className="flex items-center gap-3">
|
|
86
|
+
<span className="size-2 rounded-full bg-primary" />
|
|
87
|
+
<span>{feature}</span>
|
|
88
|
+
</li>
|
|
89
|
+
))}
|
|
90
|
+
</ul>
|
|
91
|
+
<div className="rounded-2xl border border-border bg-muted px-4 py-3 text-sm text-muted-foreground">
|
|
92
|
+
Start editing{" "}
|
|
93
|
+
<code className="font-mono">src/routes/index.tsx</code> and
|
|
94
|
+
shape the project around your product instead of setup chores.
|
|
95
|
+
</div>
|
|
96
|
+
</CardContent>
|
|
97
|
+
</Card>
|
|
98
|
+
</section>
|
|
99
|
+
</div>
|
|
100
|
+
</main>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
@import "tailwindcss" source("../");
|
|
2
|
+
|
|
3
|
+
@theme inline {
|
|
4
|
+
--color-background: var(--background);
|
|
5
|
+
--color-foreground: var(--foreground);
|
|
6
|
+
--color-card: var(--card);
|
|
7
|
+
--color-card-foreground: var(--card-foreground);
|
|
8
|
+
--color-primary: var(--primary);
|
|
9
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
10
|
+
--color-secondary: var(--secondary);
|
|
11
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
12
|
+
--color-muted: var(--muted);
|
|
13
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
14
|
+
--color-accent: var(--accent);
|
|
15
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
16
|
+
--color-destructive: var(--destructive);
|
|
17
|
+
--color-border: var(--border);
|
|
18
|
+
--color-input: var(--input);
|
|
19
|
+
--color-ring: var(--ring);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:root {
|
|
23
|
+
--background: oklch(0.985 0 0);
|
|
24
|
+
--foreground: oklch(0.145 0 0);
|
|
25
|
+
--card: oklch(1 0 0);
|
|
26
|
+
--card-foreground: oklch(0.145 0 0);
|
|
27
|
+
--primary: oklch(0.205 0 0);
|
|
28
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
29
|
+
--secondary: oklch(0.97 0 0);
|
|
30
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
31
|
+
--muted: oklch(0.97 0 0);
|
|
32
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
33
|
+
--accent: oklch(0.97 0 0);
|
|
34
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
35
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
36
|
+
--border: oklch(0.922 0 0);
|
|
37
|
+
--input: oklch(0.922 0 0);
|
|
38
|
+
--ring: oklch(0.708 0 0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@media (prefers-color-scheme: dark) {
|
|
42
|
+
:root {
|
|
43
|
+
--background: oklch(0.145 0 0);
|
|
44
|
+
--foreground: oklch(0.985 0 0);
|
|
45
|
+
--card: oklch(0.205 0 0);
|
|
46
|
+
--card-foreground: oklch(0.985 0 0);
|
|
47
|
+
--primary: oklch(0.922 0 0);
|
|
48
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
49
|
+
--secondary: oklch(0.269 0 0);
|
|
50
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
51
|
+
--muted: oklch(0.269 0 0);
|
|
52
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
53
|
+
--accent: oklch(0.269 0 0);
|
|
54
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
55
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
56
|
+
--border: oklch(1 0 0 / 10%);
|
|
57
|
+
--input: oklch(1 0 0 / 15%);
|
|
58
|
+
--ring: oklch(0.556 0 0);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@layer base {
|
|
63
|
+
*,
|
|
64
|
+
::after,
|
|
65
|
+
::before,
|
|
66
|
+
::backdrop,
|
|
67
|
+
::file-selector-button {
|
|
68
|
+
border-color: var(--color-gray-200, currentcolor);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@layer base {
|
|
73
|
+
html {
|
|
74
|
+
color-scheme: light dark;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
* {
|
|
78
|
+
@apply border-gray-200 dark:border-gray-800;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
html,
|
|
82
|
+
body {
|
|
83
|
+
@apply bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"strict": true,
|
|
5
|
+
"esModuleInterop": true,
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"moduleResolution": "Bundler",
|
|
9
|
+
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"target": "ES2022",
|
|
14
|
+
"allowJs": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"forceConsistentCasingInFileNames": true,
|
|
17
|
+
"paths": {
|
|
18
|
+
"~/*": ["./src/*"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"exclude": ["node_modules"]
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
2
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
3
|
+
import viteReact from "@vitejs/plugin-react";
|
|
4
|
+
import { defineConfig } from "vite";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
server: {
|
|
8
|
+
port: 3000,
|
|
9
|
+
},
|
|
10
|
+
resolve: {
|
|
11
|
+
tsconfigPaths: true,
|
|
12
|
+
},
|
|
13
|
+
plugins: [tanstackStart({ srcDirectory: "src" }), tailwindcss(), viteReact()],
|
|
14
|
+
});
|
|
@@ -1 +1,26 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"id": "auth",
|
|
3
|
+
"name": "JWT Auth",
|
|
4
|
+
"description": "JWT authentication with bcrypt password hashing",
|
|
5
|
+
"type": "feature",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"bcrypt": "^6.0.0",
|
|
8
|
+
"jsonwebtoken": "^9.0.3"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/bcrypt": "^6.0.0",
|
|
12
|
+
"@types/jsonwebtoken": "^9.0.10"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {},
|
|
15
|
+
"envVars": {
|
|
16
|
+
"JWT_SECRET": "please-change-me",
|
|
17
|
+
"JWT_EXPIRES_IN": "1h"
|
|
18
|
+
},
|
|
19
|
+
"templateFiles": [
|
|
20
|
+
"src/lib/jwt.ts.hbs",
|
|
21
|
+
"src/middleware/requireAuth.ts.hbs",
|
|
22
|
+
"src/services/userStore.ts.hbs",
|
|
23
|
+
"src/routes/auth.ts.hbs",
|
|
24
|
+
"src/controllers/authController.ts.hbs"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -2,7 +2,7 @@ import { Request, Response, NextFunction, Router } from 'express';
|
|
|
2
2
|
import { login, me, register } from '../controllers/authController';
|
|
3
3
|
import { requireAuth } from '../middleware/requireAuth';
|
|
4
4
|
|
|
5
|
-
const router = Router();
|
|
5
|
+
const router: Router = Router();
|
|
6
6
|
|
|
7
7
|
function asyncHandler(
|
|
8
8
|
fn: (req: Request, res: Response, next: NextFunction) => Promise<void>
|
|
@@ -1,107 +1,95 @@
|
|
|
1
|
-
{{#if (eq database '
|
|
2
|
-
import
|
|
3
|
-
{
|
|
4
|
-
{
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
{{
|
|
9
|
-
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
{{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
{{/if}}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
password: data.password,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
users.push(newUser);
|
|
100
|
-
return newUser;
|
|
101
|
-
{{/if}}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function toPublicUser(user: AuthUser): Omit<AuthUser, 'password'> {
|
|
105
|
-
const { id, email, name } = user;
|
|
106
|
-
return { id, email, name };
|
|
107
|
-
}
|
|
1
|
+
{{#if (eq database 'drizzle')}}
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { db } from "../lib/db";
|
|
4
|
+
import { users } from "../lib/schema";
|
|
5
|
+
{{/if}}
|
|
6
|
+
{{#if (eq database 'none')}}
|
|
7
|
+
import crypto from 'crypto';
|
|
8
|
+
{{/if}}
|
|
9
|
+
{{#if (eq database 'prisma')}}
|
|
10
|
+
import { prisma } from '../lib/prisma';
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if (eq database 'mongoose')}}
|
|
13
|
+
import UserModel from '../models/User';
|
|
14
|
+
{{/if}}
|
|
15
|
+
|
|
16
|
+
export interface AuthUser {
|
|
17
|
+
id: string;
|
|
18
|
+
email: string;
|
|
19
|
+
name?: string | null;
|
|
20
|
+
password: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CreateUserInput {
|
|
24
|
+
email: string;
|
|
25
|
+
name?: string | null;
|
|
26
|
+
password: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
{{#if (eq database 'none')}}
|
|
30
|
+
const users: AuthUser[] = [];
|
|
31
|
+
{{/if}}
|
|
32
|
+
|
|
33
|
+
export {{#unless (eq database 'none')}}async {{/unless}}function findUserByEmail(email: string): Promise<AuthUser | null> {
|
|
34
|
+
{{#if (eq database 'drizzle')}}
|
|
35
|
+
const [user] = await db.select().from(users).where(eq(users.email, email)).limit(1);
|
|
36
|
+
return user ?? null;
|
|
37
|
+
{{/if}}
|
|
38
|
+
{{#if (eq database 'prisma')}}
|
|
39
|
+
return await prisma.user.findUnique({ where: { email } });
|
|
40
|
+
{{/if}}
|
|
41
|
+
|
|
42
|
+
{{#if (eq database 'mongoose')}}
|
|
43
|
+
const user = await UserModel.findOne({ email }).exec();
|
|
44
|
+
if (!user) return null;
|
|
45
|
+
return {
|
|
46
|
+
id: String(user._id),
|
|
47
|
+
email: user.email,
|
|
48
|
+
name: user.name,
|
|
49
|
+
password: user.password,
|
|
50
|
+
};
|
|
51
|
+
{{/if}}
|
|
52
|
+
|
|
53
|
+
{{#if (eq database 'none')}}
|
|
54
|
+
const user = users.find((candidate) => candidate.email === email);
|
|
55
|
+
return Promise.resolve(user ?? null);
|
|
56
|
+
{{/if}}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {{#unless (eq database 'none')}}async {{/unless}}function createUser(data: CreateUserInput): Promise<AuthUser> {
|
|
60
|
+
{{#if (eq database 'drizzle')}}
|
|
61
|
+
const [user] = await db.insert(users).values(data).returning();
|
|
62
|
+
return user;
|
|
63
|
+
{{/if}}
|
|
64
|
+
{{#if (eq database 'prisma')}}
|
|
65
|
+
return await prisma.user.create({ data });
|
|
66
|
+
{{/if}}
|
|
67
|
+
|
|
68
|
+
{{#if (eq database 'mongoose')}}
|
|
69
|
+
const user = await UserModel.create(data);
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
id: String(user._id),
|
|
73
|
+
email: user.email,
|
|
74
|
+
name: user.name,
|
|
75
|
+
password: user.password,
|
|
76
|
+
};
|
|
77
|
+
{{/if}}
|
|
78
|
+
|
|
79
|
+
{{#if (eq database 'none')}}
|
|
80
|
+
const newUser: AuthUser = {
|
|
81
|
+
id: crypto.randomUUID(),
|
|
82
|
+
email: data.email,
|
|
83
|
+
name: data.name,
|
|
84
|
+
password: data.password,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
users.push(newUser);
|
|
88
|
+
return Promise.resolve(newUser);
|
|
89
|
+
{{/if}}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function toPublicUser(user: AuthUser): Omit<AuthUser, 'password'> {
|
|
93
|
+
const { id, email, name } = user;
|
|
94
|
+
return { id, email, name };
|
|
95
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
2
|
"id": "auth",
|
|
3
3
|
"name": "Better Auth",
|
|
4
4
|
"description": "Better Auth for Next.js with optional Prisma or Drizzle adapter",
|
|
5
5
|
"type": "feature",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@hookform/resolvers": "^5.
|
|
8
|
-
"better-auth": "^1.
|
|
9
|
-
"mongodb": "^6.0
|
|
10
|
-
"react-hook-form": "^7.
|
|
11
|
-
"zod": "^4.
|
|
7
|
+
"@hookform/resolvers": "^5.9.1",
|
|
8
|
+
"better-auth": "^1.7.3",
|
|
9
|
+
"mongodb": "^7.6.0",
|
|
10
|
+
"react-hook-form": "^7.87.0",
|
|
11
|
+
"zod": "^4.5.4"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {},
|
|
14
14
|
"scripts": {},
|
|
@@ -1,35 +1,37 @@
|
|
|
1
|
-
import { betterAuth } from "better-auth";
|
|
2
|
-
import { nextCookies } from "better-auth/next-js";
|
|
3
|
-
{{#if (eq database 'prisma')}}
|
|
4
|
-
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
5
|
-
import client from "./prisma";
|
|
6
|
-
{{/if}}
|
|
7
|
-
{{#if (eq database 'drizzle')}}
|
|
8
|
-
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
9
|
-
import { db } from "./db";
|
|
10
|
-
{{/if}}
|
|
11
|
-
{{#if (eq database 'mongoose')}}
|
|
12
|
-
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
13
|
-
import { MongoClient } from "mongodb";
|
|
14
|
-
|
|
15
|
-
const client = new MongoClient(process.env.MONGODB_URI!);
|
|
16
|
-
const db = client.db();
|
|
17
|
-
{{/if}}
|
|
18
|
-
|
|
19
|
-
export const auth = betterAuth({
|
|
20
|
-
{{#if (eq database 'prisma')}}
|
|
21
|
-
database: prismaAdapter(client, { provider: "postgresql" }),
|
|
22
|
-
{{/if}}
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { nextCookies } from "better-auth/next-js";
|
|
3
|
+
{{#if (eq database 'prisma')}}
|
|
4
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
5
|
+
import client from "./prisma";
|
|
6
|
+
{{/if}}
|
|
7
|
+
{{#if (eq database 'drizzle')}}
|
|
8
|
+
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
9
|
+
import { db } from "./db";
|
|
10
|
+
{{/if}}
|
|
11
|
+
{{#if (eq database 'mongoose')}}
|
|
12
|
+
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
13
|
+
import { MongoClient } from "mongodb";
|
|
14
|
+
|
|
15
|
+
const client = new MongoClient(process.env.MONGODB_URI!);
|
|
16
|
+
const db = client.db();
|
|
17
|
+
{{/if}}
|
|
18
|
+
|
|
19
|
+
export const auth = betterAuth({
|
|
20
|
+
{{#if (eq database 'prisma')}}
|
|
21
|
+
database: prismaAdapter(client, { provider: "postgresql" }),
|
|
22
|
+
{{/if}}
|
|
23
23
|
{{#if (eq database 'drizzle')}}
|
|
24
24
|
database: drizzleAdapter(db, { provider: "pg" }),
|
|
25
25
|
{{/if}}
|
|
26
|
-
{{#if (eq database 'mongoose')}}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
{{#if (eq database 'mongoose')}}
|
|
27
|
+
// Transactions require a replica set. Create user/session/account/verification
|
|
28
|
+
// collections before the first sign-up; see the database setup documentation.
|
|
29
|
+
database: mongodbAdapter(db, { client }),
|
|
30
|
+
{{/if}}
|
|
31
|
+
emailAndPassword: {
|
|
32
|
+
enabled: true,
|
|
33
|
+
},
|
|
34
|
+
plugins: [nextCookies()],
|
|
35
|
+
secret: process.env.BETTER_AUTH_SECRET,
|
|
36
|
+
baseURL: process.env.BETTER_AUTH_URL,
|
|
37
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "auth",
|
|
3
|
+
"name": "Better Auth",
|
|
4
|
+
"description": "Better Auth for TanStack Start with optional Prisma, Drizzle, or MongoDB adapter",
|
|
5
|
+
"type": "feature",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@hookform/resolvers": "^5.9.1",
|
|
8
|
+
"better-auth": "^1.7.3",
|
|
9
|
+
"dotenv": "^17.4.2",
|
|
10
|
+
"mongodb": "^7.6.0",
|
|
11
|
+
"react-hook-form": "^7.87.0",
|
|
12
|
+
"zod": "^4.5.4"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {},
|
|
15
|
+
"scripts": {},
|
|
16
|
+
"envVars": {
|
|
17
|
+
"BETTER_AUTH_SECRET": "please-change-me-to-a-random-string",
|
|
18
|
+
"BETTER_AUTH_URL": "http://localhost:3000"
|
|
19
|
+
},
|
|
20
|
+
"templateFiles": [
|
|
21
|
+
"src/lib/auth.ts.hbs",
|
|
22
|
+
"src/lib/auth-client.ts",
|
|
23
|
+
"src/lib/auth-functions.ts",
|
|
24
|
+
"src/routes/api/auth/$.ts",
|
|
25
|
+
"src/routes/dashboard.tsx",
|
|
26
|
+
"src/routes/sign-in.tsx",
|
|
27
|
+
"src/routes/sign-up.tsx",
|
|
28
|
+
"src/components/auth/sign-in-form.tsx",
|
|
29
|
+
"src/components/auth/sign-up-form.tsx",
|
|
30
|
+
"src/components/auth/sign-out-button.tsx",
|
|
31
|
+
"src/lib/auth-schema.ts"
|
|
32
|
+
]
|
|
33
|
+
}
|