underpost 3.2.9 → 3.2.10

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 (81) hide show
  1. package/.github/workflows/npmpkg.ci.yml +1 -0
  2. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  3. package/.github/workflows/release.cd.yml +1 -0
  4. package/.vscode/settings.json +10 -5
  5. package/CHANGELOG.md +122 -1
  6. package/CLI-HELP.md +22 -7
  7. package/README.md +37 -8
  8. package/bin/build.js +26 -9
  9. package/bin/deploy.js +20 -21
  10. package/bin/file.js +31 -13
  11. package/bin/index.js +2 -1
  12. package/bin/vs.js +1 -1
  13. package/bump.config.js +26 -0
  14. package/conf.js +20 -4
  15. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  16. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  17. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  18. package/manifests/deployment/dd-test-development/deployment.yaml +4 -2
  19. package/manifests/kind-config-dev.yaml +8 -0
  20. package/manifests/mongodb/pv-pvc.yaml +44 -8
  21. package/manifests/mongodb/statefulset.yaml +55 -68
  22. package/package.json +27 -12
  23. package/scripts/k3s-node-setup.sh +28 -9
  24. package/src/api/core/core.router.js +19 -14
  25. package/src/api/core/core.service.js +5 -5
  26. package/src/api/default/default.router.js +22 -18
  27. package/src/api/default/default.service.js +5 -5
  28. package/src/api/document/document.router.js +28 -23
  29. package/src/api/document/document.service.js +100 -23
  30. package/src/api/file/file.router.js +19 -13
  31. package/src/api/file/file.service.js +9 -7
  32. package/src/api/test/test.router.js +17 -12
  33. package/src/api/types.js +24 -0
  34. package/src/api/user/guest.service.js +5 -4
  35. package/src/api/user/user.router.js +297 -288
  36. package/src/api/user/user.service.js +100 -35
  37. package/src/cli/baremetal.js +20 -11
  38. package/src/cli/cluster.js +196 -55
  39. package/src/cli/db.js +59 -60
  40. package/src/cli/deploy.js +273 -159
  41. package/src/cli/fs.js +3 -1
  42. package/src/cli/index.js +16 -9
  43. package/src/cli/ipfs.js +4 -6
  44. package/src/cli/kubectl.js +4 -1
  45. package/src/cli/lxd.js +217 -135
  46. package/src/cli/release.js +289 -131
  47. package/src/cli/repository.js +58 -7
  48. package/src/cli/run.js +152 -25
  49. package/src/cli/test.js +9 -3
  50. package/src/client/Default.index.js +9 -3
  51. package/src/client/components/core/Auth.js +4 -0
  52. package/src/client/components/core/PanelForm.js +56 -52
  53. package/src/client/components/core/Worker.js +162 -363
  54. package/src/client/sw/core.sw.js +174 -112
  55. package/src/db/DataBaseProvider.js +120 -20
  56. package/src/db/mongo/MongoBootstrap.js +587 -0
  57. package/src/db/mongo/MongooseDB.js +126 -22
  58. package/src/index.js +1 -1
  59. package/src/runtime/express/Express.js +2 -2
  60. package/src/runtime/wp/Wp.js +8 -5
  61. package/src/server/auth.js +2 -2
  62. package/src/server/client-build-docs.js +1 -1
  63. package/src/server/client-build.js +94 -129
  64. package/src/server/conf.js +20 -65
  65. package/src/server/process.js +180 -19
  66. package/src/server/runtime.js +1 -1
  67. package/src/server/start.js +12 -4
  68. package/src/ws/IoInterface.js +16 -16
  69. package/src/ws/core/channels/core.ws.chat.js +11 -11
  70. package/src/ws/core/channels/core.ws.mailer.js +29 -29
  71. package/src/ws/core/channels/core.ws.stream.js +19 -19
  72. package/src/ws/core/core.ws.connection.js +8 -8
  73. package/src/ws/core/core.ws.server.js +6 -5
  74. package/src/ws/default/channels/default.ws.main.js +10 -10
  75. package/src/ws/default/default.ws.connection.js +4 -4
  76. package/src/ws/default/default.ws.server.js +4 -3
  77. package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
  78. package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
  79. /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
  80. /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
  81. /package/src/client/ssr/{pages → views}/Test.js +0 -0
