"use client" import Link from "next/link" import {useEffect, useState} from "react" import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query" import {ExternalLink, Gauge, Loader2, Save} from "lucide-react" import {toast} from "sonner" import {useAuth} from "@/components/providers/auth-provider" import {EmptyStateWithBorder} from "@/components/layout/empty" import {ErrorInline} from "@/components/layout/error" import {LoadingStateWithBorder} from "@/components/layout/loading" import {Button} from "@/components/ui/button" import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card" import {Input} from "@/components/ui/input" import {Label} from "@/components/ui/label" import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select" import {Switch} from "@/components/ui/switch" import {OptionService} from "@/lib/services/openflare" import { defaultPerformanceFields, entriesFromKeys, mapOptionsToFields, optionsToMap, type PerformanceFields, validateCacheFields, validateGzipFields, validateProxyFields, validateRuntimeFields, } from "./components/performance-utils" const optionsQueryKey = ["openflare", "options"] as const export default function PerformancePage() { const { user, loading: authLoading } = useAuth() const queryClient = useQueryClient() const [fields, setFields] = useState(defaultPerformanceFields) const [savingSection, setSavingSection] = useState(null) const optionsQuery = useQuery({ queryKey: optionsQueryKey, queryFn: () => OptionService.list(), enabled: !!user?.is_admin, }) useEffect(() => { if (!optionsQuery.data) return setFields(mapOptionsToFields(optionsToMap(optionsQuery.data))) }, [optionsQuery.data]) const saveMutation = useMutation({ mutationFn: async ({ section, entries, validator, }: { section: string entries: Array<{ key: string; value: string }> validator?: (fields: PerformanceFields) => void }) => { validator?.(fields) setSavingSection(section) await OptionService.updateBatch(entries) }, onSuccess: async () => { toast.success("性能参数已保存") await Promise.all([ queryClient.invalidateQueries({ queryKey: optionsQueryKey }), queryClient.invalidateQueries({ queryKey: ["openflare", "config-preview"] }), queryClient.invalidateQueries({ queryKey: ["openflare", "config-versions"] }), ]) setSavingSection(null) }, onError: (error) => { setSavingSection(null) toast.error(error instanceof Error ? error.message : "保存失败") }, }) const updateField = ( key: K, value: PerformanceFields[K], ) => { setFields((prev) => ({ ...prev, [key]: value })) } if (authLoading) { return (
) } if (!user?.is_admin) { return (
) } if (optionsQuery.isLoading) { return (
) } if (optionsQuery.isError) { return (
void optionsQuery.refetch()} />
) } return (

性能

集中管理 OpenResty 全局性能参数,保存后进入统一配置发布链路。

连接与事件 worker、超时、请求体大小等运行参数
updateField("openresty_worker_processes", v)} /> updateField("openresty_worker_connections", v)} type="number" /> updateField("openresty_worker_rlimit_nofile", v)} type="number" />
updateField("openresty_events_multi_accept_enabled", v)} /> updateField("openresty_keepalive_timeout", v)} type="number" /> updateField("openresty_client_max_body_size", v)} /> updateField("openresty_resolvers", v)} placeholder="1.1.1.1 8.8.8.8" /> updateField("openresty_default_server_return_status", v)} type="number" />
反代缓冲与超时 upstream 连接、缓冲与 WebSocket/HTTP3 开关
updateField("openresty_proxy_connect_timeout", v)} type="number" /> updateField("openresty_proxy_read_timeout", v)} type="number" /> updateField("openresty_websocket_enabled", v)} /> updateField("openresty_http3_enabled", v)} /> updateField("openresty_proxy_buffering_enabled", v)} /> updateField("openresty_proxy_buffers", v)} />
压缩 updateField("openresty_gzip_enabled", v)} /> updateField("openresty_gzip_min_length", v)} type="number" /> updateField("openresty_gzip_comp_level", v)} type="number" />
缓存 单节点反代缓存优化场景
updateField("openresty_cache_enabled", v)} /> updateField("openresty_cache_path", v)} disabled={!fields.openresty_cache_enabled} /> updateField("openresty_cache_levels", v)} disabled={!fields.openresty_cache_enabled} /> updateField("openresty_cache_max_size", v)} disabled={!fields.openresty_cache_enabled} />
) } function FieldInput({ label, value, onChange, type = "text", placeholder, disabled, }: { label: string value: string onChange: (value: string) => void type?: string placeholder?: string disabled?: boolean }) { return (
onChange(e.target.value)} className="h-9 text-xs" />
) } function ToggleRow({ label, checked, onChange, }: { label: string checked: boolean onChange: (value: boolean) => void }) { return (
) }