better-ts-stack 2.1.1 → 2.2.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/LICENSE +21 -21
- package/README.md +16 -8
- package/dist/args.js +7 -0
- package/dist/builder/buildPlanner.js +7 -1
- package/dist/builder/configGenerator.js +28 -3
- package/dist/builder/index.js +2 -1
- package/dist/builder/moduleSelector.js +6 -1
- package/dist/builder/readmeGenerator.js +8 -2
- package/dist/builder/templateContext.js +1 -0
- package/dist/output/nextSteps.js +2 -4
- package/dist/prompts/stack.js +6 -1
- package/dist/types/index.js +2 -0
- package/dist/ui/messages.js +2 -0
- package/package.json +71 -71
- package/templates/backend/express/.prettierignore +52 -52
- package/templates/backend/express/.prettierrc +16 -16
- package/templates/backend/express/gitignore +3 -1
- package/templates/backend/express/src/env.ts.hbs +13 -2
- package/templates/backend/express/src/index.ts.hbs +31 -2
- package/templates/backend/express/tsconfig.eslint.json +9 -9
- package/templates/backend/nestjs/.prettierignore +52 -52
- package/templates/backend/nestjs/gitignore +3 -1
- package/templates/backend/nestjs/src/app.module.ts.hbs +4 -1
- package/templates/backend/nestjs/src/env.ts.hbs +13 -2
- package/templates/backend/nestjs/src/health/health.controller.ts.hbs +7 -0
- package/templates/backend/nestjs/src/{main.ts → main.ts.hbs} +11 -0
- package/templates/backend/nestjs/tsconfig.eslint.json +9 -9
- package/templates/frontend/nextjs/app/globals.css +129 -89
- package/templates/frontend/nextjs/app/layout.tsx +36 -34
- package/templates/frontend/nextjs/app/page.tsx.hbs +63 -43
- package/templates/frontend/nextjs/components/ui/alert.tsx +75 -0
- package/templates/frontend/nextjs/components/ui/badge.tsx +51 -0
- package/templates/frontend/nextjs/components/ui/button.tsx +57 -51
- package/templates/frontend/nextjs/components/ui/card.tsx +102 -60
- package/templates/frontend/nextjs/components/ui/field.tsx +238 -67
- package/templates/frontend/nextjs/components/ui/input.tsx +19 -18
- package/templates/frontend/nextjs/components/ui/label.tsx +19 -0
- package/templates/frontend/nextjs/components/ui/separator.tsx +24 -0
- package/templates/frontend/nextjs/components/ui/skeleton.tsx +13 -0
- package/templates/frontend/nextjs/components/ui/spinner.tsx +10 -0
- package/templates/frontend/nextjs/components.json +11 -5
- package/templates/frontend/nextjs/config.json +36 -35
- package/templates/frontend/nextjs/eslint.config.mjs +11 -11
- package/templates/frontend/nextjs/lib/utils.ts +1 -6
- package/templates/frontend/nextjs/next.config.ts.hbs +12 -12
- package/templates/frontend/nextjs/postcss.config.mjs +7 -7
- package/templates/frontend/nextjs/public/file.svg +1 -1
- package/templates/frontend/nextjs/public/globe.svg +1 -1
- package/templates/frontend/nextjs/public/window.svg +1 -1
- package/templates/frontend/tanstack/components.json +25 -18
- package/templates/frontend/tanstack/config.json +5 -2
- package/templates/frontend/tanstack/eslint.config.mjs +23 -23
- package/templates/frontend/tanstack/gitignore +39 -39
- package/templates/frontend/tanstack/public/favicon.svg +7 -7
- package/templates/frontend/tanstack/src/components/ui/alert.tsx +75 -0
- package/templates/frontend/tanstack/src/components/ui/badge.tsx +51 -0
- package/templates/frontend/tanstack/src/components/ui/button.tsx +57 -51
- package/templates/frontend/tanstack/src/components/ui/card.tsx +102 -60
- package/templates/frontend/tanstack/src/components/ui/field.tsx +236 -67
- package/templates/frontend/tanstack/src/components/ui/input.tsx +19 -18
- package/templates/frontend/tanstack/src/components/ui/label.tsx +17 -0
- package/templates/frontend/tanstack/src/components/ui/separator.tsx +24 -0
- package/templates/frontend/tanstack/src/components/ui/skeleton.tsx +13 -0
- package/templates/frontend/tanstack/src/components/ui/spinner.tsx +10 -0
- package/templates/frontend/tanstack/src/lib/utils.ts +1 -6
- package/templates/frontend/tanstack/src/router.tsx +31 -31
- package/templates/frontend/tanstack/src/routes/__root.tsx +69 -69
- package/templates/frontend/tanstack/src/routes/index.tsx.hbs +64 -45
- package/templates/frontend/tanstack/src/styles/app.css +100 -55
- package/templates/frontend/tanstack/tsconfig.json +22 -22
- package/templates/modules/agents/AGENTS.md.hbs +245 -0
- package/templates/modules/agents/CLAUDE.md +1 -0
- package/templates/modules/agents/config.json +10 -0
- package/templates/modules/auth/express/config.json +12 -11
- package/templates/modules/auth/express/src/lib/auth.ts.hbs +143 -0
- package/templates/modules/auth/express/src/lib/email.ts +37 -0
- package/templates/modules/auth/express/src/middleware/requireAuth.ts +29 -0
- package/templates/modules/auth/express/src/routes/me.ts +13 -0
- package/templates/modules/auth/nestjs/config.json +13 -11
- package/templates/modules/auth/nestjs/src/auth/auth.module.ts.hbs +64 -14
- package/templates/modules/auth/nestjs/src/auth/auth.ts.hbs +152 -0
- package/templates/modules/auth/nestjs/src/auth/email.ts +37 -0
- package/templates/modules/auth/nestjs/src/me/me.controller.ts +15 -0
- package/templates/modules/auth/nextjs/app/api/auth/[...all]/route.ts +4 -4
- package/templates/modules/auth/nextjs/app/dashboard/page.tsx +101 -45
- package/templates/modules/auth/nextjs/app/forgot-password/page.tsx +11 -0
- package/templates/modules/auth/nextjs/app/reset-password/page.tsx +17 -0
- package/templates/modules/auth/nextjs/app/sign-in/page.tsx +23 -17
- package/templates/modules/auth/nextjs/app/sign-up/page.tsx +25 -19
- package/templates/modules/auth/nextjs/components/auth/change-password-form.tsx +125 -0
- package/templates/modules/auth/nextjs/components/auth/email-verification-card.tsx +91 -0
- package/templates/modules/auth/nextjs/components/auth/forgot-password-form.tsx +133 -0
- package/templates/modules/auth/nextjs/components/auth/reset-password-form.tsx +155 -0
- package/templates/modules/auth/nextjs/components/auth/sign-in-form.tsx +46 -32
- package/templates/modules/auth/nextjs/components/auth/sign-out-button.tsx +6 -5
- package/templates/modules/auth/nextjs/components/auth/sign-up-form.tsx +36 -30
- package/templates/modules/auth/nextjs/components/auth/social-buttons.tsx +73 -0
- package/templates/modules/auth/nextjs/config.json +24 -18
- package/templates/modules/auth/nextjs/lib/auth-client.ts +3 -3
- package/templates/modules/auth/nextjs/lib/auth-schema.ts +27 -2
- package/templates/modules/auth/nextjs/lib/auth.ts.hbs +65 -0
- package/templates/modules/auth/nextjs/lib/email.ts +43 -0
- package/templates/modules/auth/nextjs/lib/session.ts +12 -0
- package/templates/modules/auth/tanstack/config.json +25 -19
- package/templates/modules/auth/tanstack/src/components/auth/change-password-form.tsx +123 -0
- package/templates/modules/auth/tanstack/src/components/auth/email-verification-card.tsx +89 -0
- package/templates/modules/auth/tanstack/src/components/auth/forgot-password-form.tsx +131 -0
- package/templates/modules/auth/tanstack/src/components/auth/reset-password-form.tsx +149 -0
- package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +44 -35
- package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +7 -6
- package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +37 -34
- package/templates/modules/auth/tanstack/src/components/auth/social-buttons.tsx +71 -0
- package/templates/modules/auth/tanstack/src/lib/auth-client.ts +2 -2
- package/templates/modules/auth/tanstack/src/lib/auth-functions.ts +14 -8
- package/templates/modules/auth/tanstack/src/lib/auth-schema.ts +27 -2
- package/templates/modules/auth/tanstack/src/lib/auth.ts.hbs +98 -38
- package/templates/modules/auth/tanstack/src/lib/email.ts +43 -0
- package/templates/modules/auth/tanstack/src/routeTree.gen.ts +61 -3
- package/templates/modules/auth/tanstack/src/routes/api/auth/$.ts +15 -15
- package/templates/modules/auth/tanstack/src/routes/dashboard.tsx +119 -111
- package/templates/modules/auth/tanstack/src/routes/forgot-password.tsx +17 -0
- package/templates/modules/auth/tanstack/src/routes/reset-password.tsx +25 -0
- package/templates/modules/auth/tanstack/src/routes/sign-in.tsx +44 -37
- package/templates/modules/auth/tanstack/src/routes/sign-up.tsx +44 -37
- package/templates/modules/docker/.dockerignore +2 -0
- package/templates/modules/docker/config.json +15 -15
- package/templates/modules/drizzle/express/config.json +26 -26
- package/templates/modules/drizzle/express/src/lib/schema.ts.hbs +90 -0
- package/templates/modules/drizzle/nestjs/config.json +26 -26
- package/templates/modules/drizzle/nestjs/src/database/schema.ts.hbs +90 -0
- package/templates/modules/drizzle/nextjs/config.json +26 -26
- package/templates/modules/drizzle/nextjs/drizzle.config.ts +13 -13
- package/templates/modules/drizzle/tanstack/config.json +26 -26
- package/templates/modules/drizzle/tanstack/drizzle.config.ts +12 -12
- package/templates/modules/drizzle/tanstack/src/lib/db.ts +13 -13
- package/templates/modules/mongoose/express/config.json +15 -15
- package/templates/modules/mongoose/express/src/lib/db.ts +43 -43
- package/templates/modules/mongoose/express/src/models/User.ts +48 -48
- package/templates/modules/mongoose/nestjs/config.json +16 -15
- package/templates/modules/mongoose/nextjs/config.json +15 -15
- package/templates/modules/mongoose/nextjs/models/User.ts +48 -48
- package/templates/modules/mongoose/tanstack/config.json +16 -16
- package/templates/modules/mongoose/tanstack/src/models/User.ts +48 -48
- package/templates/modules/prisma/express/config.json +27 -27
- package/templates/modules/prisma/express/prisma/schema.prisma.hbs +92 -0
- package/templates/modules/prisma/nestjs/config.json +27 -27
- package/templates/modules/prisma/nestjs/prisma/schema.prisma.hbs +91 -0
- package/templates/modules/prisma/nextjs/config.json +28 -28
- package/templates/modules/prisma/nextjs/lib/prisma.ts +18 -18
- package/templates/modules/prisma/nextjs/prisma.config.ts +14 -14
- package/templates/modules/prisma/tanstack/config.json +28 -28
- package/templates/modules/prisma/tanstack/prisma/schema.prisma.hbs +91 -91
- package/templates/modules/prisma/tanstack/prisma.config.ts +14 -14
- package/templates/modules/prisma/tanstack/src/lib/prisma.ts +17 -17
- package/templates/modules/rate-limit/express/src/middleware/rateLimit.ts +0 -8
- package/templates/modules/vscode/.vscode/extensions.json.hbs +29 -0
- package/templates/modules/vscode/.vscode/settings.json.hbs +22 -0
- package/templates/modules/vscode/config.json +10 -0
- package/templates/modules/auth/express/src/controllers/authController.ts.hbs +0 -116
- package/templates/modules/auth/express/src/lib/jwt.ts.hbs +0 -27
- package/templates/modules/auth/express/src/middleware/requireAuth.ts.hbs +0 -25
- package/templates/modules/auth/express/src/routes/auth.ts.hbs +0 -27
- package/templates/modules/auth/express/src/services/userStore.ts.hbs +0 -117
- package/templates/modules/auth/nestjs/src/auth/auth.controller.ts.hbs +0 -61
- package/templates/modules/auth/nestjs/src/auth/auth.guard.ts +0 -36
- package/templates/modules/auth/nestjs/src/auth/auth.schemas.ts +0 -35
- package/templates/modules/auth/nestjs/src/auth/auth.service.ts +0 -70
- package/templates/modules/auth/nestjs/src/auth/jwt.ts +0 -27
- package/templates/modules/auth/nestjs/src/auth/users.service.ts.hbs +0 -144
- package/templates/modules/auth/nestjs/src/auth/zod-validation.pipe.ts +0 -16
- package/templates/modules/drizzle/express/src/lib/schema.ts +0 -12
- package/templates/modules/drizzle/nestjs/src/database/schema.ts +0 -12
- package/templates/modules/prisma/express/prisma/schema.prisma +0 -24
- package/templates/modules/prisma/nestjs/prisma/schema.prisma +0 -23
|
@@ -22,8 +22,19 @@ const envSchema = z.object({
|
|
|
22
22
|
MONGODB_URI: z.url(),
|
|
23
23
|
{{/if}}
|
|
24
24
|
{{#if useAuth}}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
BETTER_AUTH_SECRET: z
|
|
26
|
+
.string()
|
|
27
|
+
.min(32, "BETTER_AUTH_SECRET must be at least 32 characters"),
|
|
28
|
+
// The public URL of this server; links in emails point here
|
|
29
|
+
BETTER_AUTH_URL: z.url(),
|
|
30
|
+
// Optional: GitHub and Google sign-in are turned on when both are set
|
|
31
|
+
GITHUB_CLIENT_ID: z.string().optional(),
|
|
32
|
+
GITHUB_CLIENT_SECRET: z.string().optional(),
|
|
33
|
+
GOOGLE_CLIENT_ID: z.string().optional(),
|
|
34
|
+
GOOGLE_CLIENT_SECRET: z.string().optional(),
|
|
35
|
+
// Optional: without it, emails are printed to the log
|
|
36
|
+
RESEND_API_KEY: z.string().optional(),
|
|
37
|
+
EMAIL_FROM: z.string().default("onboarding@resend.dev"),
|
|
27
38
|
{{/if}}
|
|
28
39
|
});
|
|
29
40
|
|
|
@@ -16,9 +16,17 @@ import { connectDB, disconnectDB } from "./lib/db";
|
|
|
16
16
|
{{#if useRateLimit}}
|
|
17
17
|
import { apiLimiter } from "./middleware/rateLimit";
|
|
18
18
|
{{/if}}
|
|
19
|
+
{{#if useAuth}}
|
|
20
|
+
import { toNodeHandler } from "better-auth/node";
|
|
21
|
+
{{#if (eq database 'mongoose')}}
|
|
22
|
+
import { auth, mongoClient } from "./lib/auth";
|
|
23
|
+
{{else}}
|
|
24
|
+
import { auth } from "./lib/auth";
|
|
25
|
+
{{/if}}
|
|
26
|
+
{{/if}}
|
|
19
27
|
import healthRouter from "./routes/health";
|
|
20
28
|
{{#if useAuth}}
|
|
21
|
-
import
|
|
29
|
+
import meRouter from "./routes/me";
|
|
22
30
|
{{/if}}
|
|
23
31
|
|
|
24
32
|
const app: Express = express();
|
|
@@ -26,18 +34,35 @@ const port = env.PORT;
|
|
|
26
34
|
|
|
27
35
|
// Middleware
|
|
28
36
|
app.use(helmet());
|
|
37
|
+
{{#if useAuth}}
|
|
38
|
+
// Browser clients read the bearer token from the set-auth-token header
|
|
39
|
+
app.use(cors({ origin: env.CORS_ORIGIN, exposedHeaders: ["set-auth-token"] }));
|
|
40
|
+
{{else}}
|
|
29
41
|
app.use(cors({ origin: env.CORS_ORIGIN }));
|
|
42
|
+
{{/if}}
|
|
30
43
|
{{#if useRateLimit}}
|
|
31
44
|
app.use(apiLimiter);
|
|
32
45
|
{{/if}}
|
|
33
46
|
app.use(morgan("dev"));
|
|
47
|
+
{{#if useAuth}}
|
|
48
|
+
// Better Auth reads the client IP for its rate limits from this header. It is
|
|
49
|
+
// set from req.ip on every request, so a client cannot pick its own value.
|
|
50
|
+
// Behind a reverse proxy, set app.set("trust proxy", 1) for the real client IP
|
|
51
|
+
app.use("/api/auth", (req: Request, _res: Response, next: NextFunction) => {
|
|
52
|
+
req.headers["x-client-ip"] = req.ip;
|
|
53
|
+
next();
|
|
54
|
+
});
|
|
55
|
+
// Sign-up, sign-in, sessions, email verification, and password reset. Better
|
|
56
|
+
// Auth reads the request body itself, so it is mounted before express.json()
|
|
57
|
+
app.all("/api/auth/*splat", toNodeHandler(auth));
|
|
58
|
+
{{/if}}
|
|
34
59
|
app.use(express.json());
|
|
35
60
|
app.use(express.urlencoded({ extended: true }));
|
|
36
61
|
|
|
37
62
|
// Routes
|
|
38
63
|
app.use("/health", healthRouter);
|
|
39
64
|
{{#if useAuth}}
|
|
40
|
-
app.use("/
|
|
65
|
+
app.use("/api/me", meRouter);
|
|
41
66
|
{{/if}}
|
|
42
67
|
|
|
43
68
|
// 404 handler
|
|
@@ -73,7 +98,11 @@ function closeDatabase(): Promise<void> {
|
|
|
73
98
|
{{else if (eq database 'drizzle')}}
|
|
74
99
|
return db.$client.end();
|
|
75
100
|
{{else if (eq database 'mongoose')}}
|
|
101
|
+
{{#if useAuth}}
|
|
102
|
+
return Promise.all([disconnectDB(), mongoClient.close()]).then(() => undefined);
|
|
103
|
+
{{else}}
|
|
76
104
|
return disconnectDB();
|
|
105
|
+
{{/if}}
|
|
77
106
|
{{else}}
|
|
78
107
|
return Promise.resolve();
|
|
79
108
|
{{/if}}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"noEmit": true,
|
|
5
|
-
"allowJs": true,
|
|
6
|
-
"rootDir": "."
|
|
7
|
-
},
|
|
8
|
-
"include": ["src/**/*", "eslint.config.mjs", "prisma.config.ts"]
|
|
9
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": true,
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"rootDir": "."
|
|
7
|
+
},
|
|
8
|
+
"include": ["src/**/*", "eslint.config.mjs", "prisma.config.ts"]
|
|
9
|
+
}
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
# Dependencies
|
|
2
|
-
node_modules
|
|
3
|
-
**/node_modules
|
|
4
|
-
|
|
5
|
-
# Build outputs
|
|
6
|
-
.next
|
|
7
|
-
out
|
|
8
|
-
dist
|
|
9
|
-
build
|
|
10
|
-
**/dist
|
|
11
|
-
**/.next
|
|
12
|
-
**/out
|
|
13
|
-
**/build
|
|
14
|
-
|
|
15
|
-
# Generated files
|
|
16
|
-
*.min.js
|
|
17
|
-
*.min.css
|
|
18
|
-
coverage
|
|
19
|
-
.nyc_output
|
|
20
|
-
|
|
21
|
-
# Lock files
|
|
22
|
-
package-lock.json
|
|
23
|
-
yarn.lock
|
|
24
|
-
pnpm-lock.yaml
|
|
25
|
-
bun.lock
|
|
26
|
-
|
|
27
|
-
# Logs
|
|
28
|
-
*.log
|
|
29
|
-
npm-debug.log*
|
|
30
|
-
yarn-debug.log*
|
|
31
|
-
yarn-error.log*
|
|
32
|
-
|
|
33
|
-
# Environment files
|
|
34
|
-
.env
|
|
35
|
-
.env.local
|
|
36
|
-
.env.*.local
|
|
37
|
-
|
|
38
|
-
# IDE
|
|
39
|
-
.vscode
|
|
40
|
-
.idea
|
|
41
|
-
*.swp
|
|
42
|
-
*.swo
|
|
43
|
-
*~
|
|
44
|
-
|
|
45
|
-
# OS
|
|
46
|
-
.DS_Store
|
|
47
|
-
Thumbs.db
|
|
48
|
-
|
|
49
|
-
# Templates (do not format generated/scaffold content)
|
|
50
|
-
templates
|
|
51
|
-
**/templates/**
|
|
52
|
-
*.hbs
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules
|
|
3
|
+
**/node_modules
|
|
4
|
+
|
|
5
|
+
# Build outputs
|
|
6
|
+
.next
|
|
7
|
+
out
|
|
8
|
+
dist
|
|
9
|
+
build
|
|
10
|
+
**/dist
|
|
11
|
+
**/.next
|
|
12
|
+
**/out
|
|
13
|
+
**/build
|
|
14
|
+
|
|
15
|
+
# Generated files
|
|
16
|
+
*.min.js
|
|
17
|
+
*.min.css
|
|
18
|
+
coverage
|
|
19
|
+
.nyc_output
|
|
20
|
+
|
|
21
|
+
# Lock files
|
|
22
|
+
package-lock.json
|
|
23
|
+
yarn.lock
|
|
24
|
+
pnpm-lock.yaml
|
|
25
|
+
bun.lock
|
|
26
|
+
|
|
27
|
+
# Logs
|
|
28
|
+
*.log
|
|
29
|
+
npm-debug.log*
|
|
30
|
+
yarn-debug.log*
|
|
31
|
+
yarn-error.log*
|
|
32
|
+
|
|
33
|
+
# Environment files
|
|
34
|
+
.env
|
|
35
|
+
.env.local
|
|
36
|
+
.env.*.local
|
|
37
|
+
|
|
38
|
+
# IDE
|
|
39
|
+
.vscode
|
|
40
|
+
.idea
|
|
41
|
+
*.swp
|
|
42
|
+
*.swo
|
|
43
|
+
*~
|
|
44
|
+
|
|
45
|
+
# OS
|
|
46
|
+
.DS_Store
|
|
47
|
+
Thumbs.db
|
|
48
|
+
|
|
49
|
+
# Templates (do not format generated/scaffold content)
|
|
50
|
+
templates
|
|
51
|
+
**/templates/**
|
|
52
|
+
*.hbs
|
|
@@ -16,6 +16,9 @@ import { AuthModule } from "./auth/auth.module.js";
|
|
|
16
16
|
import { DatabaseModule } from "./database/database.module.js";
|
|
17
17
|
{{/if}}
|
|
18
18
|
import { HealthController } from "./health/health.controller.js";
|
|
19
|
+
{{#if useAuth}}
|
|
20
|
+
import { MeController } from "./me/me.controller.js";
|
|
21
|
+
{{/if}}
|
|
19
22
|
{{#if (eq database 'prisma')}}
|
|
20
23
|
import { PrismaModule } from "./prisma/prisma.module.js";
|
|
21
24
|
{{/if}}
|
|
@@ -39,7 +42,7 @@ import { PrismaModule } from "./prisma/prisma.module.js";
|
|
|
39
42
|
ThrottlerModule.forRoot([{ ttl: 15 * 60 * 1000, limit: 100 }]),
|
|
40
43
|
{{/if}}
|
|
41
44
|
],
|
|
42
|
-
controllers: [HealthController],
|
|
45
|
+
controllers: [HealthController{{#if useAuth}}, MeController{{/if}}],
|
|
43
46
|
{{#if useRateLimit}}
|
|
44
47
|
providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }],
|
|
45
48
|
{{/if}}
|
|
@@ -22,8 +22,19 @@ const envSchema = z.object({
|
|
|
22
22
|
MONGODB_URI: z.url(),
|
|
23
23
|
{{/if}}
|
|
24
24
|
{{#if useAuth}}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
BETTER_AUTH_SECRET: z
|
|
26
|
+
.string()
|
|
27
|
+
.min(32, "BETTER_AUTH_SECRET must be at least 32 characters"),
|
|
28
|
+
// The public URL of this server; links in emails point here
|
|
29
|
+
BETTER_AUTH_URL: z.url(),
|
|
30
|
+
// Optional: GitHub and Google sign-in are turned on when both are set
|
|
31
|
+
GITHUB_CLIENT_ID: z.string().optional(),
|
|
32
|
+
GITHUB_CLIENT_SECRET: z.string().optional(),
|
|
33
|
+
GOOGLE_CLIENT_ID: z.string().optional(),
|
|
34
|
+
GOOGLE_CLIENT_SECRET: z.string().optional(),
|
|
35
|
+
// Optional: without it, emails are printed to the log
|
|
36
|
+
RESEND_API_KEY: z.string().optional(),
|
|
37
|
+
EMAIL_FROM: z.string().default("onboarding@resend.dev"),
|
|
27
38
|
{{/if}}
|
|
28
39
|
});
|
|
29
40
|
|
|
@@ -2,12 +2,19 @@ import { Controller, Get } from "@nestjs/common";
|
|
|
2
2
|
{{#if useRateLimit}}
|
|
3
3
|
import { SkipThrottle } from "@nestjs/throttler";
|
|
4
4
|
{{/if}}
|
|
5
|
+
{{#if useAuth}}
|
|
6
|
+
import { AllowAnonymous } from "@thallesp/nestjs-better-auth";
|
|
7
|
+
{{/if}}
|
|
5
8
|
|
|
6
9
|
{{#if useRateLimit}}
|
|
7
10
|
// Docker and load balancers call the health check on a schedule, so it is not
|
|
8
11
|
// counted
|
|
9
12
|
@SkipThrottle()
|
|
10
13
|
{{/if}}
|
|
14
|
+
{{#if useAuth}}
|
|
15
|
+
// Every route needs a session unless it is marked public
|
|
16
|
+
@AllowAnonymous()
|
|
17
|
+
{{/if}}
|
|
11
18
|
@Controller("health")
|
|
12
19
|
export class HealthController {
|
|
13
20
|
@Get()
|
|
@@ -7,11 +7,22 @@ import { AppModule } from "./app.module.js";
|
|
|
7
7
|
import { env } from "./env.js";
|
|
8
8
|
|
|
9
9
|
async function bootstrap(): Promise<void> {
|
|
10
|
+
{{#if useAuth}}
|
|
11
|
+
// Better Auth reads the body of its own routes; the auth module adds the
|
|
12
|
+
// JSON and form parsers back for every other route
|
|
13
|
+
const app = await NestFactory.create(AppModule, { bodyParser: false });
|
|
14
|
+
{{else}}
|
|
10
15
|
const app = await NestFactory.create(AppModule);
|
|
16
|
+
{{/if}}
|
|
11
17
|
const port = env.PORT;
|
|
12
18
|
|
|
13
19
|
app.use(helmet());
|
|
20
|
+
{{#if useAuth}}
|
|
21
|
+
// Browser clients read the bearer token from the set-auth-token header
|
|
22
|
+
app.enableCors({ origin: env.CORS_ORIGIN, exposedHeaders: ["set-auth-token"] });
|
|
23
|
+
{{else}}
|
|
14
24
|
app.enableCors({ origin: env.CORS_ORIGIN });
|
|
25
|
+
{{/if}}
|
|
15
26
|
// Lets modules close database connections when the process is stopped
|
|
16
27
|
app.enableShutdownHooks();
|
|
17
28
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"noEmit": true,
|
|
5
|
-
"allowJs": true,
|
|
6
|
-
"rootDir": "."
|
|
7
|
-
},
|
|
8
|
-
"include": ["src/**/*", "eslint.config.mjs", "prisma.config.ts"]
|
|
9
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": true,
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"rootDir": "."
|
|
7
|
+
},
|
|
8
|
+
"include": ["src/**/*", "eslint.config.mjs", "prisma.config.ts"]
|
|
9
|
+
}
|
|
@@ -1,89 +1,129 @@
|
|
|
1
|
-
@import "tailwindcss";
|
|
2
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
@import "shadcn/tailwind.css";
|
|
4
|
+
|
|
5
|
+
@custom-variant dark (&:is(.dark *));
|
|
6
|
+
|
|
7
|
+
@theme inline {
|
|
8
|
+
--font-heading: var(--font-sans);
|
|
9
|
+
--font-sans: var(--font-sans);
|
|
10
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
11
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
12
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
13
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
14
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
15
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
16
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
17
|
+
--color-sidebar: var(--sidebar);
|
|
18
|
+
--color-chart-5: var(--chart-5);
|
|
19
|
+
--color-chart-4: var(--chart-4);
|
|
20
|
+
--color-chart-3: var(--chart-3);
|
|
21
|
+
--color-chart-2: var(--chart-2);
|
|
22
|
+
--color-chart-1: var(--chart-1);
|
|
23
|
+
--color-ring: var(--ring);
|
|
24
|
+
--color-input: var(--input);
|
|
25
|
+
--color-border: var(--border);
|
|
26
|
+
--color-destructive: var(--destructive);
|
|
27
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
28
|
+
--color-accent: var(--accent);
|
|
29
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
30
|
+
--color-muted: var(--muted);
|
|
31
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
32
|
+
--color-secondary: var(--secondary);
|
|
33
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
34
|
+
--color-primary: var(--primary);
|
|
35
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
36
|
+
--color-popover: var(--popover);
|
|
37
|
+
--color-card-foreground: var(--card-foreground);
|
|
38
|
+
--color-card: var(--card);
|
|
39
|
+
--color-foreground: var(--foreground);
|
|
40
|
+
--color-background: var(--background);
|
|
41
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
42
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
43
|
+
--radius-lg: var(--radius);
|
|
44
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
45
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
46
|
+
--radius-3xl: calc(var(--radius) * 2.2);
|
|
47
|
+
--radius-4xl: calc(var(--radius) * 2.6);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
:root {
|
|
51
|
+
--background: oklch(1 0 0);
|
|
52
|
+
--foreground: oklch(0.145 0 0);
|
|
53
|
+
--card: oklch(1 0 0);
|
|
54
|
+
--card-foreground: oklch(0.145 0 0);
|
|
55
|
+
--popover: oklch(1 0 0);
|
|
56
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
57
|
+
--primary: oklch(0.205 0 0);
|
|
58
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
59
|
+
--secondary: oklch(0.97 0 0);
|
|
60
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
61
|
+
--muted: oklch(0.97 0 0);
|
|
62
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
63
|
+
--accent: oklch(0.97 0 0);
|
|
64
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
65
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
66
|
+
--border: oklch(0.922 0 0);
|
|
67
|
+
--input: oklch(0.922 0 0);
|
|
68
|
+
--ring: oklch(0.708 0 0);
|
|
69
|
+
--chart-1: oklch(0.87 0 0);
|
|
70
|
+
--chart-2: oklch(0.556 0 0);
|
|
71
|
+
--chart-3: oklch(0.439 0 0);
|
|
72
|
+
--chart-4: oklch(0.371 0 0);
|
|
73
|
+
--chart-5: oklch(0.269 0 0);
|
|
74
|
+
--radius: 0.625rem;
|
|
75
|
+
--sidebar: oklch(0.985 0 0);
|
|
76
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
77
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
78
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
79
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
80
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
81
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
82
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.dark {
|
|
86
|
+
--background: oklch(0.145 0 0);
|
|
87
|
+
--foreground: oklch(0.985 0 0);
|
|
88
|
+
--card: oklch(0.205 0 0);
|
|
89
|
+
--card-foreground: oklch(0.985 0 0);
|
|
90
|
+
--popover: oklch(0.205 0 0);
|
|
91
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
92
|
+
--primary: oklch(0.922 0 0);
|
|
93
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
94
|
+
--secondary: oklch(0.269 0 0);
|
|
95
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
96
|
+
--muted: oklch(0.269 0 0);
|
|
97
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
98
|
+
--accent: oklch(0.269 0 0);
|
|
99
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
100
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
101
|
+
--border: oklch(1 0 0 / 10%);
|
|
102
|
+
--input: oklch(1 0 0 / 15%);
|
|
103
|
+
--ring: oklch(0.556 0 0);
|
|
104
|
+
--chart-1: oklch(0.87 0 0);
|
|
105
|
+
--chart-2: oklch(0.556 0 0);
|
|
106
|
+
--chart-3: oklch(0.439 0 0);
|
|
107
|
+
--chart-4: oklch(0.371 0 0);
|
|
108
|
+
--chart-5: oklch(0.269 0 0);
|
|
109
|
+
--sidebar: oklch(0.205 0 0);
|
|
110
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
111
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
112
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
113
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
114
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
115
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
116
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@layer base {
|
|
120
|
+
* {
|
|
121
|
+
@apply border-border outline-ring/50;
|
|
122
|
+
}
|
|
123
|
+
body {
|
|
124
|
+
@apply bg-background text-foreground;
|
|
125
|
+
}
|
|
126
|
+
html {
|
|
127
|
+
@apply font-sans;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -1,34 +1,36 @@
|
|
|
1
|
-
import type { Metadata } from "next";
|
|
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
|
-
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { Geist_Mono, Inter } from "next/font/google";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
import "./globals.css";
|
|
7
|
+
|
|
8
|
+
const fontSans = Inter({
|
|
9
|
+
variable: "--font-sans",
|
|
10
|
+
subsets: ["latin"],
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const fontMono = Geist_Mono({
|
|
14
|
+
variable: "--font-mono",
|
|
15
|
+
subsets: ["latin"],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const metadata: Metadata = {
|
|
19
|
+
title: "better-ts-stack starter",
|
|
20
|
+
description: "Generated with better-ts-stack",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default function RootLayout({
|
|
24
|
+
children,
|
|
25
|
+
}: Readonly<{
|
|
26
|
+
children: React.ReactNode;
|
|
27
|
+
}>) {
|
|
28
|
+
return (
|
|
29
|
+
<html
|
|
30
|
+
lang="en"
|
|
31
|
+
className={cn("font-sans antialiased", fontSans.variable, fontMono.variable)}
|
|
32
|
+
>
|
|
33
|
+
<body>{children}</body>
|
|
34
|
+
</html>
|
|
35
|
+
);
|
|
36
|
+
}
|