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.
Files changed (174) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +16 -8
  3. package/dist/args.js +7 -0
  4. package/dist/builder/buildPlanner.js +7 -1
  5. package/dist/builder/configGenerator.js +28 -3
  6. package/dist/builder/index.js +2 -1
  7. package/dist/builder/moduleSelector.js +6 -1
  8. package/dist/builder/readmeGenerator.js +8 -2
  9. package/dist/builder/templateContext.js +1 -0
  10. package/dist/output/nextSteps.js +2 -4
  11. package/dist/prompts/stack.js +6 -1
  12. package/dist/types/index.js +2 -0
  13. package/dist/ui/messages.js +2 -0
  14. package/package.json +71 -71
  15. package/templates/backend/express/.prettierignore +52 -52
  16. package/templates/backend/express/.prettierrc +16 -16
  17. package/templates/backend/express/gitignore +3 -1
  18. package/templates/backend/express/src/env.ts.hbs +13 -2
  19. package/templates/backend/express/src/index.ts.hbs +31 -2
  20. package/templates/backend/express/tsconfig.eslint.json +9 -9
  21. package/templates/backend/nestjs/.prettierignore +52 -52
  22. package/templates/backend/nestjs/gitignore +3 -1
  23. package/templates/backend/nestjs/src/app.module.ts.hbs +4 -1
  24. package/templates/backend/nestjs/src/env.ts.hbs +13 -2
  25. package/templates/backend/nestjs/src/health/health.controller.ts.hbs +7 -0
  26. package/templates/backend/nestjs/src/{main.ts → main.ts.hbs} +11 -0
  27. package/templates/backend/nestjs/tsconfig.eslint.json +9 -9
  28. package/templates/frontend/nextjs/app/globals.css +129 -89
  29. package/templates/frontend/nextjs/app/layout.tsx +36 -34
  30. package/templates/frontend/nextjs/app/page.tsx.hbs +63 -43
  31. package/templates/frontend/nextjs/components/ui/alert.tsx +75 -0
  32. package/templates/frontend/nextjs/components/ui/badge.tsx +51 -0
  33. package/templates/frontend/nextjs/components/ui/button.tsx +57 -51
  34. package/templates/frontend/nextjs/components/ui/card.tsx +102 -60
  35. package/templates/frontend/nextjs/components/ui/field.tsx +238 -67
  36. package/templates/frontend/nextjs/components/ui/input.tsx +19 -18
  37. package/templates/frontend/nextjs/components/ui/label.tsx +19 -0
  38. package/templates/frontend/nextjs/components/ui/separator.tsx +24 -0
  39. package/templates/frontend/nextjs/components/ui/skeleton.tsx +13 -0
  40. package/templates/frontend/nextjs/components/ui/spinner.tsx +10 -0
  41. package/templates/frontend/nextjs/components.json +11 -5
  42. package/templates/frontend/nextjs/config.json +36 -35
  43. package/templates/frontend/nextjs/eslint.config.mjs +11 -11
  44. package/templates/frontend/nextjs/lib/utils.ts +1 -6
  45. package/templates/frontend/nextjs/next.config.ts.hbs +12 -12
  46. package/templates/frontend/nextjs/postcss.config.mjs +7 -7
  47. package/templates/frontend/nextjs/public/file.svg +1 -1
  48. package/templates/frontend/nextjs/public/globe.svg +1 -1
  49. package/templates/frontend/nextjs/public/window.svg +1 -1
  50. package/templates/frontend/tanstack/components.json +25 -18
  51. package/templates/frontend/tanstack/config.json +5 -2
  52. package/templates/frontend/tanstack/eslint.config.mjs +23 -23
  53. package/templates/frontend/tanstack/gitignore +39 -39
  54. package/templates/frontend/tanstack/public/favicon.svg +7 -7
  55. package/templates/frontend/tanstack/src/components/ui/alert.tsx +75 -0
  56. package/templates/frontend/tanstack/src/components/ui/badge.tsx +51 -0
  57. package/templates/frontend/tanstack/src/components/ui/button.tsx +57 -51
  58. package/templates/frontend/tanstack/src/components/ui/card.tsx +102 -60
  59. package/templates/frontend/tanstack/src/components/ui/field.tsx +236 -67
  60. package/templates/frontend/tanstack/src/components/ui/input.tsx +19 -18
  61. package/templates/frontend/tanstack/src/components/ui/label.tsx +17 -0
  62. package/templates/frontend/tanstack/src/components/ui/separator.tsx +24 -0
  63. package/templates/frontend/tanstack/src/components/ui/skeleton.tsx +13 -0
  64. package/templates/frontend/tanstack/src/components/ui/spinner.tsx +10 -0
  65. package/templates/frontend/tanstack/src/lib/utils.ts +1 -6
  66. package/templates/frontend/tanstack/src/router.tsx +31 -31
  67. package/templates/frontend/tanstack/src/routes/__root.tsx +69 -69
  68. package/templates/frontend/tanstack/src/routes/index.tsx.hbs +64 -45
  69. package/templates/frontend/tanstack/src/styles/app.css +100 -55
  70. package/templates/frontend/tanstack/tsconfig.json +22 -22
  71. package/templates/modules/agents/AGENTS.md.hbs +245 -0
  72. package/templates/modules/agents/CLAUDE.md +1 -0
  73. package/templates/modules/agents/config.json +10 -0
  74. package/templates/modules/auth/express/config.json +12 -11
  75. package/templates/modules/auth/express/src/lib/auth.ts.hbs +143 -0
  76. package/templates/modules/auth/express/src/lib/email.ts +37 -0
  77. package/templates/modules/auth/express/src/middleware/requireAuth.ts +29 -0
  78. package/templates/modules/auth/express/src/routes/me.ts +13 -0
  79. package/templates/modules/auth/nestjs/config.json +13 -11
  80. package/templates/modules/auth/nestjs/src/auth/auth.module.ts.hbs +64 -14
  81. package/templates/modules/auth/nestjs/src/auth/auth.ts.hbs +152 -0
  82. package/templates/modules/auth/nestjs/src/auth/email.ts +37 -0
  83. package/templates/modules/auth/nestjs/src/me/me.controller.ts +15 -0
  84. package/templates/modules/auth/nextjs/app/api/auth/[...all]/route.ts +4 -4
  85. package/templates/modules/auth/nextjs/app/dashboard/page.tsx +101 -45
  86. package/templates/modules/auth/nextjs/app/forgot-password/page.tsx +11 -0
  87. package/templates/modules/auth/nextjs/app/reset-password/page.tsx +17 -0
  88. package/templates/modules/auth/nextjs/app/sign-in/page.tsx +23 -17
  89. package/templates/modules/auth/nextjs/app/sign-up/page.tsx +25 -19
  90. package/templates/modules/auth/nextjs/components/auth/change-password-form.tsx +125 -0
  91. package/templates/modules/auth/nextjs/components/auth/email-verification-card.tsx +91 -0
  92. package/templates/modules/auth/nextjs/components/auth/forgot-password-form.tsx +133 -0
  93. package/templates/modules/auth/nextjs/components/auth/reset-password-form.tsx +155 -0
  94. package/templates/modules/auth/nextjs/components/auth/sign-in-form.tsx +46 -32
  95. package/templates/modules/auth/nextjs/components/auth/sign-out-button.tsx +6 -5
  96. package/templates/modules/auth/nextjs/components/auth/sign-up-form.tsx +36 -30
  97. package/templates/modules/auth/nextjs/components/auth/social-buttons.tsx +73 -0
  98. package/templates/modules/auth/nextjs/config.json +24 -18
  99. package/templates/modules/auth/nextjs/lib/auth-client.ts +3 -3
  100. package/templates/modules/auth/nextjs/lib/auth-schema.ts +27 -2
  101. package/templates/modules/auth/nextjs/lib/auth.ts.hbs +65 -0
  102. package/templates/modules/auth/nextjs/lib/email.ts +43 -0
  103. package/templates/modules/auth/nextjs/lib/session.ts +12 -0
  104. package/templates/modules/auth/tanstack/config.json +25 -19
  105. package/templates/modules/auth/tanstack/src/components/auth/change-password-form.tsx +123 -0
  106. package/templates/modules/auth/tanstack/src/components/auth/email-verification-card.tsx +89 -0
  107. package/templates/modules/auth/tanstack/src/components/auth/forgot-password-form.tsx +131 -0
  108. package/templates/modules/auth/tanstack/src/components/auth/reset-password-form.tsx +149 -0
  109. package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +44 -35
  110. package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +7 -6
  111. package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +37 -34
  112. package/templates/modules/auth/tanstack/src/components/auth/social-buttons.tsx +71 -0
  113. package/templates/modules/auth/tanstack/src/lib/auth-client.ts +2 -2
  114. package/templates/modules/auth/tanstack/src/lib/auth-functions.ts +14 -8
  115. package/templates/modules/auth/tanstack/src/lib/auth-schema.ts +27 -2
  116. package/templates/modules/auth/tanstack/src/lib/auth.ts.hbs +98 -38
  117. package/templates/modules/auth/tanstack/src/lib/email.ts +43 -0
  118. package/templates/modules/auth/tanstack/src/routeTree.gen.ts +61 -3
  119. package/templates/modules/auth/tanstack/src/routes/api/auth/$.ts +15 -15
  120. package/templates/modules/auth/tanstack/src/routes/dashboard.tsx +119 -111
  121. package/templates/modules/auth/tanstack/src/routes/forgot-password.tsx +17 -0
  122. package/templates/modules/auth/tanstack/src/routes/reset-password.tsx +25 -0
  123. package/templates/modules/auth/tanstack/src/routes/sign-in.tsx +44 -37
  124. package/templates/modules/auth/tanstack/src/routes/sign-up.tsx +44 -37
  125. package/templates/modules/docker/.dockerignore +2 -0
  126. package/templates/modules/docker/config.json +15 -15
  127. package/templates/modules/drizzle/express/config.json +26 -26
  128. package/templates/modules/drizzle/express/src/lib/schema.ts.hbs +90 -0
  129. package/templates/modules/drizzle/nestjs/config.json +26 -26
  130. package/templates/modules/drizzle/nestjs/src/database/schema.ts.hbs +90 -0
  131. package/templates/modules/drizzle/nextjs/config.json +26 -26
  132. package/templates/modules/drizzle/nextjs/drizzle.config.ts +13 -13
  133. package/templates/modules/drizzle/tanstack/config.json +26 -26
  134. package/templates/modules/drizzle/tanstack/drizzle.config.ts +12 -12
  135. package/templates/modules/drizzle/tanstack/src/lib/db.ts +13 -13
  136. package/templates/modules/mongoose/express/config.json +15 -15
  137. package/templates/modules/mongoose/express/src/lib/db.ts +43 -43
  138. package/templates/modules/mongoose/express/src/models/User.ts +48 -48
  139. package/templates/modules/mongoose/nestjs/config.json +16 -15
  140. package/templates/modules/mongoose/nextjs/config.json +15 -15
  141. package/templates/modules/mongoose/nextjs/models/User.ts +48 -48
  142. package/templates/modules/mongoose/tanstack/config.json +16 -16
  143. package/templates/modules/mongoose/tanstack/src/models/User.ts +48 -48
  144. package/templates/modules/prisma/express/config.json +27 -27
  145. package/templates/modules/prisma/express/prisma/schema.prisma.hbs +92 -0
  146. package/templates/modules/prisma/nestjs/config.json +27 -27
  147. package/templates/modules/prisma/nestjs/prisma/schema.prisma.hbs +91 -0
  148. package/templates/modules/prisma/nextjs/config.json +28 -28
  149. package/templates/modules/prisma/nextjs/lib/prisma.ts +18 -18
  150. package/templates/modules/prisma/nextjs/prisma.config.ts +14 -14
  151. package/templates/modules/prisma/tanstack/config.json +28 -28
  152. package/templates/modules/prisma/tanstack/prisma/schema.prisma.hbs +91 -91
  153. package/templates/modules/prisma/tanstack/prisma.config.ts +14 -14
  154. package/templates/modules/prisma/tanstack/src/lib/prisma.ts +17 -17
  155. package/templates/modules/rate-limit/express/src/middleware/rateLimit.ts +0 -8
  156. package/templates/modules/vscode/.vscode/extensions.json.hbs +29 -0
  157. package/templates/modules/vscode/.vscode/settings.json.hbs +22 -0
  158. package/templates/modules/vscode/config.json +10 -0
  159. package/templates/modules/auth/express/src/controllers/authController.ts.hbs +0 -116
  160. package/templates/modules/auth/express/src/lib/jwt.ts.hbs +0 -27
  161. package/templates/modules/auth/express/src/middleware/requireAuth.ts.hbs +0 -25
  162. package/templates/modules/auth/express/src/routes/auth.ts.hbs +0 -27
  163. package/templates/modules/auth/express/src/services/userStore.ts.hbs +0 -117
  164. package/templates/modules/auth/nestjs/src/auth/auth.controller.ts.hbs +0 -61
  165. package/templates/modules/auth/nestjs/src/auth/auth.guard.ts +0 -36
  166. package/templates/modules/auth/nestjs/src/auth/auth.schemas.ts +0 -35
  167. package/templates/modules/auth/nestjs/src/auth/auth.service.ts +0 -70
  168. package/templates/modules/auth/nestjs/src/auth/jwt.ts +0 -27
  169. package/templates/modules/auth/nestjs/src/auth/users.service.ts.hbs +0 -144
  170. package/templates/modules/auth/nestjs/src/auth/zod-validation.pipe.ts +0 -16
  171. package/templates/modules/drizzle/express/src/lib/schema.ts +0 -12
  172. package/templates/modules/drizzle/nestjs/src/database/schema.ts +0 -12
  173. package/templates/modules/prisma/express/prisma/schema.prisma +0 -24
  174. 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
