underpost 2.8.872 → 2.8.873
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/.env.development +2 -1
- package/.env.production +2 -1
- package/.env.test +2 -1
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +1 -1
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +2 -4
- package/README.md +22 -2
- package/bin/build.js +4 -0
- package/bin/deploy.js +4 -0
- package/cli.md +3 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +138 -0
- package/manifests/deployment/dd-test-development/proxy.yaml +26 -0
- package/package.json +5 -1
- package/src/api/core/core.router.js +2 -1
- package/src/api/default/default.controller.js +6 -1
- package/src/api/default/default.router.js +6 -2
- package/src/api/default/default.service.js +10 -1
- package/src/api/file/file.router.js +2 -1
- package/src/api/test/test.router.js +1 -1
- package/src/api/user/postman_collection.json +216 -0
- package/src/api/user/user.controller.js +25 -60
- package/src/api/user/user.model.js +29 -7
- package/src/api/user/user.router.js +6 -3
- package/src/api/user/user.service.js +80 -32
- package/src/cli/baremetal.js +33 -3
- package/src/cli/cloud-init.js +11 -0
- package/src/cli/deploy.js +5 -2
- package/src/cli/index.js +1 -0
- package/src/cli/lxd.js +7 -0
- package/src/cli/run.js +17 -5
- package/src/cli/ssh.js +20 -6
- package/src/client/components/core/AgGrid.js +28 -6
- package/src/client/components/core/Auth.js +98 -55
- package/src/client/components/core/LogIn.js +16 -23
- package/src/client/components/core/LogOut.js +5 -1
- package/src/client/components/core/Pagination.js +202 -9
- package/src/client/components/core/Router.js +30 -3
- package/src/client/components/core/SignUp.js +1 -2
- package/src/client/components/default/LogInDefault.js +0 -6
- package/src/client/components/default/LogOutDefault.js +0 -16
- package/src/client/services/core/core.service.js +5 -1
- package/src/client/services/default/default.management.js +115 -18
- package/src/client/services/default/default.service.js +9 -4
- package/src/client/services/user/user.management.js +6 -0
- package/src/client/services/user/user.service.js +11 -4
- package/src/index.js +24 -2
- package/src/runtime/lampp/Lampp.js +89 -2
- package/src/runtime/xampp/Xampp.js +48 -1
- package/src/server/auth.js +518 -155
- package/src/server/conf.js +19 -1
- package/src/server/runtime.js +62 -228
- package/src/server/ssr.js +85 -0
- package/src/server/valkey.js +2 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
{
|
|
2
|
+
"info": {
|
|
3
|
+
"_postman_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
|
|
4
|
+
"name": "User API",
|
|
5
|
+
"description": "A collection of requests for the User API.",
|
|
6
|
+
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
|
7
|
+
},
|
|
8
|
+
"item": [
|
|
9
|
+
{
|
|
10
|
+
"name": "user",
|
|
11
|
+
"item": [
|
|
12
|
+
{
|
|
13
|
+
"name": "Log in",
|
|
14
|
+
"request": {
|
|
15
|
+
"method": "POST",
|
|
16
|
+
"header": [
|
|
17
|
+
{
|
|
18
|
+
"key": "Content-Type",
|
|
19
|
+
"value": "application/json",
|
|
20
|
+
"type": "text"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"body": {
|
|
24
|
+
"mode": "raw",
|
|
25
|
+
"raw": "{\n \"email\": \"user@example.com\",\n \"password\": \"Password123\"\n}",
|
|
26
|
+
"options": {
|
|
27
|
+
"raw": {
|
|
28
|
+
"language": "json"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"url": {
|
|
33
|
+
"raw": "{{baseUrl}}/user/auth",
|
|
34
|
+
"host": ["{{baseUrl}}"],
|
|
35
|
+
"path": ["user", "auth"]
|
|
36
|
+
},
|
|
37
|
+
"description": "This endpoint get a JWT for authenticated user"
|
|
38
|
+
},
|
|
39
|
+
"response": [
|
|
40
|
+
{
|
|
41
|
+
"name": "User logged in successfully",
|
|
42
|
+
"originalRequest": {
|
|
43
|
+
"method": "POST",
|
|
44
|
+
"header": [],
|
|
45
|
+
"body": {
|
|
46
|
+
"mode": "raw",
|
|
47
|
+
"raw": ""
|
|
48
|
+
},
|
|
49
|
+
"url": {
|
|
50
|
+
"raw": "{{baseUrl}}/user/auth",
|
|
51
|
+
"host": ["{{baseUrl}}"],
|
|
52
|
+
"path": ["user", "auth"]
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"status": "OK",
|
|
56
|
+
"code": 200,
|
|
57
|
+
"_postman_previewlanguage": "json",
|
|
58
|
+
"header": [
|
|
59
|
+
{
|
|
60
|
+
"key": "Content-Type",
|
|
61
|
+
"value": "application/json"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"cookie": [],
|
|
65
|
+
"body": "{\n \"status\": \"success\",\n \"data\": {\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7Il9pZCI6IjY2YzM3N2Y1N2Y5OWU1OTY5YjgxZG...\",\n \"user\": {\n \"_id\": \"66c377f57f99e5969b81de89\",\n \"email\": \"user@example.com\",\n \"emailConfirmed\": false,\n \"username\": \"user123\",\n \"role\": \"user\",\n \"profileImageId\": \"66c377f57f99e5969b81de87\"\n }\n }\n}"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "Create user",
|
|
71
|
+
"request": {
|
|
72
|
+
"method": "POST",
|
|
73
|
+
"header": [
|
|
74
|
+
{
|
|
75
|
+
"key": "Content-Type",
|
|
76
|
+
"value": "application/json",
|
|
77
|
+
"type": "text"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"body": {
|
|
81
|
+
"mode": "raw",
|
|
82
|
+
"raw": "{\n \"username\": \"user123\",\n \"password\": \"Password123\",\n \"email\": \"user@example.com\"\n}",
|
|
83
|
+
"options": {
|
|
84
|
+
"raw": {
|
|
85
|
+
"language": "json"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"url": {
|
|
90
|
+
"raw": "{{baseUrl}}/user",
|
|
91
|
+
"host": ["{{baseUrl}}"],
|
|
92
|
+
"path": ["user"]
|
|
93
|
+
},
|
|
94
|
+
"description": "This endpoint will create a new user account"
|
|
95
|
+
},
|
|
96
|
+
"response": []
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "Get user data by ID",
|
|
100
|
+
"request": {
|
|
101
|
+
"auth": {
|
|
102
|
+
"type": "bearer",
|
|
103
|
+
"bearer": [
|
|
104
|
+
{
|
|
105
|
+
"key": "token",
|
|
106
|
+
"value": "{{authToken}}",
|
|
107
|
+
"type": "string"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"method": "GET",
|
|
112
|
+
"header": [],
|
|
113
|
+
"url": {
|
|
114
|
+
"raw": "{{baseUrl}}/user/:id",
|
|
115
|
+
"host": ["{{baseUrl}}"],
|
|
116
|
+
"path": ["user", ":id"],
|
|
117
|
+
"variable": [
|
|
118
|
+
{
|
|
119
|
+
"key": "id",
|
|
120
|
+
"value": "your_user_id"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
"description": "This endpoint get user data by ID"
|
|
125
|
+
},
|
|
126
|
+
"response": []
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "Update user data by ID",
|
|
130
|
+
"request": {
|
|
131
|
+
"auth": {
|
|
132
|
+
"type": "bearer",
|
|
133
|
+
"bearer": [
|
|
134
|
+
{
|
|
135
|
+
"key": "token",
|
|
136
|
+
"value": "{{authToken}}",
|
|
137
|
+
"type": "string"
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
"method": "PUT",
|
|
142
|
+
"header": [
|
|
143
|
+
{
|
|
144
|
+
"key": "Content-Type",
|
|
145
|
+
"value": "application/json",
|
|
146
|
+
"type": "text"
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"body": {
|
|
150
|
+
"mode": "raw",
|
|
151
|
+
"raw": "{\n \"username\": \"newuser123\",\n \"password\": \"NewPassword123\",\n \"email\": \"newuser@example.com\"\n}",
|
|
152
|
+
"options": {
|
|
153
|
+
"raw": {
|
|
154
|
+
"language": "json"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"url": {
|
|
159
|
+
"raw": "{{baseUrl}}/user/:id",
|
|
160
|
+
"host": ["{{baseUrl}}"],
|
|
161
|
+
"path": ["user", ":id"],
|
|
162
|
+
"variable": [
|
|
163
|
+
{
|
|
164
|
+
"key": "id",
|
|
165
|
+
"value": "your_user_id"
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
"description": "This endpoint will update user data by ID"
|
|
170
|
+
},
|
|
171
|
+
"response": []
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"name": "Delete user data by ID",
|
|
175
|
+
"request": {
|
|
176
|
+
"auth": {
|
|
177
|
+
"type": "bearer",
|
|
178
|
+
"bearer": [
|
|
179
|
+
{
|
|
180
|
+
"key": "token",
|
|
181
|
+
"value": "{{authToken}}",
|
|
182
|
+
"type": "string"
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
"method": "DELETE",
|
|
187
|
+
"header": [],
|
|
188
|
+
"url": {
|
|
189
|
+
"raw": "{{baseUrl}}/user/:id",
|
|
190
|
+
"host": ["{{baseUrl}}"],
|
|
191
|
+
"path": ["user", ":id"],
|
|
192
|
+
"variable": [
|
|
193
|
+
{
|
|
194
|
+
"key": "id",
|
|
195
|
+
"value": "your_user_id"
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
"description": "This endpoint deletes user data by ID, the path ID must match with the ID of the authenticated user"
|
|
200
|
+
},
|
|
201
|
+
"response": []
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
"variable": [
|
|
207
|
+
{
|
|
208
|
+
"key": "baseUrl",
|
|
209
|
+
"value": "http://localhost:3000/api"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"key": "authToken",
|
|
213
|
+
"value": ""
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
@@ -3,68 +3,33 @@ import { UserService } from './user.service.js';
|
|
|
3
3
|
|
|
4
4
|
const logger = loggerFactory(import.meta);
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return
|
|
11
|
-
status: 'success',
|
|
12
|
-
data: result,
|
|
13
|
-
});
|
|
14
|
-
} catch (error) {
|
|
15
|
-
logger.error(error, error.stack);
|
|
16
|
-
return res.status(400).json({
|
|
17
|
-
status: 'error',
|
|
18
|
-
message: error.message,
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
get: async (req, res, options) => {
|
|
23
|
-
try {
|
|
24
|
-
const result = await UserService.get(req, res, options);
|
|
25
|
-
if (result instanceof Buffer) return res.status(200).end(result);
|
|
26
|
-
return res.status(200).json({
|
|
27
|
-
status: 'success',
|
|
28
|
-
data: result,
|
|
29
|
-
});
|
|
30
|
-
} catch (error) {
|
|
31
|
-
logger.error(error, error.stack);
|
|
32
|
-
return res.status(400).json({
|
|
33
|
-
status: 'error',
|
|
34
|
-
message: error.message,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
delete: async (req, res, options) => {
|
|
39
|
-
try {
|
|
40
|
-
const result = await UserService.delete(req, res, options);
|
|
41
|
-
return res.status(200).json({
|
|
42
|
-
status: 'success',
|
|
43
|
-
data: result,
|
|
44
|
-
});
|
|
45
|
-
} catch (error) {
|
|
46
|
-
logger.error(error, error.stack);
|
|
47
|
-
return res.status(400).json({
|
|
48
|
-
status: 'error',
|
|
49
|
-
message: error.message,
|
|
50
|
-
});
|
|
6
|
+
const handleRequest = (serviceMethod) => async (req, res, options) => {
|
|
7
|
+
try {
|
|
8
|
+
const result = await serviceMethod(req, res, options);
|
|
9
|
+
if (res.headersSent) {
|
|
10
|
+
return;
|
|
51
11
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const result = await UserService.put(req, res, options);
|
|
56
|
-
return res.status(200).json({
|
|
57
|
-
status: 'success',
|
|
58
|
-
data: result,
|
|
59
|
-
});
|
|
60
|
-
} catch (error) {
|
|
61
|
-
logger.error(error, error.stack);
|
|
62
|
-
return res.status(400).json({
|
|
63
|
-
status: 'error',
|
|
64
|
-
message: error.message,
|
|
65
|
-
});
|
|
12
|
+
if (result instanceof Buffer) {
|
|
13
|
+
return res.status(200).end(result);
|
|
66
14
|
}
|
|
67
|
-
|
|
15
|
+
return res.status(200).json({
|
|
16
|
+
status: 'success',
|
|
17
|
+
data: result,
|
|
18
|
+
});
|
|
19
|
+
} catch (error) {
|
|
20
|
+
logger.error(error, error.stack);
|
|
21
|
+
return res.status(400).json({
|
|
22
|
+
status: 'error',
|
|
23
|
+
message: error.message,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const UserController = {
|
|
29
|
+
post: handleRequest(UserService.post),
|
|
30
|
+
get: handleRequest(UserService.get),
|
|
31
|
+
delete: handleRequest(UserService.delete),
|
|
32
|
+
put: handleRequest(UserService.put),
|
|
68
33
|
};
|
|
69
34
|
|
|
70
35
|
export { UserController };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Schema, model } from 'mongoose';
|
|
2
2
|
import validator from 'validator';
|
|
3
3
|
import { userRoleEnum } from '../../client/components/core/CommonJs.js';
|
|
4
|
-
|
|
4
|
+
import crypto from 'crypto';
|
|
5
5
|
// https://mongoosejs.com/docs/2.7.x/docs/schematypes.html
|
|
6
6
|
|
|
7
7
|
const UserSchema = new Schema(
|
|
@@ -22,6 +22,19 @@ const UserSchema = new Schema(
|
|
|
22
22
|
password: { type: String, trim: true, required: 'Password is required' },
|
|
23
23
|
username: { type: String, trim: true, unique: true, required: 'Username is required' },
|
|
24
24
|
role: { type: String, enum: userRoleEnum, default: 'guest' },
|
|
25
|
+
activeSessions: {
|
|
26
|
+
type: [
|
|
27
|
+
{
|
|
28
|
+
tokenHash: { type: String, required: true },
|
|
29
|
+
ip: { type: String },
|
|
30
|
+
userAgent: { type: String },
|
|
31
|
+
expiresAt: { type: Date, required: true },
|
|
32
|
+
host: { type: String },
|
|
33
|
+
path: { type: String },
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
default: [],
|
|
37
|
+
},
|
|
25
38
|
profileImageId: { type: Schema.Types.ObjectId, ref: 'File' },
|
|
26
39
|
phoneNumbers: [
|
|
27
40
|
{
|
|
@@ -63,15 +76,24 @@ const UserDto = {
|
|
|
63
76
|
return { _id: 1, username: 1, email: 1, role: 1, emailConfirmed: 1, profileImageId: 1 };
|
|
64
77
|
},
|
|
65
78
|
getAll: () => {
|
|
66
|
-
return { _id: 1
|
|
79
|
+
return { _id: 1 };
|
|
67
80
|
},
|
|
68
81
|
},
|
|
69
82
|
auth: {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
payload: (user, jwtid, ip, userAgent, host, path) => {
|
|
84
|
+
const tokenPayload = {
|
|
85
|
+
_id: user._id.toString(),
|
|
86
|
+
role: user.role,
|
|
87
|
+
email: user.email,
|
|
88
|
+
ip,
|
|
89
|
+
userAgent,
|
|
90
|
+
host,
|
|
91
|
+
path,
|
|
92
|
+
jwtid: jwtid ?? crypto.randomBytes(8).toString('hex'),
|
|
93
|
+
refreshExpiresAt: Date.now() + parseInt(process.env.REFRESH_EXPIRE_MINUTES) * 60 * 1000,
|
|
94
|
+
};
|
|
95
|
+
return tokenPayload;
|
|
96
|
+
},
|
|
75
97
|
},
|
|
76
98
|
};
|
|
77
99
|
|
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { hashPassword } from '../../server/auth.js';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import { loggerFactory } from '../../server/logger.js';
|
|
4
4
|
import { UserController } from './user.controller.js';
|
|
5
5
|
import express from 'express';
|
|
6
6
|
import { DataBaseProvider } from '../../db/DataBaseProvider.js';
|
|
7
7
|
import { FileFactory } from '../file/file.service.js';
|
|
8
|
+
import { s4 } from '../../client/components/core/CommonJs.js';
|
|
8
9
|
|
|
9
10
|
const logger = loggerFactory(import.meta);
|
|
10
11
|
|
|
11
12
|
const UserRouter = (options) => {
|
|
12
13
|
const router = express.Router();
|
|
14
|
+
const authMiddleware = options.authMiddleware;
|
|
13
15
|
|
|
14
16
|
(async () => {
|
|
15
17
|
// admin user seed
|
|
16
18
|
try {
|
|
17
19
|
const models = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models;
|
|
20
|
+
let adminUser;
|
|
18
21
|
if (models.User) {
|
|
19
|
-
|
|
22
|
+
adminUser = await models.User.findOne({ role: 'admin' });
|
|
20
23
|
if (!adminUser) {
|
|
21
24
|
const defaultPassword = process.env.DEFAULT_ADMIN_PASSWORD || 'changethis';
|
|
22
|
-
const hashedPassword = hashPassword(defaultPassword);
|
|
25
|
+
const hashedPassword = await hashPassword(defaultPassword);
|
|
23
26
|
|
|
24
27
|
const result = await models.User.create({
|
|
25
28
|
username: 'admin',
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { loggerFactory } from '../../server/logger.js';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
hashPassword,
|
|
4
|
+
verifyPassword,
|
|
5
|
+
verifyJWT,
|
|
6
|
+
createSessionAndUserToken,
|
|
7
|
+
createUserAndSession,
|
|
8
|
+
refreshSessionAndToken,
|
|
9
|
+
hashToken,
|
|
10
|
+
jwtSign,
|
|
11
|
+
getBearerToken,
|
|
12
|
+
validatePasswordMiddleware,
|
|
13
|
+
} from '../../server/auth.js';
|
|
3
14
|
import { MailerProvider } from '../../mailer/MailerProvider.js';
|
|
4
15
|
import { CoreWsMailerManagement } from '../../ws/core/management/core.ws.mailer.js';
|
|
5
16
|
import { CoreWsEmit } from '../../ws/core/core.ws.emit.js';
|
|
@@ -27,8 +38,8 @@ const UserService = {
|
|
|
27
38
|
|
|
28
39
|
if (!user) throw new Error('Email address does not exist');
|
|
29
40
|
|
|
30
|
-
const token =
|
|
31
|
-
const payloadToken =
|
|
41
|
+
const token = jwtSign({ email: req.body.email }, options, 15);
|
|
42
|
+
const payloadToken = jwtSign({ email: req.body.email }, options, 15);
|
|
32
43
|
const id = `${options.host}${options.path}`;
|
|
33
44
|
const translate = MailerProvider.instance[id].translateTemplates.recoverEmail;
|
|
34
45
|
const recoverUrl = `${process.env.NODE_ENV === 'development' ? 'http://' : 'https://'}${req.body.hostname}${
|
|
@@ -65,7 +76,7 @@ const UserService = {
|
|
|
65
76
|
if (req.path.startsWith('/mailer') && req.params.id === 'verify-email') {
|
|
66
77
|
if (!validator.isEmail(req.body.email)) throw { message: 'invalid email' };
|
|
67
78
|
|
|
68
|
-
const token =
|
|
79
|
+
const token = jwtSign({ email: req.body.email }, options, 15);
|
|
69
80
|
const id = `${options.host}${options.path}`;
|
|
70
81
|
const user = await User.findById(req.auth.user._id);
|
|
71
82
|
|
|
@@ -145,8 +156,13 @@ const UserService = {
|
|
|
145
156
|
runValidators: true,
|
|
146
157
|
},
|
|
147
158
|
);
|
|
159
|
+
|
|
160
|
+
const { jwtid } = await createSessionAndUserToken(user, User, req, res, options);
|
|
148
161
|
return {
|
|
149
|
-
token:
|
|
162
|
+
token: jwtSign(
|
|
163
|
+
UserDto.auth.payload(user, jwtid, req.ip, req.headers['user-agent'], options.host, options.path),
|
|
164
|
+
options,
|
|
165
|
+
),
|
|
150
166
|
user,
|
|
151
167
|
};
|
|
152
168
|
} else throw new Error(accountLocketMessage());
|
|
@@ -193,33 +209,24 @@ const UserService = {
|
|
|
193
209
|
} catch (error) {
|
|
194
210
|
logger.error(error, { params: req.params, body: req.body });
|
|
195
211
|
}
|
|
196
|
-
throw new Error(`
|
|
212
|
+
throw new Error(`Invalid credentials. Remaining attempts: ${5 - user.failedLoginAttempts}`);
|
|
197
213
|
}
|
|
198
|
-
} else throw new Error('
|
|
214
|
+
} else throw new Error('Invalid credentials');
|
|
199
215
|
|
|
200
216
|
case 'guest': {
|
|
201
217
|
const user = await ValkeyAPI.valkeyObjectFactory(options, 'user');
|
|
202
218
|
await ValkeyAPI.setValkeyObject(options, user.email, user);
|
|
203
219
|
return {
|
|
204
|
-
token:
|
|
220
|
+
token: jwtSign(
|
|
221
|
+
UserDto.auth.payload(user, null, req.ip, req.headers['user-agent'], options.host, options.path),
|
|
222
|
+
options,
|
|
223
|
+
),
|
|
205
224
|
user: selectDtoFactory(user, UserDto.select.get()),
|
|
206
225
|
};
|
|
207
226
|
}
|
|
208
227
|
|
|
209
228
|
default: {
|
|
210
|
-
|
|
211
|
-
if (validatePassword.status === 'error') throw new Error(validatePassword.message);
|
|
212
|
-
req.body.password = await hashPassword(req.body.password);
|
|
213
|
-
req.body.role = req.body.role === 'guest' ? 'guest' : 'user';
|
|
214
|
-
req.body.profileImageId = await options.getDefaultProfileImageId(File);
|
|
215
|
-
const { _id } = await new User(req.body).save();
|
|
216
|
-
if (_id) {
|
|
217
|
-
const user = await User.findOne({ _id }).select(UserDto.select.get());
|
|
218
|
-
return {
|
|
219
|
-
token: hashJWT({ user: UserDto.auth.payload(user) }),
|
|
220
|
-
user,
|
|
221
|
-
};
|
|
222
|
-
} else throw new Error('failed to create user');
|
|
229
|
+
return await createUserAndSession(req, res, User, File, options);
|
|
223
230
|
}
|
|
224
231
|
}
|
|
225
232
|
},
|
|
@@ -239,7 +246,7 @@ const UserService = {
|
|
|
239
246
|
if (req.path.startsWith('/recover')) {
|
|
240
247
|
let payload;
|
|
241
248
|
try {
|
|
242
|
-
payload = verifyJWT(req.params.id);
|
|
249
|
+
payload = verifyJWT(req.params.id, options);
|
|
243
250
|
} catch (error) {
|
|
244
251
|
logger.error(error, { 'req.params.id': req.params.id });
|
|
245
252
|
options.png.header(res);
|
|
@@ -254,7 +261,7 @@ const UserService = {
|
|
|
254
261
|
_id,
|
|
255
262
|
{ recoverTimeOut: new Date(+new Date() + 1000 * 60 * 15) },
|
|
256
263
|
{ runValidators: true },
|
|
257
|
-
);
|
|
264
|
+
);
|
|
258
265
|
options.png.header(res);
|
|
259
266
|
return options.png.buffer['recover'];
|
|
260
267
|
} else {
|
|
@@ -266,7 +273,7 @@ const UserService = {
|
|
|
266
273
|
if (req.path.startsWith('/mailer')) {
|
|
267
274
|
let payload;
|
|
268
275
|
try {
|
|
269
|
-
payload = verifyJWT(req.params.id);
|
|
276
|
+
payload = verifyJWT(req.params.id, options);
|
|
270
277
|
} catch (error) {
|
|
271
278
|
logger.error(error, { 'req.params.id': req.params.id });
|
|
272
279
|
options.png.header(res);
|
|
@@ -294,8 +301,24 @@ const UserService = {
|
|
|
294
301
|
}
|
|
295
302
|
|
|
296
303
|
switch (req.params.id) {
|
|
297
|
-
case 'all':
|
|
298
|
-
|
|
304
|
+
case 'all': {
|
|
305
|
+
if (req.auth.user.role === 'admin') {
|
|
306
|
+
const page = parseInt(req.query.page) || 1;
|
|
307
|
+
const limit = parseInt(req.query.limit) || 10;
|
|
308
|
+
const skip = (page - 1) * limit;
|
|
309
|
+
|
|
310
|
+
const data = await User.find().select(UserDto.select.get()).skip(skip).limit(limit);
|
|
311
|
+
const total = await User.countDocuments();
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
data,
|
|
315
|
+
page,
|
|
316
|
+
limit,
|
|
317
|
+
total,
|
|
318
|
+
totalPages: Math.ceil(total / limit),
|
|
319
|
+
};
|
|
320
|
+
} else throw new Error(`Invalid token user id`);
|
|
321
|
+
}
|
|
299
322
|
|
|
300
323
|
case 'auth': {
|
|
301
324
|
let user;
|
|
@@ -307,6 +330,8 @@ const UserService = {
|
|
|
307
330
|
_id: req.auth.user._id,
|
|
308
331
|
});
|
|
309
332
|
|
|
333
|
+
if (!user) throw new Error('user not found');
|
|
334
|
+
|
|
310
335
|
const file = await File.findOne({ _id: user.profileImageId });
|
|
311
336
|
|
|
312
337
|
if (!file && !(await ValkeyAPI.getValkeyObject(options, req.auth.user.email))) {
|
|
@@ -318,11 +343,20 @@ const UserService = {
|
|
|
318
343
|
},
|
|
319
344
|
);
|
|
320
345
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
346
|
+
|
|
347
|
+
const guestUser = await ValkeyAPI.getValkeyObject(options, req.auth.user.email);
|
|
348
|
+
if (guestUser)
|
|
349
|
+
return {
|
|
350
|
+
user: selectDtoFactory(guestUser, UserDto.select.get()),
|
|
351
|
+
token: getBearerToken(req),
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
token: await refreshSessionAndToken(req, res, User, options),
|
|
356
|
+
user: await User.findOne({
|
|
357
|
+
_id: req.auth.user._id,
|
|
358
|
+
}).select(UserDto.select.get()),
|
|
359
|
+
};
|
|
326
360
|
}
|
|
327
361
|
|
|
328
362
|
default: {
|
|
@@ -346,6 +380,20 @@ const UserService = {
|
|
|
346
380
|
delete: async (req, res, options) => {
|
|
347
381
|
/** @type {import('./user.model.js').UserModel} */
|
|
348
382
|
const User = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.User;
|
|
383
|
+
|
|
384
|
+
if (req.params.id === 'logout') {
|
|
385
|
+
const refreshToken = req.cookies?.refreshToken;
|
|
386
|
+
if (refreshToken) {
|
|
387
|
+
const hashedToken = hashToken(refreshToken);
|
|
388
|
+
await User.updateOne(
|
|
389
|
+
{ 'activeSessions.tokenHash': hashedToken },
|
|
390
|
+
{ $pull: { activeSessions: { tokenHash: hashedToken } } },
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
res.clearCookie('refreshToken');
|
|
394
|
+
return { message: 'Logged out successfully' };
|
|
395
|
+
}
|
|
396
|
+
|
|
349
397
|
switch (req.params.id) {
|
|
350
398
|
default: {
|
|
351
399
|
const user = await User.findOne({
|
|
@@ -401,7 +449,7 @@ const UserService = {
|
|
|
401
449
|
}
|
|
402
450
|
|
|
403
451
|
if (req.path.startsWith('/recover')) {
|
|
404
|
-
const payload = verifyJWT(req.params.id);
|
|
452
|
+
const payload = verifyJWT(req.params.id, options);
|
|
405
453
|
const user = await User.findOne({
|
|
406
454
|
email: payload.email,
|
|
407
455
|
});
|