Auto-extend session cookie every 30 minutes
| @@ -9,6 +9,9 @@ import { ensureImageFormatSupported, getBase64Async, humanFileSize } from './uti | |||
| 9 | export let currentUser = null; | 9 | export let currentUser = null; |
| 10 | export let accountsEnabled = false; | 10 | export let accountsEnabled = false; |
| 11 | 11 | ||
| 12 | // Extend the session every 30 minutes | ||
| 13 | const SESSION_EXTEND_INTERVAL = 30 * 60 * 1000; | ||
| 14 | |||
| 12 | /** | 15 | /** |
| 13 | * Enable or disable user account controls in the UI. | 16 | * Enable or disable user account controls in the UI. |
| 14 | * @param {boolean} isEnabled User account controls enabled | 17 | * @param {boolean} isEnabled User account controls enabled |
| @@ -894,6 +897,24 @@ async function slugify(text) { | |||
| 894 | } | 897 | } |
| 895 | } | 898 | } |
| 896 | 899 | ||
| 900 | /** | ||
| 901 | * Pings the server to extend the user session. | ||
| 902 | */ | ||
| 903 | async function extendUserSession() { | ||
| 904 | try { | ||
| 905 | const response = await fetch('/api/ping?extend=1', { | ||
| 906 | method: 'GET', | ||
| 907 | headers: getRequestHeaders(), | ||
| 908 | }); | ||
| 909 | |||
| 910 | if (!response.ok) { | ||
| 911 | throw new Error('Ping did not succeed', { cause: response.status }); | ||
| 912 | } | ||
| 913 | } catch (error) { | ||
| 914 | console.error('Failed to extend user session', error); | ||
| 915 | } | ||
| 916 | } | ||
| 917 | |||
| 897 | jQuery(() => { | 918 | jQuery(() => { |
| 898 | $('#logout_button').on('click', () => { | 919 | $('#logout_button').on('click', () => { |
| 899 | logout(); | 920 | logout(); |
| @@ -904,4 +925,9 @@ jQuery(() => { | |||
| 904 | $('#account_button').on('click', () => { | 925 | $('#account_button').on('click', () => { |
| 905 | openUserProfile(); | 926 | openUserProfile(); |
| 906 | }); | 927 | }); |
| 928 | setInterval(async () => { | ||
| 929 | if (currentUser) { | ||
| 930 | await extendUserSession(); | ||
| 931 | } | ||
| 932 | }, SESSION_EXTEND_INTERVAL); | ||
| 907 | }); | 933 | }); |
| @@ -556,7 +556,13 @@ app.use('/api/users', usersPublicRouter); | |||
| 556 | 556 | ||
| 557 | // Everything below this line requires authentication | 557 | // Everything below this line requires authentication |
| 558 | app.use(requireLoginMiddleware); | 558 | app.use(requireLoginMiddleware); |
| 559 | app.get('/api/ping', (_, response) => response.sendStatus(204)); | 559 | app.get('/api/ping', (request, response) => { |
| 560 | if (request.query.extend && request.session) { | ||
| 561 | request.session.touch = Date.now(); | ||
| 562 | } | ||
| 563 | |||
| 564 | response.sendStatus(204); | ||
| 565 | }); | ||
| 560 | 566 | ||
| 561 | // File uploads | 567 | // File uploads |
| 562 | app.use(multer({ dest: uploadsPath, limits: { fieldSize: 10 * 1024 * 1024 } }).single('avatar')); | 568 | app.use(multer({ dest: uploadsPath, limits: { fieldSize: 10 * 1024 * 1024 } }).single('avatar')); |