underpost 2.8.881 → 2.8.882

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 (32) hide show
  1. package/.github/workflows/release.cd.yml +1 -2
  2. package/README.md +46 -36
  3. package/cli.md +86 -86
  4. package/conf.js +1 -0
  5. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  6. package/manifests/deployment/dd-test-development/deployment.yaml +6 -6
  7. package/package.json +1 -1
  8. package/src/api/document/document.service.js +9 -1
  9. package/src/cli/repository.js +2 -0
  10. package/src/client/components/core/Auth.js +258 -89
  11. package/src/client/components/core/BtnIcon.js +10 -1
  12. package/src/client/components/core/CssCore.js +36 -27
  13. package/src/client/components/core/Docs.js +189 -85
  14. package/src/client/components/core/LoadingAnimation.js +5 -10
  15. package/src/client/components/core/Modal.js +255 -120
  16. package/src/client/components/core/ObjectLayerEngine.js +154 -158
  17. package/src/client/components/core/Panel.js +2 -0
  18. package/src/client/components/core/PanelForm.js +94 -60
  19. package/src/client/components/core/Router.js +15 -15
  20. package/src/client/components/core/ToolTip.js +83 -19
  21. package/src/client/components/core/Translate.js +1 -1
  22. package/src/client/components/core/VanillaJs.js +4 -3
  23. package/src/client/components/core/windowGetDimensions.js +202 -0
  24. package/src/client/components/default/MenuDefault.js +11 -0
  25. package/src/client/ssr/Render.js +1 -1
  26. package/src/index.js +1 -1
  27. package/src/server/auth.js +68 -17
  28. package/src/server/crypto.js +195 -76
  29. package/src/server/peer.js +47 -5
  30. package/src/server/process.js +85 -1
  31. package/src/server/runtime.js +13 -10
  32. package/test/crypto.test.js +117 -0
@@ -1,25 +1,89 @@
1
- import { append } from './VanillaJs.js';
1
+ import { renderCssAttr } from './Css.js';
2
+ import { append, s } from './VanillaJs.js';
3
+ import { Modal } from './Modal.js';
2
4
 
