create-stackkit-app 0.4.2 → 0.4.4
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/README.md +1 -0
- package/bin/create-stackkit.js +10 -1
- package/dist/index.js +2 -1
- package/dist/lib/create-project.js +138 -412
- package/dist/lib/utils/config-utils.d.ts +2 -0
- package/dist/lib/utils/config-utils.js +33 -0
- package/dist/lib/utils/file-utils.d.ts +8 -0
- package/dist/lib/utils/file-utils.js +75 -0
- package/dist/lib/utils/git-utils.d.ts +1 -0
- package/dist/lib/utils/git-utils.js +9 -0
- package/dist/lib/utils/js-conversion.d.ts +1 -0
- package/dist/lib/utils/js-conversion.js +244 -0
- package/dist/lib/utils/module-utils.d.ts +2 -0
- package/dist/lib/utils/module-utils.js +311 -0
- package/dist/lib/utils/package-utils.d.ts +1 -0
- package/dist/lib/utils/package-utils.js +39 -0
- package/modules/auth/better-auth/files/api/auth/[...all]/route.ts +4 -0
- package/modules/auth/better-auth/files/lib/auth.ts +13 -0
- package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +63 -0
- package/modules/auth/better-auth/module.json +54 -0
- package/modules/auth/{clerk-express/files/lib → clerk/files/express}/auth.ts +1 -1
- package/modules/auth/{clerk-nextjs/files/lib → clerk/files/nextjs}/auth-provider.tsx +1 -1
- package/modules/auth/clerk/files/nextjs/middleware.ts +9 -0
- package/modules/auth/{clerk-react/files/lib → clerk/files/react}/auth-provider.tsx +2 -2
- package/modules/auth/clerk/module.json +115 -0
- package/modules/database/mongoose-mongodb/files/lib/db.ts +45 -7
- package/modules/database/mongoose-mongodb/files/models/User.ts +39 -0
- package/modules/database/mongoose-mongodb/module.json +27 -12
- package/modules/database/prisma/files/lib/prisma.ts +6 -0
- package/modules/database/prisma/files/prisma/schema.prisma +8 -0
- package/modules/database/prisma/files/prisma.config.ts +12 -0
- package/modules/database/prisma/module.json +140 -0
- package/package.json +12 -3
- package/templates/express/.env.example +2 -10
- package/templates/express/package.json +13 -18
- package/templates/express/src/app.ts +21 -39
- package/templates/express/src/config/env.ts +6 -17
- package/templates/express/src/features/auth/auth.controller.ts +48 -0
- package/templates/express/src/features/auth/auth.route.ts +10 -0
- package/templates/express/src/features/auth/auth.service.ts +21 -0
- package/templates/express/src/middlewares/error.middleware.ts +5 -5
- package/templates/express/src/server.ts +3 -3
- package/templates/express/template.json +34 -1
- package/templates/express/tsconfig.json +17 -1
- package/templates/nextjs/app/layout.tsx +1 -5
- package/templates/nextjs/app/page.tsx +26 -34
- package/templates/nextjs/package.json +2 -1
- package/templates/nextjs/template.json +13 -1
- package/templates/react-vite/eslint.config.js +9 -9
- package/templates/react-vite/package.json +1 -2
- package/templates/react-vite/src/api/client.ts +16 -16
- package/templates/react-vite/src/api/services/user.service.ts +2 -10
- package/templates/react-vite/src/components/ErrorBoundary.tsx +4 -4
- package/templates/react-vite/src/components/Layout.tsx +1 -1
- package/templates/react-vite/src/components/Loading.tsx +1 -1
- package/templates/react-vite/src/components/SEO.tsx +5 -5
- package/templates/react-vite/src/config/constants.ts +3 -3
- package/templates/react-vite/src/hooks/index.ts +5 -5
- package/templates/react-vite/src/lib/queryClient.ts +2 -2
- package/templates/react-vite/src/main.tsx +12 -12
- package/templates/react-vite/src/pages/About.tsx +6 -2
- package/templates/react-vite/src/pages/Home.tsx +8 -4
- package/templates/react-vite/src/pages/NotFound.tsx +2 -2
- package/templates/react-vite/src/pages/UserProfile.tsx +6 -6
- package/templates/react-vite/src/router.tsx +13 -13
- package/templates/react-vite/src/types/{api.ts → api.d.ts} +6 -6
- package/templates/react-vite/src/types/user.d.ts +6 -0
- package/templates/react-vite/src/utils/helpers.ts +11 -11
- package/templates/react-vite/src/utils/storage.ts +4 -4
- package/templates/react-vite/template.json +26 -0
- package/templates/react-vite/tsconfig.json +1 -4
- package/templates/react-vite/vite.config.ts +5 -5
- package/dist/lib/template-composer.d.ts +0 -16
- package/dist/lib/template-composer.js +0 -197
- package/modules/auth/better-auth-express/adapters/mongoose-mongodb.ts +0 -13
- package/modules/auth/better-auth-express/adapters/prisma-mongodb.ts +0 -15
- package/modules/auth/better-auth-express/adapters/prisma-postgresql.ts +0 -15
- package/modules/auth/better-auth-express/files/lib/auth.ts +0 -16
- package/modules/auth/better-auth-express/files/routes/auth.ts +0 -12
- package/modules/auth/better-auth-express/files/schemas/prisma-mongodb-schema.prisma +0 -72
- package/modules/auth/better-auth-express/files/schemas/prisma-postgresql-schema.prisma +0 -72
- package/modules/auth/better-auth-express/module.json +0 -61
- package/modules/auth/better-auth-nextjs/adapters/mongoose-mongodb.ts +0 -24
- package/modules/auth/better-auth-nextjs/adapters/prisma-mongodb.ts +0 -26
- package/modules/auth/better-auth-nextjs/adapters/prisma-postgresql.ts +0 -26
- package/modules/auth/better-auth-nextjs/files/api/auth/[...all]/route.ts +0 -5
- package/modules/auth/better-auth-nextjs/files/lib/auth.ts +0 -26
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-mongodb-schema.prisma +0 -72
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-postgresql-schema.prisma +0 -72
- package/modules/auth/better-auth-nextjs/module.json +0 -62
- package/modules/auth/better-auth-react/files/lib/auth-client.ts +0 -9
- package/modules/auth/better-auth-react/module.json +0 -28
- package/modules/auth/clerk-express/module.json +0 -34
- package/modules/auth/clerk-nextjs/files/middleware.ts +0 -9
- package/modules/auth/clerk-nextjs/module.json +0 -64
- package/modules/auth/clerk-react/module.json +0 -28
- package/modules/database/prisma-mongodb/files/lib/db.ts +0 -9
- package/modules/database/prisma-mongodb/files/prisma/schema.prisma +0 -17
- package/modules/database/prisma-mongodb/module.json +0 -60
- package/modules/database/prisma-postgresql/files/lib/db.ts +0 -9
- package/modules/database/prisma-postgresql/files/prisma/schema.prisma +0 -17
- package/modules/database/prisma-postgresql/module.json +0 -60
|
@@ -1,28 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import mongoose from "mongoose";
|
|
2
3
|
|
|
3
|
-
const MONGODB_URI = process.env.
|
|
4
|
+
const MONGODB_URI = process.env.DATABASE_URL || "mongodb://localhost:27017/myapp";
|
|
4
5
|
|
|
5
6
|
if (!MONGODB_URI) {
|
|
6
|
-
throw new Error(
|
|
7
|
+
throw new Error("Please define the DATABASE_URL environment variable");
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
interface MongooseCache {
|
|
11
|
+
conn: typeof mongoose | null;
|
|
12
|
+
promise: Promise<typeof mongoose> | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
var mongoose: MongooseCache | undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const cached: MongooseCache = global.mongoose || { conn: null, promise: null };
|
|
10
20
|
|
|
11
|
-
if (!
|
|
12
|
-
|
|
21
|
+
if (!global.mongoose) {
|
|
22
|
+
global.mongoose = cached;
|
|
13
23
|
}
|
|
14
24
|
|
|
15
|
-
async function connectDB() {
|
|
25
|
+
async function connectDB(): Promise<typeof mongoose> {
|
|
16
26
|
if (cached.conn) {
|
|
27
|
+
console.log("Using existing MongoDB connection");
|
|
17
28
|
return cached.conn;
|
|
18
29
|
}
|
|
19
30
|
|
|
20
31
|
if (!cached.promise) {
|
|
21
32
|
const opts = {
|
|
22
33
|
bufferCommands: false,
|
|
34
|
+
maxPoolSize: 10, // Maintain up to 10 socket connections
|
|
35
|
+
serverSelectionTimeoutMS: 5000, // Keep trying to send operations for 5 seconds
|
|
36
|
+
socketTimeoutMS: 45000, // Close sockets after 45 seconds of inactivity
|
|
37
|
+
family: 4, // Use IPv4, skip trying IPv6
|
|
23
38
|
};
|
|
24
39
|
|
|
40
|
+
console.log("Creating new MongoDB connection");
|
|
25
41
|
cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
|
|
42
|
+
console.log("MongoDB connected successfully");
|
|
26
43
|
return mongoose;
|
|
27
44
|
});
|
|
28
45
|
}
|
|
@@ -31,10 +48,31 @@ async function connectDB() {
|
|
|
31
48
|
cached.conn = await cached.promise;
|
|
32
49
|
} catch (e) {
|
|
33
50
|
cached.promise = null;
|
|
51
|
+
console.error("MongoDB connection error:", e);
|
|
34
52
|
throw e;
|
|
35
53
|
}
|
|
36
54
|
|
|
37
55
|
return cached.conn;
|
|
38
56
|
}
|
|
39
57
|
|
|
58
|
+
// Handle connection events
|
|
59
|
+
mongoose.connection.on("connected", () => {
|
|
60
|
+
console.log("Mongoose connected to MongoDB");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
mongoose.connection.on("error", (err) => {
|
|
64
|
+
console.error("Mongoose connection error:", err);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
mongoose.connection.on("disconnected", () => {
|
|
68
|
+
console.log("Mongoose disconnected");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Close connection on app termination
|
|
72
|
+
process.on("SIGINT", async () => {
|
|
73
|
+
await mongoose.connection.close();
|
|
74
|
+
console.log("MongoDB connection closed due to app termination");
|
|
75
|
+
process.exit(0);
|
|
76
|
+
});
|
|
77
|
+
|
|
40
78
|
export default connectDB;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import mongoose, { Document, Schema } from "mongoose";
|
|
2
|
+
|
|
3
|
+
export interface IUser extends Document {
|
|
4
|
+
name: string;
|
|
5
|
+
email: string;
|
|
6
|
+
password: string;
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
updatedAt: Date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const UserSchema: Schema = new Schema(
|
|
12
|
+
{
|
|
13
|
+
name: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: true,
|
|
16
|
+
trim: true,
|
|
17
|
+
},
|
|
18
|
+
email: {
|
|
19
|
+
type: String,
|
|
20
|
+
required: true,
|
|
21
|
+
unique: true,
|
|
22
|
+
lowercase: true,
|
|
23
|
+
trim: true,
|
|
24
|
+
},
|
|
25
|
+
password: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
timestamps: true,
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Add indexes for better performance
|
|
36
|
+
UserSchema.index({ email: 1 });
|
|
37
|
+
UserSchema.index({ createdAt: -1 });
|
|
38
|
+
|
|
39
|
+
export default mongoose.models.User || mongoose.model<IUser>("User", UserSchema);
|
|
@@ -7,28 +7,35 @@
|
|
|
7
7
|
"database": "mongodb",
|
|
8
8
|
"supportedFrameworks": ["nextjs", "express"],
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"mongoose": "^8.8.4"
|
|
10
|
+
"mongoose": "^8.8.4",
|
|
11
|
+
"dotenv": "^17.2.3"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/mongoose": "^5.11.97"
|
|
15
|
+
},
|
|
16
|
+
"envVars": {
|
|
17
|
+
"common": [
|
|
18
|
+
{
|
|
19
|
+
"key": "DATABASE_URL",
|
|
20
|
+
"value": "mongodb://localhost:27017/myapp",
|
|
21
|
+
"description": "MongoDB connection string",
|
|
22
|
+
"required": true
|
|
23
|
+
}
|
|
24
|
+
]
|
|
11
25
|
},
|
|
12
|
-
"devDependencies": {},
|
|
13
|
-
"envVars": [
|
|
14
|
-
{
|
|
15
|
-
"key": "MONGODB_URI",
|
|
16
|
-
"value": "mongodb://localhost:27017/myapp",
|
|
17
|
-
"description": "MongoDB connection string",
|
|
18
|
-
"required": true
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
26
|
"frameworkPatches": {
|
|
22
27
|
"express": {
|
|
23
28
|
"tsconfig.json": {
|
|
24
29
|
"merge": {
|
|
25
30
|
"compilerOptions": {
|
|
31
|
+
"baseUrl": ".",
|
|
26
32
|
"paths": {
|
|
27
33
|
"@/*": ["./src/*"],
|
|
28
34
|
"@/models/*": ["./src/models/*"]
|
|
29
35
|
}
|
|
30
36
|
},
|
|
31
|
-
"include": ["src/**/*", "src/models/**/*"]
|
|
37
|
+
"include": ["src/**/*", "src/models/**/*"],
|
|
38
|
+
"exclude": ["node_modules", "dist"]
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
41
|
},
|
|
@@ -36,10 +43,12 @@
|
|
|
36
43
|
"tsconfig.json": {
|
|
37
44
|
"merge": {
|
|
38
45
|
"compilerOptions": {
|
|
46
|
+
"baseUrl": ".",
|
|
39
47
|
"paths": {
|
|
40
48
|
"@/models/*": ["./models/*"]
|
|
41
49
|
}
|
|
42
|
-
}
|
|
50
|
+
},
|
|
51
|
+
"exclude": ["node_modules", ".next", "out"]
|
|
43
52
|
}
|
|
44
53
|
}
|
|
45
54
|
}
|
|
@@ -50,6 +59,12 @@
|
|
|
50
59
|
"description": "Create MongoDB connection with Mongoose",
|
|
51
60
|
"source": "lib/db.ts",
|
|
52
61
|
"destination": "{{lib}}/db.ts"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"type": "create-file",
|
|
65
|
+
"description": "Create sample User model",
|
|
66
|
+
"source": "models/User.ts",
|
|
67
|
+
"destination": "{{models}}/User.ts"
|
|
53
68
|
}
|
|
54
69
|
]
|
|
55
70
|
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "prisma",
|
|
3
|
+
"displayName": "Prisma",
|
|
4
|
+
"description": "Prisma ORM",
|
|
5
|
+
"category": "database",
|
|
6
|
+
"provider": "prisma",
|
|
7
|
+
"supportedFrameworks": ["nextjs", "express"],
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"common": {
|
|
10
|
+
"@prisma/client": "^7.2.0",
|
|
11
|
+
"dotenv": "^17.2.3"
|
|
12
|
+
},
|
|
13
|
+
"providers": {
|
|
14
|
+
"postgresql": {
|
|
15
|
+
"@prisma/adapter-pg": "^7.2.0",
|
|
16
|
+
"pg": "^8.16.3"
|
|
17
|
+
},
|
|
18
|
+
"mongodb": {
|
|
19
|
+
"@prisma/client": "6.19"
|
|
20
|
+
},
|
|
21
|
+
"mysql": {
|
|
22
|
+
"@prisma/adapter-mariadb": "^7.2.0"
|
|
23
|
+
},
|
|
24
|
+
"sqlite": {
|
|
25
|
+
"@prisma/adapter-better-sqlite3": "^7.2.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"common": {
|
|
31
|
+
"prisma": "^7.2.0",
|
|
32
|
+
"tsx": "^4.21.0"
|
|
33
|
+
},
|
|
34
|
+
"providers": {
|
|
35
|
+
"postgresql": {
|
|
36
|
+
"@types/pg": "^8.16.0"
|
|
37
|
+
},
|
|
38
|
+
"mongodb": {
|
|
39
|
+
"prisma": "6.19"
|
|
40
|
+
},
|
|
41
|
+
"mysql": {},
|
|
42
|
+
"sqlite": {
|
|
43
|
+
"@types/better-sqlite3": "^7.6.13"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"envVars": {
|
|
48
|
+
"common": [
|
|
49
|
+
{
|
|
50
|
+
"key": "DATABASE_URL",
|
|
51
|
+
"value": "{{connectionString}}",
|
|
52
|
+
"description": "Database connection URL",
|
|
53
|
+
"required": true
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"providers": {
|
|
57
|
+
"mysql": [
|
|
58
|
+
{
|
|
59
|
+
"key": "DATABASE_HOST",
|
|
60
|
+
"value": "localhost",
|
|
61
|
+
"description": "MySQL host",
|
|
62
|
+
"required": true
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"key": "DATABASE_USER",
|
|
66
|
+
"value": "",
|
|
67
|
+
"description": "MySQL username",
|
|
68
|
+
"required": true
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"key": "DATABASE_PASSWORD",
|
|
72
|
+
"value": "",
|
|
73
|
+
"description": "MySQL password",
|
|
74
|
+
"required": true
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"key": "DATABASE_NAME",
|
|
78
|
+
"value": "mydb",
|
|
79
|
+
"description": "MySQL database name",
|
|
80
|
+
"required": true
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"key": "DATABASE_PORT",
|
|
84
|
+
"value": "3306",
|
|
85
|
+
"description": "MySQL port",
|
|
86
|
+
"required": false
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"frameworkPatches": {
|
|
92
|
+
"express": {
|
|
93
|
+
"tsconfig.json": {
|
|
94
|
+
"merge": {
|
|
95
|
+
"compilerOptions": {
|
|
96
|
+
"baseUrl": ".",
|
|
97
|
+
"paths": {
|
|
98
|
+
"@/*": ["./src/*"]
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"include": ["src/**/*"],
|
|
102
|
+
"exclude": ["node_modules", "dist", "prisma/migrations"]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"nextjs": {
|
|
107
|
+
"tsconfig.json": {
|
|
108
|
+
"merge": {
|
|
109
|
+
"compilerOptions": {
|
|
110
|
+
"paths": {
|
|
111
|
+
"@/*": ["./*"]
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"exclude": ["node_modules", ".next", "out", "prisma/migrations"]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"patches": [
|
|
120
|
+
{
|
|
121
|
+
"type": "create-file",
|
|
122
|
+
"description": "Create Prisma schema",
|
|
123
|
+
"source": "prisma/schema.prisma",
|
|
124
|
+
"destination": "prisma/schema.prisma"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"type": "create-file",
|
|
128
|
+
"description": "Create Prisma config",
|
|
129
|
+
"source": "prisma.config.ts",
|
|
130
|
+
"destination": "prisma.config.ts"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"type": "create-file",
|
|
134
|
+
"description": "Create Prisma client singleton",
|
|
135
|
+
"source": "lib/prisma.ts",
|
|
136
|
+
"destination": "{{lib}}/prisma.ts"
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"postInstall": ["npx prisma generate"]
|
|
140
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-stackkit-app",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Create a new StackKit project with modular composition",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -44,13 +44,22 @@
|
|
|
44
44
|
"fs-extra": "^11.2.0",
|
|
45
45
|
"inquirer": "^9.3.8",
|
|
46
46
|
"ora": "^5.4.1",
|
|
47
|
-
"validate-npm-package-name": "^5.0.1"
|
|
47
|
+
"validate-npm-package-name": "^5.0.1",
|
|
48
|
+
"@babel/core": "^7.28.5",
|
|
49
|
+
"@babel/preset-env": "^7.28.5",
|
|
50
|
+
"@babel/preset-typescript": "^7.28.5",
|
|
51
|
+
"@babel/preset-react": "^7.28.5"
|
|
48
52
|
},
|
|
49
53
|
"devDependencies": {
|
|
50
54
|
"@types/fs-extra": "^11.0.4",
|
|
51
55
|
"@types/inquirer": "^9.0.7",
|
|
52
56
|
"@types/node": "^20.11.0",
|
|
53
57
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
54
|
-
"typescript": "^5.3.3"
|
|
58
|
+
"typescript": "^5.3.3",
|
|
59
|
+
"recast": "^0.20.5",
|
|
60
|
+
"@babel/core": "^7.28.5",
|
|
61
|
+
"@babel/plugin-transform-typescript": "^7.28.5",
|
|
62
|
+
"@babel/parser": "^7.28.5",
|
|
63
|
+
"@babel/plugin-transform-react-jsx": "^7.27.1"
|
|
55
64
|
}
|
|
56
65
|
}
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
+
# APP CONFIGURATION
|
|
1
2
|
PORT=3000
|
|
2
|
-
NODE_ENV=development
|
|
3
|
-
APP_URL=http://localhost:3000
|
|
4
|
-
SITE_URL=http://localhost:5173
|
|
5
|
-
|
|
6
|
-
# Proxy (set to true if running behind a proxy/load-balancer)
|
|
7
|
-
TRUST_PROXY=false
|
|
8
|
-
|
|
9
|
-
# Rate limit
|
|
10
|
-
RATE_LIMIT_MAX=100
|
|
11
|
-
RATE_LIMIT_WINDOW_MS=900000
|
|
3
|
+
NODE_ENV=development
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "my-app",
|
|
2
|
+
"name": "my-express-app",
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"private": true,
|
|
5
|
+
"type": "module",
|
|
5
6
|
"scripts": {
|
|
6
7
|
"dev": "tsx watch src/server.ts",
|
|
7
|
-
"clean": "rimraf dist",
|
|
8
|
-
"prebuild": "npm run clean",
|
|
9
8
|
"build": "tsc",
|
|
10
9
|
"lint": "eslint src --ext .ts",
|
|
11
10
|
"lint:fix": "eslint src --ext .ts --fix",
|
|
@@ -13,26 +12,22 @@
|
|
|
13
12
|
"start:prod": "cross-env NODE_ENV=production node dist/server.js"
|
|
14
13
|
},
|
|
15
14
|
"dependencies": {
|
|
16
|
-
"compression": "^1.7.4",
|
|
17
15
|
"cors": "^2.8.5",
|
|
18
|
-
"dotenv": "^
|
|
19
|
-
"express": "^
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"morgan": "^1.10.0"
|
|
16
|
+
"dotenv": "^17.2.3",
|
|
17
|
+
"express": "^5.2.1",
|
|
18
|
+
"helmet": "^8.1.0",
|
|
19
|
+
"morgan": "^1.10.1"
|
|
23
20
|
},
|
|
24
21
|
"devDependencies": {
|
|
25
|
-
"@types/
|
|
26
|
-
"@types/
|
|
27
|
-
"@types/
|
|
28
|
-
"@types/
|
|
29
|
-
"@types/node": "^22.10.5",
|
|
22
|
+
"@types/cors": "^2.8.19",
|
|
23
|
+
"@types/express": "^5.0.6",
|
|
24
|
+
"@types/morgan": "^1.9.10",
|
|
25
|
+
"@types/node": "^25.0.6",
|
|
30
26
|
"@typescript-eslint/eslint-plugin": "^8.52.0",
|
|
31
27
|
"@typescript-eslint/parser": "^8.52.0",
|
|
32
|
-
"cross-env": "^
|
|
28
|
+
"cross-env": "^10.1.0",
|
|
33
29
|
"eslint": "^9.39.2",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"typescript": "^5.7.2"
|
|
30
|
+
"tsx": "^4.21.0",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
37
32
|
}
|
|
38
33
|
}
|
|
@@ -1,62 +1,44 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import { errorHandler } from './middlewares/error.middleware';
|
|
1
|
+
import cors from "cors";
|
|
2
|
+
import express, { Application, NextFunction, Request, Response } from "express";
|
|
3
|
+
import helmet from "helmet";
|
|
4
|
+
import morgan from "morgan";
|
|
5
|
+
import { env } from "./config/env";
|
|
6
|
+
import { authRoutes } from "./features/auth/auth.route";
|
|
7
|
+
import { errorHandler } from "./middlewares/error.middleware";
|
|
9
8
|
|
|
10
9
|
// app initialization
|
|
11
10
|
const app: Application = express();
|
|
12
11
|
app.use(express.json());
|
|
13
12
|
|
|
14
|
-
// trust proxy when behind reverse proxy (like Heroku, Vercel, Load Balancers)
|
|
15
|
-
if (env.app.trust_proxy) {
|
|
16
|
-
app.set('trust proxy', 1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
13
|
// security headers
|
|
20
14
|
app.use(helmet());
|
|
21
15
|
|
|
22
|
-
// response compression
|
|
23
|
-
app.use(compression());
|
|
24
|
-
|
|
25
|
-
// rate limiting
|
|
26
|
-
const limiter = rateLimit({
|
|
27
|
-
windowMs: env.app.rateLimit.windowMs,
|
|
28
|
-
max: env.app.rateLimit.max,
|
|
29
|
-
standardHeaders: true,
|
|
30
|
-
legacyHeaders: false,
|
|
31
|
-
});
|
|
32
|
-
app.use(limiter);
|
|
33
|
-
|
|
34
16
|
// logging
|
|
35
|
-
if (env.
|
|
36
|
-
app.use(morgan(
|
|
17
|
+
if (env.isProduction) {
|
|
18
|
+
app.use(morgan("combined"));
|
|
37
19
|
} else {
|
|
38
|
-
app.use(morgan(
|
|
20
|
+
app.use(morgan("dev"));
|
|
39
21
|
}
|
|
40
22
|
|
|
41
23
|
// cors configuration
|
|
42
|
-
app.use(
|
|
43
|
-
cors({
|
|
44
|
-
origin: [env.app.site_url],
|
|
45
|
-
credentials: true,
|
|
46
|
-
})
|
|
47
|
-
);
|
|
24
|
+
app.use(cors());
|
|
48
25
|
|
|
49
26
|
// Home page route
|
|
50
|
-
app.get(
|
|
51
|
-
res.json({
|
|
52
|
-
title:
|
|
27
|
+
app.get("/", (_req: Request, res: Response) => {
|
|
28
|
+
res.status(200).json({
|
|
29
|
+
title: "Welcome to your Express app",
|
|
53
30
|
description:
|
|
54
|
-
|
|
31
|
+
"Built with StackKit - A production-ready Express template with TypeScript, security, and best practices.",
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
docs: "https://github.com/tariqul420/stackkit",
|
|
55
34
|
});
|
|
56
35
|
});
|
|
57
36
|
|
|
37
|
+
// routes
|
|
38
|
+
app.use("/api/auth", authRoutes);
|
|
39
|
+
|
|
58
40
|
// unhandled routes
|
|
59
|
-
app.use((req: Request,
|
|
41
|
+
app.use((req: Request, _res: Response, next: NextFunction) => {
|
|
60
42
|
const error: any = new Error(`Can't find ${req.originalUrl} on this server!`);
|
|
61
43
|
error.status = 404;
|
|
62
44
|
|
|
@@ -1,23 +1,12 @@
|
|
|
1
|
-
import dotenv from
|
|
2
|
-
import path from
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import path from "path";
|
|
3
3
|
|
|
4
|
-
dotenv.config({ path: path.join(process.cwd(),
|
|
4
|
+
dotenv.config({ path: path.join(process.cwd(), ".env") });
|
|
5
5
|
|
|
6
6
|
const env = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
site_url: process.env.SITE_URL || 'http://localhost:5173',
|
|
11
|
-
trust_proxy: (process.env.TRUST_PROXY || 'false') === 'true',
|
|
12
|
-
rateLimit: {
|
|
13
|
-
max: Number(process.env.RATE_LIMIT_MAX) || 100,
|
|
14
|
-
windowMs: Number(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
node: {
|
|
18
|
-
env: process.env.NODE_ENV || 'development',
|
|
19
|
-
isProduction: (process.env.NODE_ENV || 'development') === 'production',
|
|
20
|
-
},
|
|
7
|
+
port: Number(process.env.PORT) || 3000,
|
|
8
|
+
node_env: process.env.NODE_ENV || "development",
|
|
9
|
+
isProduction: (process.env.NODE_ENV || "development") === "production",
|
|
21
10
|
};
|
|
22
11
|
|
|
23
12
|
export { env };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from "express";
|
|
2
|
+
import { authServices } from "./auth.service";
|
|
3
|
+
|
|
4
|
+
const signup = async (req: Request, res: Response, next: NextFunction) => {
|
|
5
|
+
try {
|
|
6
|
+
const result = await authServices.signup(req.body);
|
|
7
|
+
|
|
8
|
+
res.status(201).json({
|
|
9
|
+
success: true,
|
|
10
|
+
message: "User registered successfully",
|
|
11
|
+
data: result,
|
|
12
|
+
});
|
|
13
|
+
} catch (error) {
|
|
14
|
+
next(error);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const signin = async (req: Request, res: Response, next: NextFunction) => {
|
|
19
|
+
try {
|
|
20
|
+
const { email, password } = req.body;
|
|
21
|
+
|
|
22
|
+
const result = await authServices.signin(email, password);
|
|
23
|
+
|
|
24
|
+
if (!result) {
|
|
25
|
+
res.status(401).json({
|
|
26
|
+
success: false,
|
|
27
|
+
message: "Invalid email or password",
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
res.status(200).json({
|
|
33
|
+
success: true,
|
|
34
|
+
message: "Login successful",
|
|
35
|
+
data: {
|
|
36
|
+
token: result.token,
|
|
37
|
+
user: result.user,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
next(error);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const authController = {
|
|
46
|
+
signup,
|
|
47
|
+
signin,
|
|
48
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import { authController } from "./auth.controller";
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
|
|
6
|
+
// routes
|
|
7
|
+
router.post("/signup", authController.signup);
|
|
8
|
+
router.post("/signin", authController.signin);
|
|
9
|
+
|
|
10
|
+
export const authRoutes = router;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
3
|
+
|
|
4
|
+
const signup = async (payload: Record<string, unknown>) => {
|
|
5
|
+
// your logic here
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const signin = async (
|
|
9
|
+
email: string,
|
|
10
|
+
password: string,
|
|
11
|
+
): Promise<{ token: string; user: any } | null> => {
|
|
12
|
+
// your logic here
|
|
13
|
+
return null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const authServices = {
|
|
17
|
+
signup,
|
|
18
|
+
signin,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type { User };
|