From 3629aa835d55d943464da128f8a665ff3365a947 Mon Sep 17 00:00:00 2001 From: Sebastian Rager Date: Mon, 28 Nov 2022 17:23:58 +0100 Subject: [PATCH 1/3] Add resave option --- package-lock.json | 7 +++---- package.json | 1 + src/module.ts | 3 ++- src/runtime/server/middleware/session/index.ts | 13 ++++++++++--- src/types.ts | 7 +++++++ 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a80593..36d1ea0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "argon2": "^0.30.2", "dayjs": "^1.11.6", "defu": "^6.1.0", + "fast-deep-equal": "^3.1.3", "h3": "^1.0.1", "unstorage": "^1.0.1" }, @@ -4461,8 +4462,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.2.12", @@ -13218,8 +13218,7 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { "version": "3.2.12", diff --git a/package.json b/package.json index edb5d2c..4285630 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "argon2": "^0.30.2", "dayjs": "^1.11.6", "defu": "^6.1.0", + "fast-deep-equal": "^3.1.3", "h3": "^1.0.1", "unstorage": "^1.0.1" }, diff --git a/src/module.ts b/src/module.ts index efcc69f..7c312fc 100644 --- a/src/module.ts +++ b/src/module.ts @@ -23,7 +23,8 @@ const defaults: FilledModuleOptions = { options: {} }, domain: null, - ipPinning: false as boolean|SessionIpPinningOptions + ipPinning: false as boolean|SessionIpPinningOptions, + resave: true }, api: { isEnabled: true, diff --git a/src/runtime/server/middleware/session/index.ts b/src/runtime/server/middleware/session/index.ts index 5ccb4e7..02ad5d2 100644 --- a/src/runtime/server/middleware/session/index.ts +++ b/src/runtime/server/middleware/session/index.ts @@ -1,6 +1,7 @@ import { deleteCookie, eventHandler, H3Event, parseCookies, setCookie } from 'h3' import { nanoid } from 'nanoid' import dayjs from 'dayjs' +import equal from 'fast-deep-equal' import { SameSiteOptions, Session, SessionOptions } from '../../../../types' import { dropStorageSession, getStorageSession, setStorageSession } from './storage' import { processSessionIp, getHashedIpAddress } from './ipPinning' @@ -129,10 +130,14 @@ const ensureSession = async (event: H3Event) => { } export default eventHandler(async (event: H3Event) => { + const sessionConfig = useRuntimeConfig().session.session + // 1. Ensure that a session is present by either loading or creating one - await ensureSession(event) + const session = await ensureSession(event) + // 2. Save current state of the session + const source = { ...session } - // 2. Setup a hook that saves any changed made to the session by the subsequent endpoints & middlewares + // 3. Setup a hook that saves any changed made to the session by the subsequent endpoints & middlewares event.res.on('finish', async () => { // Session id may not exist if session was deleted const session = await getSession(event) @@ -140,6 +145,8 @@ export default eventHandler(async (event: H3Event) => { return } - await setStorageSession(session.id, event.context.session) + if (sessionConfig.resave || !equal(event.context.session, source)) { + await setStorageSession(session.id, event.context.session) + } }) }) diff --git a/src/types.ts b/src/types.ts index fe059c7..a3d3880 100644 --- a/src/types.ts +++ b/src/types.ts @@ -82,6 +82,13 @@ export interface SessionOptions { * @type {SessionIpPinningOptions|boolean} */ ipPinning: SessionIpPinningOptions|boolean, + /** + * Forces the session to be saved back to the session store, even if the session was never modified during the request. + * @default true + * @example false + * @type boolean + */ + resave: boolean } export interface ApiOptions { From 8fbed4a12fd92a7857f147cd34b292fddcac0a3d Mon Sep 17 00:00:00 2001 From: Sebastian Rager Date: Mon, 28 Nov 2022 17:51:29 +0100 Subject: [PATCH 2/3] Update configuration example --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 140969f..4be8d8d 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,9 @@ Here's what the full _default_ module configuration looks like: // The request-domain is strictly used for the cookie, no sub-domains allowed domain: null, // Sessions aren't pinned to the user's IP address - ipPinning: false + ipPinning: false, + // Sessions are saved to the store, even if they were never modified during the request + resave: true }, api: { // The API is enabled From 1cb021587159886441f93c0fb6ea9ae983901ae9 Mon Sep 17 00:00:00 2001 From: Sebastian Rager Date: Mon, 28 Nov 2022 19:36:28 +0100 Subject: [PATCH 3/3] Use types for session options --- src/runtime/server/middleware/session/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/server/middleware/session/index.ts b/src/runtime/server/middleware/session/index.ts index 02ad5d2..257bce7 100644 --- a/src/runtime/server/middleware/session/index.ts +++ b/src/runtime/server/middleware/session/index.ts @@ -130,7 +130,7 @@ const ensureSession = async (event: H3Event) => { } export default eventHandler(async (event: H3Event) => { - const sessionConfig = useRuntimeConfig().session.session + const sessionOptions = useRuntimeConfig().session.session as SessionOptions // 1. Ensure that a session is present by either loading or creating one const session = await ensureSession(event) @@ -145,7 +145,7 @@ export default eventHandler(async (event: H3Event) => { return } - if (sessionConfig.resave || !equal(event.context.session, source)) { + if (sessionOptions.resave || !equal(event.context.session, source)) { await setStorageSession(session.id, event.context.session) } })