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.
- package/bin/stackkit.js +8 -5
- package/dist/cli/add.js +4 -9
- package/dist/cli/create.js +4 -9
- package/dist/cli/doctor.js +11 -32
- package/dist/lib/constants.js +3 -4
- package/dist/lib/conversion/js-conversion.js +20 -16
- package/dist/lib/discovery/installed-detection.js +28 -38
- package/dist/lib/discovery/module-discovery.d.ts +0 -15
- package/dist/lib/discovery/module-discovery.js +15 -50
- package/dist/lib/framework/framework-utils.d.ts +4 -5
- package/dist/lib/framework/framework-utils.js +38 -49
- package/dist/lib/generation/code-generator.d.ts +11 -12
- package/dist/lib/generation/code-generator.js +54 -134
- package/dist/lib/generation/generator-utils.js +3 -15
- package/dist/lib/project/detect.js +11 -19
- package/modules/auth/better-auth/files/express/middlewares/authorize.ts +66 -8
- package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +7 -0
- package/modules/auth/better-auth/files/express/modules/auth/auth.route.ts +5 -0
- package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +145 -11
- package/modules/auth/better-auth/files/express/modules/auth/{auth.interface.ts → auth.type.ts} +16 -6
- package/modules/auth/better-auth/files/shared/config/env.ts +8 -4
- package/modules/auth/better-auth/files/shared/lib/auth.ts +24 -25
- package/modules/auth/better-auth/files/shared/mongoose/auth/constants.ts +11 -0
- package/modules/auth/better-auth/files/shared/mongoose/auth/helper.ts +51 -0
- package/modules/auth/better-auth/files/shared/utils/email.ts +0 -1
- package/modules/auth/better-auth/generator.json +24 -20
- package/modules/database/mongoose/files/lib/mongoose.ts +28 -3
- package/modules/database/mongoose/generator.json +1 -1
- package/package.json +2 -2
- package/templates/express/env.example +1 -0
- 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 {
|
|
21
|
-
import {
|
|
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
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
33
|
+
await mongoose();
|
|
34
|
+
const client = getMongoClient();
|
|
35
|
+
const db = getMongoDb();
|
|
36
|
+
const usersCollection = db.collection("user");
|
|
37
37
|
{{/if}}
|
|
38
38
|
|
|
39
|
-
|
|
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,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
|
|
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":
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stackkit",
|
|
3
|
-
"version": "0.
|
|
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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
};
|