Files
Charon/frontend/src/utils/proxyHostsHelpers.ts
GitHub Actions 81085ec890 feat: add standard proxy headers with backward compatibility
Add X-Real-IP, X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-Port headers to all proxy hosts for proper client IP detection, HTTPS enforcement, and logging.

- New feature flag: enable_standard_headers (default: true for new hosts, false for existing)
- UI: Checkbox in proxy host form and bulk apply modal for easy migration
- Security: Always configure trusted_proxies when headers enabled
- Backward compatible: Existing hosts preserve legacy behavior until explicitly enabled

BREAKING CHANGE: New proxy hosts will have standard headers enabled by default. Existing hosts maintain legacy behavior. Users can opt-in via UI.

Backend: 98.7% coverage, 8 new tests
Frontend: 87.7% coverage, full TypeScript support
Docs: Comprehensive migration guide and troubleshooting

Closes #<issue-number> (FileFlows WebSocket fix)
2025-12-19 20:32:03 +00:00

110 lines
3.1 KiB
TypeScript

import type { ProxyHost } from '../api/proxyHosts'
export function formatSettingLabel(key: string) {
switch (key) {
case 'ssl_forced':
return 'Force SSL'
case 'http2_support':
return 'HTTP/2 Support'
case 'hsts_enabled':
return 'HSTS Enabled'
case 'hsts_subdomains':
return 'HSTS Subdomains'
case 'block_exploits':
return 'Block Exploits'
case 'websocket_support':
return 'Websockets Support'
case 'enable_standard_headers':
return 'Standard Proxy Headers'
default:
return key
}
}
export function settingHelpText(key: string) {
switch (key) {
case 'ssl_forced':
return 'Redirect all HTTP traffic to HTTPS.'
case 'http2_support':
return 'Enable HTTP/2 for improved performance.'
case 'hsts_enabled':
return 'Send HSTS header to enforce HTTPS.'
case 'hsts_subdomains':
return 'Include subdomains in HSTS policy.'
case 'block_exploits':
return 'Add common exploit-mitigation headers and rules.'
case 'websocket_support':
return 'Enable websocket proxying support.'
case 'enable_standard_headers':
return 'Add X-Real-IP and X-Forwarded-* headers for client IP detection.'
default:
return ''
}
}
export function settingKeyToField(key: string) {
switch (key) {
case 'ssl_forced':
return 'ssl_forced'
case 'http2_support':
return 'http2_support'
case 'hsts_enabled':
return 'hsts_enabled'
case 'hsts_subdomains':
return 'hsts_subdomains'
case 'block_exploits':
return 'block_exploits'
case 'websocket_support':
return 'websocket_support'
case 'enable_standard_headers':
return 'enable_standard_headers'
default:
return key
}
}
export async function applyBulkSettingsToHosts(options: {
hosts: ProxyHost[]
hostUUIDs: string[]
keysToApply: string[]
bulkApplySettings: Record<string, { apply: boolean; value: boolean }>
updateHost: (uuid: string, data: Partial<ProxyHost>) => Promise<ProxyHost>
setApplyProgress?: (p: { current: number; total: number } | null) => void
}) {
const { hosts, hostUUIDs, keysToApply, bulkApplySettings, updateHost, setApplyProgress } = options
let completed = 0
let errors = 0
setApplyProgress?.({ current: 0, total: hostUUIDs.length })
for (const uuid of hostUUIDs) {
const patch: Partial<ProxyHost> = {}
for (const key of keysToApply) {
const field = settingKeyToField(key) as keyof ProxyHost
;(patch as unknown as Record<string, unknown>)[field as string] = bulkApplySettings[key].value
}
const host = hosts.find(h => h.uuid === uuid)
if (!host) {
errors++
completed++
setApplyProgress?.({ current: completed, total: hostUUIDs.length })
continue
}
const merged: Partial<ProxyHost> = { ...host, ...patch }
try {
await updateHost(uuid, merged)
} catch {
errors++
}
completed++
setApplyProgress?.({ current: completed, total: hostUUIDs.length })
}
setApplyProgress?.(null)
return { errors, completed }
}
export default {}