3
5
  const ToolTip = {
4
6
  Tokens: {},
5
- Render: async function (options = { container: '', htmlRender: '', id: '', classList: '' }) {
6
- const { container, htmlRender, id } = options;
7
- const tooltipId = 'tooltip-' + id;
8
- append(
9
- container,
10
- html`
11
- <style>
12
- ${container}:hover .${tooltipId} {
13
- visibility: visible;
14
- }
15
- .${tooltipId} {
16
- visibility: hidden;
17
- }
18
- </style>
19
- <div class="tooltip ${options?.classList ? `${options.classList} ` : ' '}${tooltipId}">${htmlRender}</div>
20
- `,
21
- );
22
- return '';
7
+ Render: async function (
8
+ options = { container: '', htmlRender: '', id: '', classList: '', useVisibilityHover: false, useMenuBtn: false },
9
+ ) {
10
+ const { container, htmlRender, id, useVisibilityHover } = options;
11
+
12
+ if (useVisibilityHover) {
13
+ const tooltipId = 'tooltip-' + id;
14
+ append(
15
+ container,
16
+ html`
17
+ <style>
18
+ ${container}:hover .${tooltipId} {
19
+ visibility: visible;
20
+ }
21
+ .${tooltipId} {
22
+ visibility: hidden;
23
+ }
24
+ </style>
25
+ <div class="tooltip ${options?.classList ? `${options.classList} ` : ' '}${tooltipId}">${htmlRender}</div>
26
+ `,
27
+ );
28
+ return;
29
+ }
30
+
31
+ const containerEl = s(container);
32
+ if (!containerEl) return;
33
+
34
+ const tooltipId = `tooltip-${id}`;
35
+ const tooltip = html`
36
+ <div
37
+ class="fix fix-tooltip ${tooltipId}"
38
+ style="${renderCssAttr({
39
+ style: {
40
+ 'z-index': 10,
41
+ position: 'absolute',
42
+ opacity: 0,
43
+ transition: 'opacity 0.2s ease-in-out',
44
+ 'pointer-events': 'none',
45
+ },
46
+ })}"
47
+ >
48
+ ${htmlRender}
49
+ </div>
50
+ `;
51
+ append('body', tooltip);
52
+
53
+ const tooltipEl = s(`.${tooltipId}`);
54
+
55
+ containerEl.addEventListener('mouseenter', () => {
56
+ if (
57
+ options.useMenuBtn &&
58
+ s(
59
+ `.btn-icon-menu-mode-${Modal.Data['modal-menu'].options.mode === 'slide-menu-right' ? 'left' : 'right'}`,
60
+ ).classList.contains('hide')
61
+ )
62
+ return;
63
+
64
+ const containerRect = containerEl.getBoundingClientRect();
65
+ const tooltipRect = tooltipEl.getBoundingClientRect();
66
+
67
+ let top = containerRect.bottom + window.scrollY + 5;
68
+ let left = containerRect.left + window.scrollX + containerRect.width / 2 - tooltipRect.width / 2;
69
+
70
+ // Adjust if it goes off-screen
71
+ if (left < 0) left = 5;
72
+ if (left + tooltipRect.width > window.innerWidth) {
73
+ left = window.innerWidth - tooltipRect.width - 5;
74
+ }
75
+ if (top + tooltipRect.height > window.innerHeight) {
76
+ top = containerRect.top + window.scrollY - tooltipRect.height - 5;
77
+ }
78
+
79
+ tooltipEl.style.top = `${top}px`;
80
+ tooltipEl.style.left = `${left}px`;
81
+ tooltipEl.style.opacity = '1';
82
+ });
83
+
84
+ containerEl.addEventListener('mouseleave', () => {
85
+ tooltipEl.style.opacity = '0';
86
+ });
23
87
  },
24
88
  };
25
89
 
@@ -34,7 +34,7 @@ const Translate = {
34
34
  Render: function (keyLang, placeholder, options = { disableTextFormat: false }) {
35
35
  if (!(keyLang in this.Data)) {
36
36
  // TODO: add translate package or library for this case
37
- logger.warn('translate key lang does not exist: ', keyLang);
37
+ // logger.warn('translate key lang does not exist: ', keyLang);
38
38
  return options.disableTextFormat ? keyLang : textFormatted(keyLang);
39
39
  }
40
40
  if (placeholder) this.Data[keyLang].placeholder = placeholder;
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { s4 } from './CommonJs.js';
8
+ import { windowGetH, windowGetW } from './windowGetDimensions.js';
8
9
 
9
10
  /*
10
11
 
@@ -232,10 +233,10 @@ const fullScreenIn = () => {
232
233
  * @memberof VanillaJS
233
234
  */
234
235
  const getResponsiveData = () => {
235
- const inner = { width: window.innerWidth, height: window.innerHeight };
236
+ const inner = { width: windowGetW(), height: windowGetH() };
236
237
  return inner.width > inner.height
237
- ? { ...inner, minValue: window.innerHeight, maxValue: window.innerWidth, minType: 'height', maxType: 'width' }
238
- : { ...inner, minValue: window.innerWidth, maxValue: window.innerHeight, minType: 'width', maxType: 'height' };
238
+ ? { ...inner, minValue: windowGetH(), maxValue: windowGetW(), minType: 'height', maxType: 'width' }
239
+ : { ...inner, minValue: windowGetW(), maxValue: windowGetH(), minType: 'width', maxType: 'height' };
239
240
  };
240
241
 
241
242
  /**
@@ -0,0 +1,202 @@
1
+ /*
2
+ * windowGetDimensions.js
3
+ * ES6 vanilla utilities: windowGetH and windowGetW
4
+ * Returns the most reliable viewport height/width available, with fallbacks
5
+ * from modern to old browsers.
6
+ *
7
+ * Usage:
8
+ * import { windowGetH, windowGetW } from './windowGetDimensions.js';
9
+ * const h = windowGetH();
10
+ * const w = windowGetW();
11
+ *
12
+ * Notes:
13
+ * - visualViewport (when present) reflects the *visible* viewport (changes when
14
+ * the on-screen keyboard opens, or when mobile address/toolbars show/hide).
15
+ * - documentElement.clientHeight/Width reflect the layout viewport.
16
+ * - window.innerHeight/innerWidth include scrollbars and are widely supported.
17
+ * - screen.* values are last-resort and reflect the physical screen, not the
18
+ * browser chrome.
19
+ */
20
+
21
+ // Helper: coerce a candidate to a finite integer (or null if not usable)
22
+ const toInt = (v) => {
23
+ const n = Number(v);
24
+ return Number.isFinite(n) && n > 0 ? Math.round(n) : null;
25
+ };
26
+
27
+ /**
28
+ * Try visualViewport values (most accurate for "what's actually visible").
29
+ * @returns {{height: number|null, width: number|null}}
30
+ */
31
+ const getFromVisualViewport = () => {
32
+ if (typeof window !== 'undefined' && window.visualViewport) {
33
+ const { height, width } = window.visualViewport;
34
+ return { height: toInt(height), width: toInt(width) };
35
+ }
36
+ return { height: null, width: null };
37
+ };
38
+
39
+ /**
40
+ * Try layout viewport (doctype-root) measurements.
41
+ * document.documentElement.clientHeight/clientWidth are stable and widely used.
42
+ * @returns {{height: number|null, width: number|null}}
43
+ */
44
+ const getFromDocumentElement = () => {
45
+ if (typeof document !== 'undefined' && document.documentElement) {
46
+ const { clientHeight, clientWidth } = document.documentElement;
47
+ return { height: toInt(clientHeight), width: toInt(clientWidth) };
48
+ }
49
+ return { height: null, width: null };
50
+ };
51
+
52
+ /**
53
+ * Try window.* measurements (innerHeight/innerWidth are widely supported).
54
+ * @returns {{height: number|null, width: number|null}}
55
+ */
56
+ const getFromWindowInner = () => {
57
+ if (typeof window !== 'undefined') {
58
+ return { height: toInt(window.innerHeight), width: toInt(window.innerWidth) };
59
+ }
60
+ return { height: null, width: null };
61
+ };
62
+
63
+ /**
64
+ * Try body measurements.
65
+ * @returns {{height: number|null, width: number|null}}
66
+ */
67
+ const getFromBody = () => {
68
+ if (typeof document !== 'undefined' && document.body) {
69
+ return { height: toInt(document.body.clientHeight), width: toInt(document.body.clientWidth) };
70
+ }
71
+ return { height: null, width: null };
72
+ };
73
+
74
+ /**
75
+ * Try screen measurements (physical screen/fallback).
76
+ * screen.availHeight/availWidth are often available; outer* might also exist.
77
+ * @returns {{height: number|null, width: number|null}}
78
+ */
79
+ const getFromScreen = () => {
80
+ if (typeof window !== 'undefined' && window.screen) {
81
+ const { availHeight, availWidth, height, width } = window.screen;
82
+ return {
83
+ height: toInt(availHeight) || toInt(height) || null,
84
+ width: toInt(availWidth) || toInt(width) || null,
85
+ };
86
+ }
87
+ return { height: null, width: null };
88
+ };
89
+
90
+ /**
91
+ * Try outer dimensions (less reliable, but sometimes available).
92
+ * @returns {{height: number|null, width: number|null}}
93
+ */
94
+ const getFromOuter = () => {
95
+ if (typeof window !== 'undefined') {
96
+ return { height: toInt(window.outerHeight), width: toInt(window.outerWidth) };
97
+ }
98
+ return { height: null, width: null };
99
+ };
100
+
101
+ /**
102
+ * Merge candidates in priority order and return first valid value.
103
+ * @param {...(number|null)[]} candidates
104
+ * @returns {number|null}
105
+ */
106
+ const pickFirst = (...candidates) => {
107
+ for (const c of candidates) {
108
+ if (Number.isFinite(c) && c > 0) return Math.round(c);
109
+ }
110
+ return null;
111
+ };
112
+
113
+ /**
114
+ * Get the best-available viewport height in pixels.
115
+ * Priority (from most reliable for "visible" to least):
116
+ * 1. window.visualViewport.height
117
+ * 2. document.documentElement.clientHeight
118
+ * 3. window.innerHeight
119
+ * 4. document.body.clientHeight
120
+ * 5. window.screen.availHeight / window.screen.height
121
+ * 6. window.outerHeight
122
+ *
123
+ * @param {Object} [options]
124
+ * @param {boolean} [options.preferVisualViewport=true] - when true, prefer visualViewport if present
125
+ * @returns {number|null} height in px (rounded integer) or null if none found
126
+ */
127
+ export const windowGetH = (options = {}) => {
128
+ const { preferVisualViewport = true } = options;
129
+
130
+ const vv = getFromVisualViewport();
131
+ const de = getFromDocumentElement();
132
+ const wi = getFromWindowInner();
133
+ const bd = getFromBody();
134
+ const sc = getFromScreen();
135
+ const ot = getFromOuter();
136
+
137
+ if (preferVisualViewport) {
138
+ return pickFirst(vv.height, de.height, wi.height, bd.height, sc.height, ot.height) || null;
139
+ }
140
+
141
+ // if not preferring visualViewport, still include it but later
142
+ return pickFirst(de.height, wi.height, bd.height, vv.height, sc.height, ot.height) || null;
143
+ };
144
+
145
+ /**
146
+ * Get the best-available viewport width in pixels.
147
+ * Priority (from most reliable for "visible" to least):
148
+ * 1. window.visualViewport.width
149
+ * 2. document.documentElement.clientWidth
150
+ * 3. window.innerWidth
151
+ * 4. document.body.clientWidth
152
+ * 5. window.screen.availWidth / window.screen.width
153
+ * 6. window.outerWidth
154
+ *
155
+ * @param {Object} [options]
156
+ * @param {boolean} [options.preferVisualViewport=true] - when true, prefer visualViewport if present
157
+ * @returns {number|null} width in px (rounded integer) or null if none found
158
+ */
159
+ export const windowGetW = (options = {}) => {
160
+ const { preferVisualViewport = true } = options;
161
+
162
+ const vv = getFromVisualViewport();
163
+ const de = getFromDocumentElement();
164
+ const wi = getFromWindowInner();
165
+ const bd = getFromBody();
166
+ const sc = getFromScreen();
167
+ const ot = getFromOuter();
168
+
169
+ if (preferVisualViewport) {
170
+ return pickFirst(vv.width, de.width, wi.width, bd.width, sc.width, ot.width) || null;
171
+ }
172
+
173
+ return pickFirst(de.width, wi.width, bd.width, vv.width, sc.width, ot.width) || null;
174
+ };
175
+
176
+ // Convenience default export (optional)
177
+ export default {
178
+ windowGetH,
179
+ windowGetW,
180
+ };
181
+
182
+ /* --------------------------------------------------------------------------
183
+ * Example usage:
184
+ *
185
+ * import { windowGetH, windowGetW } from './windowGetDimensions.js';
186
+ *
187
+ * // Get values now
188
+ * const currentH = windowGetH();
189
+ * const currentW = windowGetW();
190
+ *
191
+ * // React to changes (recommended on mobile)
192
+ * if (window.visualViewport) {
193
+ * window.visualViewport.addEventListener('resize', () => {
194
+ * console.log('visualViewport resize ->', windowGetH(), windowGetW());
195
+ * });
196
+ * } else {
197
+ * window.addEventListener('resize', () => {
198
+ * console.log('window resize ->', windowGetH(), windowGetW());
199
+ * });
200
+ * }
201
+ *
202
+ * --------------------------------------------------------------------------*/
@@ -51,6 +51,7 @@ const MenuDefault = {
51
51
  <div class="fl menu-btn-container">
52
52
  ${await BtnIcon.Render({
53
53
  class: 'in wfa main-btn-menu main-btn-home main-btn-menu-active',
54
+ useMenuBtn: true,
54
55
  label: renderMenuLabel({
55
56
  icon: html`<i class="fas fa-home"></i>`,
56
57
  text: html`<span class="menu-label-text">${Translate.Render('home')}</span>`,
@@ -63,6 +64,7 @@ const MenuDefault = {
63
64
  })}
64
65
  ${await BtnIcon.Render({
65
66
  class: 'in wfa main-btn-menu main-btn-log-in',
67
+ useMenuBtn: true,
66
68
  label: renderMenuLabel({
67
69
  icon: html`<i class="fas fa-sign-in-alt"></i>`,
68
70
  text: html`<span class="menu-label-text">${Translate.Render('log-in')}</span>`,
@@ -74,6 +76,7 @@ const MenuDefault = {
74
76
  })}
75
77
  ${await BtnIcon.Render({
76
78
  class: 'in wfa main-btn-menu main-btn-sign-up',
79
+ useMenuBtn: true,
77
80
  label: renderMenuLabel({
78
81
  icon: html`<i class="fas fa-user-plus"></i>`,
79
82
  text: html`<span class="menu-label-text">${Translate.Render('sign-up')}</span>`,
@@ -85,6 +88,7 @@ const MenuDefault = {
85
88
  })}
86
89
  ${await BtnIcon.Render({
87
90
  class: 'in wfa main-btn-menu main-btn-log-out',
91
+ useMenuBtn: true,
88
92
  label: renderMenuLabel({
89
93
  icon: html`<i class="fas fa-sign-out-alt"></i>`,
90
94
  text: html`<span class="menu-label-text">${Translate.Render('log-out')}</span>`,
@@ -97,6 +101,7 @@ const MenuDefault = {
97
101
  })}
98
102
  ${await BtnIcon.Render({
99
103
  class: 'in wfa main-btn-menu main-btn-account',
104
+ useMenuBtn: true,
100
105
  label: renderMenuLabel({
101
106
  icon: html`<i class="fas fa-user-circle"></i>`,
102
107
  text: html`<span class="menu-label-text">${Translate.Render('account')}</span>`,
@@ -109,6 +114,7 @@ const MenuDefault = {
109
114
  })}
110
115
  ${await BtnIcon.Render({
111
116
  class: 'in wfa main-btn-menu main-btn-settings',
117
+ useMenuBtn: true,
112
118
  label: renderMenuLabel({
113
119
  icon: html`<i class="fas fa-sliders-h"></i>`,
114
120
  text: html`<span class="menu-label-text">${Translate.Render('settings')}</span>`,
@@ -120,6 +126,7 @@ const MenuDefault = {
120
126
  })}
121
127
  ${await BtnIcon.Render({
122
128
  class: 'in wfa main-btn-menu main-btn-recover hide',
129
+ useMenuBtn: true,
123
130
  label: renderMenuLabel({
124
131
  icon: html`<i class="fa-solid fa-arrow-rotate-left"></i>`,
125
132
  text: html`<span class="menu-label-text">${Translate.Render('recover')}</span>`,
@@ -131,6 +138,7 @@ const MenuDefault = {
131
138
  })}
132
139
  ${await BtnIcon.Render({
133
140
  class: 'in wfa main-btn-menu main-btn-default-management',
141
+ useMenuBtn: true,
134
142
  label: renderMenuLabel({
135
143
  icon: html`<i class="fa-solid fa-rectangle-list"></i>`,
136
144
  text: html`<span class="menu-label-text">${Translate.Render('default-management')}</span>`,
@@ -142,6 +150,7 @@ const MenuDefault = {
142
150
  })}
143
151
  ${await BtnIcon.Render({
144
152
  class: 'in wfa main-btn-menu main-btn-404 hide',
153
+ useMenuBtn: true,
145
154
  label: renderMenuLabel({
146
155
  icon: html`<i class="fa-solid fa-triangle-exclamation"></i>`,
147
156
  text: html`<span class="menu-label-text">${Translate.Render('404')}</span>`,
@@ -153,6 +162,7 @@ const MenuDefault = {
153
162
  })}
154
163
  ${await BtnIcon.Render({
155
164
  class: 'in wfa main-btn-menu main-btn-500 hide',
165
+ useMenuBtn: true,
156
166
  label: renderMenuLabel({
157
167
  icon: html`<i class="fa-solid fa-circle-exclamation"></i>`,
158
168
  text: html`<span class="menu-label-text">${Translate.Render('500')}</span>`,
@@ -164,6 +174,7 @@ const MenuDefault = {
164
174
  })}
165
175
  ${await BtnIcon.Render({
166
176
  class: 'in wfa main-btn-menu main-btn-blog',
177
+ useMenuBtn: true,
167
178
  label: renderMenuLabel({
168
179
  icon: html`<i class="fa-solid fa-file-invoice"></i>`,
169
180
  text: html`<span class="menu-label-text">${Translate.Render('blog')}</span>`,
@@ -5,7 +5,7 @@ SrrComponent = ({ title, ssrPath, buildId, ssrHeadComponents, ssrBodyComponents,
5
5
  <title>${title}</title>
6
6
  <link rel="icon" type="image/x-icon" href="${ssrPath}favicon.ico" />
7
7
  <meta charset="UTF-8" />
8
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
9
9
  <script>
10
10
  window.renderPayload = ${renderApi.JSONweb(renderPayload)};
11
11
  </script>
package/src/index.js CHANGED
@@ -35,7 +35,7 @@ class Underpost {
35
35
  * @type {String}
36
36
  * @memberof Underpost
37
37
  */
38
- static version = 'v2.8.881';
38
+ static version = 'v2.8.882';
39
39
  /**
40
40
  * Repository cli API
41
41
  * @static
@@ -306,6 +306,61 @@ const validatePasswordMiddleware = (req) => {
306
306
  };
307
307
 
308
308
  // ---------- Session & Refresh token management ----------
309
+
310
+ /**
311
+ * Creates cookie options for the refresh token.
312
+ * @param {import('express').Request} req The Express request object.
313
+ * @returns {object} Cookie options.
314
+ * @memberof Auth
315
+ */
316
+ const cookieOptionsFactory = (req) => {
317
+ const isProduction = process.env.NODE_ENV === 'production';
318
+
319
+ // Determine hostname safely:
320
+ // Prefer origin header if present (it contains protocol + host)
321
+ let candidateHost = undefined;
322
+ try {
323
+ if (req.headers && req.headers.origin) {
324
+ candidateHost = new URL(req.headers.origin).hostname;
325
+ }
326
+ } catch (e) {
327
+ /* ignore parse error */
328
+ logger.error(e);
329
+ }
330
+
331
+ // fallback to req.hostname (Express sets this; ensure trust proxy if behind proxy)
332
+ if (!candidateHost) candidateHost = (req.hostname || '').split(':')[0];
333
+
334
+ candidateHost = (candidateHost || '').trim().replace(/^www\./i, '');
335
+
336
+ // Do not set domain for localhost, 127.x.x.x, or plain IPs
337
+ const isIpOrLocal = /^(localhost|127(?:\.\d+){0,2}\.\d+|\[::1\]|\d+\.\d+\.\d+\.\d+)$/i.test(candidateHost);
338
+ const domain = isProduction && candidateHost && !isIpOrLocal ? `.${candidateHost}` : undefined;
339
+
340
+ // Determine if request is secure: respect X-Forwarded-Proto when behind proxy
341
+ const forwardedProto = (req.headers && req.headers['x-forwarded-proto']) || '';
342
+ const reqIsSecure = Boolean(req.secure || forwardedProto.split(',')[0] === 'https');
343
+
344
+ // secure must be true for SameSite=None to work across sites
345
+ const secure = isProduction ? reqIsSecure : false;
346
+ const sameSite = secure ? 'None' : 'Lax';
347
+
348
+ // Safe parse of maxAge minutes
349
+ const minutes = Number.parseInt(process.env.REFRESH_EXPIRE_MINUTES, 10);
350
+ const maxAge = Number.isFinite(minutes) && minutes > 0 ? minutes * 60 * 1000 : undefined;
351
+
352
+ const opts = {
353
+ httpOnly: true,
354
+ secure,
355
+ sameSite,
356
+ path: '/',
357
+ };
358
+ if (typeof maxAge !== 'undefined') opts.maxAge = maxAge;
359
+ if (domain) opts.domain = domain;
360
+
361
+ return opts;
362
+ };
363
+
309
364
  /**
310
365
  * Create session and set refresh cookie. Rotating and hashed stored token.
311
366
  * @param {object} user The user object.
@@ -339,13 +394,7 @@ async function createSessionAndUserToken(user, User, req, res, options = { host:
339
394
  const jwtid = session._id.toString();
340
395
 
341
396
  // Secure cookie settings
342
- res.cookie('refreshToken', refreshToken, {
343
- httpOnly: true,
344
- secure: process.env.NODE_ENV === 'production',
345
- sameSite: 'Lax',
346
- maxAge: parseInt(process.env.REFRESH_EXPIRE_MINUTES) * 60 * 1000,
347
- path: '/',
348
- });
397
+ res.cookie('refreshToken', refreshToken, cookieOptionsFactory(req));
349
398
 
350
399
  return { jwtid };
351
400
  }
@@ -439,13 +488,7 @@ async function refreshSessionAndToken(req, res, User, options = { host: '', path
439
488
 
440
489
  logger.warn('Refreshed session for user ' + user.email);
441
490
 
442
- res.cookie('refreshToken', refreshToken, {
443
- httpOnly: true,
444
- secure: process.env.NODE_ENV === 'production',
445
- sameSite: 'Lax',
446
- maxAge: parseInt(process.env.REFRESH_EXPIRE_MINUTES) * 60 * 1000,
447
- path: '/',
448
- });
491
+ res.cookie('refreshToken', refreshToken, cookieOptionsFactory(req));
449
492
 
450
493
  return jwtSign(
451
494
  UserDto.auth.payload(user, session._id.toString(), req.ip, req.headers['user-agent'], options.host, options.path),
@@ -533,14 +576,22 @@ function applySecurity(app, opts = {}) {
533
576
  blockAllMixedContent: [],
534
577
  fontSrc: ["'self'", httpDirective, 'data:'],
535
578
  frameAncestors: frameAncestors,
536
- imgSrc: ["'self'", 'data:', httpDirective],
579
+ imgSrc: ["'self'", 'data:', httpDirective, 'https:', 'blob:'],
537
580
  objectSrc: ["'none'"],
538
581
  // script-src and script-src-elem include dynamic nonce
539
- scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
582
+ scriptSrc: [
583
+ "'self'",
584
+ (req, res) => `'nonce-${res.locals.nonce}'`,
585
+ (req, res) => (res.locals.isSwagger ? "'unsafe-inline'" : ''),
586
+ ],
540
587
  scriptSrcElem: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
541
588
  // style-src: avoid 'unsafe-inline' when possible; if you must inline styles,
542
589
  // use a nonce for them too (or hash).
543
- styleSrc: ["'self'", httpDirective, (req, res) => `'nonce-${res.locals.nonce}'`],
590
+ styleSrc: [
591
+ "'self'",
592
+ httpDirective,
593
+ (req, res) => (res.locals.isSwagger ? "'unsafe-inline'" : `'nonce-${res.locals.nonce}'`),
594
+ ],
544
595
  // deny plugins
545
596
  objectSrc: ["'none'"],
546
597
  },