mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-05 15:26:36 +08:00
[功能] 添加支持多个上游地址,优化代理路由配置和负载均衡逻辑
This commit is contained in:
@@ -36,14 +36,14 @@ const defaultPerformanceFields = {
|
||||
OpenRestyWorkerRlimitNofile: '65535',
|
||||
OpenRestyEventsUse: 'epoll',
|
||||
OpenRestyEventsMultiAcceptEnabled: true,
|
||||
OpenRestyKeepaliveTimeout: '65',
|
||||
OpenRestyKeepaliveTimeout: '20',
|
||||
OpenRestyKeepaliveRequests: '1000',
|
||||
OpenRestyClientHeaderTimeout: '15',
|
||||
OpenRestyClientBodyTimeout: '15',
|
||||
OpenRestyClientMaxBodySize: '64m',
|
||||
OpenRestyLargeClientHeaderBuffers: '4 16k',
|
||||
OpenRestySendTimeout: '30',
|
||||
OpenRestyProxyConnectTimeout: '5',
|
||||
OpenRestyProxyConnectTimeout: '3',
|
||||
OpenRestyProxySendTimeout: '60',
|
||||
OpenRestyProxyReadTimeout: '60',
|
||||
OpenRestyWebsocketEnabled: true,
|
||||
@@ -61,7 +61,7 @@ const defaultPerformanceFields = {
|
||||
OpenRestyCacheLevels: '1:2',
|
||||
OpenRestyCacheInactive: '30m',
|
||||
OpenRestyCacheMaxSize: '1g',
|
||||
OpenRestyCacheKeyTemplate: '$scheme$proxy_host$request_uri',
|
||||
OpenRestyCacheKeyTemplate: '$scheme$host$request_uri',
|
||||
OpenRestyCacheLockEnabled: true,
|
||||
OpenRestyCacheLockTimeout: '5s',
|
||||
OpenRestyCacheUseStale:
|
||||
@@ -211,7 +211,7 @@ export function PerformancePage() {
|
||||
optionMap.OpenRestyEventsMultiAcceptEnabled,
|
||||
true,
|
||||
),
|
||||
OpenRestyKeepaliveTimeout: optionMap.OpenRestyKeepaliveTimeout ?? '65',
|
||||
OpenRestyKeepaliveTimeout: optionMap.OpenRestyKeepaliveTimeout ?? '20',
|
||||
OpenRestyKeepaliveRequests:
|
||||
optionMap.OpenRestyKeepaliveRequests ?? '1000',
|
||||
OpenRestyClientHeaderTimeout:
|
||||
@@ -222,7 +222,7 @@ export function PerformancePage() {
|
||||
optionMap.OpenRestyLargeClientHeaderBuffers ?? '4 16k',
|
||||
OpenRestySendTimeout: optionMap.OpenRestySendTimeout ?? '30',
|
||||
OpenRestyProxyConnectTimeout:
|
||||
optionMap.OpenRestyProxyConnectTimeout ?? '5',
|
||||
optionMap.OpenRestyProxyConnectTimeout ?? '3',
|
||||
OpenRestyProxySendTimeout: optionMap.OpenRestyProxySendTimeout ?? '60',
|
||||
OpenRestyProxyReadTimeout: optionMap.OpenRestyProxyReadTimeout ?? '60',
|
||||
OpenRestyWebsocketEnabled: toBoolean(
|
||||
@@ -251,7 +251,7 @@ export function PerformancePage() {
|
||||
OpenRestyCacheInactive: optionMap.OpenRestyCacheInactive ?? '30m',
|
||||
OpenRestyCacheMaxSize: optionMap.OpenRestyCacheMaxSize ?? '1g',
|
||||
OpenRestyCacheKeyTemplate:
|
||||
optionMap.OpenRestyCacheKeyTemplate ?? '$scheme$proxy_host$request_uri',
|
||||
optionMap.OpenRestyCacheKeyTemplate ?? '$scheme$host$request_uri',
|
||||
OpenRestyCacheLockEnabled: toBoolean(
|
||||
optionMap.OpenRestyCacheLockEnabled,
|
||||
true,
|
||||
|
||||
@@ -89,6 +89,7 @@ const proxyRouteSchema = z
|
||||
})()),
|
||||
'请输入合法的回源主机名',
|
||||
),
|
||||
upstreams_text: z.string(),
|
||||
enabled: z.boolean(),
|
||||
enable_https: z.boolean(),
|
||||
cert_id: z.string(),
|
||||
@@ -108,6 +109,15 @@ const proxyRouteSchema = z
|
||||
});
|
||||
}
|
||||
|
||||
const upstreams = parseUpstreamsText(value.origin_url, value.upstreams_text);
|
||||
if (upstreams.length === 0) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['origin_url'],
|
||||
message: '至少需要一个上游地址',
|
||||
});
|
||||
}
|
||||
|
||||
if (value.cache_enabled) {
|
||||
const cacheRules = parseCacheRulesText(value.cache_rules_text);
|
||||
if (value.cache_policy !== 'url' && cacheRules.length === 0) {
|
||||
@@ -164,6 +174,7 @@ const defaultValues: ProxyRouteFormValues = {
|
||||
domain: '',
|
||||
origin_url: '',
|
||||
origin_host: '',
|
||||
upstreams_text: '',
|
||||
enabled: true,
|
||||
enable_https: false,
|
||||
cert_id: '',
|
||||
@@ -234,6 +245,25 @@ function parseCacheRulesText(value: string) {
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function parseUpstreams(rawValue: string) {
|
||||
if (!rawValue) {
|
||||
return [] as string[];
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(rawValue) as string[];
|
||||
return Array.isArray(parsed) ? parsed.filter(Boolean) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function parseUpstreamsText(primary: string, value: string) {
|
||||
return [primary.trim(), ...value.split(/\r?\n/)]
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function buildCachePolicyLabel(policy: string) {
|
||||
switch (policy) {
|
||||
case 'suffix':
|
||||
@@ -271,6 +301,7 @@ function toPayload(values: ProxyRouteFormValues): ProxyRouteMutationPayload {
|
||||
domain: values.domain.trim(),
|
||||
origin_url: values.origin_url.trim(),
|
||||
origin_host: values.origin_host.trim(),
|
||||
upstreams: parseUpstreamsText(values.origin_url, values.upstreams_text).slice(1),
|
||||
enabled: values.enabled,
|
||||
enable_https: values.enable_https,
|
||||
cert_id:
|
||||
@@ -291,11 +322,13 @@ function toPayload(values: ProxyRouteFormValues): ProxyRouteMutationPayload {
|
||||
function toFormValues(route: ProxyRouteItem): ProxyRouteFormValues {
|
||||
const headers = parseCustomHeaders(route.custom_headers);
|
||||
const cacheRules = parseCacheRules(route.cache_rules);
|
||||
const upstreams = parseUpstreams(route.upstreams);
|
||||
|
||||
return {
|
||||
domain: route.domain,
|
||||
origin_url: route.origin_url,
|
||||
origin_host: route.origin_host || '',
|
||||
upstreams_text: upstreams.slice(1).join('\n'),
|
||||
enabled: route.enabled,
|
||||
enable_https: route.enable_https,
|
||||
cert_id: route.cert_id ? String(route.cert_id) : '',
|
||||
@@ -608,6 +641,7 @@ export function ProxyRoutesPage() {
|
||||
{routes.map((route) => {
|
||||
const headers = parseCustomHeaders(route.custom_headers);
|
||||
const cacheRules = parseCacheRules(route.cache_rules);
|
||||
const upstreams = parseUpstreams(route.upstreams);
|
||||
|
||||
return (
|
||||
<tr key={route.id} className="align-top">
|
||||
@@ -620,6 +654,9 @@ export function ProxyRoutesPage() {
|
||||
<p className="text-xs text-[var(--foreground-muted)]">
|
||||
回源主机名: {route.origin_host || '$host'}
|
||||
</p>
|
||||
<p className="text-xs text-[var(--foreground-muted)]">
|
||||
上游数量: {Math.max(upstreams.length, 1)}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-3 py-4">
|
||||
@@ -770,6 +807,17 @@ export function ProxyRoutesPage() {
|
||||
/>
|
||||
</ResourceField>
|
||||
|
||||
<ResourceField
|
||||
label="附加上游"
|
||||
hint="可选。每行一个上游地址,用于同一规则下的负载均衡;需与主上游保持相同协议,且不能带路径或查询参数。"
|
||||
error={form.formState.errors.upstreams_text?.message}
|
||||
>
|
||||
<ResourceTextarea
|
||||
placeholder={'https://origin-b.internal\nhttps://origin-c.internal'}
|
||||
{...form.register('upstreams_text')}
|
||||
/>
|
||||
</ResourceField>
|
||||
|
||||
<div className="grid gap-4 lg:grid-cols-2">
|
||||
<ToggleField
|
||||
label="启用规则"
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface ProxyRouteItem {
|
||||
domain: string;
|
||||
origin_url: string;
|
||||
origin_host: string;
|
||||
upstreams: string;
|
||||
enabled: boolean;
|
||||
enable_https: boolean;
|
||||
cert_id: number | null;
|
||||
@@ -23,8 +24,9 @@ export interface ProxyRouteItem {
|
||||
|
||||
export interface ProxyRouteMutationPayload {
|
||||
domain: string;
|
||||
origin_url: string;
|
||||
origin_host: string;
|
||||
origin_url: string;
|
||||
origin_host: string;
|
||||
upstreams: string[];
|
||||
enabled: boolean;
|
||||
enable_https: boolean;
|
||||
cert_id: number | null;
|
||||
|
||||
@@ -80,12 +80,12 @@ const defaultOperationFields = {
|
||||
OpenRestyWorkerRlimitNofile: '65535',
|
||||
OpenRestyEventsUse: 'epoll',
|
||||
OpenRestyEventsMultiAcceptEnabled: true,
|
||||
OpenRestyKeepaliveTimeout: '65',
|
||||
OpenRestyKeepaliveTimeout: '20',
|
||||
OpenRestyKeepaliveRequests: '1000',
|
||||
OpenRestyClientHeaderTimeout: '15',
|
||||
OpenRestyClientBodyTimeout: '15',
|
||||
OpenRestySendTimeout: '30',
|
||||
OpenRestyProxyConnectTimeout: '5',
|
||||
OpenRestyProxyConnectTimeout: '3',
|
||||
OpenRestyProxySendTimeout: '60',
|
||||
OpenRestyProxyReadTimeout: '60',
|
||||
OpenRestyProxyBufferingEnabled: true,
|
||||
@@ -100,7 +100,7 @@ const defaultOperationFields = {
|
||||
OpenRestyCacheLevels: '1:2',
|
||||
OpenRestyCacheInactive: '30m',
|
||||
OpenRestyCacheMaxSize: '1g',
|
||||
OpenRestyCacheKeyTemplate: '$scheme$proxy_host$request_uri',
|
||||
OpenRestyCacheKeyTemplate: '$scheme$host$request_uri',
|
||||
OpenRestyCacheLockEnabled: true,
|
||||
OpenRestyCacheLockTimeout: '5s',
|
||||
OpenRestyCacheUseStale:
|
||||
@@ -353,7 +353,7 @@ export function SettingsPage() {
|
||||
optionMap.OpenRestyEventsMultiAcceptEnabled,
|
||||
true,
|
||||
),
|
||||
OpenRestyKeepaliveTimeout: optionMap.OpenRestyKeepaliveTimeout ?? '65',
|
||||
OpenRestyKeepaliveTimeout: optionMap.OpenRestyKeepaliveTimeout ?? '20',
|
||||
OpenRestyKeepaliveRequests:
|
||||
optionMap.OpenRestyKeepaliveRequests ?? '1000',
|
||||
OpenRestyClientHeaderTimeout:
|
||||
@@ -361,7 +361,7 @@ export function SettingsPage() {
|
||||
OpenRestyClientBodyTimeout: optionMap.OpenRestyClientBodyTimeout ?? '15',
|
||||
OpenRestySendTimeout: optionMap.OpenRestySendTimeout ?? '30',
|
||||
OpenRestyProxyConnectTimeout:
|
||||
optionMap.OpenRestyProxyConnectTimeout ?? '5',
|
||||
optionMap.OpenRestyProxyConnectTimeout ?? '3',
|
||||
OpenRestyProxySendTimeout: optionMap.OpenRestyProxySendTimeout ?? '60',
|
||||
OpenRestyProxyReadTimeout: optionMap.OpenRestyProxyReadTimeout ?? '60',
|
||||
OpenRestyProxyBufferingEnabled: toBoolean(
|
||||
@@ -381,7 +381,7 @@ export function SettingsPage() {
|
||||
OpenRestyCacheInactive: optionMap.OpenRestyCacheInactive ?? '30m',
|
||||
OpenRestyCacheMaxSize: optionMap.OpenRestyCacheMaxSize ?? '1g',
|
||||
OpenRestyCacheKeyTemplate:
|
||||
optionMap.OpenRestyCacheKeyTemplate ?? '$scheme$proxy_host$request_uri',
|
||||
optionMap.OpenRestyCacheKeyTemplate ?? '$scheme$host$request_uri',
|
||||
OpenRestyCacheLockEnabled: toBoolean(
|
||||
optionMap.OpenRestyCacheLockEnabled,
|
||||
true,
|
||||
|
||||
Reference in New Issue
Block a user