diff --git a/README.md b/README.md index 5c6c5af..0bbbaa5 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,9 @@ Here's what the full _default_ module configuration looks like: // Sessions aren't pinned to the user's IP address ipPinning: false, // Expiration of the sessions are not reset to the original expiryInSeconds on every request - rolling: false + rolling: false, + // Sessions are saved to the store, even if they were never modified during the request + resave: true }, api: { // The API is enabled diff --git a/package-lock.json b/package-lock.json index 504ac59..8e9bf87 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 88e65d1..e2333df 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 d7d0c80..d9dd70e 100644 --- a/src/module.ts +++ b/src/module.ts @@ -26,7 +26,8 @@ const defaults: FilledModuleOptions = { }, domain: false, ipPinning: false as boolean|SessionIpPinningOptions, - rolling: false + rolling: false, + resave: true }, api: { isEnabled: true, diff --git a/src/runtime/server/middleware/session/index.ts b/src/runtime/server/middleware/session/index.ts index 6ee76e1..9a45875 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' @@ -147,10 +148,14 @@ const ensureSession = async (event: H3Event) => { } export default eventHandler(async (event: H3Event) => { + const sessionOptions = useRuntimeConfig().session.session as SessionOptions + // 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) @@ -158,6 +163,8 @@ export default eventHandler(async (event: H3Event) => { return } - await setStorageSession(session.id, event.context.session) + if (sessionOptions.resave || !equal(event.context.session, source)) { + await setStorageSession(session.id, event.context.session) + } }) }) diff --git a/src/types.ts b/src/types.ts index af9b8ce..402ffe5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -104,7 +104,14 @@ export interface SessionOptions { * @example true * @type boolean */ - rolling: boolean + rolling: 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 {