开启 ppr 优化渲染性能
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
export type AuthContext = {
|
||||
payload: Payload
|
||||
permissions: Record<string, never>
|
||||
metadata: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type Payload = {
|
||||
id: number
|
||||
name: string
|
||||
avatar: string
|
||||
}
|
||||
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 () => {},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -6,12 +6,8 @@ type ResourceShort = {
|
||||
expire_at: Date
|
||||
quota: number
|
||||
used: number
|
||||
daily_limit: number
|
||||
daily_used: number
|
||||
last_at: Date
|
||||
created_at: Date
|
||||
updated_at: Date
|
||||
daily: number
|
||||
last_at?: Date
|
||||
}
|
||||
|
||||
type ResourceLong = {
|
||||
@@ -22,12 +18,8 @@ type ResourceLong = {
|
||||
expire_at: Date
|
||||
quota: number
|
||||
used: number
|
||||
daily_limit: number
|
||||
daily_used: number
|
||||
last_at: Date
|
||||
created_at: Date
|
||||
updated_at: Date
|
||||
daily: number
|
||||
last_at?: Date
|
||||
}
|
||||
|
||||
export type Resource<T extends 1 | 2 = 1 | 2> = {
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import {createStore} from 'zustand/vanilla'
|
||||
import {persist} from 'zustand/middleware'
|
||||
|
||||
export type ClientStore = ClientState & ClientActions
|
||||
|
||||
type Point = 'sm' | 'md' | 'lg' | 'xl'
|
||||
|
||||
export type ClientState = {
|
||||
breakpoint: {
|
||||
sm: boolean
|
||||
md: boolean
|
||||
lg: boolean
|
||||
xl: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type ClientActions = {
|
||||
setBreakpoints: (breakpoints: Partial<ClientState['breakpoint']>) => void
|
||||
}
|
||||
|
||||
export const createClientStore = () => {
|
||||
return createStore<ClientStore>()(persist(
|
||||
setState => ({
|
||||
breakpoint: {
|
||||
sm: false,
|
||||
md: false,
|
||||
lg: false,
|
||||
xl: false,
|
||||
},
|
||||
|
||||
setBreakpoints: breakpoints => setState(state => ({
|
||||
breakpoint: {
|
||||
...state.breakpoint,
|
||||
...breakpoints,
|
||||
},
|
||||
})),
|
||||
}),
|
||||
|
||||
{
|
||||
name: 'client-store',
|
||||
partialize: state => ({
|
||||
device: state.breakpoint,
|
||||
}),
|
||||
},
|
||||
))
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import {createStore} from 'zustand/vanilla'
|
||||
import {persist} from 'zustand/middleware'
|
||||
|
||||
export type LayoutStore = LayoutState & LayoutActions
|
||||
|
||||
export type LayoutState = {
|
||||
navbar: boolean
|
||||
}
|
||||
|
||||
export type LayoutActions = {
|
||||
toggleNavbar: () => void
|
||||
setNavbar: (navbar: boolean) => void
|
||||
}
|
||||
|
||||
export const createLayoutStore = () => {
|
||||
return createStore<LayoutStore>()(persist(
|
||||
setState => ({
|
||||
navbar: false,
|
||||
|
||||
toggleNavbar: () => setState((state) => {
|
||||
return {navbar: !state.navbar}
|
||||
}),
|
||||
|
||||
setNavbar: navbar => setState((_) => {
|
||||
return {navbar}
|
||||
}),
|
||||
}),
|
||||
|
||||
{
|
||||
name: 'layout-store',
|
||||
partialize: state => ({
|
||||
navbar: state.navbar,
|
||||
}),
|
||||
},
|
||||
))
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import {User} from '@/lib/models'
|
||||
import {createStore} from 'zustand/vanilla'
|
||||
import {getProfile} from '@/actions/auth'
|
||||
|
||||
export type ProfileStore = ProfileState & ProfileActions
|
||||
|
||||
export type ProfileState = {
|
||||
profile: User | null
|
||||
}
|
||||
|
||||
export type ProfileActions = {
|
||||
refreshProfile: () => Promise<void>
|
||||
}
|
||||
|
||||
export const createProfileStore = () => {
|
||||
return createStore<ProfileStore>()(set => ({
|
||||
profile: null,
|
||||
refreshProfile: async () => {
|
||||
const result = await getProfile()
|
||||
set({profile: result.success ? result.data : null})
|
||||
},
|
||||
}))
|
||||
}
|
||||
Reference in New Issue
Block a user