diff --git a/package-lock.json b/package-lock.json index db93a3d..c65fd49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@monetizationos/proxy": "1.4.3" + "@monetizationos/proxy": "1.5.2" }, "devDependencies": { "@biomejs/biome": "2.4.16", @@ -1517,9 +1517,9 @@ } }, "node_modules/@monetizationos/proxy": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@monetizationos/proxy/-/proxy-1.4.3.tgz", - "integrity": "sha512-nSzTbQVLzXripFF8bzXRhZ2nqZVIQLwZINspE/HARBeBv2fzm0YQhqvEXnqmgoMHAZjxOPhkw8E3ELRKnlP2dg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@monetizationos/proxy/-/proxy-1.5.2.tgz", + "integrity": "sha512-qzUETTxMnh4squEDkJysC8rhM4yMu68Vm/GbhU9fRlKGLA9niJG8qa5lbC8HUgTbnmC1bSsWhipRvvyn6OlvOg==", "license": "MIT", "dependencies": { "cookie": "^1.1.1" diff --git a/package.json b/package.json index bf34776..571904c 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,6 @@ "wrangler": "^4.95.0" }, "dependencies": { - "@monetizationos/proxy": "1.4.3" + "@monetizationos/proxy": "1.5.2" } } diff --git a/src/index.ts b/src/index.ts index 92b4aaf..c8ecab3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,6 +35,7 @@ const proxy = new MOSProxyBuilder() } }, }) + .withClientIP((request) => request.headers.get('CF-Connecting-IP') ?? undefined) .build() export default { diff --git a/test/index.spec.ts b/test/index.spec.ts index d990967..a828c81 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -1,5 +1,5 @@ import { env, fetchMock, SELF } from 'cloudflare:test' -import { afterEach, beforeAll, describe, expect, it } from 'vitest' +import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest' import { mockOriginFetch, mockSurfaceDecisionsFetch, surfaceDecisionsResponse } from './helpers' describe('MonetizationOS Proxy', () => { @@ -102,6 +102,75 @@ describe('MonetizationOS Proxy', () => { ) }) + it('sends CF-Connecting-IP as http.clientIP in surface decisions payload', async () => { + mockOriginFetch() + const mockSurfaceDecision = mockSurfaceDecisionsFetch() + + const req = new Request('https://test.example/index.html', { + headers: { 'CF-Connecting-IP': '203.0.113.10' }, + }) + await SELF.fetch(req) + + expect(mockSurfaceDecision).toHaveBeenCalledWith( + expect.objectContaining({ + http: expect.objectContaining({ clientIP: '203.0.113.10' }), + }), + ) + }) + + it('sends Referer header in http.referer in surface decisions payload', async () => { + mockOriginFetch() + const mockSurfaceDecision = mockSurfaceDecisionsFetch() + + const req = new Request('https://test.example/index.html', { + headers: { Referer: 'https://test.example/previous-page' }, + }) + await SELF.fetch(req) + + expect(mockSurfaceDecision).toHaveBeenCalledWith( + expect.objectContaining({ + http: expect.objectContaining({ referer: 'https://test.example/previous-page' }), + }), + ) + }) + + it('sends Referer header in http.referer for offer redemption requests', async () => { + const mockOfferRedemption = vi.fn() + fetchMock + .get('https://api.monetizationos.com') + .intercept({ path: '/api/v1/offer-redemptions', method: 'POST' }) + .reply((data) => { + mockOfferRedemption(JSON.parse(data.body as string)) + return { + statusCode: 200, + data: JSON.stringify({ success: true }), + responseOptions: { headers: { 'Content-Type': 'application/json' } }, + } + }) + + const req = new Request('https://test.example/mos-api/offer-redemptions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: 'anon-session=the-session', + Referer: 'https://test.example/article/tombstone', + }, + body: JSON.stringify({ offerToken: 'offer.abc' }), + }) + const res = await SELF.fetch(req) + + expect(res.status).toBe(200) + expect(mockOfferRedemption).toHaveBeenCalledWith( + expect.objectContaining({ + offerToken: 'offer.abc', + http: expect.objectContaining({ + url: 'https://test.example/mos-api/offer-redemptions', + referer: 'https://test.example/article/tombstone', + }), + }), + ) + }) + it('sends origin status code in http.proxyOrigin.status in surface decisions payload', async () => { mockOriginFetch({ status: 404, responseBody: 'Not Found' }) const mockSurfaceDecision = mockSurfaceDecisionsFetch()