@@ -3,26 +3,28 @@ 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
- import { DataBaseProvider } from '../../db/DataBaseProvider.js';
6
+ import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
7
7
 
8
8
  const logger = loggerFactory(import.meta);
9
9
 
10
- const UserRouter = (options) => {
11
- const router = express.Router();
12
- const authMiddleware = options.authMiddleware;
13
-
14
- (async () => {
15
- // admin user seed
16
- try {
17
- const models = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models;
18
- let adminUser;
19
- if (models.User) {
20
- adminUser = await models.User.findOne({ role: 'admin' });
10
+ class UserRouter {
11
+ /**
12
+ * @param {import('../types.js').RouterOptions} options
13
+ * @returns {import('express').Router}
14
+ */
15
+ static router(options) {
16
+ const router = express.Router();
17
+
18
+ // Admin user seed — fire-and-forget async on first router mount.
19
+ (async () => {
20
+ try {
21
+ const User = DataBaseProviderService.getModel('user', options);
22
+ const adminUser = await User.findOne({ role: 'admin' });
21
23
  if (!adminUser) {
22
24
  const defaultPassword = process.env.DEFAULT_ADMIN_PASSWORD || 'changethis';
23
25
  const hashedPassword = await hashPassword(defaultPassword);
24
26
 
25
- const result = await models.User.create({
27
+ const result = await User.create({
26
28
  username: 'admin',
27
29
  email: process.env.DEFAULT_ADMIN_EMAIL || 'admin@' + options.host,
28
30
  password: hashedPassword,
@@ -36,106 +38,73 @@ const UserRouter = (options) => {
36
38
  role: result._doc.role,
37
39
  });
38
40
  }
41
+ } catch (error) {
42
+ logger.error('Error checking/creating admin user', { error: error.message });
39
43
  }
40
- } catch (error) {
41
- logger.error('Error checking/creating admin user', { error: error.message });
42
- }
43
-
44
- // Cache mailer images
45
- options.png = {
46
- buffer: {
47
- 'invalid-token': fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-invalid-token.png`),
48
- recover: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-recover.png`),
49
- check: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-check.png`),
50
- avatar: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-default-avatar.png`),
51
- },
52
- header: (res, req) => {
53
- res.set('Cross-Origin-Resource-Policy', 'cross-origin');
54
- res.set('Access-Control-Allow-Headers', '*');
55
- if (req && req.headers && req.headers.origin) {
56
- res.set('Access-Control-Allow-Origin', req.headers.origin);
57
- } else res.setHeader('Access-Control-Allow-Origin', '*');
58
- res.set('Content-Type', 'image/png');
59
- },
60
- };
61
- })();
62
-
63
- router.post(`/mailer/:id`, authMiddleware, async (req, res) => {
64
- /*
65
- #swagger.ignore = true
66
- */
67
- return await UserController.post(req, res, options);
68
- });
69
-
70
- router.get(`/assets/:id`, async (req, res) => {
71
- /*
72
- #swagger.ignore = true
73
- */
74
- return await UserController.get(req, res, options);
75
- });
76
-
77
- router.get(`/mailer/:id`, async (req, res) => {
78
- /*
79
- #swagger.ignore = true
80
- */
81
- return await UserController.get(req, res, options);
82
- });
83
-
84
- router.get(`/email/:email`, authMiddleware, async (req, res) => {
85
- /*
86
- #swagger.ignore = true
87
- */
88
- return await UserController.get(req, res, options);
89
- });
90
-
91
- router.post(`/:id`, async (req, res) => {
92
- /*
93
- #swagger.ignore = true
94
- */
95
- return await UserController.post(req, res, options);
96
- });
97
-
98
- // #swagger.start
99
-
100
- /*
101
- #swagger.auto = false
102
- #swagger.tags = ['user']
103
- #swagger.summary = 'Log in'
104
- #swagger.description = 'This endpoint get a JWT for authenticated user'
105
- #swagger.path = '/user/auth'
106
- #swagger.method = 'post'
107
- #swagger.responses[200] = {
108
- description: 'User created successfully',
109
- content: {
110
- 'application/json': {
111
- schema: {
112
- $ref: '#/components/schemas/userResponse'
113
- }
114
- }
115
- }
116
- }
117
-
118
- #swagger.responses[400] = {
119
- description: 'Bad request. Please check the input data',
120
- content: {
121
- 'application/json': {
122
- schema: {
123
- $ref: '#/components/schemas/userBadRequestResponse'
124
- }
125
- }
126
- }
127
- }
128
- */
129
44
 
130
- // #swagger.end
45
+ // Cache mailer images into options.png so route handlers can serve them
46
+ // without re-reading the filesystem on every request.
47
+ options.png = {
48
+ buffer: {
49
+ 'invalid-token': fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-invalid-token.png`),
50
+ recover: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-recover.png`),
51
+ check: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-check.png`),
52
+ avatar: fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-default-avatar.png`),
53
+ },
54
+ header: (res, req) => {
55
+ res.set('Cross-Origin-Resource-Policy', 'cross-origin');
56
+ res.set('Access-Control-Allow-Headers', '*');
57
+ if (req && req.headers && req.headers.origin) {
58
+ res.set('Access-Control-Allow-Origin', req.headers.origin);
59
+ } else res.setHeader('Access-Control-Allow-Origin', '*');
60
+ res.set('Content-Type', 'image/png');
61
+ },
62
+ };
63
+ })();
64
+
65
+ router.post(`/mailer/:id`, options.authMiddleware, async (req, res) => {
66
+ /*
67
+ #swagger.ignore = true
68
+ */
69
+ return await UserController.post(req, res, options);
70
+ });
71
+
72
+ router.get(`/assets/:id`, async (req, res) => {
73
+ /*
74
+ #swagger.ignore = true
75
+ */
76
+ return await UserController.get(req, res, options);
77
+ });
78
+
79
+ router.get(`/mailer/:id`, async (req, res) => {
80
+ /*
81
+ #swagger.ignore = true
82
+ */
83
+ return await UserController.get(req, res, options);
84
+ });
85
+
86
+ router.get(`/email/:email`, options.authMiddleware, async (req, res) => {
87
+ /*
88
+ #swagger.ignore = true
89
+ */
90
+ return await UserController.get(req, res, options);
91
+ });
92
+
93
+ router.post(`/:id`, async (req, res) => {
94
+ /*
95
+ #swagger.ignore = true
96
+ */
97
+ return await UserController.post(req, res, options);
98
+ });
99
+
100
+ // #swagger.start
131
101
 
132
- router.post(`/`, async (req, res) => {
133
102
  /*
134
103
  #swagger.auto = false
135
104
  #swagger.tags = ['user']
136
- #swagger.summary = 'Create user'
137
- #swagger.description = 'This endpoint will create a new user account'
138
- #swagger.path = '/user'
105
+ #swagger.summary = 'Log in'
106
+ #swagger.description = 'This endpoint get a JWT for authenticated user'
107
+ #swagger.path = '/user/auth'
139
108
  #swagger.method = 'post'
140
109
  #swagger.responses[200] = {
141
110
  description: 'User created successfully',
@@ -159,200 +128,240 @@ const UserRouter = (options) => {
159
128
  }
160
129
  }
161
130
  */
162
- return await UserController.post(req, res, options);
163
- });
164
-
165
- router.get(`/recover/:id`, async (req, res) => {
166
- /*
167
- #swagger.ignore = true
168
- */
169
- return await UserController.get(req, res, options);
170
- });
171
-
172
- router.get(`/u/:username`, async (req, res) => {
173
- /*
174
- #swagger.ignore = true
175
- */
176
- return await UserController.get(req, res, options);
177
- });
178
-
179
- router.get(`/:id`, authMiddleware, async (req, res) => {
180
- /*
181
- #swagger.auto = false
182
- #swagger.tags = ['user']
183
- #swagger.summary = 'Get user data by ID'
184
- #swagger.description = 'This endpoint get user data by ID'
185
- #swagger.path = '/user/{id}'
186
- #swagger.method = 'get'
187
- #swagger.produces = ['application/json']
188
- #swagger.consumes = ['application/json']
189
- #swagger.security = [{
190
- 'bearerAuth': []
191
- }]
192
-
193
- #swagger.parameters['id'] = {
194
- in: 'path',
195
- description: 'User ID',
196
- required: true,
197
- type: 'string'
198
- }
199
131
 
200
- #swagger.responses[200] = {
201
- description: 'get user successfully',
202
- content: {
203
- 'application/json': {
204
- schema: {
205
- $ref: '#/components/schemas/userGetResponse'
206
- }
207
- }
132
+ // #swagger.end
133
+
134
+ router.post(`/`, async (req, res) => {
135
+ /*
136
+ #swagger.auto = false
137
+ #swagger.tags = ['user']
138
+ #swagger.summary = 'Create user'
139
+ #swagger.description = 'This endpoint will create a new user account'
140
+ #swagger.path = '/user'
141
+ #swagger.method = 'post'
142
+ #swagger.responses[200] = {
143
+ description: 'User created successfully',
144
+ content: {
145
+ 'application/json': {
146
+ schema: {
147
+ $ref: '#/components/schemas/userResponse'
148
+ }
149
+ }
150
+ }
208
151
  }
209
- }
210
152
 
211
- #swagger.responses[400] = {
212
- description: 'Bad request. Please check the input data',
213
- content: {
214
- 'application/json': {
215
- schema: {
216
- $ref: '#/components/schemas/userBadRequestResponse'
217
- }
218
- }
153
+ #swagger.responses[400] = {
154
+ description: 'Bad request. Please check the input data',
155
+ content: {
156
+ 'application/json': {
157
+ schema: {
158
+ $ref: '#/components/schemas/userBadRequestResponse'
159
+ }
160
+ }
161
+ }
219
162
  }
220
- }
221
- */
222
- return await UserController.get(req, res, options);
223
- });
224
- router.get(`/`, authMiddleware, async (req, res) => {
225
- /*
226
- #swagger.ignore = true
227
- */
228
- return await UserController.get(req, res, options);
229
- });
230
- router.put(`/recover/:id`, async (req, res) => {
231
- /*
232
- #swagger.ignore = true
233
- */
234
- return await UserController.put(req, res, options);
235
- });
236
- router.put(`/profile-image/:id`, authMiddleware, async (req, res) => {
237
- /*
238
- #swagger.ignore = true
239
- */
240
- return await UserController.put(req, res, options);
241
- });
242
- router.put(`/:id`, authMiddleware, async (req, res) => {
243
- /*
244
- #swagger.auto = false
245
- #swagger.tags = ['user']
246
- #swagger.summary = 'Update user data by ID'
247
- #swagger.description = 'This endpoint will update user data by ID'
248
- #swagger.path = '/user/{id}'
249
- #swagger.method = 'put'
250
- #swagger.security = [{
251
- 'bearerAuth': []
252
- }]
253
-
254
- #swagger.parameters['id'] = {
255
- in: 'path',
256
- description: 'User ID',
257
- required: true,
258
- type: 'string'
259
- }
260
-
261
- #swagger.responses[200] = {
262
- description: 'User updated successfully',
263
- content: {
264
- 'application/json': {
265
- schema: {
266
- $ref: '#/components/schemas/userUpdateResponse'
267
- }
268
- }
163
+ */
164
+ return await UserController.post(req, res, options);
165
+ });
166
+
167
+ router.get(`/recover/:id`, async (req, res) => {
168
+ /*
169
+ #swagger.ignore = true
170
+ */
171
+ return await UserController.get(req, res, options);
172
+ });
173
+
174
+ router.get(`/u/:username`, async (req, res) => {
175
+ /*
176
+ #swagger.ignore = true
177
+ */
178
+ return await UserController.get(req, res, options);
179
+ });
180
+
181
+ router.get(`/:id`, options.authMiddleware, async (req, res) => {
182
+ /*
183
+ #swagger.auto = false
184
+ #swagger.tags = ['user']
185
+ #swagger.summary = 'Get user data by ID'
186
+ #swagger.description = 'This endpoint get user data by ID'
187
+ #swagger.path = '/user/{id}'
188
+ #swagger.method = 'get'
189
+ #swagger.produces = ['application/json']
190
+ #swagger.consumes = ['application/json']
191
+ #swagger.security = [{
192
+ 'bearerAuth': []
193
+ }]
194
+
195
+ #swagger.parameters['id'] = {
196
+ in: 'path',
197
+ description: 'User ID',
198
+ required: true,
199
+ type: 'string'
269
200
  }
270
- }
271
201
 
272
- #swagger.responses[400] = {
273
- description: 'Bad request. Please check the input data',
274
- content: {
275
- 'application/json': {
276
- schema: {
277
- $ref: '#/components/schemas/userBadRequestResponse'
278
- }
279
- }
202
+ #swagger.responses[200] = {
203
+ description: 'get user successfully',
204
+ content: {
205
+ 'application/json': {
206
+ schema: {
207
+ $ref: '#/components/schemas/userGetResponse'
208
+ }
209
+ }
210
+ }
280
211
  }
281
- }
282
- */
283
- return await UserController.put(req, res, options);
284
- });
285
- router.put(`/`, authMiddleware, async (req, res) => {
286
- /*
287
- #swagger.ignore = true
288
- */
289
- return await UserController.put(req, res, options);
290
- });
291
-
292
- router.delete(`/:id`, authMiddleware, async (req, res) => {
293
- /*
294
- #swagger.auto = false
295
- #swagger.tags = ['user']
296
- #swagger.summary = 'Delete user data by ID'
297
- #swagger.description = 'This endpoint deletes user data by ID, the path ID must match with the ID of the authenticated user'
298
- #swagger.path = '/user/{id}'
299
- #swagger.method = 'delete'
300
- #swagger.produces = ['application/json']
301
- #swagger.consumes = ['application/json']
302
- #swagger.security = [{
303
- 'bearerAuth': []
304
- }]
305
-
306
- #swagger.parameters['id'] = {
307
- in: 'path',
308
- description: 'User ID',
309
- required: true,
310
- type: 'string'
311
- }
312
212
 
313
- #swagger.responses[200] = {
314
- description: 'get user successfully',
315
- content: {
316
- 'application/json': {
317
- schema: {
318
- $ref: '#/components/schemas/userGetResponse'
319
- }
320
- }
213
+ #swagger.responses[400] = {
214
+ description: 'Bad request. Please check the input data',
215
+ content: {
216
+ 'application/json': {
217
+ schema: {
218
+ $ref: '#/components/schemas/userBadRequestResponse'
219
+ }
220
+ }
221
+ }
321
222
  }
322
- }
323
-
324
- #swagger.responses[400] = {
325
- description: 'Bad request. Please check the input data',
326
- content: {
327
- 'application/json': {
328
- schema: {
329
- $ref: '#/components/schemas/userBadRequestResponse'
330
- }
331
- }
223
+ */
224
+ return await UserController.get(req, res, options);
225
+ });
226
+
227
+ router.get(`/`, options.authMiddleware, async (req, res) => {
228
+ /*
229
+ #swagger.ignore = true
230
+ */
231
+ return await UserController.get(req, res, options);
232
+ });
233
+
234
+ router.put(`/recover/:id`, async (req, res) => {
235
+ /*
236
+ #swagger.ignore = true
237
+ */
238
+ return await UserController.put(req, res, options);
239
+ });
240
+
241
+ router.put(`/profile-image/:id`, options.authMiddleware, async (req, res) => {
242
+ /*
243
+ #swagger.ignore = true
244
+ */
245
+ return await UserController.put(req, res, options);
246
+ });
247
+
248
+ router.put(`/:id`, options.authMiddleware, async (req, res) => {
249
+ /*
250
+ #swagger.auto = false
251
+ #swagger.tags = ['user']
252
+ #swagger.summary = 'Update user data by ID'
253
+ #swagger.description = 'This endpoint will update user data by ID'
254
+ #swagger.path = '/user/{id}'
255
+ #swagger.method = 'put'
256
+ #swagger.security = [{
257
+ 'bearerAuth': []
258
+ }]
259
+
260
+ #swagger.parameters['id'] = {
261
+ in: 'path',
262
+ description: 'User ID',
263
+ required: true,
264
+ type: 'string'
332
265
  }
333
- }
334
- */
335
- return await UserController.delete(req, res, options);
336
- });
337
266
 
338
- router.delete(`/`, authMiddleware, async (req, res) => {
339
- /*
340
- #swagger.ignore = true
341
- */
342
- return await UserController.delete(req, res, options);
343
- });
267
+ #swagger.responses[200] = {
268
+ description: 'User updated successfully',
269
+ content: {
270
+ 'application/json': {
271
+ schema: {
272
+ $ref: '#/components/schemas/userUpdateResponse'
273
+ }
274
+ }
275
+ }
276
+ }
344
277
 
345
- // Username public profile redirect
346
- options.app.get(`${options.path === '/' ? '' : options.path}/u/:username`, async (req, res, next) => {
347
- /*
348
- #swagger.ignore = true
349
- */
350
- return res.redirect(`${options.path === '/' ? '' : options.path}/u?cid=${req.params.username}`);
351
- });
278
+ #swagger.responses[400] = {
279
+ description: 'Bad request. Please check the input data',
280
+ content: {
281
+ 'application/json': {
282
+ schema: {
283
+ $ref: '#/components/schemas/userBadRequestResponse'
284
+ }
285
+ }
286
+ }
287
+ }
288
+ */
289
+ return await UserController.put(req, res, options);
290
+ });
291
+
292
+ router.put(`/`, options.authMiddleware, async (req, res) => {
293
+ /*
294
+ #swagger.ignore = true
295
+ */
296
+ return await UserController.put(req, res, options);
297
+ });
298
+
299
+ router.delete(`/:id`, options.authMiddleware, async (req, res) => {
300
+ /*
301
+ #swagger.auto = false
302
+ #swagger.tags = ['user']
303
+ #swagger.summary = 'Delete user data by ID'
304
+ #swagger.description = 'This endpoint deletes user data by ID, the path ID must match with the ID of the authenticated user'
305
+ #swagger.path = '/user/{id}'
306
+ #swagger.method = 'delete'
307
+ #swagger.produces = ['application/json']
308
+ #swagger.consumes = ['application/json']
309
+ #swagger.security = [{
310
+ 'bearerAuth': []
311
+ }]
312
+
313
+ #swagger.parameters['id'] = {
314
+ in: 'path',
315
+ description: 'User ID',
316
+ required: true,
317
+ type: 'string'
318
+ }
352
319
 
353
- return router;
354
- };
320
+ #swagger.responses[200] = {
321
+ description: 'get user successfully',
322
+ content: {
323
+ 'application/json': {
324
+ schema: {
325
+ $ref: '#/components/schemas/userGetResponse'
326
+ }
327
+ }
328
+ }
329
+ }
355
330
 
356
- const ApiRouter = UserRouter;
331
+ #swagger.responses[400] = {
332
+ description: 'Bad request. Please check the input data',
333
+ content: {
334
+ 'application/json': {
335
+ schema: {
336
+ $ref: '#/components/schemas/userBadRequestResponse'
337
+ }
338
+ }
339
+ }
340
+ }
341
+ */
342
+ return await UserController.delete(req, res, options);
343
+ });
344
+
345
+ router.delete(`/`, options.authMiddleware, async (req, res) => {
346
+ /*
347
+ #swagger.ignore = true
348
+ */
349
+ return await UserController.delete(req, res, options);
350
+ });
351
+
352
+ // Username public profile redirect — registered directly on the app so the
353
+ // /u/:username shortlink works outside the API path prefix.
354
+ options.app.get(`${options.path === '/' ? '' : options.path}/u/:username`, async (req, res, next) => {
355
+ /*
356
+ #swagger.ignore = true
357
+ */
358
+ return res.redirect(`${options.path === '/' ? '' : options.path}/u?cid=${req.params.username}`);
359
+ });
360
+
361
+ return router;
362
+ }
363
+ }
364
+
365
+ const ApiRouter = (options) => UserRouter.router(options);
357
366
 
358
367
  export { ApiRouter, UserRouter };