nodejs-structure-cli 1.0.5 → 1.0.6
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/package.json +1 -1
- package/templates/common/database/js/models/User.js.ejs +12 -0
- package/templates/common/database/js/models/User.js.mongoose.ejs +6 -2
- package/templates/common/database/ts/models/User.ts.ejs +13 -0
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +2 -0
- package/templates/common/src/controllers/authController.js.ejs +77 -14
- package/templates/common/src/controllers/authController.ts.ejs +77 -14
- package/templates/common/src/routes/authRoutes.js.ejs +3 -0
- package/templates/common/src/routes/authRoutes.ts.ejs +3 -0
package/package.json
CHANGED
|
@@ -10,6 +10,14 @@ class UserModel {
|
|
|
10
10
|
return this.mockData;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
static async findOne(query) {
|
|
14
|
+
const { where } = query || {};
|
|
15
|
+
if (where && where.email) {
|
|
16
|
+
return this.mockData.find((u) => u.email === where.email);
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
static async create(data) {
|
|
14
22
|
const { id, ...rest } = data;
|
|
15
23
|
const newUser = { id: String(this.mockData.length + 1), ...rest };
|
|
@@ -78,6 +86,10 @@ User.init(
|
|
|
78
86
|
type: DataTypes.DATE,
|
|
79
87
|
allowNull: true,
|
|
80
88
|
},
|
|
89
|
+
refresh_token: {
|
|
90
|
+
type: DataTypes.STRING,
|
|
91
|
+
allowNull: true,
|
|
92
|
+
},
|
|
81
93
|
},
|
|
82
94
|
{
|
|
83
95
|
sequelize,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
2
|
|
|
3
3
|
const UserSchema = new mongoose.Schema({
|
|
4
4
|
name: {
|
|
@@ -22,6 +22,10 @@ const UserSchema = new mongoose.Schema({
|
|
|
22
22
|
default: null
|
|
23
23
|
},
|
|
24
24
|
<%_ } _%>
|
|
25
|
+
refresh_token: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: null
|
|
28
|
+
},
|
|
25
29
|
createdAt: {
|
|
26
30
|
type: Date,
|
|
27
31
|
default: Date.now
|
|
@@ -32,4 +36,4 @@ const UserSchema = new mongoose.Schema({
|
|
|
32
36
|
}
|
|
33
37
|
});
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
export default mongoose.model('User', UserSchema);
|
|
@@ -4,6 +4,7 @@ export interface User {
|
|
|
4
4
|
email: string;
|
|
5
5
|
<% if (auth && auth !== 'None') { %>password?: string;<% } %>
|
|
6
6
|
<% if (includeMulter) { %>imageUrl?: string;<% } %>
|
|
7
|
+
refresh_token?: string;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export default class UserModel {
|
|
@@ -17,6 +18,14 @@ export default class UserModel {
|
|
|
17
18
|
return this.mockData;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
static async findOne(query: any) {
|
|
22
|
+
const { where } = query || {};
|
|
23
|
+
if (where && where.email) {
|
|
24
|
+
return this.mockData.find((u) => u.email === where.email);
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
20
29
|
static async create(data: Omit<User, 'id'> & { id?: string | number | null }) {
|
|
21
30
|
const { id: _, ...rest } = data;
|
|
22
31
|
const newUser: User = { id: String(this.mockData.length + 1), ...rest };
|
|
@@ -89,6 +98,10 @@ User.init(
|
|
|
89
98
|
type: DataTypes.DATE,
|
|
90
99
|
allowNull: true,
|
|
91
100
|
},
|
|
101
|
+
refresh_token: {
|
|
102
|
+
type: DataTypes.STRING,
|
|
103
|
+
allowNull: true,
|
|
104
|
+
},
|
|
92
105
|
},
|
|
93
106
|
{
|
|
94
107
|
sequelize,
|
|
@@ -5,6 +5,7 @@ export interface IUser extends Document {
|
|
|
5
5
|
email: string;
|
|
6
6
|
<% if (auth && auth !== 'None') { %>password?: string;<% } %>
|
|
7
7
|
<% if (includeMulter) { %>imageUrl?: string;<% } %>
|
|
8
|
+
refresh_token?: string;
|
|
8
9
|
createdAt: Date;
|
|
9
10
|
deletedAt?: Date | null;
|
|
10
11
|
}
|
|
@@ -21,6 +22,7 @@ const UserSchema: Schema = new Schema({
|
|
|
21
22
|
},
|
|
22
23
|
<% if (auth && auth !== 'None') { %>password: { type: String },<% } %>
|
|
23
24
|
<% if (includeMulter) { %>imageUrl: { type: String },<% } %>
|
|
25
|
+
refresh_token: { type: String, default: null },
|
|
24
26
|
createdAt: {
|
|
25
27
|
type: Date,
|
|
26
28
|
default: Date.now
|
|
@@ -35,26 +35,51 @@ export const login = async (req, res, next) => {
|
|
|
35
35
|
import jwt from 'jsonwebtoken';
|
|
36
36
|
import bcrypt from 'bcryptjs';
|
|
37
37
|
import { ApiError } from '../errors/ApiError<% if (language === 'TypeScript') { %>.ts<% } else { %>.js<% } %>';
|
|
38
|
+
import User from '<% if (architecture === 'MVC') { %>../models/User.js<% } else { %>../../infrastructure/database/models/User.js<% } %>';
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
const
|
|
40
|
+
const generateTokens = (user) => {
|
|
41
|
+
const accessToken = jwt.sign(
|
|
42
|
+
{ id: user.id || user._id, email: user.email },
|
|
43
|
+
process.env.JWT_SECRET || 'secret',
|
|
44
|
+
{ expiresIn: process.env.JWT_EXPIRES_IN || '15m' }
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const refreshToken = jwt.sign(
|
|
48
|
+
{ id: user.id || user._id },
|
|
49
|
+
process.env.JWT_REFRESH_SECRET || 'refresh_secret',
|
|
50
|
+
{ expiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d' }
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return { accessToken, refreshToken };
|
|
54
|
+
};
|
|
41
55
|
|
|
42
56
|
export const register = async (req, res, next) => {
|
|
43
57
|
try {
|
|
44
58
|
const { email, password, name } = req.body;
|
|
45
59
|
|
|
46
|
-
const existingUser =
|
|
60
|
+
const existingUser = await User.findOne(<% if (database === 'MongoDB') { %>{ email }<% } else { %>{ where: { email } }<% } %>);
|
|
47
61
|
if (existingUser) {
|
|
48
62
|
throw new ApiError(400, 'User already exists');
|
|
49
63
|
}
|
|
50
64
|
|
|
51
65
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
52
|
-
const
|
|
53
|
-
|
|
66
|
+
const user = await User.create({ email, password: hashedPassword, name });
|
|
67
|
+
|
|
68
|
+
const { accessToken, refreshToken } = generateTokens(user);
|
|
69
|
+
|
|
70
|
+
// Store refresh token in DB
|
|
71
|
+
<% if (database === 'MongoDB') { %>
|
|
72
|
+
user.refresh_token = refreshToken;
|
|
73
|
+
await user.save();
|
|
74
|
+
<% } else { %>
|
|
75
|
+
await User.update({ refresh_token: refreshToken }, { where: { id: user.id } });
|
|
76
|
+
<% } %>
|
|
54
77
|
|
|
55
78
|
res.status(201).json({
|
|
56
79
|
message: 'User registered successfully',
|
|
57
|
-
|
|
80
|
+
accessToken,
|
|
81
|
+
refreshToken,
|
|
82
|
+
user: { id: user.id || user._id, email: user.email, name: user.name }
|
|
58
83
|
});
|
|
59
84
|
} catch (error) {
|
|
60
85
|
next(error);
|
|
@@ -65,7 +90,7 @@ export const login = async (req, res, next) => {
|
|
|
65
90
|
try {
|
|
66
91
|
const { email, password } = req.body;
|
|
67
92
|
|
|
68
|
-
const user =
|
|
93
|
+
const user = await User.findOne(<% if (database === 'MongoDB') { %>{ email }<% } else { %>{ where: { email } }<% } %>);
|
|
69
94
|
if (!user) {
|
|
70
95
|
throw new ApiError(401, 'Invalid credentials');
|
|
71
96
|
}
|
|
@@ -75,21 +100,59 @@ export const login = async (req, res, next) => {
|
|
|
75
100
|
throw new ApiError(401, 'Invalid credentials');
|
|
76
101
|
}
|
|
77
102
|
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
103
|
+
const { accessToken, refreshToken } = generateTokens(user);
|
|
104
|
+
|
|
105
|
+
// Update refresh token in DB
|
|
106
|
+
<% if (database === 'MongoDB') { %>
|
|
107
|
+
user.refresh_token = refreshToken;
|
|
108
|
+
await user.save();
|
|
109
|
+
<% } else { %>
|
|
110
|
+
await User.update({ refresh_token: refreshToken }, { where: { id: user.id } });
|
|
111
|
+
<% } %>
|
|
83
112
|
|
|
84
113
|
res.status(200).json({
|
|
85
114
|
message: 'Login successful',
|
|
86
|
-
|
|
87
|
-
|
|
115
|
+
accessToken,
|
|
116
|
+
refreshToken,
|
|
117
|
+
user: { id: user.id || user._id, email: user.email, name: user.name }
|
|
88
118
|
});
|
|
89
119
|
} catch (error) {
|
|
90
120
|
next(error);
|
|
91
121
|
}
|
|
92
122
|
};
|
|
123
|
+
|
|
124
|
+
export const refreshToken = async (req, res, next) => {
|
|
125
|
+
try {
|
|
126
|
+
const { token } = req.body;
|
|
127
|
+
if (!token) {
|
|
128
|
+
throw new ApiError(400, 'Refresh token is required');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const decoded = jwt.verify(token, process.env.JWT_REFRESH_SECRET || 'refresh_secret');
|
|
132
|
+
const user = await User.findOne(<% if (database === 'MongoDB') { %>{ _id: decoded.id, refresh_token: token }<% } else { %>{ where: { id: decoded.id, refresh_token: token } }<% } %>);
|
|
133
|
+
|
|
134
|
+
if (!user) {
|
|
135
|
+
throw new ApiError(401, 'Invalid refresh token');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const tokens = generateTokens(user);
|
|
139
|
+
|
|
140
|
+
// Update refresh token in DB (Rotation)
|
|
141
|
+
<% if (database === 'MongoDB') { %>
|
|
142
|
+
user.refresh_token = tokens.refreshToken;
|
|
143
|
+
await user.save();
|
|
144
|
+
<% } else { %>
|
|
145
|
+
await User.update({ refresh_token: tokens.refreshToken }, { where: { id: user.id } });
|
|
146
|
+
<% } %>
|
|
147
|
+
|
|
148
|
+
res.status(200).json({
|
|
149
|
+
message: 'Token refreshed successfully',
|
|
150
|
+
...tokens
|
|
151
|
+
});
|
|
152
|
+
} catch (error) {
|
|
153
|
+
next(new ApiError(401, 'Invalid or expired refresh token'));
|
|
154
|
+
}
|
|
155
|
+
};
|
|
93
156
|
<% } else if (auth === 'OAuth' || googleLogin === 'Google Login') { %>
|
|
94
157
|
export const googleCallback = (req, res) => {
|
|
95
158
|
// Passport handles the authentication, we just redirect or respond
|
|
@@ -36,26 +36,51 @@ export const login = async (req: Request, res: Response, next: NextFunction) =>
|
|
|
36
36
|
import jwt from 'jsonwebtoken';
|
|
37
37
|
import bcrypt from 'bcryptjs';
|
|
38
38
|
import { ApiError } from '../errors/ApiError';
|
|
39
|
+
import User from '<% if (architecture === 'MVC') { %>../models/User<% if (language === 'TypeScript') { %><% } else { %>.js<% } %><% } else { %>../../infrastructure/database/models/User<% if (language === 'TypeScript') { %><% } else { %>.js<% } %><% } %>';
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
const
|
|
41
|
+
const generateTokens = (user: any) => {
|
|
42
|
+
const accessToken = jwt.sign(
|
|
43
|
+
{ id: user.id || user._id, email: user.email },
|
|
44
|
+
process.env.JWT_SECRET || 'secret',
|
|
45
|
+
{ expiresIn: process.env.JWT_EXPIRES_IN || '15m' }
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const refreshToken = jwt.sign(
|
|
49
|
+
{ id: user.id || user._id },
|
|
50
|
+
process.env.JWT_REFRESH_SECRET || 'refresh_secret',
|
|
51
|
+
{ expiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d' }
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return { accessToken, refreshToken };
|
|
55
|
+
};
|
|
42
56
|
|
|
43
57
|
export const register = async (req: Request, res: Response, next: NextFunction) => {
|
|
44
58
|
try {
|
|
45
59
|
const { email, password, name } = req.body;
|
|
46
60
|
|
|
47
|
-
const existingUser =
|
|
61
|
+
const existingUser = await (User as any).findOne(<% if (database === 'MongoDB') { %>{ email }<% } else { %>{ where: { email } }<% } %>);
|
|
48
62
|
if (existingUser) {
|
|
49
63
|
throw new ApiError(400, 'User already exists');
|
|
50
64
|
}
|
|
51
65
|
|
|
52
66
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
53
|
-
const
|
|
54
|
-
|
|
67
|
+
const user = await (User as any).create({ email, password: hashedPassword, name });
|
|
68
|
+
|
|
69
|
+
const { accessToken, refreshToken } = generateTokens(user);
|
|
70
|
+
|
|
71
|
+
// Store refresh token in DB
|
|
72
|
+
<% if (database === 'MongoDB') { %>
|
|
73
|
+
user.refresh_token = refreshToken;
|
|
74
|
+
await user.save();
|
|
75
|
+
<% } else { %>
|
|
76
|
+
await (User as any).update({ refresh_token: refreshToken }, { where: { id: user.id } });
|
|
77
|
+
<% } %>
|
|
55
78
|
|
|
56
79
|
res.status(201).json({
|
|
57
80
|
message: 'User registered successfully',
|
|
58
|
-
|
|
81
|
+
accessToken,
|
|
82
|
+
refreshToken,
|
|
83
|
+
user: { id: user.id || user._id, email: user.email, name: user.name }
|
|
59
84
|
});
|
|
60
85
|
} catch (error: any) {
|
|
61
86
|
next(error);
|
|
@@ -66,7 +91,7 @@ export const login = async (req: Request, res: Response, next: NextFunction) =>
|
|
|
66
91
|
try {
|
|
67
92
|
const { email, password } = req.body;
|
|
68
93
|
|
|
69
|
-
const user =
|
|
94
|
+
const user = await (User as any).findOne(<% if (database === 'MongoDB') { %>{ email }<% } else { %>{ where: { email } }<% } %>);
|
|
70
95
|
if (!user) {
|
|
71
96
|
throw new ApiError(401, 'Invalid credentials');
|
|
72
97
|
}
|
|
@@ -76,21 +101,59 @@ export const login = async (req: Request, res: Response, next: NextFunction) =>
|
|
|
76
101
|
throw new ApiError(401, 'Invalid credentials');
|
|
77
102
|
}
|
|
78
103
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
104
|
+
const { accessToken, refreshToken } = generateTokens(user);
|
|
105
|
+
|
|
106
|
+
// Update refresh token in DB
|
|
107
|
+
<% if (database === 'MongoDB') { %>
|
|
108
|
+
user.refresh_token = refreshToken;
|
|
109
|
+
await user.save();
|
|
110
|
+
<% } else { %>
|
|
111
|
+
await (User as any).update({ refresh_token: refreshToken }, { where: { id: user.id } });
|
|
112
|
+
<% } %>
|
|
84
113
|
|
|
85
114
|
res.status(200).json({
|
|
86
115
|
message: 'Login successful',
|
|
87
|
-
|
|
88
|
-
|
|
116
|
+
accessToken,
|
|
117
|
+
refreshToken,
|
|
118
|
+
user: { id: user.id || user._id, email: user.email, name: user.name }
|
|
89
119
|
});
|
|
90
120
|
} catch (error: any) {
|
|
91
121
|
next(error);
|
|
92
122
|
}
|
|
93
123
|
};
|
|
124
|
+
|
|
125
|
+
export const refreshToken = async (req: Request, res: Response, next: NextFunction) => {
|
|
126
|
+
try {
|
|
127
|
+
const { token } = req.body;
|
|
128
|
+
if (!token) {
|
|
129
|
+
throw new ApiError(400, 'Refresh token is required');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const decoded = jwt.verify(token, process.env.JWT_REFRESH_SECRET || 'refresh_secret') as any;
|
|
133
|
+
const user = await (User as any).findOne(<% if (database === 'MongoDB') { %>{ _id: decoded.id, refresh_token: token }<% } else { %>{ where: { id: decoded.id, refresh_token: token } }<% } %>);
|
|
134
|
+
|
|
135
|
+
if (!user) {
|
|
136
|
+
throw new ApiError(401, 'Invalid refresh token');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const tokens = generateTokens(user);
|
|
140
|
+
|
|
141
|
+
// Update refresh token in DB (Rotation)
|
|
142
|
+
<% if (database === 'MongoDB') { %>
|
|
143
|
+
user.refresh_token = tokens.refreshToken;
|
|
144
|
+
await user.save();
|
|
145
|
+
<% } else { %>
|
|
146
|
+
await (User as any).update({ refresh_token: tokens.refreshToken }, { where: { id: user.id } });
|
|
147
|
+
<% } %>
|
|
148
|
+
|
|
149
|
+
res.status(200).json({
|
|
150
|
+
message: 'Token refreshed successfully',
|
|
151
|
+
...tokens
|
|
152
|
+
});
|
|
153
|
+
} catch (error: any) {
|
|
154
|
+
next(new ApiError(401, 'Invalid or expired refresh token'));
|
|
155
|
+
}
|
|
156
|
+
};
|
|
94
157
|
<% } else if (auth === 'OAuth' || googleLogin === 'Google Login') { %>
|
|
95
158
|
export const googleCallback = (req: Request, res: Response) => {
|
|
96
159
|
res.status(200).json({
|
|
@@ -7,6 +7,9 @@ const router = express.Router();
|
|
|
7
7
|
<% if (auth === 'Better-Auth' || auth === 'JWT') { %>
|
|
8
8
|
router.post('/register', authController.register);
|
|
9
9
|
router.post('/login', authController.login);
|
|
10
|
+
<% if (auth === 'JWT') { %>
|
|
11
|
+
router.post('/refresh-token', authController.refreshToken);
|
|
12
|
+
<% } %>
|
|
10
13
|
<% } %>
|
|
11
14
|
|
|
12
15
|
<% if (auth === 'OAuth' || googleLogin === 'Google Login') { %>
|
|
@@ -7,6 +7,9 @@ const router: Router = express.Router();
|
|
|
7
7
|
<% if (auth === 'Better-Auth' || auth === 'JWT') { %>
|
|
8
8
|
router.post('/register', authController.register);
|
|
9
9
|
router.post('/login', authController.login);
|
|
10
|
+
<% if (auth === 'JWT') { %>
|
|
11
|
+
router.post('/refresh-token', authController.refreshToken);
|
|
12
|
+
<% } %>
|
|
10
13
|
<% } %>
|
|
11
14
|
|
|
12
15
|
<% if (auth === 'OAuth' || googleLogin === 'Google Login') { %>
|