mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-09 00:56:37 +08:00
[优化] 增加 OpenResty 配置选项和性能页面工具提示
This commit is contained in:
@@ -65,10 +65,13 @@ var OpenRestyKeepaliveTimeout = 65
|
|||||||
var OpenRestyKeepaliveRequests = 1000
|
var OpenRestyKeepaliveRequests = 1000
|
||||||
var OpenRestyClientHeaderTimeout = 15
|
var OpenRestyClientHeaderTimeout = 15
|
||||||
var OpenRestyClientBodyTimeout = 15
|
var OpenRestyClientBodyTimeout = 15
|
||||||
|
var OpenRestyClientMaxBodySize = "64m"
|
||||||
|
var OpenRestyLargeClientHeaderBuffers = "4 16k"
|
||||||
var OpenRestySendTimeout = 30
|
var OpenRestySendTimeout = 30
|
||||||
var OpenRestyProxyConnectTimeout = 5
|
var OpenRestyProxyConnectTimeout = 5
|
||||||
var OpenRestyProxySendTimeout = 60
|
var OpenRestyProxySendTimeout = 60
|
||||||
var OpenRestyProxyReadTimeout = 60
|
var OpenRestyProxyReadTimeout = 60
|
||||||
|
var OpenRestyProxyRequestBufferingEnabled = true
|
||||||
var OpenRestyProxyBufferingEnabled = true
|
var OpenRestyProxyBufferingEnabled = true
|
||||||
var OpenRestyProxyBuffers = "16 16k"
|
var OpenRestyProxyBuffers = "16 16k"
|
||||||
var OpenRestyProxyBufferSize = "8k"
|
var OpenRestyProxyBufferSize = "8k"
|
||||||
@@ -104,10 +107,13 @@ http {
|
|||||||
keepalive_requests {{OpenRestyKeepaliveRequests}};
|
keepalive_requests {{OpenRestyKeepaliveRequests}};
|
||||||
client_header_timeout {{OpenRestyClientHeaderTimeout}};
|
client_header_timeout {{OpenRestyClientHeaderTimeout}};
|
||||||
client_body_timeout {{OpenRestyClientBodyTimeout}};
|
client_body_timeout {{OpenRestyClientBodyTimeout}};
|
||||||
|
client_max_body_size {{OpenRestyClientMaxBodySize}};
|
||||||
|
large_client_header_buffers {{OpenRestyLargeClientHeaderBuffers}};
|
||||||
send_timeout {{OpenRestySendTimeout}};
|
send_timeout {{OpenRestySendTimeout}};
|
||||||
proxy_connect_timeout {{OpenRestyProxyConnectTimeout}};
|
proxy_connect_timeout {{OpenRestyProxyConnectTimeout}};
|
||||||
proxy_send_timeout {{OpenRestyProxySendTimeout}};
|
proxy_send_timeout {{OpenRestyProxySendTimeout}};
|
||||||
proxy_read_timeout {{OpenRestyProxyReadTimeout}};
|
proxy_read_timeout {{OpenRestyProxyReadTimeout}};
|
||||||
|
proxy_request_buffering {{OpenRestyProxyRequestBuffering}};
|
||||||
proxy_buffering {{OpenRestyProxyBuffering}};
|
proxy_buffering {{OpenRestyProxyBuffering}};
|
||||||
proxy_buffers {{OpenRestyProxyBuffers}};
|
proxy_buffers {{OpenRestyProxyBuffers}};
|
||||||
proxy_buffer_size {{OpenRestyProxyBufferSize}};
|
proxy_buffer_size {{OpenRestyProxyBufferSize}};
|
||||||
|
|||||||
@@ -64,10 +64,13 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["OpenRestyKeepaliveRequests"] = strconv.Itoa(common.OpenRestyKeepaliveRequests)
|
common.OptionMap["OpenRestyKeepaliveRequests"] = strconv.Itoa(common.OpenRestyKeepaliveRequests)
|
||||||
common.OptionMap["OpenRestyClientHeaderTimeout"] = strconv.Itoa(common.OpenRestyClientHeaderTimeout)
|
common.OptionMap["OpenRestyClientHeaderTimeout"] = strconv.Itoa(common.OpenRestyClientHeaderTimeout)
|
||||||
common.OptionMap["OpenRestyClientBodyTimeout"] = strconv.Itoa(common.OpenRestyClientBodyTimeout)
|
common.OptionMap["OpenRestyClientBodyTimeout"] = strconv.Itoa(common.OpenRestyClientBodyTimeout)
|
||||||
|
common.OptionMap["OpenRestyClientMaxBodySize"] = common.OpenRestyClientMaxBodySize
|
||||||
|
common.OptionMap["OpenRestyLargeClientHeaderBuffers"] = common.OpenRestyLargeClientHeaderBuffers
|
||||||
common.OptionMap["OpenRestySendTimeout"] = strconv.Itoa(common.OpenRestySendTimeout)
|
common.OptionMap["OpenRestySendTimeout"] = strconv.Itoa(common.OpenRestySendTimeout)
|
||||||
common.OptionMap["OpenRestyProxyConnectTimeout"] = strconv.Itoa(common.OpenRestyProxyConnectTimeout)
|
common.OptionMap["OpenRestyProxyConnectTimeout"] = strconv.Itoa(common.OpenRestyProxyConnectTimeout)
|
||||||
common.OptionMap["OpenRestyProxySendTimeout"] = strconv.Itoa(common.OpenRestyProxySendTimeout)
|
common.OptionMap["OpenRestyProxySendTimeout"] = strconv.Itoa(common.OpenRestyProxySendTimeout)
|
||||||
common.OptionMap["OpenRestyProxyReadTimeout"] = strconv.Itoa(common.OpenRestyProxyReadTimeout)
|
common.OptionMap["OpenRestyProxyReadTimeout"] = strconv.Itoa(common.OpenRestyProxyReadTimeout)
|
||||||
|
common.OptionMap["OpenRestyProxyRequestBufferingEnabled"] = strconv.FormatBool(common.OpenRestyProxyRequestBufferingEnabled)
|
||||||
common.OptionMap["OpenRestyProxyBufferingEnabled"] = strconv.FormatBool(common.OpenRestyProxyBufferingEnabled)
|
common.OptionMap["OpenRestyProxyBufferingEnabled"] = strconv.FormatBool(common.OpenRestyProxyBufferingEnabled)
|
||||||
common.OptionMap["OpenRestyProxyBuffers"] = common.OpenRestyProxyBuffers
|
common.OptionMap["OpenRestyProxyBuffers"] = common.OpenRestyProxyBuffers
|
||||||
common.OptionMap["OpenRestyProxyBufferSize"] = common.OpenRestyProxyBufferSize
|
common.OptionMap["OpenRestyProxyBufferSize"] = common.OpenRestyProxyBufferSize
|
||||||
@@ -240,6 +243,14 @@ func updateOptionMap(key string, value string) {
|
|||||||
if v, err := strconv.Atoi(value); err == nil && v > 0 {
|
if v, err := strconv.Atoi(value); err == nil && v > 0 {
|
||||||
common.OpenRestyClientBodyTimeout = v
|
common.OpenRestyClientBodyTimeout = v
|
||||||
}
|
}
|
||||||
|
case "OpenRestyClientMaxBodySize":
|
||||||
|
if strings.TrimSpace(value) != "" {
|
||||||
|
common.OpenRestyClientMaxBodySize = value
|
||||||
|
}
|
||||||
|
case "OpenRestyLargeClientHeaderBuffers":
|
||||||
|
if strings.TrimSpace(value) != "" {
|
||||||
|
common.OpenRestyLargeClientHeaderBuffers = value
|
||||||
|
}
|
||||||
case "OpenRestySendTimeout":
|
case "OpenRestySendTimeout":
|
||||||
if v, err := strconv.Atoi(value); err == nil && v > 0 {
|
if v, err := strconv.Atoi(value); err == nil && v > 0 {
|
||||||
common.OpenRestySendTimeout = v
|
common.OpenRestySendTimeout = v
|
||||||
@@ -256,6 +267,8 @@ func updateOptionMap(key string, value string) {
|
|||||||
if v, err := strconv.Atoi(value); err == nil && v > 0 {
|
if v, err := strconv.Atoi(value); err == nil && v > 0 {
|
||||||
common.OpenRestyProxyReadTimeout = v
|
common.OpenRestyProxyReadTimeout = v
|
||||||
}
|
}
|
||||||
|
case "OpenRestyProxyRequestBufferingEnabled":
|
||||||
|
common.OpenRestyProxyRequestBufferingEnabled = value == "true"
|
||||||
case "OpenRestyProxyBufferingEnabled":
|
case "OpenRestyProxyBufferingEnabled":
|
||||||
common.OpenRestyProxyBufferingEnabled = value == "true"
|
common.OpenRestyProxyBufferingEnabled = value == "true"
|
||||||
case "OpenRestyProxyBuffers":
|
case "OpenRestyProxyBuffers":
|
||||||
|
|||||||
@@ -61,6 +61,47 @@ const defaultPerformanceFields = {
|
|||||||
'error timeout updating http_500 http_502 http_503 http_504',
|
'error timeout updating http_500 http_502 http_503 http_504',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const performanceFieldTooltips: Record<string, string> = {
|
||||||
|
worker_processes:
|
||||||
|
'Nginx worker 进程数。通常建议保持 auto,让 OpenResty 按 CPU 核数自动分配。',
|
||||||
|
worker_connections:
|
||||||
|
'每个 worker 可同时处理的最大连接数。值越大,可承载的并发连接越多。',
|
||||||
|
worker_rlimit_nofile:
|
||||||
|
'提升 worker 可打开的文件描述符上限,避免高并发下连接或文件句柄不足。',
|
||||||
|
events_use:
|
||||||
|
'指定事件驱动模型。Linux 常见是 epoll,留空时由 OpenResty 自动选择。',
|
||||||
|
multi_accept:
|
||||||
|
'开启后,worker 会尽可能一次接受多个新连接,适合高吞吐接入场景。',
|
||||||
|
keepalive_timeout: '客户端 Keep-Alive 空闲保持时间,单位秒。',
|
||||||
|
keepalive_requests: '单个长连接允许复用的最大请求数。',
|
||||||
|
client_header_timeout: '读取客户端请求头的超时时间,单位秒。',
|
||||||
|
client_body_timeout: '读取客户端请求体的超时时间,单位秒。',
|
||||||
|
send_timeout: '向客户端发送响应时的超时时间,单位秒。',
|
||||||
|
proxy_connect_timeout: '连接上游源站的超时时间,单位秒。',
|
||||||
|
proxy_send_timeout: '向上游发送请求的超时时间,单位秒。',
|
||||||
|
proxy_read_timeout: '等待上游返回响应的超时时间,单位秒。',
|
||||||
|
proxy_buffering:
|
||||||
|
'控制是否启用代理响应缓冲。开启后通常有更平滑的吞吐,但会增加内存占用。',
|
||||||
|
proxy_buffers: '设置代理响应缓冲区的数量和大小,例如 16 16k。',
|
||||||
|
proxy_buffer_size: '保存响应头等小块数据的基础缓冲区大小。',
|
||||||
|
proxy_busy_buffers_size: '限制 busy 状态下可同时占用的缓冲区总大小。',
|
||||||
|
gzip: '控制是否启用 gzip 压缩响应。',
|
||||||
|
gzip_min_length:
|
||||||
|
'只有响应体超过该字节数时才会启用 gzip,避免对极小响应做无意义压缩。',
|
||||||
|
gzip_comp_level: 'gzip 压缩等级,1 更省 CPU,9 压缩更高但更耗 CPU。',
|
||||||
|
proxy_cache_path: '缓存目录路径,对应 proxy_cache_path 指令中的磁盘位置。',
|
||||||
|
levels: '缓存目录层级,例如 1:2,可控制缓存文件的目录分布。',
|
||||||
|
inactive: '缓存对象在未命中访问时的失活时间,例如 30m。',
|
||||||
|
max_size:
|
||||||
|
'缓存目录允许占用的最大磁盘空间,会渲染到 proxy_cache_path 的 max_size。',
|
||||||
|
cache_key_template: '生成缓存 Key 的模板,决定不同请求如何命中同一缓存对象。',
|
||||||
|
proxy_cache_lock:
|
||||||
|
'启用后,同一缓存 Key 未命中时只允许一个请求回源,减少击穿。',
|
||||||
|
proxy_cache_lock_timeout: '等待缓存锁的最长时间,例如 5s。',
|
||||||
|
proxy_cache_use_stale:
|
||||||
|
'上游异常时允许返回旧缓存的条件列表,例如 error、timeout、http_500。',
|
||||||
|
};
|
||||||
|
|
||||||
type PerformanceTab = 'settings' | 'editor';
|
type PerformanceTab = 'settings' | 'editor';
|
||||||
|
|
||||||
type FeedbackState = {
|
type FeedbackState = {
|
||||||
@@ -609,7 +650,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="grid gap-5 md:grid-cols-2 xl:grid-cols-3">
|
<div className="grid gap-5 md:grid-cols-2 xl:grid-cols-3">
|
||||||
<ResourceField label="worker_processes">
|
<ResourceField
|
||||||
|
label="worker_processes"
|
||||||
|
tooltip={performanceFieldTooltips.worker_processes}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyWorkerProcesses}
|
value={performanceFields.OpenRestyWorkerProcesses}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
@@ -621,7 +665,10 @@ export function PerformancePage() {
|
|||||||
placeholder="auto"
|
placeholder="auto"
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="worker_connections">
|
<ResourceField
|
||||||
|
label="worker_connections"
|
||||||
|
tooltip={performanceFieldTooltips.worker_connections}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyWorkerConnections}
|
value={performanceFields.OpenRestyWorkerConnections}
|
||||||
@@ -633,7 +680,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="worker_rlimit_nofile">
|
<ResourceField
|
||||||
|
label="worker_rlimit_nofile"
|
||||||
|
tooltip={performanceFieldTooltips.worker_rlimit_nofile}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyWorkerRlimitNofile}
|
value={performanceFields.OpenRestyWorkerRlimitNofile}
|
||||||
@@ -645,7 +695,11 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="events use" hint="留空表示不显式渲染。">
|
<ResourceField
|
||||||
|
label="events use"
|
||||||
|
hint="留空表示不显式渲染。"
|
||||||
|
tooltip={performanceFieldTooltips.events_use}
|
||||||
|
>
|
||||||
<ResourceSelect
|
<ResourceSelect
|
||||||
value={performanceFields.OpenRestyEventsUse}
|
value={performanceFields.OpenRestyEventsUse}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
@@ -665,6 +719,7 @@ export function PerformancePage() {
|
|||||||
<ToggleField
|
<ToggleField
|
||||||
label="multi_accept"
|
label="multi_accept"
|
||||||
description=""
|
description=""
|
||||||
|
tooltip={performanceFieldTooltips.multi_accept}
|
||||||
checked={performanceFields.OpenRestyEventsMultiAcceptEnabled}
|
checked={performanceFields.OpenRestyEventsMultiAcceptEnabled}
|
||||||
onChange={(checked) =>
|
onChange={(checked) =>
|
||||||
setPerformanceFields((previous) => ({
|
setPerformanceFields((previous) => ({
|
||||||
@@ -673,7 +728,10 @@ export function PerformancePage() {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ResourceField label="keepalive_timeout (秒)">
|
<ResourceField
|
||||||
|
label="keepalive_timeout (秒)"
|
||||||
|
tooltip={performanceFieldTooltips.keepalive_timeout}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyKeepaliveTimeout}
|
value={performanceFields.OpenRestyKeepaliveTimeout}
|
||||||
@@ -685,7 +743,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="keepalive_requests">
|
<ResourceField
|
||||||
|
label="keepalive_requests"
|
||||||
|
tooltip={performanceFieldTooltips.keepalive_requests}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyKeepaliveRequests}
|
value={performanceFields.OpenRestyKeepaliveRequests}
|
||||||
@@ -697,7 +758,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="client_header_timeout (秒)">
|
<ResourceField
|
||||||
|
label="client_header_timeout (秒)"
|
||||||
|
tooltip={performanceFieldTooltips.client_header_timeout}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyClientHeaderTimeout}
|
value={performanceFields.OpenRestyClientHeaderTimeout}
|
||||||
@@ -709,7 +773,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="client_body_timeout (秒)">
|
<ResourceField
|
||||||
|
label="client_body_timeout (秒)"
|
||||||
|
tooltip={performanceFieldTooltips.client_body_timeout}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyClientBodyTimeout}
|
value={performanceFields.OpenRestyClientBodyTimeout}
|
||||||
@@ -721,7 +788,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="send_timeout (秒)">
|
<ResourceField
|
||||||
|
label="send_timeout (秒)"
|
||||||
|
tooltip={performanceFieldTooltips.send_timeout}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestySendTimeout}
|
value={performanceFields.OpenRestySendTimeout}
|
||||||
@@ -753,7 +823,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="grid gap-5 md:grid-cols-2">
|
<div className="grid gap-5 md:grid-cols-2">
|
||||||
<ResourceField label="proxy_connect_timeout (秒)">
|
<ResourceField
|
||||||
|
label="proxy_connect_timeout (秒)"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_connect_timeout}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyProxyConnectTimeout}
|
value={performanceFields.OpenRestyProxyConnectTimeout}
|
||||||
@@ -765,7 +838,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="proxy_send_timeout (秒)">
|
<ResourceField
|
||||||
|
label="proxy_send_timeout (秒)"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_send_timeout}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyProxySendTimeout}
|
value={performanceFields.OpenRestyProxySendTimeout}
|
||||||
@@ -777,7 +853,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="proxy_read_timeout (秒)">
|
<ResourceField
|
||||||
|
label="proxy_read_timeout (秒)"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_read_timeout}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyProxyReadTimeout}
|
value={performanceFields.OpenRestyProxyReadTimeout}
|
||||||
@@ -792,6 +871,7 @@ export function PerformancePage() {
|
|||||||
<ToggleField
|
<ToggleField
|
||||||
label="proxy_buffering"
|
label="proxy_buffering"
|
||||||
description="是否启用 proxy_buffering。"
|
description="是否启用 proxy_buffering。"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_buffering}
|
||||||
checked={performanceFields.OpenRestyProxyBufferingEnabled}
|
checked={performanceFields.OpenRestyProxyBufferingEnabled}
|
||||||
onChange={(checked) =>
|
onChange={(checked) =>
|
||||||
setPerformanceFields((previous) => ({
|
setPerformanceFields((previous) => ({
|
||||||
@@ -800,7 +880,10 @@ export function PerformancePage() {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ResourceField label="proxy_buffers">
|
<ResourceField
|
||||||
|
label="proxy_buffers"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_buffers}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyProxyBuffers}
|
value={performanceFields.OpenRestyProxyBuffers}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
@@ -812,7 +895,10 @@ export function PerformancePage() {
|
|||||||
placeholder="16 16k"
|
placeholder="16 16k"
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="proxy_buffer_size">
|
<ResourceField
|
||||||
|
label="proxy_buffer_size"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_buffer_size}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyProxyBufferSize}
|
value={performanceFields.OpenRestyProxyBufferSize}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
@@ -824,7 +910,10 @@ export function PerformancePage() {
|
|||||||
placeholder="8k"
|
placeholder="8k"
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="proxy_busy_buffers_size">
|
<ResourceField
|
||||||
|
label="proxy_busy_buffers_size"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_busy_buffers_size}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyProxyBusyBuffersSize}
|
value={performanceFields.OpenRestyProxyBusyBuffersSize}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
@@ -860,6 +949,7 @@ export function PerformancePage() {
|
|||||||
<ToggleField
|
<ToggleField
|
||||||
label="gzip"
|
label="gzip"
|
||||||
description="是否启用 gzip on。"
|
description="是否启用 gzip on。"
|
||||||
|
tooltip={performanceFieldTooltips.gzip}
|
||||||
checked={performanceFields.OpenRestyGzipEnabled}
|
checked={performanceFields.OpenRestyGzipEnabled}
|
||||||
onChange={(checked) =>
|
onChange={(checked) =>
|
||||||
setPerformanceFields((previous) => ({
|
setPerformanceFields((previous) => ({
|
||||||
@@ -882,7 +972,10 @@ export function PerformancePage() {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ResourceField label="gzip_min_length">
|
<ResourceField
|
||||||
|
label="gzip_min_length"
|
||||||
|
tooltip={performanceFieldTooltips.gzip_min_length}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
value={performanceFields.OpenRestyGzipMinLength}
|
value={performanceFields.OpenRestyGzipMinLength}
|
||||||
@@ -894,7 +987,10 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="gzip_comp_level">
|
<ResourceField
|
||||||
|
label="gzip_comp_level"
|
||||||
|
tooltip={performanceFieldTooltips.gzip_comp_level}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
@@ -1018,6 +1114,7 @@ export function PerformancePage() {
|
|||||||
>
|
>
|
||||||
<ResourceField
|
<ResourceField
|
||||||
label="proxy_cache_path"
|
label="proxy_cache_path"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_cache_path}
|
||||||
hint={
|
hint={
|
||||||
performanceFields.OpenRestyCacheEnabled
|
performanceFields.OpenRestyCacheEnabled
|
||||||
? '缓存目录,启用缓存时必填。'
|
? '缓存目录,启用缓存时必填。'
|
||||||
@@ -1036,7 +1133,10 @@ export function PerformancePage() {
|
|||||||
placeholder="/var/cache/openresty/atsflare"
|
placeholder="/var/cache/openresty/atsflare"
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="levels">
|
<ResourceField
|
||||||
|
label="levels"
|
||||||
|
tooltip={performanceFieldTooltips.levels}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyCacheLevels}
|
value={performanceFields.OpenRestyCacheLevels}
|
||||||
disabled={!performanceFields.OpenRestyCacheEnabled}
|
disabled={!performanceFields.OpenRestyCacheEnabled}
|
||||||
@@ -1049,7 +1149,10 @@ export function PerformancePage() {
|
|||||||
placeholder="1:2"
|
placeholder="1:2"
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="inactive">
|
<ResourceField
|
||||||
|
label="inactive"
|
||||||
|
tooltip={performanceFieldTooltips.inactive}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyCacheInactive}
|
value={performanceFields.OpenRestyCacheInactive}
|
||||||
disabled={!performanceFields.OpenRestyCacheEnabled}
|
disabled={!performanceFields.OpenRestyCacheEnabled}
|
||||||
@@ -1062,7 +1165,10 @@ export function PerformancePage() {
|
|||||||
placeholder="30m"
|
placeholder="30m"
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="max_size">
|
<ResourceField
|
||||||
|
label="max_size"
|
||||||
|
tooltip={performanceFieldTooltips.max_size}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyCacheMaxSize}
|
value={performanceFields.OpenRestyCacheMaxSize}
|
||||||
disabled={!performanceFields.OpenRestyCacheEnabled}
|
disabled={!performanceFields.OpenRestyCacheEnabled}
|
||||||
@@ -1075,7 +1181,10 @@ export function PerformancePage() {
|
|||||||
placeholder="1g"
|
placeholder="1g"
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="cache key template">
|
<ResourceField
|
||||||
|
label="cache key template"
|
||||||
|
tooltip={performanceFieldTooltips.cache_key_template}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyCacheKeyTemplate}
|
value={performanceFields.OpenRestyCacheKeyTemplate}
|
||||||
disabled={!performanceFields.OpenRestyCacheEnabled}
|
disabled={!performanceFields.OpenRestyCacheEnabled}
|
||||||
@@ -1090,6 +1199,7 @@ export function PerformancePage() {
|
|||||||
<ToggleField
|
<ToggleField
|
||||||
label="proxy_cache_lock"
|
label="proxy_cache_lock"
|
||||||
description="是否启用 proxy_cache_lock。"
|
description="是否启用 proxy_cache_lock。"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_cache_lock}
|
||||||
checked={performanceFields.OpenRestyCacheLockEnabled}
|
checked={performanceFields.OpenRestyCacheLockEnabled}
|
||||||
disabled={!performanceFields.OpenRestyCacheEnabled}
|
disabled={!performanceFields.OpenRestyCacheEnabled}
|
||||||
onChange={(checked) =>
|
onChange={(checked) =>
|
||||||
@@ -1099,7 +1209,12 @@ export function PerformancePage() {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ResourceField label="proxy_cache_lock_timeout">
|
<ResourceField
|
||||||
|
label="proxy_cache_lock_timeout"
|
||||||
|
tooltip={
|
||||||
|
performanceFieldTooltips.proxy_cache_lock_timeout
|
||||||
|
}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyCacheLockTimeout}
|
value={performanceFields.OpenRestyCacheLockTimeout}
|
||||||
disabled={!performanceFields.OpenRestyCacheEnabled}
|
disabled={!performanceFields.OpenRestyCacheEnabled}
|
||||||
@@ -1112,7 +1227,10 @@ export function PerformancePage() {
|
|||||||
placeholder="5s"
|
placeholder="5s"
|
||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
<ResourceField label="proxy_cache_use_stale">
|
<ResourceField
|
||||||
|
label="proxy_cache_use_stale"
|
||||||
|
tooltip={performanceFieldTooltips.proxy_cache_use_stale}
|
||||||
|
>
|
||||||
<ResourceInput
|
<ResourceInput
|
||||||
value={performanceFields.OpenRestyCacheUseStale}
|
value={performanceFields.OpenRestyCacheUseStale}
|
||||||
disabled={!performanceFields.OpenRestyCacheEnabled}
|
disabled={!performanceFields.OpenRestyCacheEnabled}
|
||||||
|
|||||||
@@ -1,179 +1,213 @@
|
|||||||
import type {
|
import type {
|
||||||
ButtonHTMLAttributes,
|
ButtonHTMLAttributes,
|
||||||
InputHTMLAttributes,
|
InputHTMLAttributes,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
SelectHTMLAttributes,
|
SelectHTMLAttributes,
|
||||||
TextareaHTMLAttributes,
|
TextareaHTMLAttributes,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import { cn } from '@/lib/utils/cn';
|
import { cn } from '@/lib/utils/cn';
|
||||||
|
|
||||||
interface ResourceFieldProps {
|
interface ResourceFieldProps {
|
||||||
label: string;
|
label: string;
|
||||||
hint?: string;
|
hint?: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
children: ReactNode;
|
tooltip?: string;
|
||||||
}
|
children: ReactNode;
|
||||||
|
}
|
||||||
export function ResourceField({
|
|
||||||
label,
|
function FieldTooltip({ content }: { content: string }) {
|
||||||
hint,
|
return (
|
||||||
error,
|
<span className="group/tooltip relative inline-flex">
|
||||||
className,
|
<span className="inline-flex h-5 w-5 items-center justify-center rounded-full border border-[var(--border-default)] bg-[var(--surface-elevated)] text-[11px] font-semibold text-[var(--foreground-secondary)] transition group-hover/tooltip:border-[var(--border-strong)] group-hover/tooltip:text-[var(--foreground-primary)]">
|
||||||
children,
|
?
|
||||||
}: ResourceFieldProps) {
|
</span>
|
||||||
return (
|
<span className="pointer-events-none absolute top-full left-1/2 z-20 mt-2 w-64 -translate-x-1/2 rounded-2xl border border-[var(--border-default)] bg-[var(--surface-panel)] px-3 py-2 text-xs leading-5 text-[var(--foreground-secondary)] opacity-0 shadow-[var(--shadow-soft)] transition-opacity delay-700 duration-200 group-hover/tooltip:opacity-100">
|
||||||
<label className={cn('block space-y-2', className)}>
|
{content}
|
||||||
<span className='text-sm font-medium text-[var(--foreground-primary)]'>{label}</span>
|
</span>
|
||||||
{children}
|
</span>
|
||||||
{error ? (
|
);
|
||||||
<span className='block text-xs text-[var(--status-danger-foreground)]'>{error}</span>
|
}
|
||||||
) : hint ? (
|
|
||||||
<span className='block text-xs text-[var(--foreground-secondary)]'>{hint}</span>
|
export function ResourceField({
|
||||||
) : null}
|
label,
|
||||||
</label>
|
hint,
|
||||||
);
|
error,
|
||||||
}
|
className,
|
||||||
|
tooltip,
|
||||||
export function ResourceInput(props: InputHTMLAttributes<HTMLInputElement>) {
|
children,
|
||||||
return (
|
}: ResourceFieldProps) {
|
||||||
<input
|
return (
|
||||||
{...props}
|
<label className={cn('block space-y-2', className)}>
|
||||||
className={cn(
|
<span className="flex items-center gap-2 text-sm font-medium text-[var(--foreground-primary)]">
|
||||||
'w-full rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-3 text-sm text-[var(--foreground-primary)] outline-none transition placeholder:text-[var(--foreground-muted)] focus:border-[var(--border-strong)] focus:ring-2 focus:ring-[var(--accent-soft)] disabled:cursor-not-allowed disabled:opacity-60',
|
<span>{label}</span>
|
||||||
props.className,
|
{tooltip ? <FieldTooltip content={tooltip} /> : null}
|
||||||
)}
|
</span>
|
||||||
/>
|
{children}
|
||||||
);
|
{error ? (
|
||||||
}
|
<span className="block text-xs text-[var(--status-danger-foreground)]">
|
||||||
|
{error}
|
||||||
export function ResourceTextarea(props: TextareaHTMLAttributes<HTMLTextAreaElement>) {
|
</span>
|
||||||
return (
|
) : hint ? (
|
||||||
<textarea
|
<span className="block text-xs text-[var(--foreground-secondary)]">
|
||||||
{...props}
|
{hint}
|
||||||
className={cn(
|
</span>
|
||||||
'min-h-28 w-full rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-3 text-sm text-[var(--foreground-primary)] outline-none transition placeholder:text-[var(--foreground-muted)] focus:border-[var(--border-strong)] focus:ring-2 focus:ring-[var(--accent-soft)] disabled:cursor-not-allowed disabled:opacity-60',
|
) : null}
|
||||||
props.className,
|
</label>
|
||||||
)}
|
);
|
||||||
/>
|
}
|
||||||
);
|
|
||||||
}
|
export function ResourceInput(props: InputHTMLAttributes<HTMLInputElement>) {
|
||||||
|
return (
|
||||||
export function ResourceSelect(props: SelectHTMLAttributes<HTMLSelectElement>) {
|
<input
|
||||||
return (
|
{...props}
|
||||||
<select
|
className={cn(
|
||||||
{...props}
|
'w-full rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-3 text-sm text-[var(--foreground-primary)] transition outline-none placeholder:text-[var(--foreground-muted)] focus:border-[var(--border-strong)] focus:ring-2 focus:ring-[var(--accent-soft)] disabled:cursor-not-allowed disabled:opacity-60',
|
||||||
className={cn(
|
props.className,
|
||||||
'w-full rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-3 text-sm text-[var(--foreground-primary)] outline-none transition focus:border-[var(--border-strong)] focus:ring-2 focus:ring-[var(--accent-soft)] disabled:cursor-not-allowed disabled:opacity-60',
|
)}
|
||||||
props.className,
|
/>
|
||||||
)}
|
);
|
||||||
/>
|
}
|
||||||
);
|
|
||||||
}
|
export function ResourceTextarea(
|
||||||
|
props: TextareaHTMLAttributes<HTMLTextAreaElement>,
|
||||||
function baseButtonClassName(className?: string) {
|
) {
|
||||||
return cn(
|
return (
|
||||||
'inline-flex items-center justify-center rounded-2xl px-4 py-3 text-sm font-medium transition disabled:cursor-not-allowed disabled:opacity-60',
|
<textarea
|
||||||
className,
|
{...props}
|
||||||
);
|
className={cn(
|
||||||
}
|
'min-h-28 w-full rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-3 text-sm text-[var(--foreground-primary)] transition outline-none placeholder:text-[var(--foreground-muted)] focus:border-[var(--border-strong)] focus:ring-2 focus:ring-[var(--accent-soft)] disabled:cursor-not-allowed disabled:opacity-60',
|
||||||
|
props.className,
|
||||||
export function PrimaryButton({
|
)}
|
||||||
className,
|
/>
|
||||||
...props
|
);
|
||||||
}: ButtonHTMLAttributes<HTMLButtonElement>) {
|
}
|
||||||
return (
|
|
||||||
<button
|
export function ResourceSelect(props: SelectHTMLAttributes<HTMLSelectElement>) {
|
||||||
{...props}
|
return (
|
||||||
className={baseButtonClassName(
|
<select
|
||||||
cn('bg-[var(--brand-primary)] text-[var(--foreground-inverse)] hover:opacity-90', className),
|
{...props}
|
||||||
)}
|
className={cn(
|
||||||
/>
|
'w-full rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-3 text-sm text-[var(--foreground-primary)] transition outline-none focus:border-[var(--border-strong)] focus:ring-2 focus:ring-[var(--accent-soft)] disabled:cursor-not-allowed disabled:opacity-60',
|
||||||
);
|
props.className,
|
||||||
}
|
)}
|
||||||
|
/>
|
||||||
export function SecondaryButton({
|
);
|
||||||
className,
|
}
|
||||||
...props
|
|
||||||
}: ButtonHTMLAttributes<HTMLButtonElement>) {
|
function baseButtonClassName(className?: string) {
|
||||||
return (
|
return cn(
|
||||||
<button
|
'inline-flex items-center justify-center rounded-2xl px-4 py-3 text-sm font-medium transition disabled:cursor-not-allowed disabled:opacity-60',
|
||||||
{...props}
|
className,
|
||||||
className={baseButtonClassName(
|
);
|
||||||
cn(
|
}
|
||||||
'border border-[var(--border-default)] bg-[var(--control-background)] text-[var(--foreground-primary)] hover:bg-[var(--control-background-hover)]',
|
|
||||||
className,
|
export function PrimaryButton({
|
||||||
),
|
className,
|
||||||
)}
|
...props
|
||||||
/>
|
}: ButtonHTMLAttributes<HTMLButtonElement>) {
|
||||||
);
|
return (
|
||||||
}
|
<button
|
||||||
|
{...props}
|
||||||
export function DangerButton({
|
className={baseButtonClassName(
|
||||||
className,
|
cn(
|
||||||
...props
|
'bg-[var(--brand-primary)] text-[var(--foreground-inverse)] hover:opacity-90',
|
||||||
}: ButtonHTMLAttributes<HTMLButtonElement>) {
|
className,
|
||||||
return (
|
),
|
||||||
<button
|
)}
|
||||||
{...props}
|
/>
|
||||||
className={baseButtonClassName(
|
);
|
||||||
cn(
|
}
|
||||||
'border border-[var(--status-danger-border)] bg-[var(--status-danger-soft)] text-[var(--status-danger-foreground)] hover:opacity-90',
|
|
||||||
className,
|
export function SecondaryButton({
|
||||||
),
|
className,
|
||||||
)}
|
...props
|
||||||
/>
|
}: ButtonHTMLAttributes<HTMLButtonElement>) {
|
||||||
);
|
return (
|
||||||
}
|
<button
|
||||||
|
{...props}
|
||||||
interface ToggleFieldProps {
|
className={baseButtonClassName(
|
||||||
label: string;
|
cn(
|
||||||
description: string;
|
'border border-[var(--border-default)] bg-[var(--control-background)] text-[var(--foreground-primary)] hover:bg-[var(--control-background-hover)]',
|
||||||
checked: boolean;
|
className,
|
||||||
disabled?: boolean;
|
),
|
||||||
onChange: (checked: boolean) => void;
|
)}
|
||||||
}
|
/>
|
||||||
|
);
|
||||||
export function ToggleField({
|
}
|
||||||
label,
|
|
||||||
description,
|
export function DangerButton({
|
||||||
checked,
|
className,
|
||||||
disabled,
|
...props
|
||||||
onChange,
|
}: ButtonHTMLAttributes<HTMLButtonElement>) {
|
||||||
}: ToggleFieldProps) {
|
return (
|
||||||
return (
|
<button
|
||||||
<label className='flex cursor-pointer items-start gap-3 rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-3'>
|
{...props}
|
||||||
<input
|
className={baseButtonClassName(
|
||||||
type='checkbox'
|
cn(
|
||||||
checked={checked}
|
'border border-[var(--status-danger-border)] bg-[var(--status-danger-soft)] text-[var(--status-danger-foreground)] hover:opacity-90',
|
||||||
disabled={disabled}
|
className,
|
||||||
onChange={(event) => onChange(event.target.checked)}
|
),
|
||||||
className='mt-1 h-4 w-4 rounded border-[var(--border-default)] accent-[var(--brand-primary)]'
|
)}
|
||||||
/>
|
/>
|
||||||
<span className='space-y-1'>
|
);
|
||||||
<span className='block text-sm font-medium text-[var(--foreground-primary)]'>{label}</span>
|
}
|
||||||
<span className='block text-xs leading-5 text-[var(--foreground-secondary)]'>{description}</span>
|
|
||||||
</span>
|
interface ToggleFieldProps {
|
||||||
</label>
|
label: string;
|
||||||
);
|
description: string;
|
||||||
}
|
checked: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
export function CodeBlock({
|
tooltip?: string;
|
||||||
children,
|
onChange: (checked: boolean) => void;
|
||||||
className,
|
}
|
||||||
}: {
|
|
||||||
children: ReactNode;
|
export function ToggleField({
|
||||||
className?: string;
|
label,
|
||||||
}) {
|
description,
|
||||||
return (
|
checked,
|
||||||
<pre
|
disabled,
|
||||||
className={cn(
|
tooltip,
|
||||||
'overflow-x-auto rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4 text-xs leading-6 text-[var(--foreground-primary)]',
|
onChange,
|
||||||
className,
|
}: ToggleFieldProps) {
|
||||||
)}
|
return (
|
||||||
>
|
<label className="flex cursor-pointer items-start gap-3 rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-3">
|
||||||
{children}
|
<input
|
||||||
</pre>
|
type="checkbox"
|
||||||
);
|
checked={checked}
|
||||||
}
|
disabled={disabled}
|
||||||
|
onChange={(event) => onChange(event.target.checked)}
|
||||||
|
className="mt-1 h-4 w-4 rounded border-[var(--border-default)] accent-[var(--brand-primary)]"
|
||||||
|
/>
|
||||||
|
<span className="space-y-1">
|
||||||
|
<span className="flex items-center gap-2 text-sm font-medium text-[var(--foreground-primary)]">
|
||||||
|
<span>{label}</span>
|
||||||
|
{tooltip ? <FieldTooltip content={tooltip} /> : null}
|
||||||
|
</span>
|
||||||
|
<span className="block text-xs leading-5 text-[var(--foreground-secondary)]">
|
||||||
|
{description}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CodeBlock({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<pre
|
||||||
|
className={cn(
|
||||||
|
'overflow-x-auto rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4 text-xs leading-6 text-[var(--foreground-primary)]',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</pre>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user