stackkit-cli 0.4.3 → 0.4.5

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 (102) hide show
  1. package/README.md +26 -2
  2. package/bin/stackkit.js +10 -1
  3. package/dist/commands/add.js +128 -10
  4. package/dist/commands/doctor.d.ts +11 -0
  5. package/dist/commands/doctor.js +483 -0
  6. package/dist/commands/list.d.ts +1 -1
  7. package/dist/commands/list.js +59 -38
  8. package/dist/index.js +11 -13
  9. package/dist/types/index.d.ts +29 -2
  10. package/dist/utils/config-utils.d.ts +2 -0
  11. package/dist/utils/config-utils.js +88 -0
  12. package/dist/utils/detect.js +12 -2
  13. package/dist/utils/env-editor.d.ts +0 -1
  14. package/dist/utils/env-editor.js +50 -25
  15. package/dist/utils/files.d.ts +8 -0
  16. package/dist/utils/files.js +51 -0
  17. package/dist/utils/js-conversion.d.ts +1 -0
  18. package/dist/utils/js-conversion.js +244 -0
  19. package/dist/utils/module-utils.d.ts +2 -0
  20. package/dist/utils/module-utils.js +461 -0
  21. package/dist/utils/package-manager.js +15 -31
  22. package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +6 -0
  23. package/modules/auth/authjs/files/lib/auth-client.ts +11 -0
  24. package/modules/auth/authjs/files/lib/auth.ts +41 -0
  25. package/modules/auth/authjs/files/schemas/prisma-schema.prisma +45 -0
  26. package/modules/auth/authjs/module.json +95 -0
  27. package/modules/auth/better-auth/files/lib/auth-client.ts +7 -0
  28. package/modules/auth/better-auth/files/lib/auth.ts +75 -0
  29. package/modules/auth/better-auth/files/lib/email-service.ts +34 -0
  30. package/modules/auth/better-auth/files/lib/email-templates.ts +89 -0
  31. package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +63 -0
  32. package/modules/auth/better-auth/module.json +191 -0
  33. package/modules/database/mongoose/files/lib/db.ts +68 -0
  34. package/modules/database/mongoose/files/models/User.ts +34 -0
  35. package/modules/database/mongoose/module.json +55 -0
  36. package/modules/database/prisma/files/lib/prisma.ts +4 -0
  37. package/modules/database/prisma/files/prisma/schema.prisma +8 -0
  38. package/modules/database/prisma/files/prisma.config.ts +12 -0
  39. package/modules/database/prisma/module.json +122 -0
  40. package/package.json +1 -1
  41. package/templates/express/.env.example +1 -10
  42. package/templates/express/package.json +15 -21
  43. package/templates/express/src/app.ts +9 -29
  44. package/templates/express/src/config/env.ts +3 -14
  45. package/templates/express/src/features/health/health.controller.ts +18 -0
  46. package/templates/express/src/features/health/health.route.ts +9 -0
  47. package/templates/express/src/features/health/health.service.ts +6 -0
  48. package/templates/express/src/middlewares/error.middleware.ts +2 -2
  49. package/templates/express/src/server.ts +1 -1
  50. package/templates/express/template.json +1 -5
  51. package/templates/express/tsconfig.json +0 -1
  52. package/templates/nextjs/lib/env.ts +8 -0
  53. package/templates/nextjs/package.json +7 -7
  54. package/templates/react-vite/.env.example +1 -2
  55. package/templates/react-vite/.prettierignore +4 -0
  56. package/templates/react-vite/.prettierrc +9 -0
  57. package/templates/react-vite/README.md +22 -0
  58. package/templates/react-vite/package.json +16 -16
  59. package/templates/react-vite/src/router.tsx +0 -12
  60. package/templates/react-vite/vite.config.ts +0 -6
  61. package/dist/commands/init.d.ts +0 -10
  62. package/dist/commands/init.js +0 -157
  63. package/dist/utils/code-inject.d.ts +0 -14
  64. package/dist/utils/code-inject.js +0 -70
  65. package/dist/utils/json-editor.d.ts +0 -8
  66. package/dist/utils/json-editor.js +0 -45
  67. package/modules/auth/better-auth-express/adapters/mongoose-mongodb.ts +0 -13
  68. package/modules/auth/better-auth-express/adapters/prisma-mongodb.ts +0 -15
  69. package/modules/auth/better-auth-express/adapters/prisma-postgresql.ts +0 -15
  70. package/modules/auth/better-auth-express/files/lib/auth.ts +0 -16
  71. package/modules/auth/better-auth-express/files/routes/auth.ts +0 -12
  72. package/modules/auth/better-auth-express/files/schemas/prisma-mongodb-schema.prisma +0 -72
  73. package/modules/auth/better-auth-express/files/schemas/prisma-postgresql-schema.prisma +0 -72
  74. package/modules/auth/better-auth-express/module.json +0 -61
  75. package/modules/auth/better-auth-nextjs/adapters/mongoose-mongodb.ts +0 -24
  76. package/modules/auth/better-auth-nextjs/adapters/prisma-mongodb.ts +0 -26
  77. package/modules/auth/better-auth-nextjs/adapters/prisma-postgresql.ts +0 -26
  78. package/modules/auth/better-auth-nextjs/files/lib/auth.ts +0 -26
  79. package/modules/auth/better-auth-nextjs/files/schemas/prisma-mongodb-schema.prisma +0 -72
  80. package/modules/auth/better-auth-nextjs/files/schemas/prisma-postgresql-schema.prisma +0 -72
  81. package/modules/auth/better-auth-nextjs/module.json +0 -62
  82. package/modules/auth/better-auth-react/files/lib/auth-client.ts +0 -9
  83. package/modules/auth/better-auth-react/module.json +0 -28
  84. package/modules/auth/clerk-express/files/lib/auth.ts +0 -7
  85. package/modules/auth/clerk-express/module.json +0 -34
  86. package/modules/auth/clerk-nextjs/files/lib/auth-provider.tsx +0 -5
  87. package/modules/auth/clerk-nextjs/files/middleware.ts +0 -9
  88. package/modules/auth/clerk-nextjs/module.json +0 -64
  89. package/modules/auth/clerk-react/files/lib/auth-provider.tsx +0 -15
  90. package/modules/auth/clerk-react/module.json +0 -28
  91. package/modules/database/mongoose-mongodb/files/lib/db.ts +0 -40
  92. package/modules/database/mongoose-mongodb/module.json +0 -55
  93. package/modules/database/prisma-mongodb/files/lib/db.ts +0 -9
  94. package/modules/database/prisma-mongodb/files/prisma/schema.prisma +0 -17
  95. package/modules/database/prisma-mongodb/module.json +0 -60
  96. package/modules/database/prisma-postgresql/files/lib/db.ts +0 -9
  97. package/modules/database/prisma-postgresql/files/prisma/schema.prisma +0 -17
  98. package/modules/database/prisma-postgresql/module.json +0 -60
  99. package/templates/react-vite/src/api/services/user.service.ts +0 -18
  100. package/templates/react-vite/src/pages/UserProfile.tsx +0 -40
  101. package/templates/react-vite/src/types/user.d.ts +0 -6
  102. /package/modules/auth/{better-auth-nextjs → better-auth}/files/api/auth/[...all]/route.ts +0 -0
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "authjs",
3
+ "displayName": "Auth.js",
4
+ "description": "Authentication with Auth.js (NextAuth.js v5)",
5
+ "category": "auth",
6
+ "provider": "authjs",
7
+ "supportedFrameworks": ["nextjs"],
8
+ "databaseAdapters": {
9
+ "common": {
10
+ "dependencies": {
11
+ "@auth/prisma-adapter": "^2.7.1"
12
+ },
13
+ "devDependencies": {}
14
+ },
15
+ "prisma-postgresql": {
16
+ "schema": "files/schemas/prisma-schema.prisma",
17
+ "schemaDestination": "prisma/schema.prisma",
18
+ "dependencies": {}
19
+ },
20
+ "prisma-mongodb": {
21
+ "schema": "files/schemas/prisma-schema.prisma",
22
+ "schemaDestination": "prisma/schema.prisma",
23
+ "dependencies": {}
24
+ },
25
+ "prisma-mysql": {
26
+ "schema": "files/schemas/prisma-schema.prisma",
27
+ "schemaDestination": "prisma/schema.prisma",
28
+ "dependencies": {}
29
+ },
30
+ "prisma-sqlite": {
31
+ "schema": "files/schemas/prisma-schema.prisma",
32
+ "schemaDestination": "prisma/schema.prisma",
33
+ "dependencies": {}
34
+ }
35
+ },
36
+ "frameworkConfigs": {
37
+ "nextjs": {
38
+ "dependencies": {
39
+ "next-auth": "^5.0.0"
40
+ },
41
+ "envVars": [
42
+ {
43
+ "key": "NEXTAUTH_SECRET",
44
+ "value": "",
45
+ "required": true
46
+ },
47
+ {
48
+ "key": "NEXTAUTH_URL",
49
+ "value": "http://localhost:3000",
50
+ "required": true
51
+ },
52
+ {
53
+ "key": "GOOGLE_CLIENT_ID",
54
+ "value": "",
55
+ "required": false
56
+ },
57
+ {
58
+ "key": "GOOGLE_CLIENT_SECRET",
59
+ "value": "",
60
+ "required": false
61
+ },
62
+ {
63
+ "key": "GITHUB_ID",
64
+ "value": "",
65
+ "required": false
66
+ },
67
+ {
68
+ "key": "GITHUB_SECRET",
69
+ "value": "",
70
+ "required": false
71
+ }
72
+ ],
73
+ "patches": [
74
+ {
75
+ "type": "create-file",
76
+ "description": "{{authDescription}}",
77
+ "source": "lib/{{authFile}}",
78
+ "destination": "{{lib}}/{{authFile}}"
79
+ },
80
+ {
81
+ "type": "create-file",
82
+ "description": "Create Auth.js API routes",
83
+ "source": "api/auth/[...nextauth]/route.ts",
84
+ "destination": "app/api/auth/[...nextauth]/route.ts"
85
+ },
86
+ {
87
+ "type": "create-file",
88
+ "description": "Create Auth.js client utilities",
89
+ "source": "lib/auth-client.ts",
90
+ "destination": "{{lib}}/auth-client.ts"
91
+ }
92
+ ]
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,7 @@
1
+ import { createAuthClient } from "better-auth/react";
2
+
3
+ export const authClient = createAuthClient({
4
+ baseURL: "http://localhost:3000",
5
+ });
6
+
7
+ export const { signIn, signUp, signOut, useSession } = authClient;
@@ -0,0 +1,75 @@
1
+ import { betterAuth } from "better-auth";
2
+ import { sendEmail } from "./email/email-service";
3
+ import { getVerificationEmailTemplate, getPasswordResetEmailTemplate } from "./email/email-templates";
4
+ {{dbImport}}
5
+
6
+ export const auth = betterAuth({
7
+ {{databaseAdapter}}
8
+ user: {
9
+ additionalFields: {
10
+ role: {
11
+ type: "string",
12
+ defaultValue: "USER",
13
+ required: true,
14
+ },
15
+ },
16
+ },
17
+ emailAndPassword: {
18
+ enabled: true,
19
+ requireEmailVerification: true,
20
+ },
21
+ socialProviders: {
22
+ google: {
23
+ clientId: process.env.GOOGLE_CLIENT_ID!,
24
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
25
+ },
26
+ },
27
+ emailVerification: {
28
+ sendVerificationEmail: async ({ user, url }) => {
29
+ const { html, text } = getVerificationEmailTemplate(user, url);
30
+ await sendEmail({
31
+ to: user.email,
32
+ subject: "Verify Your Email Address",
33
+ text,
34
+ html,
35
+ });
36
+ },
37
+ sendOnSignIn: true,
38
+ },
39
+ password: {
40
+ reset: {
41
+ sendResetEmail: async ({ user, url }) => {
42
+ const { html, text } = getPasswordResetEmailTemplate(user, url);
43
+ await sendEmail({
44
+ to: user.email,
45
+ subject: "Reset Your Password",
46
+ text,
47
+ html,
48
+ });
49
+ },
50
+ },
51
+ },
52
+ // Additional production-ready options
53
+ rateLimit: {
54
+ window: 10, // 10 seconds
55
+ max: 100, // max requests per window
56
+ },
57
+ account: {
58
+ accountLinking: {
59
+ enabled: true,
60
+ trustedProviders: ["google"],
61
+ },
62
+ },
63
+ session: {
64
+ cookieCache: {
65
+ enabled: true,
66
+ },
67
+ expiresIn: 60 * 60 * 24 * 7, // 7 days
68
+ updateAge: 60 * 60 * 24, // 1 day
69
+ },
70
+ user: {
71
+ changeEmail: {
72
+ enabled: true,
73
+ },
74
+ },
75
+ });
@@ -0,0 +1,34 @@
1
+ import nodemailer from "nodemailer";
2
+
3
+ // Create email transporter
4
+ const transporter = nodemailer.createTransporter({
5
+ host: process.env.EMAIL_HOST,
6
+ port: parseInt(process.env.EMAIL_PORT || "587"),
7
+ secure: process.env.EMAIL_PORT === "465",
8
+ auth: {
9
+ user: process.env.EMAIL_USER,
10
+ pass: process.env.EMAIL_PASS,
11
+ },
12
+ });
13
+
14
+ // Send email function
15
+ export const sendEmail = async ({ to, subject, text, html }: {
16
+ to: string;
17
+ subject: string;
18
+ text?: string;
19
+ html?: string;
20
+ }) => {
21
+ try {
22
+ await transporter.sendMail({
23
+ from: process.env.EMAIL_FROM,
24
+ to,
25
+ subject,
26
+ text,
27
+ html,
28
+ });
29
+ } catch (error) {
30
+ // eslint-disable-next-line no-console
31
+ console.error("Email sending failed:", error);
32
+ throw error;
33
+ }
34
+ };
@@ -0,0 +1,89 @@
1
+ export const getVerificationEmailTemplate = (user: { name?: string; email: string }, url: string) => {
2
+ const html = `
3
+ <!DOCTYPE html>
4
+ <html lang="en">
5
+ <head>
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+ <title>Verify Your Email</title>
9
+ <style>
10
+ body { font-family: Arial, sans-serif; line-height: 1.6; color: #000; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #fff; }
11
+ .header { padding: 20px; text-align: center; border-bottom: 1px solid #000; }
12
+ .content { padding: 20px; }
13
+ .button { display: inline-block; background-color: #fff; color: #000; padding: 10px 20px; text-decoration: none; border: 1px solid #000; border-radius: 5px; margin: 20px 0; }
14
+ .footer { font-size: 12px; color: #000; text-align: center; margin-top: 20px; border-top: 1px solid #000; padding-top: 20px; }
15
+ </style>
16
+ </head>
17
+ <body>
18
+ <div class="header">
19
+ <h1>Verify Your Email Address</h1>
20
+ </div>
21
+ <div class="content">
22
+ <p>Hi ${user.name || user.email},</p>
23
+ <p>Thank you for signing up. Please verify your email address to complete your registration.</p>
24
+ <a href="${url}" class="button">Verify Email</a>
25
+ <p>If the button doesn't work, copy and paste this link: <a href="${url}">${url}</a></p>
26
+ <p>This link expires in 24 hours.</p>
27
+ </div>
28
+ <div class="footer">
29
+ <p>If you didn't create an account, ignore this email.</p>
30
+ </div>
31
+ </body>
32
+ </html>
33
+ `;
34
+
35
+ const text = `Hi ${user.name || user.email},
36
+
37
+ Thank you for signing up. Please verify your email address by clicking this link: ${url}
38
+
39
+ This link expires in 24 hours.
40
+
41
+ If you didn't create an account, ignore this email.`;
42
+
43
+ return { html, text };
44
+ };
45
+
46
+ export const getPasswordResetEmailTemplate = (user: { name?: string; email: string }, url: string) => {
47
+ const html = `
48
+ <!DOCTYPE html>
49
+ <html lang="en">
50
+ <head>
51
+ <meta charset="UTF-8">
52
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
53
+ <title>Reset Your Password</title>
54
+ <style>
55
+ body { font-family: Arial, sans-serif; line-height: 1.6; color: #000; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #fff; }
56
+ .header { padding: 20px; text-align: center; border-bottom: 1px solid #000; }
57
+ .content { padding: 20px; }
58
+ .button { display: inline-block; background-color: #fff; color: #000; padding: 10px 20px; text-decoration: none; border: 1px solid #000; border-radius: 5px; margin: 20px 0; }
59
+ .footer { font-size: 12px; color: #000; text-align: center; margin-top: 20px; border-top: 1px solid #000; padding-top: 20px; }
60
+ </style>
61
+ </head>
62
+ <body>
63
+ <div class="header">
64
+ <h1>Reset Your Password</h1>
65
+ </div>
66
+ <div class="content">
67
+ <p>Hi ${user.name || user.email},</p>
68
+ <p>You requested a password reset. Click the link below to reset your password.</p>
69
+ <a href="${url}" class="button">Reset Password</a>
70
+ <p>If the button doesn't work, copy and paste this link: <a href="${url}">${url}</a></p>
71
+ <p>This link expires in 1 hour.</p>
72
+ </div>
73
+ <div class="footer">
74
+ <p>If you didn't request this, ignore this email.</p>
75
+ </div>
76
+ </body>
77
+ </html>
78
+ `;
79
+
80
+ const text = `Hi ${user.name || user.email},
81
+
82
+ You requested a password reset. Click this link to reset your password: ${url}
83
+
84
+ This link expires in 1 hour.
85
+
86
+ If you didn't request this, ignore this email.`;
87
+
88
+ return { html, text };
89
+ };
@@ -0,0 +1,63 @@
1
+
2
+ model User {
3
+ id String @id {{idDefault}}
4
+ name String
5
+ email String
6
+ emailVerified Boolean @default(false)
7
+ image String?
8
+ createdAt DateTime @default(now())
9
+ updatedAt DateTime @updatedAt
10
+ sessions Session[]
11
+ accounts Account[]
12
+ role String @default("USER")
13
+
14
+ @@unique([email])
15
+ @@map("user")
16
+ }
17
+
18
+ model Session {
19
+ id String @id {{idDefault}}
20
+ expiresAt DateTime
21
+ token String @unique
22
+ createdAt DateTime @default(now())
23
+ updatedAt DateTime @updatedAt
24
+ ipAddress String?
25
+ userAgent String?
26
+ userId String {{userIdType}}
27
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
28
+
29
+ @@index([userId])
30
+ @@map("session")
31
+ }
32
+
33
+ model Account {
34
+ id String @id {{idDefault}}
35
+ accountId String
36
+ providerId String
37
+ userId String {{userIdType}}
38
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
39
+ accessToken String?
40
+ refreshToken String?
41
+ idToken String?
42
+ accessTokenExpiresAt DateTime?
43
+ refreshTokenExpiresAt DateTime?
44
+ scope String?
45
+ password String?
46
+ createdAt DateTime @default(now())
47
+ updatedAt DateTime @updatedAt
48
+
49
+ @@index([userId])
50
+ @@map("account")
51
+ }
52
+
53
+ model Verification {
54
+ id String @id {{idDefault}}
55
+ identifier String
56
+ value String
57
+ expiresAt DateTime
58
+ createdAt DateTime @default(now())
59
+ updatedAt DateTime @updatedAt
60
+
61
+ @@index([identifier])
62
+ @@map("verification")
63
+ }
@@ -0,0 +1,191 @@
1
+ {
2
+ "name": "better-auth",
3
+ "displayName": "Better Auth",
4
+ "description": "Modern authentication with Better Auth",
5
+ "category": "auth",
6
+ "provider": "better-auth",
7
+ "supportedFrameworks": ["nextjs", "express", "react-vite"],
8
+ "databaseAdapters": {
9
+ "common": {
10
+ "dependencies": {},
11
+ "devDependencies": {}
12
+ },
13
+ "prisma-postgresql": {
14
+ "schema": "files/schemas/prisma-schema.prisma",
15
+ "schemaDestination": "prisma/schema.prisma",
16
+ "dependencies": {
17
+ "@prisma/adapter-pg": "^5.0.0",
18
+ "pg": "^8.0.0"
19
+ }
20
+ },
21
+ "prisma-mongodb": {
22
+ "schema": "files/schemas/prisma-schema.prisma",
23
+ "schemaDestination": "prisma/schema.prisma",
24
+ "dependencies": {}
25
+ },
26
+ "prisma-mysql": {
27
+ "schema": "files/schemas/prisma-schema.prisma",
28
+ "schemaDestination": "prisma/schema.prisma",
29
+ "dependencies": {
30
+ "@prisma/adapter-mariadb": "^5.0.0",
31
+ "mysql2": "^3.0.0"
32
+ }
33
+ },
34
+ "prisma-sqlite": {
35
+ "schema": "files/schemas/prisma-schema.prisma",
36
+ "schemaDestination": "prisma/schema.prisma",
37
+ "dependencies": {
38
+ "@prisma/adapter-better-sqlite3": "^5.0.0",
39
+ "better-sqlite3": "^9.0.0"
40
+ }
41
+ }
42
+ },
43
+ "frameworkConfigs": {
44
+ "shared": {
45
+ "dependencies": {
46
+ "better-auth": "^1.4.12",
47
+ "nodemailer": "^7.0.12"
48
+ },
49
+ "devDependencies": {
50
+ "@types/nodemailer": "^6.4.17"
51
+ },
52
+ "envVars": [
53
+ {
54
+ "key": "BETTER_AUTH_SECRET",
55
+ "value": "",
56
+ "required": true
57
+ },
58
+ {
59
+ "key": "BETTER_AUTH_URL",
60
+ "value": "http://localhost:3000",
61
+ "required": true
62
+ },
63
+ {
64
+ "key": "EMAIL_HOST",
65
+ "value": "smtp.gmail.com",
66
+ "required": true
67
+ },
68
+ {
69
+ "key": "EMAIL_PORT",
70
+ "value": "587",
71
+ "required": true
72
+ },
73
+ {
74
+ "key": "EMAIL_USER",
75
+ "value": "",
76
+ "required": true
77
+ },
78
+ {
79
+ "key": "EMAIL_PASS",
80
+ "value": "",
81
+ "required": true
82
+ },
83
+ {
84
+ "key": "EMAIL_FROM",
85
+ "value": "noreply@yourapp.com",
86
+ "required": true
87
+ }
88
+ ]
89
+ },
90
+ "nextjs": {
91
+ "patches": [
92
+ {
93
+ "type": "create-file",
94
+ "description": "{{authDescription}}",
95
+ "source": "lib/{{authFile}}",
96
+ "destination": "{{lib}}/{{authFile}}"
97
+ },
98
+ {
99
+ "type": "create-file",
100
+ "description": "Create Better Auth API routes",
101
+ "source": "api/auth/[...all]/route.ts",
102
+ "destination": "app/api/auth/[...all]/route.ts"
103
+ },
104
+ {
105
+ "type": "create-file",
106
+ "description": "Create Better Auth client for React",
107
+ "source": "lib/auth-client.ts",
108
+ "destination": "{{lib}}/auth-client.ts"
109
+ },
110
+ {
111
+ "type": "create-file",
112
+ "description": "Create email service for sending emails",
113
+ "source": "lib/email-service.ts",
114
+ "destination": "{{lib}}/email/email-service.ts"
115
+ },
116
+ {
117
+ "type": "create-file",
118
+ "description": "Create email templates",
119
+ "source": "lib/email-templates.ts",
120
+ "destination": "{{lib}}/email/email-templates.ts"
121
+ }
122
+ ]
123
+ },
124
+ "express": {
125
+ "patches": [
126
+ {
127
+ "type": "create-file",
128
+ "description": "{{authDescription}}",
129
+ "source": "lib/{{authFile}}",
130
+ "destination": "{{lib}}/{{authFile}}"
131
+ },
132
+ {
133
+ "type": "patch-file",
134
+ "description": "Add Better Auth routes to Express app",
135
+ "file": "src/app.ts",
136
+ "operations": [
137
+ {
138
+ "type": "add-import",
139
+ "imports": [
140
+ "import { auth } from \"@/lib/auth\";",
141
+ "import { toNodeHandler } from \"better-auth/node\";"
142
+ ]
143
+ },
144
+ {
145
+ "type": "add-code",
146
+ "after": "// routes",
147
+ "code": "\napp.all(\"/api/auth/*splat\", toNodeHandler(auth));"
148
+ }
149
+ ]
150
+ },
151
+ {
152
+ "type": "create-file",
153
+ "description": "Create email service for sending emails",
154
+ "source": "lib/email-service.ts",
155
+ "destination": "{{lib}}/email/email-service.ts"
156
+ },
157
+ {
158
+ "type": "create-file",
159
+ "description": "Create email templates",
160
+ "source": "lib/email-templates.ts",
161
+ "destination": "{{lib}}/email/email-templates.ts"
162
+ }
163
+ ]
164
+ },
165
+ "react-vite": {
166
+ "dependencies": {
167
+ "better-auth": "^1.4.12"
168
+ },
169
+ "envVars": [
170
+ {
171
+ "key": "BETTER_AUTH_SECRET",
172
+ "value": "",
173
+ "required": true
174
+ },
175
+ {
176
+ "key": "BETTER_AUTH_URL",
177
+ "value": "http://localhost:3000",
178
+ "required": true
179
+ }
180
+ ],
181
+ "patches": [
182
+ {
183
+ "type": "create-file",
184
+ "description": "Create Better Auth client for React",
185
+ "source": "lib/auth-client.ts",
186
+ "destination": "{{lib}}/auth-client.ts"
187
+ }
188
+ ]
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,68 @@
1
+ import mongoose from "mongoose";
2
+ import { ServerApiVersion } from "mongodb";
3
+
4
+ type MongooseCache = {
5
+ conn: typeof mongoose | null;
6
+ promise: Promise<typeof mongoose> | null;
7
+ };
8
+
9
+ const globalWithMongoose = globalThis as unknown as {
10
+ mongoose: MongooseCache;
11
+ };
12
+
13
+ // Initialize the cache if not already present
14
+ const cached = globalWithMongoose.mongoose || {
15
+ conn: null,
16
+ promise: null,
17
+ };
18
+
19
+ if (!globalWithMongoose.mongoose) {
20
+ globalWithMongoose.mongoose = cached;
21
+ }
22
+
23
+ async function dbConnect(): Promise<typeof mongoose> {
24
+ if (cached.conn) {
25
+ return cached.conn;
26
+ }
27
+
28
+ const uri = process.env.DATABASE_URL as string;
29
+
30
+ if (!cached.promise) {
31
+ const opts = {
32
+ bufferCommands: false,
33
+ connectTimeoutMS: 10000,
34
+ serverSelectionTimeoutMS: 10000,
35
+ serverApi: {
36
+ version: ServerApiVersion.v1,
37
+ strict: true,
38
+ deprecationErrors: true,
39
+ },
40
+ };
41
+
42
+ cached.promise = mongoose
43
+ .connect(uri, opts)
44
+ .then(async (mongooseInstance: typeof mongoose) => {
45
+ console.info("MongoDB connected successfully");
46
+ if (mongoose.connection.db) {
47
+ await mongoose.connection.db.admin().command({ ping: 1 });
48
+ console.info("Pinged your deployment. You successfully connected to MongoDB!");
49
+ }
50
+ return mongooseInstance;
51
+ })
52
+ .catch((error: Error) => {
53
+ console.error("MongoDB connection failed", { error });
54
+ throw error;
55
+ });
56
+ }
57
+
58
+ try {
59
+ cached.conn = await cached.promise;
60
+ } catch (error) {
61
+ cached.promise = null;
62
+ throw error;
63
+ }
64
+
65
+ return cached.conn;
66
+ }
67
+
68
+ export default dbConnect;
@@ -0,0 +1,34 @@
1
+ import mongoose, { Document, Schema } from "mongoose";
2
+
3
+ export interface IUser extends Document {
4
+ name: string;
5
+ email: string;
6
+ createdAt: Date;
7
+ updatedAt: Date;
8
+ }
9
+
10
+ const UserSchema: Schema = new Schema(
11
+ {
12
+ name: {
13
+ type: String,
14
+ required: true,
15
+ trim: true,
16
+ },
17
+ email: {
18
+ type: String,
19
+ required: true,
20
+ unique: true,
21
+ lowercase: true,
22
+ trim: true,
23
+ },
24
+ },
25
+ {
26
+ timestamps: true,
27
+ },
28
+ );
29
+
30
+ // Add indexes for better performance
31
+ UserSchema.index({ email: 1 });
32
+ UserSchema.index({ createdAt: -1 });
33
+
34
+ export default mongoose.models.User || mongoose.model<IUser>("User", UserSchema);