开启 ppr 优化渲染性能

This commit is contained in:
2025-12-11 14:10:52 +08:00
parent 8fb6ba2f22
commit 5db63273bc
50 changed files with 2635 additions and 10426 deletions

View File

@@ -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
View 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 () => {},
},
},
})
}

View File

@@ -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> = {

View File

@@ -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,
}),
},
))
}

View File

@@ -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,
}),
},
))
}

View File

@@ -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})
},
}))
}