stackkit 0.2.9 → 0.3.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 (31) hide show
  1. package/bin/stackkit.js +8 -5
  2. package/dist/cli/add.js +4 -9
  3. package/dist/cli/create.js +4 -9
  4. package/dist/cli/doctor.js +11 -32
  5. package/dist/lib/constants.js +3 -4
  6. package/dist/lib/conversion/js-conversion.js +20 -16
  7. package/dist/lib/discovery/installed-detection.js +28 -38
  8. package/dist/lib/discovery/module-discovery.d.ts +0 -15
  9. package/dist/lib/discovery/module-discovery.js +15 -50
  10. package/dist/lib/framework/framework-utils.d.ts +4 -5
  11. package/dist/lib/framework/framework-utils.js +38 -49
  12. package/dist/lib/generation/code-generator.d.ts +11 -12
  13. package/dist/lib/generation/code-generator.js +54 -134
  14. package/dist/lib/generation/generator-utils.js +3 -15
  15. package/dist/lib/project/detect.js +11 -19
  16. package/modules/auth/better-auth/files/express/middlewares/authorize.ts +66 -8
  17. package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +7 -0
  18. package/modules/auth/better-auth/files/express/modules/auth/auth.route.ts +5 -0
  19. package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +145 -11
  20. package/modules/auth/better-auth/files/express/modules/auth/{auth.interface.ts → auth.type.ts} +16 -6
  21. package/modules/auth/better-auth/files/shared/config/env.ts +8 -4
  22. package/modules/auth/better-auth/files/shared/lib/auth.ts +24 -25
  23. package/modules/auth/better-auth/files/shared/mongoose/auth/constants.ts +11 -0
  24. package/modules/auth/better-auth/files/shared/mongoose/auth/helper.ts +51 -0
  25. package/modules/auth/better-auth/files/shared/utils/email.ts +0 -1
  26. package/modules/auth/better-auth/generator.json +24 -20
  27. package/modules/database/mongoose/files/lib/mongoose.ts +28 -3
  28. package/modules/database/mongoose/generator.json +1 -1
  29. package/package.json +2 -2
  30. package/templates/express/env.example +1 -0
  31. package/templates/express/src/config/env.ts +4 -2
@@ -1,49 +1,49 @@
1
- import { Role, UserStatus } from "@prisma/client";
2
1
  import { betterAuth } from "better-auth";
3
2
  import { bearer, emailOTP } from "better-auth/plugins";
