开启 ppr 优化渲染性能
This commit is contained in:
91
src/lib/cap/index.ts
Normal file
91
src/lib/cap/index.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import 'server-only'
|
||||
import Cap, {ChallengeData} from '@cap.js/server'
|
||||
import {cookies} from 'next/headers'
|
||||
import {createHmac, randomBytes, timingSafeEqual} from 'crypto'
|
||||
import {CLIENT_SECRET} from '@/lib/api'
|
||||
|
||||
type Sign = {
|
||||
data: string
|
||||
timestamp: number
|
||||
nonce: string
|
||||
sign: string
|
||||
}
|
||||
|
||||
function sign(data: string, timestamp: number) {
|
||||
if (!CLIENT_SECRET) throw new Error('无法完成签名')
|
||||
const hash = createHmac('sha256', CLIENT_SECRET)
|
||||
const nonce = String(randomBytes(16))
|
||||
hash.update(data).update(nonce).update(String(timestamp))
|
||||
return {
|
||||
data, timestamp, nonce,
|
||||
sign: hash.digest('hex'),
|
||||
}
|
||||
}
|
||||
|
||||
function verify({data, timestamp, nonce, sign}: Sign) {
|
||||
if (!CLIENT_SECRET) throw new Error('无法完成验证')
|
||||
const hash = createHmac('sha256', CLIENT_SECRET)
|
||||
hash.update(data).update(nonce).update(String(timestamp))
|
||||
const excepted = Buffer.from(sign)
|
||||
const received = Buffer.from(hash.digest('hex'))
|
||||
return timingSafeEqual(excepted, received)
|
||||
}
|
||||
|
||||
export async function getCap() {
|
||||
return new Cap({
|
||||
disableAutoCleanup: true,
|
||||
noFSState: true,
|
||||
storage: {
|
||||
challenges: {
|
||||
store: async (token: string, data: ChallengeData) => {
|
||||
const cookie = await cookies()
|
||||
const rs = sign(JSON.stringify({token, data}), data.expires)
|
||||
cookie.set(`challenge:${token}`, JSON.stringify(rs), {
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
expires: data.expires,
|
||||
sameSite: true,
|
||||
httpOnly: true,
|
||||
})
|
||||
},
|
||||
read: async (token: string) => {
|
||||
const cookie = await cookies()
|
||||
const json = cookie.get(`challenge:${token}`)?.value
|
||||
if (!json) return null
|
||||
const sign = JSON.parse(json) as Sign
|
||||
if (!verify(sign)) return null
|
||||
return JSON.parse(sign.data)['data']
|
||||
},
|
||||
delete: async (token: string) => {
|
||||
const cookie = await cookies()
|
||||
cookie.delete(`challenge:${token}`)
|
||||
},
|
||||
deleteExpired: async () => {},
|
||||
},
|
||||
tokens: {
|
||||
store: async (token: string, expires: number) => {
|
||||
const cookie = await cookies()
|
||||
const rs = sign(JSON.stringify({token, expires}), expires)
|
||||
cookie.set(token, JSON.stringify(rs), {
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
expires: expires,
|
||||
sameSite: true,
|
||||
httpOnly: true,
|
||||
})
|
||||
},
|
||||
get: async (token: string) => {
|
||||
const cookie = await cookies()
|
||||
const json = cookie.get(token)?.value
|
||||
if (!json) return null
|
||||
const sign = JSON.parse(json) as Sign
|
||||
if (!verify(sign)) return null
|
||||
return JSON.parse(sign.data)['expires']
|
||||
},
|
||||
delete: async (token: string) => {
|
||||
const cookie = await cookies()
|
||||
cookie.delete(`token:${token}`)
|
||||
},
|
||||
deleteExpired: async () => {},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user