2021-11-22 20:43:00 +00:00
|
|
|
import type { ServerResponse } from 'http'
|
|
|
|
import { Ref, ref, watch } from 'vue'
|
2021-11-22 23:20:20 +00:00
|
|
|
import { parse, serialize, CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
|
2021-11-22 20:43:00 +00:00
|
|
|
import { appendHeader } from 'h3'
|
|
|
|
import type { NuxtApp } from '@nuxt/schema'
|
|
|
|
import destr from 'destr'
|
|
|
|
import { useNuxtApp } from '#app'
|
|
|
|
|
|
|
|
type _CookieOptions = Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'>
|
2021-11-29 10:40:12 +00:00
|
|
|
|
2021-11-22 20:43:00 +00:00
|
|
|
export interface CookieOptions<T=any> extends _CookieOptions {
|
|
|
|
decode?(value: string): T
|
|
|
|
encode?(value: T): string;
|
2021-11-29 10:40:12 +00:00
|
|
|
default?: () => T
|
2021-11-22 20:43:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface CookieRef<T> extends Ref<T> {}
|
|
|
|
|
|
|
|
const CookieDefaults: CookieOptions<any> = {
|
|
|
|
decode: val => destr(decodeURIComponent(val)),
|
|
|
|
encode: val => encodeURIComponent(typeof val === 'string' ? val : JSON.stringify(val))
|
|
|
|
}
|
|
|
|
|
2021-11-23 00:23:33 +00:00
|
|
|
export function useCookie <T=string> (name: string, _opts?: CookieOptions<T>): CookieRef<T> {
|
2021-11-22 20:43:00 +00:00
|
|
|
const opts = { ...CookieDefaults, ..._opts }
|
|
|
|
const cookies = readRawCookies(opts)
|
|
|
|
|
2021-12-02 11:29:26 +00:00
|
|
|
const cookie = ref(cookies[name] ?? opts.default?.())
|
2021-11-22 20:43:00 +00:00
|
|
|
|
|
|
|
if (process.client) {
|
2021-11-29 10:40:12 +00:00
|
|
|
watch(cookie, () => { writeClientCookie(name, cookie.value, opts as CookieSerializeOptions) })
|
2021-11-22 20:43:00 +00:00
|
|
|
} else if (process.server) {
|
|
|
|
const initialValue = cookie.value
|
|
|
|
const nuxtApp = useNuxtApp()
|
|
|
|
nuxtApp.hooks.hookOnce('app:rendered', () => {
|
|
|
|
if (cookie.value !== initialValue) {
|
|
|
|
// @ts-ignore
|
|
|
|
writeServerCookie(useSSRRes(nuxtApp), name, cookie.value, opts)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-11-29 10:40:12 +00:00
|
|
|
return cookie as CookieRef<T>
|
2021-11-22 20:43:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
function useSSRReq (nuxtApp?: NuxtApp = useNuxtApp()) { return nuxtApp.ssrContext?.req }
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
function useSSRRes (nuxtApp?: NuxtApp = useNuxtApp()) { return nuxtApp.ssrContext?.res }
|
|
|
|
|
|
|
|
function readRawCookies (opts: CookieOptions = {}): Record<string, string> {
|
|
|
|
if (process.server) {
|
2021-11-22 23:20:20 +00:00
|
|
|
return parse(useSSRReq().headers.cookie || '', opts)
|
2021-11-22 20:43:00 +00:00
|
|
|
} else if (process.client) {
|
2021-11-22 23:20:20 +00:00
|
|
|
return parse(document.cookie, opts)
|
2021-11-22 20:43:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function serializeCookie (name: string, value: any, opts: CookieSerializeOptions = {}) {
|
|
|
|
if (value === null || value === undefined) {
|
2021-12-21 12:02:55 +00:00
|
|
|
return serialize(name, value, { ...opts, maxAge: -1 })
|
2021-11-22 20:43:00 +00:00
|
|
|
}
|
2021-11-22 23:20:20 +00:00
|
|
|
return serialize(name, value, opts)
|
2021-11-22 20:43:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function writeClientCookie (name: string, value: any, opts: CookieSerializeOptions = {}) {
|
|
|
|
if (process.client) {
|
|
|
|
document.cookie = serializeCookie(name, value, opts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function writeServerCookie (res: ServerResponse, name: string, value: any, opts: CookieSerializeOptions = {}) {
|
|
|
|
if (res) {
|
2021-11-29 10:40:12 +00:00
|
|
|
// TODO: Try to smart join with existing Set-Cookie headers
|
2021-11-22 20:43:00 +00:00
|
|
|
appendHeader(res, 'Set-Cookie', serializeCookie(name, value, opts))
|
|
|
|
}
|
|
|
|
}
|