underpost 2.8.87 → 2.8.88

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 (99) hide show
  1. package/.env.development +35 -3
  2. package/.env.production +39 -4
  3. package/.env.test +35 -3
  4. package/.github/workflows/ghpkg.ci.yml +1 -1
  5. package/.github/workflows/npmpkg.ci.yml +1 -1
  6. package/.github/workflows/pwa-microservices-template-page.cd.yml +6 -5
  7. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  8. package/.github/workflows/release.cd.yml +3 -3
  9. package/README.md +56 -2
  10. package/bin/build.js +4 -0
  11. package/bin/deploy.js +62 -8
  12. package/bin/file.js +3 -2
  13. package/cli.md +8 -2
  14. package/conf.js +30 -4
  15. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  16. package/manifests/deployment/dd-test-development/deployment.yaml +174 -0
  17. package/manifests/deployment/dd-test-development/proxy.yaml +51 -0
  18. package/package.json +6 -5
  19. package/src/api/core/core.router.js +2 -1
  20. package/src/api/default/default.controller.js +6 -1
  21. package/src/api/default/default.router.js +6 -2
  22. package/src/api/default/default.service.js +10 -1
  23. package/src/api/document/document.controller.js +66 -0
  24. package/src/api/document/document.model.js +51 -0
  25. package/src/api/document/document.router.js +24 -0
  26. package/src/api/document/document.service.js +125 -0
  27. package/src/api/file/file.controller.js +15 -1
  28. package/src/api/file/file.router.js +2 -1
  29. package/src/api/test/test.router.js +1 -1
  30. package/src/api/user/postman_collection.json +216 -0
  31. package/src/api/user/user.controller.js +25 -60
  32. package/src/api/user/user.model.js +29 -7
  33. package/src/api/user/user.router.js +9 -3
  34. package/src/api/user/user.service.js +84 -32
  35. package/src/cli/baremetal.js +33 -3
  36. package/src/cli/cloud-init.js +11 -0
  37. package/src/cli/cron.js +0 -1
  38. package/src/cli/deploy.js +46 -23
  39. package/src/cli/index.js +5 -0
  40. package/src/cli/lxd.js +7 -0
  41. package/src/cli/repository.js +42 -6
  42. package/src/cli/run.js +45 -13
  43. package/src/cli/ssh.js +20 -6
  44. package/src/client/Default.index.js +42 -1
  45. package/src/client/components/core/Account.js +10 -2
  46. package/src/client/components/core/AgGrid.js +30 -8
  47. package/src/client/components/core/Auth.js +99 -56
  48. package/src/client/components/core/BtnIcon.js +3 -2
  49. package/src/client/components/core/CalendarCore.js +2 -3
  50. package/src/client/components/core/CommonJs.js +1 -2
  51. package/src/client/components/core/Content.js +15 -12
  52. package/src/client/components/core/Css.js +2 -1
  53. package/src/client/components/core/CssCore.js +6 -1
  54. package/src/client/components/core/Docs.js +5 -5
  55. package/src/client/components/core/FileExplorer.js +3 -3
  56. package/src/client/components/core/Input.js +22 -17
  57. package/src/client/components/core/JoyStick.js +2 -2
  58. package/src/client/components/core/LoadingAnimation.js +2 -2
  59. package/src/client/components/core/LogIn.js +16 -23
  60. package/src/client/components/core/LogOut.js +5 -1
  61. package/src/client/components/core/Logger.js +4 -1
  62. package/src/client/components/core/Modal.js +82 -53
  63. package/src/client/components/core/ObjectLayerEngineModal.js +2 -1
  64. package/src/client/components/core/Pagination.js +207 -0
  65. package/src/client/components/core/Panel.js +10 -10
  66. package/src/client/components/core/PanelForm.js +130 -33
  67. package/src/client/components/core/Recover.js +2 -2
  68. package/src/client/components/core/Router.js +210 -34
  69. package/src/client/components/core/SignUp.js +1 -2
  70. package/src/client/components/core/Stream.js +1 -1
  71. package/src/client/components/core/VanillaJs.js +3 -84
  72. package/src/client/components/core/Worker.js +2 -2
  73. package/src/client/components/default/LogInDefault.js +0 -6
  74. package/src/client/components/default/LogOutDefault.js +0 -16
  75. package/src/client/components/default/MenuDefault.js +97 -44
  76. package/src/client/components/default/RoutesDefault.js +5 -2
  77. package/src/client/services/core/core.service.js +8 -20
  78. package/src/client/services/default/default.management.js +115 -18
  79. package/src/client/services/default/default.service.js +13 -4
  80. package/src/client/services/document/document.service.js +97 -0
  81. package/src/client/services/file/file.service.js +2 -0
  82. package/src/client/services/test/test.service.js +3 -0
  83. package/src/client/services/user/user.management.js +6 -0
  84. package/src/client/services/user/user.service.js +15 -4
  85. package/src/client/ssr/Render.js +1 -1
  86. package/src/client/ssr/head/DefaultScripts.js +3 -0
  87. package/src/client/ssr/head/Seo.js +1 -0
  88. package/src/index.js +24 -2
  89. package/src/runtime/lampp/Lampp.js +89 -2
  90. package/src/runtime/xampp/Xampp.js +48 -1
  91. package/src/server/auth.js +519 -155
  92. package/src/server/backup.js +2 -2
  93. package/src/server/conf.js +66 -8
  94. package/src/server/process.js +2 -1
  95. package/src/server/runtime.js +135 -286
  96. package/src/server/ssl.js +1 -2
  97. package/src/server/ssr.js +85 -0
  98. package/src/server/start.js +2 -2
  99. 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 UserController = {
7
- post: async (req, res, options) => {
8
- try {
9
- const result = await UserService.post(req, res, options);
10
- return res.status(200).json({
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
- put: async (req, res, options) => {
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, name: 1 };
79
+ return { _id: 1 };
67
80
  },
68
81
  },
69
82
  auth: {
70
- // TODO: -> set login device, location, ip, fingerprint
71
- // and validate on authorization middleware
72
- // -> dynamic refresh 100 tokens per session with 12h interval
73
- // -> back secret per user, registrarion user model -> secret: { type: String }
74
- payload: (user) => ({ _id: user._id.toString(), role: user.role, email: user.email }),
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 { authMiddleware, hashPassword } from '../../server/auth.js';
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
- const adminUser = await models.User.findOne({ role: 'admin' });
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',
@@ -45,6 +48,9 @@ const UserRouter = (options) => {
45
48
  check: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-check.png`),
46
49
  },
47
50
  header: (res) => {
51
+ res.set('Cross-Origin-Resource-Policy', 'cross-origin');
52
+ res.set('Access-Control-Allow-Origin', '*');
53
+ res.set('Access-Control-Allow-Headers', '*');
48
54
  res.set('Content-Type', 'image/png');
49
55
  },
50
56
  };
@@ -1,5 +1,16 @@
1
1
  import { loggerFactory } from '../../server/logger.js';
2
- import { hashPassword, verifyPassword, hashJWT, verifyJWT, validatePasswordMiddleware } from '../../server/auth.js';
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 = hashJWT({ email: req.body.email }, '15m');
31
- const payloadToken = hashJWT({ email: req.body.email }, '15m');
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 = hashJWT({ email: req.body.email });
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: hashJWT({ user: UserDto.auth.payload(user) }),
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(`invalid email or password, remaining attempts: ${5 - user.failedLoginAttempts}`);
212
+ throw new Error(`Invalid credentials. Remaining attempts: ${5 - user.failedLoginAttempts}`);
197
213
  }
198
- } else throw new Error('invalid email or password');
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: hashJWT({ user: UserDto.auth.payload(user) }),
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
- const validatePassword = validatePasswordMiddleware(req.body.password);
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
- ); // 15m
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
- return await User.find().select(UserDto.select.getAll());
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
- return (await ValkeyAPI.getValkeyObject(options, req.auth.user.email))
322
- ? selectDtoFactory(await ValkeyAPI.getValkeyObject(options, req.auth.user.email), UserDto.select.get())
323
- : await User.findOne({
324
- _id: req.auth.user._id,
325
- }).select(UserDto.select.get());
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,10 +449,14 @@ 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
  });
456
+ if (process.env.NODE_ENV === 'development') {
457
+ logger.warn('Only production check image token request on mailer GET /user/recover, set fallback timeout');
458
+ user.recoverTimeOut = new Date(+new Date() + 1000 * 60 * 15);
459
+ }
408
460
  if (user && new Date().getTime() < new Date(user.recoverTimeOut).getTime()) {
409
461
  const validatePassword = validatePasswordMiddleware(req.body.password);
410
462
  if (validatePassword.status === 'error') throw new Error(validatePassword.message);