Skip to content
This repository was archived by the owner on Dec 12, 2023. It is now read-only.

Commit a34d5d8

Browse files
committed
Add saveUninitialized option
1 parent f82d2c1 commit a34d5d8

File tree

4 files changed

+45
-3
lines changed

4 files changed

+45
-3
lines changed

src/module.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ const defaults: FilledModuleOptions = {
2323
options: {}
2424
},
2525
domain: null,
26-
ipPinning: false as boolean|SessionIpPinningOptions
26+
ipPinning: false as boolean|SessionIpPinningOptions,
27+
saveUninitialized: true
2728
},
2829
api: {
2930
isEnabled: true,

src/runtime/server/middleware/session/index.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { SameSiteOptions, Session, SessionOptions } from '../../../../types'
55
import { dropStorageSession, getStorageSession, setStorageSession } from './storage'
66
import { processSessionIp, getHashedIpAddress } from './ipPinning'
77
import { SessionExpired } from './exceptions'
8+
import { resEndProxy } from './resEndProxy'
89
import { useRuntimeConfig } from '#imports'
910

1011
const SESSION_COOKIE_NAME = 'sessionId'
@@ -58,7 +59,7 @@ export const deleteSession = async (event: H3Event) => {
5859
deleteCookie(event, SESSION_COOKIE_NAME)
5960
}
6061

61-
const newSession = async (event: H3Event) => {
62+
const newSession = async (event: H3Event, copyContextSession?: boolean) => {
6263
const runtimeConfig = useRuntimeConfig()
6364
const sessionOptions = runtimeConfig.session.session
6465

@@ -72,6 +73,10 @@ const newSession = async (event: H3Event) => {
7273
createdAt: new Date(),
7374
ip: sessionOptions.ipPinning ? await getHashedIpAddress(event) : undefined
7475
}
76+
// Copy the session object from the event context to the new session
77+
if (copyContextSession) {
78+
Object.assign(session, event.context.session)
79+
}
7580
await setStorageSession(sessionId, session)
7681

7782
return session
@@ -118,9 +123,23 @@ function isSession (shape: unknown): shape is Session {
118123
}
119124

120125
const ensureSession = async (event: H3Event) => {
126+
const sessionConfig = useRuntimeConfig().session.session
127+
121128
let session = await getSession(event)
122129
if (!session) {
123-
session = await newSession(event)
130+
if (sessionConfig.saveUninitialized) {
131+
session = await newSession(event)
132+
} else {
133+
// 1. Create an empty session object
134+
event.context.session = {}
135+
// 2. Create a new session if the object has been modified by any event handler
136+
resEndProxy(event.res, async () => {
137+
if (Object.keys(event.context.session).length) {
138+
await newSession(event, true)
139+
}
140+
})
141+
return null
142+
}
124143
}
125144

126145
event.context.sessionId = session.id
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { ServerResponse } from 'node:http'
2+
3+
type MiddleWare = () => Promise<void>
4+
5+
// Proxy res.end() to get a callback at the end of all event handlers
6+
export const resEndProxy = (res: ServerResponse, middleWare: MiddleWare) => {
7+
const _end = res.end
8+
9+
// @ts-ignore Replacing res.end() will lead to type checking error
10+
res.end = async (chunk: any, encoding: BufferEncoding) => {
11+
await middleWare()
12+
return _end.call(res, chunk, encoding) as ServerResponse
13+
}
14+
}

src/types.ts

+8
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ export interface SessionOptions {
8282
* @type {SessionIpPinningOptions|boolean}
8383
*/
8484
ipPinning: SessionIpPinningOptions|boolean,
85+
/**
86+
* Forces a session that is "uninitialized" to be saved to the store. A session is uninitialized when it is new but not modified.
87+
* Choosing false is useful for implementing login sessions, reducing server storage usage, or complying with laws that require permission before setting a cookie.
88+
* @default true
89+
* @example false
90+
* @type boolean
91+
*/
92+
saveUninitialized: boolean
8593
}
8694

8795
export interface ApiOptions {

0 commit comments

Comments
 (0)