- JWT_SECRET: z.string().min(32, "JWT_SECRET must be at least 32 characters"),
26
- JWT_EXPIRES_IN: z.string().default("1h"),
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 authRouter from "./routes/auth";
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("/auth", authRouter);
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
@@ -18,7 +18,9 @@ yarn-debug.log*
18
18
  yarn-error.log*
19
19
 
20
20
  # IDE
21
- .vscode/
21
+ .vscode/*
22
+ !.vscode/settings.json
23
+ !.vscode/extensions.json
22
24
  .idea/
23
25
  *.swp
24
26
  *.swo
@@ -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
- JWT_SECRET: z.string().min(32, "JWT_SECRET must be at least 32 characters"),
26
- JWT_EXPIRES_IN: z.string().default("1h"),
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
- :root {
4
- --radius: 0.65rem;
5
- --background: oklch(1 0 0);
6
- --foreground: oklch(0.145 0 0);
7
- --card: oklch(1 0 0);
8
- --card-foreground: oklch(0.145 0 0);
9
- --popover: oklch(1 0 0);
10
- --popover-foreground: oklch(0.145 0 0);
11
- --primary: oklch(0.205 0 0);
12
- --primary-foreground: oklch(0.985 0 0);
13
- --secondary: oklch(0.97 0 0);
14
- --secondary-foreground: oklch(0.205 0 0);
15
- --muted: oklch(0.97 0 0);
16
- --muted-foreground: oklch(0.556 0 0);
17
- --accent: oklch(0.97 0 0);
18
- --accent-foreground: oklch(0.205 0 0);
19
- --destructive: oklch(0.577 0.245 27.325);
20
- --border: oklch(0.922 0 0);
21
- --input: oklch(0.922 0 0);
22
- --ring: oklch(0.708 0 0);
23
- --chart-1: oklch(0.646 0.222 41.116);
24
- --chart-2: oklch(0.6 0.118 184.704);
25
- --chart-3: oklch(0.398 0.07 227.392);
26
- --chart-4: oklch(0.828 0.189 84.429);
27
- --chart-5: oklch(0.769 0.188 70.08);
28
- --radius: 0.625rem;
29
- --sidebar: oklch(0.985 0 0);
30
- --sidebar-foreground: oklch(0.145 0 0);
31
- --sidebar-primary: oklch(0.205 0 0);
32
- --sidebar-primary-foreground: oklch(0.985 0 0);
33
- --sidebar-accent: oklch(0.97 0 0);
34
- --sidebar-accent-foreground: oklch(0.205 0 0);
35
- --sidebar-border: oklch(0.922 0 0);
36
- --sidebar-ring: oklch(0.708 0 0);
37
- }
38
-
39
- .dark {
40
- --background: oklch(0.145 0 0);
41
- --foreground: oklch(0.985 0 0);
42
- --card: oklch(0.205 0 0);
43
- --card-foreground: oklch(0.985 0 0);
44
- --popover: oklch(0.205 0 0);
45
- --popover-foreground: oklch(0.985 0 0);
46
- --primary: oklch(0.922 0 0);
47
- --primary-foreground: oklch(0.205 0 0);
48
- --secondary: oklch(0.269 0 0);
49
- --secondary-foreground: oklch(0.985 0 0);
50
- --muted: oklch(0.269 0 0);
51
- --muted-foreground: oklch(0.708 0 0);
52
- --accent: oklch(0.269 0 0);
53
- --accent-foreground: oklch(0.985 0 0);
54
- --destructive: oklch(0.704 0.191 22.216);
55
- --border: oklch(1 0 0 / 10%);
56
- --input: oklch(1 0 0 / 15%);
57
- --ring: oklch(0.556 0 0);
58
- --chart-1: oklch(0.488 0.243 264.376);
59
- --chart-2: oklch(0.696 0.17 162.48);
60
- --chart-3: oklch(0.769 0.188 70.08);
61
- --chart-4: oklch(0.627 0.265 303.9);
62
- --chart-5: oklch(0.645 0.246 16.439);
63
- --sidebar: oklch(0.205 0 0);
64
- --sidebar-foreground: oklch(0.985 0 0);
65
- --sidebar-primary: oklch(0.488 0.243 264.376);
66
- --sidebar-primary-foreground: oklch(0.985 0 0);
67
- --sidebar-accent: oklch(0.269 0 0);
68
- --sidebar-accent-foreground: oklch(0.985 0 0);
69
- --sidebar-border: oklch(1 0 0 / 10%);
70
- --sidebar-ring: oklch(0.556 0 0);
71
- }
72
- * {
73
- border-color: var(--border);
74
- }
75
-
76
- body {
77
- background: var(--background);
78
- color: var(--foreground);
79
- font-family: var(--font-geist-sans), sans-serif;
80
- }
81
-
82
- a {
83
- text-underline-offset: 0.2rem;
84
- }
85
-
86
- ::selection {
87
- background: color-mix(in oklab, var(--accent) 65%, white);
88
- color: var(--foreground);
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 { Geist, Geist_Mono } from "next/font/google";
3
- import "./globals.css";
4
-
5
- const geistSans = Geist({
6
- variable: "--font-geist-sans",
7
- subsets: ["latin"],
8
- });
9
-
10
- const geistMono = Geist_Mono({
11
- variable: "--font-geist-mono",
12
- subsets: ["latin"],
13
- });
14
-
15
- export const metadata: Metadata = {
16
- title: "better-ts-stack starter",
17
- description: "Generated with better-ts-stack",
18
- };
19
-
20
- export default function RootLayout({
21
- children,
22
- }: Readonly<{
23
- children: React.ReactNode;
24
- }>) {
25
- return (
26
- <html lang="en">
27
- <body
28
- className={`${geistSans.variable} ${geistMono.variable} bg-background text-foreground antialiased`}
29
- >
30
- {children}
31
- </body>
32
- </html>
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
+ }