4
3
  {{#if combo == "prisma:express"}}
4
+ import { Role, UserStatus } from "@prisma/client";
5
5
  import { envVars } from "../config/env";
6
6
  import { sendEmail } from "../shared/utils/email";
7
7
  import { prisma } from "../database/prisma";
8
8
  import { prismaAdapter } from "better-auth/adapters/prisma";
9
9
  {{/if}}
10
-
11
10
  {{#if combo == "prisma:nextjs"}}
11
+ import { Role, UserStatus } from "@prisma/client";
12
12
  import { sendEmail } from "../service/email/email-service";
13
13
  import { prisma } from "../database/prisma";
14
14
  import { envVars } from "@/lib/env";
15
15
  import { prismaAdapter } from "better-auth/adapters/prisma";
16
16
  {{/if}}
17
-
18
17
  {{#if combo == "mongoose:express"}}
19
18
  import { envVars } from "../config/env";
20
- import { sendEmail } from "../../shared/email/email-service";
21
- import { mongoose } from "../../database/mongoose";
19
+ import { Role, UserStatus } from "../modules/auth/auth.constants";
20
+ import { sendEmail } from "../shared/utils/email";
21
+ import { getMongoClient, getMongoDb, mongoose } from "../database/mongoose";
22
22
  import { mongodbAdapter } from "better-auth/adapters/mongodb";
23
23
  {{/if}}
24
-
25
24
  {{#if combo == "mongoose:nextjs"}}
26
25
  import { sendEmail } from "../service/email/email-service";
27
- import { mongoose } from "../database/mongoose";
26
+ import { getMongoClient, getMongoDb, mongoose } from "../database/mongoose";
27
+ import { Role, UserStatus } from "@/lib/auth/auth-constants";
28
28
  import { envVars } from "@/lib/env";
29
29
  import { mongodbAdapter } from "better-auth/adapters/mongodb";
30
30
  {{/if}}
31
31
 
32
32
  {{#if database == "mongoose"}}
33
- export async function initAuth() {
34
- const mongooseInstance = await mongoose();
35
- const client = mongooseInstance.connection.getClient();
36
- const db = client.db();
33
+ await mongoose();
34
+ const client = getMongoClient();
35
+ const db = getMongoDb();
36
+ const usersCollection = db.collection("user");
37
37
  {{/if}}
38
38
 
39
- {{#if database == "mongoose"}}return{{else}}export const auth = {{/if}} betterAuth({
39
+ export const auth = betterAuth({
40
40
  {{#if database == "prisma"}}
41
41
  database: prismaAdapter(prisma, {
42
42
  provider: "{{prismaProvider}}",
43
43
  }),
44
44
  {{/if}}
45
45
  {{#if database == "mongoose"}}
46
- database: mongodbAdapter(db, { client }),
46
+ database: mongodbAdapter(db, { client, transaction: false }),
47
47
  {{/if}}
48
48
  baseURL: envVars.BETTER_AUTH_URL,
49
49
  secret: envVars.BETTER_AUTH_SECRET,
@@ -112,11 +112,16 @@ const db = client.db();
112
112
  overrideDefaultEmailVerification: true,
113
113
  async sendVerificationOTP({ email, otp, type }) {
114
114
  if (type === "email-verification") {
115
+ {{#if database == "prisma"}}
115
116
  const user = await prisma.user.findUnique({
116
117
  where: {
117
118
  email,
118
119
  },
119
120
  });
121
+ {{/if}}
122
+ {{#if database == "mongoose"}}
123
+ const user = await usersCollection.findOne({ email });
124
+ {{/if}}
120
125
 
121
126
  if (!user) {
122
127
  console.error(
@@ -144,11 +149,16 @@ const db = client.db();
144
149
  });
145
150
  }
146
151
  } else if (type === "forget-password") {
152
+ {{#if database == "prisma"}}
147
153
  const user = await prisma.user.findUnique({
148
154
  where: {
149
155
  email,
150
156
  },
151
157
  });
158
+ {{/if}}
159
+ {{#if database == "mongoose"}}
160
+ const user = await usersCollection.findOne({ email });
161
+ {{/if}}
152
162
 
153
163
  if (user) {
154
164
  sendEmail({
@@ -179,7 +189,6 @@ const db = client.db();
179
189
  signIn: `${envVars.BETTER_AUTH_URL}/api/v1/auth/google/success`,
180
190
  },
181
191
  advanced: {
182
- // disableCSRFCheck: true,
183
192
  useSecureCookies: false,
184
193
  cookies: {
185
194
  state: {
@@ -200,14 +209,4 @@ const db = client.db();
200
209
  },
201
210
  },
202
211
  },
203
- })
204
- {{#if database == "mongoose"}}
205
- };
206
-
207
- export let auth: ReturnType<typeof betterAuth> | undefined = undefined;
208
-
209
- export async function setupAuth() {
210
- auth = await initAuth();
211
- return auth;
212
- }
213
- {{/if}}
212
+ });
@@ -0,0 +1,11 @@
1
+ export const Role = {
2
+ ADMIN: "ADMIN",
3
+ USER: "USER",
4
+ SUPER_ADMIN: "SUPER_ADMIN",
5
+ } as const;
6
+
7
+ export const UserStatus = {
8
+ ACTIVE: "ACTIVE",
9
+ BLOCKED: "BLOCKED",
10
+ DELETED: "DELETED",
11
+ } as const;
@@ -0,0 +1,51 @@
1
+ import status from "http-status";
2
+ import { getMongoDb, mongoose } from "../../database/mongoose";
3
+ import { AppError } from "../../shared/errors/app-error";
4
+
5
+ export type AuthUser = {
6
+ id: string;
7
+ role: string;
8
+ name: string;
9
+ email: string;
10
+ status?: string;
11
+ isDeleted?: boolean;
12
+ emailVerified?: boolean;
13
+ needPasswordChange?: boolean;
14
+ deletedAt?: Date | null;
15
+ };
16
+
17
+ export type AuthUserDocument = AuthUser & {
18
+ createdAt?: Date;
19
+ updatedAt?: Date;
20
+ };
21
+
22
+ export type AuthSessionDocument = {
23
+ token: string;
24
+ userId: string;
25
+ createdAt?: Date;
26
+ updatedAt?: Date;
27
+ expiresAt?: Date;
28
+ };
29
+
30
+ export const getAuthCollections = async () => {
31
+ await mongoose();
32
+
33
+ try {
34
+ const db = getMongoDb();
35
+
36
+ return {
37
+ users: db.collection<AuthUserDocument>("user"),
38
+ sessions: db.collection<AuthSessionDocument>("session"),
39
+ };
40
+ } catch {
41
+ throw new AppError(
42
+ status.INTERNAL_SERVER_ERROR,
43
+ "Auth database is not initialized",
44
+ );
45
+ }
46
+ };
47
+
48
+ export const deleteAuthUserById = async (id: string) => {
49
+ const { users } = await getAuthCollections();
50
+ await users.deleteOne({ id });
51
+ };
@@ -7,7 +7,6 @@ import { envVars } from "../../config/env";
7
7
  import { AppError } from "../errors/app-error";
8
8
  {{/if}}
9
9
  {{#if framework == "nextjs"}}
10
- import nodemailer from "nodemailer";
11
10
  import { renderEmailTemplate } from "../email/otp-template";
12
11
  import { envVars } from "../env";
13
12
  {{/if}}
@@ -27,6 +27,18 @@
27
27
  "destination": "src/modules/auth/*",
28
28
  "condition": { "framework": "express" }
29
29
  },
30
+ {
31
+ "type": "create-file",
32
+ "source": "shared/mongoose/auth/helper.ts",
33
+ "destination": "src/modules/auth/auth.helper.ts",
34
+ "condition": { "framework": "express", "database": "mongoose" }
35
+ },
36
+ {
37
+ "type": "create-file",
38
+ "source": "shared/mongoose/auth/constants.ts",
39
+ "destination": "src/modules/auth/auth.constants.ts",
40
+ "condition": { "framework": "express", "database": "mongoose" }
41
+ },
30
42
  {
31
43
  "type": "create-file",
32
44
  "source": "express/middlewares/authorize.ts",
@@ -67,7 +79,7 @@
67
79
  {
68
80
  "type": "add-code",
69
81
  "after": "// API routes",
70
- "code": ["app.use(\"/api/auth\", toNodeHandler(auth));"]
82
+ "code": ["app.use(\"/api/auth\", (req, res) => {", " return toNodeHandler(auth)(req, res);", "});"]
71
83
  },
72
84
  {
73
85
  "type": "add-code",
@@ -95,22 +107,6 @@
95
107
  }
96
108
  ]
97
109
  },
98
- {
99
- "type": "patch-file",
100
- "destination": "src/server.ts",
101
- "condition": { "framework": "express", "database": "mongoose" },
102
- "operations": [
103
- {
104
- "type": "add-import",
105
- "imports": ["import { setupAuth } from \"./modules/auth/auth\";"]
106
- },
107
- {
108
- "type": "add-code",
109
- "before": "app.listen(envVars.PORT, () => {",
110
- "code": "await setupAuth();"
111
- }
112
- ]
113
- },
114
110
  {
115
111
  "type": "add-dependency",
116
112
  "condition": { "framework": "express" },
@@ -127,7 +123,7 @@
127
123
  "type": "add-env",
128
124
  "condition": { "framework": "express" },
129
125
  "envVars": {
130
- "BETTER_AUTH_URL": "http://localhost:3000/api/auth",
126
+ "BETTER_AUTH_URL": "http://localhost:3000",
131
127
  "BETTER_AUTH_SECRET": "your_better_auth_secret",
132
128
  "BETTER_AUTH_SESSION_TOKEN_EXPIRES_IN": "1d",
133
129
  "BETTER_AUTH_SESSION_TOKEN_UPDATE_AGE": "1d",
@@ -135,6 +131,8 @@
135
131
  "ACCESS_TOKEN_EXPIRES_IN": "1d",
136
132
  "REFRESH_TOKEN_SECRET": "your_refresh_token_secret",
137
133
  "REFRESH_TOKEN_EXPIRES_IN": "7d",
134
+ "GOOGLE_CLIENT_ID": "your_google_client_id",
135
+ "GOOGLE_CLIENT_SECRET": "your_google_client_secret",
138
136
  "GOOGLE_CALLBACK_URL": "http://localhost:5000/api/auth/callback/google",
139
137
  "EMAIL_SENDER_SMTP_USER": "your_email@gmail.com",
140
138
  "EMAIL_SENDER_SMTP_PASS": "your_email_password",
@@ -173,7 +171,13 @@
173
171
  "type": "create-file",
174
172
  "source": "shared/lib/auth-client.ts",
175
173
  "destination": "lib/auth/auth-client.ts",
176
- "condition": { "framework": ["nextjs"] }
174
+ "condition": { "framework": "nextjs" }
175
+ },
176
+ {
177
+ "type": "create-file",
178
+ "source": "shared/mongoose/auth/constants.ts",
179
+ "destination": "lib/auth/auth-constants.ts",
180
+ "condition": { "framework": "nextjs" }
177
181
  },
178
182
  {
179
183
  "type": "create-file",
@@ -199,7 +203,7 @@
199
203
  "envVars": {
200
204
  "NEXT_PUBLIC_APP_URL": "http://localhost:3000",
201
205
  "NEXT_PUBLIC_FRONTEND_URL": "http://localhost:3000",
202
- "NEXT_PUBLIC_BETTER_AUTH_URL": "http://localhost:3000/api/auth",
206
+ "NEXT_PUBLIC_BETTER_AUTH_URL": "http://localhost:3000",
203
207
  "BETTER_AUTH_SECRET": "your_better_auth_secret",
204
208
  "GOOGLE_CLIENT_ID": "your_google_client_id",
205
209
  "GOOGLE_CLIENT_SECRET": "your_google_client_secret",
@@ -1,4 +1,5 @@
1
1
  import mongoose from "mongoose";
2
+ import { envVars } from "../config/env";
2
3
 
3
4
  type MongooseCache = {
4
5
  conn: typeof mongoose | null;
@@ -24,14 +25,13 @@ async function dbConnect(): Promise<typeof mongoose> {
24
25
  return cached.conn;
25
26
  }
26
27
 
27
- const uri = process.env.DATABASE_URL as string;
28
+ const uri = envVars.DATABASE_URL;
28
29
 
29
30
  if (!cached.promise) {
30
31
  const opts = {
31
32
  bufferCommands: false,
32
33
  connectTimeoutMS: 10000,
33
34
  serverSelectionTimeoutMS: 10000,
34
- // serverApi removed: not needed for mongoose-only connection
35
35
  };
36
36
 
37
37
  cached.promise = mongoose
@@ -60,4 +60,29 @@ async function dbConnect(): Promise<typeof mongoose> {
60
60
  return cached.conn;
61
61
  }
62
62
 
63
- export { dbConnect as mongoose, dbConnect as connectMongoose };
63
+ const getMongoClient = () => {
64
+ if (!mongoose.connection.readyState) {
65
+ throw new Error("MongoDB is not connected. Call mongoose() first.");
66
+ }
67
+
68
+ return mongoose.connection.getClient();
69
+ };
70
+
71
+ const getMongoDb = () => {
72
+ const db = mongoose.connection.db;
73
+
74
+ if (!db) {
75
+ throw new Error("MongoDB is not connected. Call mongoose() first.");
76
+ }
77
+
78
+ return db;
79
+ };
80
+
81
+ export {
82
+ dbConnect as connectMongoose,
83
+ getMongoClient,
84
+ getMongoDb,
85
+ dbConnect as mongoose,
86
+ dbConnect,
87
+ };
88
+
@@ -52,7 +52,7 @@
52
52
  {
53
53
  "type": "add-env",
54
54
  "envVars": {
55
- "DATABASE_URL": "mongodb://localhost:27017/database_name"
55
+ "DATABASE_URL": "mongodb://username:password@localhost:27017/database_name?authSource=admin"
56
56
  }
57
57
  }
58
58
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stackkit",
3
- "version": "0.2.9",
3
+ "version": "0.3.0",
4
4
  "description": "Production-ready CLI to create and extend JavaScript or TypeScript apps with modular stacks.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -80,4 +80,4 @@
80
80
  "@types/validate-npm-package-name": "^4.0.2",
81
81
  "typescript": "^5.3.3"
82
82
  }
83
- }
83
+ }
@@ -1,3 +1,4 @@
1
1
  PORT=5000
2
2
  NODE_ENV=development
3
+ APP_URL=http://localhost:5000
3
4
  FRONTEND_URL=http://localhost:3000
@@ -8,11 +8,12 @@ dotenv.config({ path: path.join(process.cwd(), ".env") });
8
8
  interface EnvConfig {
9
9
  NODE_ENV: string;
10
10
  PORT: string;
11
- FRONTEND_URL?: string;
11
+ APP_URL: string;
12
+ FRONTEND_URL: string;
12
13
  }
13
14
 
14
15
  const loadEnvVars = (): EnvConfig => {
15
- const requiredEnvVars = ["NODE_ENV", "PORT", "FRONTEND_URL"];
16
+ const requiredEnvVars = ["NODE_ENV", "PORT", "APP_URL", "FRONTEND_URL"];
16
17
 
17
18
  requiredEnvVars.forEach((varName) => {
18
19
  if (!process.env[varName]) {
@@ -26,6 +27,7 @@ const loadEnvVars = (): EnvConfig => {
26
27
  return {
27
28
  NODE_ENV: process.env.NODE_ENV as string,
28
29
  PORT: process.env.PORT as string,
30
+ APP_URL: process.env.APP_URL as string,
29
31
  FRONTEND_URL: process.env.FRONTEND_URL as string,
30
32
  };
31
33
  };