Merge branch 'codex/feat/traffic-billing-mvp'
# Conflicts: # .github/workflows/pipeline.yaml
This commit is contained in:
commit
651101c253
10
.github/workflows/pipeline.yaml
vendored
10
.github/workflows/pipeline.yaml
vendored
@ -71,7 +71,7 @@ jobs:
|
|||||||
push_latest: ${{ steps.push.outputs.push_latest }}
|
push_latest: ${{ steps.push.outputs.push_latest }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check Out Repository
|
- name: Check Out Repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Resolve Inputs
|
- name: Resolve Inputs
|
||||||
id: inputs
|
id: inputs
|
||||||
@ -118,7 +118,7 @@ jobs:
|
|||||||
image_tag: ${{ needs.prep.outputs.image_tag }}
|
image_tag: ${{ needs.prep.outputs.image_tag }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check Out Repository
|
- name: Check Out Repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Set Up Docker Buildx
|
- name: Set Up Docker Buildx
|
||||||
run: |
|
run: |
|
||||||
@ -152,10 +152,10 @@ jobs:
|
|||||||
PLAYBOOKS_REPO: git@github.com:x-evor/playbooks.git
|
PLAYBOOKS_REPO: git@github.com:x-evor/playbooks.git
|
||||||
steps:
|
steps:
|
||||||
- name: Check Out Repository
|
- name: Check Out Repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Set Up Python
|
- name: Set Up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ jobs:
|
|||||||
if: ${{ always() && needs.deploy.result == 'success' }}
|
if: ${{ always() && needs.deploy.result == 'success' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check Out Repository
|
- name: Check Out Repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Verify Frontend Release
|
- name: Verify Frontend Release
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
52
src/lib/apiProxy.test.ts
Normal file
52
src/lib/apiProxy.test.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// @vitest-environment node
|
||||||
|
|
||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
import { NextRequest } from 'next/server'
|
||||||
|
|
||||||
|
import { proxyRequestToUpstream } from './apiProxy'
|
||||||
|
|
||||||
|
describe('proxyRequestToUpstream', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.unstubAllGlobals()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('strips stale encoding headers when the upstream body is already decoded', async () => {
|
||||||
|
const fetchMock = vi.fn().mockResolvedValue(
|
||||||
|
new Response(JSON.stringify([{ name: 'JP', address: 'jp-xhttp.svc.plus' }]), {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Encoding': 'gzip',
|
||||||
|
'Content-Length': '405',
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
vi.stubGlobal('fetch', fetchMock)
|
||||||
|
|
||||||
|
const response = await proxyRequestToUpstream(
|
||||||
|
new NextRequest('https://console.svc.plus/api/agent/nodes', {
|
||||||
|
headers: {
|
||||||
|
host: 'console.svc.plus',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
upstreamBaseUrl: 'https://accounts.svc.plus',
|
||||||
|
upstreamPathPrefix: '/api/agent',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(fetchMock).toHaveBeenCalledWith(
|
||||||
|
'https://accounts.svc.plus/api/agent/nodes',
|
||||||
|
expect.objectContaining({
|
||||||
|
cache: 'no-store',
|
||||||
|
method: 'GET',
|
||||||
|
redirect: 'manual',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
expect(response.status).toBe(200)
|
||||||
|
expect(response.headers.get('content-encoding')).toBeNull()
|
||||||
|
expect(response.headers.get('content-length')).toBeNull()
|
||||||
|
expect(response.headers.get('content-type')).toBe('application/json; charset=utf-8')
|
||||||
|
await expect(response.json()).resolves.toEqual([{ name: 'JP', address: 'jp-xhttp.svc.plus' }])
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -14,6 +14,14 @@ const DEFAULT_FORWARD_HEADERS = [
|
|||||||
'x-trace-id',
|
'x-trace-id',
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
|
const RESPONSE_HEADERS_TO_STRIP = new Set([
|
||||||
|
'connection',
|
||||||
|
'content-encoding',
|
||||||
|
'content-length',
|
||||||
|
'keep-alive',
|
||||||
|
'transfer-encoding',
|
||||||
|
])
|
||||||
|
|
||||||
const BODYLESS_METHODS = new Set(['GET', 'HEAD'])
|
const BODYLESS_METHODS = new Set(['GET', 'HEAD'])
|
||||||
|
|
||||||
type ProxyOptions = {
|
type ProxyOptions = {
|
||||||
@ -107,7 +115,8 @@ export async function proxyRequestToUpstream(request: NextRequest, options: Prox
|
|||||||
|
|
||||||
const responseHeaders = new Headers()
|
const responseHeaders = new Headers()
|
||||||
upstreamResponse.headers.forEach((value, key) => {
|
upstreamResponse.headers.forEach((value, key) => {
|
||||||
if (key.toLowerCase() === 'set-cookie') {
|
const normalizedKey = key.toLowerCase()
|
||||||
|
if (normalizedKey === 'set-cookie' || RESPONSE_HEADERS_TO_STRIP.has(normalizedKey)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
responseHeaders.set(key, value)
|
responseHeaders.set(key, value)
|
||||||
|
|||||||
@ -20,7 +20,7 @@ describe('fetchAgentNodes', () => {
|
|||||||
await expect(fetchAgentNodes()).resolves.toEqual([{ name: 'JP', address: 'jp-xhttp.svc.plus' }])
|
await expect(fetchAgentNodes()).resolves.toEqual([{ name: 'JP', address: 'jp-xhttp.svc.plus' }])
|
||||||
expect(fetchMock).toHaveBeenCalledTimes(1)
|
expect(fetchMock).toHaveBeenCalledTimes(1)
|
||||||
expect(fetchMock).toHaveBeenCalledWith(
|
expect(fetchMock).toHaveBeenCalledWith(
|
||||||
'/api/agent-server/v1/nodes',
|
'/api/auth/sync/config?since_version=0',
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
cache: 'no-store',
|
cache: 'no-store',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user