import { Button, Input, Tooltip } from "antd"; import { Plus, Trash2 } from "lucide-react"; import type { ChangeEvent } from "react"; import type { ChannelHeader } from "@/stores/use-config-store"; const DEFAULT_USER_AGENT = "InfiniteCanvas/1.0 (+https://github.com/ddcat-ai/open-ai-canvas)"; const MAX_HEADER_COUNT = 32; const BLOCKED_HEADERS = new Set([ "authorization", "proxy-authorization", "cookie", "set-cookie", "host", "content-length", "content-type", "accept", "connection", "proxy-connection", "keep-alive", "transfer-encoding", "te", "trailer", "upgrade", "forwarded", "x-goog-api-key", ]); const HEADER_NAME_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/; type Props = { value?: ChannelHeader[]; onChange?: (headers: ChannelHeader[]) => void; disabled?: boolean; }; export function ChannelHeadersEditor({ value = [], onChange, disabled }: Props) { const headers = Array.isArray(value) ? value : []; const validation = validateChannelHeaders(headers); const hasUserAgent = headers.some((header) => header.name.trim().toLowerCase() === "user-agent"); const updateHeader = (index: number, patch: Partial) => { onChange?.(headers.map((header, itemIndex) => (itemIndex === index ? { ...header, ...patch } : header))); }; return (
自定义请求头
默认发送 {DEFAULT_USER_AGENT};添加 User-Agent 后会覆盖默认值。
{!hasUserAgent ? ( ) : null}
{headers.length ? (
{headers.map((header, index) => { const inputProps = { "aria-label": `请求头 ${index + 1} 值`, disabled, value: header.value, placeholder: "Header 值", onChange: (event: ChangeEvent) => updateHeader(index, { value: event.target.value }), }; return (
updateHeader(index, { name: event.target.value })} /> {/token|key|secret/i.test(header.name) ? : }
); })}
) : null}
{validation || `${headers.length}/${MAX_HEADER_COUNT}`}
); } export function validateChannelHeaders(headers?: ChannelHeader[]) { if (!headers?.length) return ""; if (headers.length > MAX_HEADER_COUNT) return `自定义请求头最多支持 ${MAX_HEADER_COUNT} 项`; const seen = new Set(); let totalBytes = 0; for (const header of headers) { const name = header.name.trim(); const value = header.value.trim(); if (!name && !value) continue; if (!name || !HEADER_NAME_PATTERN.test(name)) return "请求头名称无效,请使用标准 HTTP Header 名称"; const normalizedName = name.toLowerCase(); if (BLOCKED_HEADERS.has(normalizedName) || normalizedName.startsWith("x-canvas-") || normalizedName.startsWith("x-forwarded-")) return `${name} 由系统管理,不允许自定义`; if (seen.has(normalizedName)) return `请求头不能重复:${name}`; if (/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/.test(value)) return `${name} 的值包含非法控制字符`; if (name.length > 128 || value.length > 4096) return "单个请求头名称或值过长"; totalBytes += new TextEncoder().encode(name + value).length; if (totalBytes > 16 * 1024) return "自定义请求头总大小不能超过 16KB"; seen.add(normalizedName); } return ""; }