Refactoring Opportunity
Summary
- File:
containers/api-proxy/proxy-utils.js
- Current size: 410 lines
- Responsibilities identified: 5 distinct utility concerns in a single "grab-bag" module
- Score: 5 (+3 clearly >2 distinct responsibilities, +2 security-critical: credential injection infrastructure)
Evidence
proxy-utils.js has accumulated utilities from multiple conceptual domains:
Concern 1 — URL normalization (lines 20–115, ~96 lines)
Four functions that normalize API target URLs and build upstream request paths:
normalizeApiTarget(value) — strips scheme, injects https:// for bare hostnames
normalizeBasePath(rawPath) — normalizes URL path prefixes
buildUpstreamPath(reqUrl, targetHost, basePath) — constructs the final upstream URL path
stripGeminiKeyParam(reqUrl) — strips key= query param from Gemini URLs
Concern 2 — Proxy request utilities (lines 142–202, ~61 lines)
Two functions used during request forwarding:
shouldStripHeader(name) — determines which inbound headers to drop
composeBodyTransforms(first, second) — chains two body-transform functions
Concern 3 — Provider response factories (lines 205–232, ~28 lines)
Two factory functions for generating standard error/health responses:
makeProviderNotConfiguredResponse(provider, port, message)
makeUnconfiguredHealthResponse(service, error, status)
Concern 4 — Auth validation utilities (lines 234–274, ~41 lines)
Two auth/credential validation helpers:
isValidHeaderName(name) — validates that a custom auth-header name is a legal HTTP header
validateAuthHeaderEnv(envVarName, rawValue, defaultHeader) — validates API key env vars
Concern 5 — Provider adapter factory (lines 275–408, ~134 lines)
Two factory functions that are the foundation of the credential-injection infrastructure:
createBaseAdapterConfig(env, { keyEnvVar, targetEnvVar, ... }) — reads API key from env and builds base provider config
createAdapterMethods(opts) — 78 lines — creates the getAuthHeaders, injectHeaders, getTargetHost, isConfigured methods used by every provider adapter (OpenAI, Anthropic, Copilot, Gemini)
Concerns #4 and #5 touch security-critical credential handling: createBaseAdapterConfig reads API keys from environment variables, and createAdapterMethods generates the injectHeaders closures that insert x-api-key, Authorization, and x-goog-api-key headers into upstream requests.
All 5 concerns are currently imported à la carte by different callers; providers import URL functions AND adapter factories from the same file.
Proposed Split
containers/api-proxy/adapter-factory.js (~134 lines) — extract concern #5
- Moves
createBaseAdapterConfig and createAdapterMethods here
- This is the highest-risk section to change (credential injection path); isolating it makes security reviews easier
- Already referenced in
ADDING-A-PROVIDER.md as the canonical import for new providers
containers/api-proxy/proxy-utils.js (reduced to ~275 lines) — retains concerns #1–#4
- URL normalization, proxy request utilities, response factories, auth validation
Or if a further split is desired:
The minimal change (extract only adapter-factory.js) has the best blast-radius/benefit ratio.
Affected Callers
grep -rn "require.*proxy-utils" containers/ src/ 2>/dev/null
Known callers importing from proxy-utils:
containers/api-proxy/proxy-request.js — buildUpstreamPath, shouldStripHeader
containers/api-proxy/server.js — URL and response utilities
containers/api-proxy/providers/openai.js, anthropic.js, copilot.js, gemini.js — adapter factory + auth utils
containers/api-proxy/providers/copilot-byok.js — isValidHeaderName
containers/api-proxy/providers/copilot-auth.js — normalizeApiTarget
- Test files:
server.auth.test.js, server.routing.test.js, server.gemini.test.js, server.models.test.js
Provider callers currently import both URL utilities and adapter factories from the same module; separating them makes each provider's dependency surface explicit.
Effort Estimate
Low — extracting createBaseAdapterConfig and createAdapterMethods to a new adapter-factory.js is a mechanical file split. Only provider files need their require('./proxy-utils') calls updated to require('./adapter-factory') for those two symbols.
Benefits
- The credential-injection adapter factory is isolated for easier security review
proxy-utils.js becomes a focused proxy-request utilities file
- Providers can import just the adapter factory without pulling in URL normalization utilities
ADDING-A-PROVIDER.md already documents createBaseAdapterConfig/createAdapterMethods as the provider extension point — a dedicated module makes this clearer
Detected by Refactoring Scanner workflow. Run date: 2026-06-13
Generated by Refactoring Opportunity Scanner · ◷
Refactoring Opportunity
Summary
containers/api-proxy/proxy-utils.jsEvidence
proxy-utils.jshas accumulated utilities from multiple conceptual domains:Concern 1 — URL normalization (lines 20–115, ~96 lines)
Four functions that normalize API target URLs and build upstream request paths:
normalizeApiTarget(value)— strips scheme, injectshttps://for bare hostnamesnormalizeBasePath(rawPath)— normalizes URL path prefixesbuildUpstreamPath(reqUrl, targetHost, basePath)— constructs the final upstream URL pathstripGeminiKeyParam(reqUrl)— stripskey=query param from Gemini URLsConcern 2 — Proxy request utilities (lines 142–202, ~61 lines)
Two functions used during request forwarding:
shouldStripHeader(name)— determines which inbound headers to dropcomposeBodyTransforms(first, second)— chains two body-transform functionsConcern 3 — Provider response factories (lines 205–232, ~28 lines)
Two factory functions for generating standard error/health responses:
makeProviderNotConfiguredResponse(provider, port, message)makeUnconfiguredHealthResponse(service, error, status)Concern 4 — Auth validation utilities (lines 234–274, ~41 lines)
Two auth/credential validation helpers:
isValidHeaderName(name)— validates that a custom auth-header name is a legal HTTP headervalidateAuthHeaderEnv(envVarName, rawValue, defaultHeader)— validates API key env varsConcern 5 — Provider adapter factory (lines 275–408, ~134 lines)
Two factory functions that are the foundation of the credential-injection infrastructure:
createBaseAdapterConfig(env, { keyEnvVar, targetEnvVar, ... })— reads API key from env and builds base provider configcreateAdapterMethods(opts)— 78 lines — creates thegetAuthHeaders,injectHeaders,getTargetHost,isConfiguredmethods used by every provider adapter (OpenAI, Anthropic, Copilot, Gemini)Concerns #4 and #5 touch security-critical credential handling:
createBaseAdapterConfigreads API keys from environment variables, andcreateAdapterMethodsgenerates theinjectHeadersclosures that insertx-api-key,Authorization, andx-goog-api-keyheaders into upstream requests.All 5 concerns are currently imported à la carte by different callers; providers import URL functions AND adapter factories from the same file.
Proposed Split
containers/api-proxy/adapter-factory.js(~134 lines) — extract concern #5createBaseAdapterConfigandcreateAdapterMethodshereADDING-A-PROVIDER.mdas the canonical import for new providerscontainers/api-proxy/proxy-utils.js(reduced to ~275 lines) — retains concerns #1–#4Or if a further split is desired:
containers/api-proxy/url-utils.js— concerns Improve links in readme to AW project #1 only (URL normalization)containers/api-proxy/proxy-utils.js— concerns Secret proxying #2–fix: add missing Docker image pulls to robustness test workflow #4 (~130 lines)The minimal change (extract only
adapter-factory.js) has the best blast-radius/benefit ratio.Affected Callers
Known callers importing from
proxy-utils:containers/api-proxy/proxy-request.js—buildUpstreamPath,shouldStripHeadercontainers/api-proxy/server.js— URL and response utilitiescontainers/api-proxy/providers/openai.js,anthropic.js,copilot.js,gemini.js— adapter factory + auth utilscontainers/api-proxy/providers/copilot-byok.js—isValidHeaderNamecontainers/api-proxy/providers/copilot-auth.js—normalizeApiTargetserver.auth.test.js,server.routing.test.js,server.gemini.test.js,server.models.test.jsProvider callers currently import both URL utilities and adapter factories from the same module; separating them makes each provider's dependency surface explicit.
Effort Estimate
Low — extracting
createBaseAdapterConfigandcreateAdapterMethodsto a newadapter-factory.jsis a mechanical file split. Only provider files need theirrequire('./proxy-utils')calls updated torequire('./adapter-factory')for those two symbols.Benefits
proxy-utils.jsbecomes a focused proxy-request utilities fileADDING-A-PROVIDER.mdalready documentscreateBaseAdapterConfig/createAdapterMethodsas the provider extension point — a dedicated module makes this clearerDetected by Refactoring Scanner workflow. Run date: 2026-06-13