'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 (
); }