Skip to content

Commit e4a1929

Browse files
Chore: Remove sdk reference proxying (#270)
<!-- CURSOR_SUMMARY --> > [!NOTE] > **Medium Risk** > Changes rewrite/sitemap behavior for `/docs/sdk-reference` paths, which can affect production routing and SEO indexing if misconfigured. Test coverage was added/updated, but incorrect domain/path config could still cause broken docs links or missing sitemap entries. > > **Overview** > Stops treating the SDK reference as a separately proxied site: removes `SDK_REFERENCE_DOMAIN` and its middleware rewrite rule, relying on the main `DOCUMENTATION_DOMAIN` for `/docs/*` rewrites. > > Updates sitemap generation to **not include** SDK reference URLs by introducing `SITEMAP_EXCLUDE_CONFIG` and filtering out excluded path prefixes when merging external sitemaps; adds a unit test asserting `/docs/sdk-reference*` is omitted. Integration tests were updated to assert middleware rewrites send docs traffic (including `/docs/sdk-reference/*`) to the docs host. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 5181544. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 2aaa3c9 commit e4a1929

File tree

7 files changed

+171
-29
lines changed

7 files changed

+171
-29
lines changed

src/__test__/integration/proxy.test.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { NextRequest, NextResponse } from 'next/server'
33
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
44
import { AUTH_URLS, PROTECTED_URLS } from '@/configs/urls'
55
import { proxy } from '@/proxy'
6+
import { DOCUMENTATION_DOMAIN } from '../../../next.config.mjs'
67

78
// mock supabase auth
89
vi.mock('@supabase/ssr', () => ({
@@ -46,7 +47,7 @@ vi.mock('next/server', async () => {
4647
return response
4748
})
4849

49-
const mockNext = vi.fn((init?: { request?: NextRequest }) => {
50+
const mockNext = vi.fn((_init?: { request?: NextRequest }) => {
5051
const response = new actual.NextResponse()
5152

5253
Object.defineProperty(response, 'cookies', {
@@ -61,7 +62,7 @@ vi.mock('next/server', async () => {
6162
})
6263

6364
const mockRewrite = vi.fn(
64-
(url: URL | string, init?: { request?: { headers?: Headers } }) => {
65+
(_url: URL | string, _init?: { request?: { headers?: Headers } }) => {
6566
const response = new actual.NextResponse()
6667

6768
Object.defineProperty(response, 'headers', {
@@ -140,7 +141,7 @@ describe('Proxy Integration Tests', () => {
140141
path: PROTECTED_URLS.DASHBOARD,
141142
})
142143

143-
const response = await proxy(request)
144+
await proxy(request)
144145

145146
expect(NextResponse.redirect).toHaveBeenCalled()
146147
const redirectUrl = vi
@@ -154,7 +155,7 @@ describe('Proxy Integration Tests', () => {
154155
path: AUTH_URLS.SIGN_IN,
155156
})
156157

157-
const response = await proxy(request)
158+
await proxy(request)
158159

159160
expect(NextResponse.redirect).toHaveBeenCalled()
160161
const redirectUrl = vi
@@ -168,7 +169,7 @@ describe('Proxy Integration Tests', () => {
168169
path: '/dashboard/team-123/sandboxes',
169170
})
170171

171-
const response = await proxy(request)
172+
await proxy(request)
172173

173174
expect(NextResponse.redirect).not.toHaveBeenCalled()
174175
expect(NextResponse.next).toHaveBeenCalled()
@@ -191,7 +192,7 @@ describe('Proxy Integration Tests', () => {
191192
path: '/',
192193
})
193194

194-
const response = await proxy(request)
195+
await proxy(request)
195196

196197
expect(NextResponse.redirect).not.toHaveBeenCalled()
197198
expect(NextResponse.next).toHaveBeenCalled()
@@ -215,10 +216,44 @@ describe('Proxy Integration Tests', () => {
215216
path: '/',
216217
})
217218

218-
const response = await proxy(request)
219+
await proxy(request)
219220

220221
// should not crash, should return next()
221222
expect(NextResponse.next).toHaveBeenCalled()
222223
})
223224
})
225+
226+
describe('Middleware Rewrites', () => {
227+
it('rewrites supported docs paths to the docs host', async () => {
228+
const request = createMockRequest({
229+
path: '/docs/quickstart',
230+
})
231+
232+
await proxy(request)
233+
234+
expect(NextResponse.rewrite).toHaveBeenCalledTimes(1)
235+
expect(NextResponse.rewrite).toHaveBeenCalledWith(
236+
expect.objectContaining({
237+
href: `https://${DOCUMENTATION_DOMAIN}/docs/quickstart`,
238+
}),
239+
expect.anything()
240+
)
241+
})
242+
243+
it('rewrites the sdk reference subtree to the docs host', async () => {
244+
const request = createMockRequest({
245+
path: '/docs/sdk-reference/python',
246+
})
247+
248+
await proxy(request)
249+
250+
expect(NextResponse.rewrite).toHaveBeenCalledTimes(1)
251+
expect(NextResponse.rewrite).toHaveBeenCalledWith(
252+
expect.objectContaining({
253+
href: `https://${DOCUMENTATION_DOMAIN}/docs/sdk-reference/python`,
254+
}),
255+
expect.anything()
256+
)
257+
})
258+
})
224259
})

src/__test__/unit/sitemap.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2+
import { LANDING_PAGE_DOMAIN } from '@/configs/rewrites'
3+
import { DOCUMENTATION_DOMAIN } from '../../../next.config.mjs'
4+
5+
vi.mock('@/configs/flags', () => ({
6+
ALLOW_SEO_INDEXING: true,
7+
}))
8+
9+
import { constructSitemap } from '@/app/sitemap'
10+
11+
describe('constructSitemap', () => {
12+
const fetchMock = vi.fn<typeof fetch>()
13+
14+
beforeEach(() => {
15+
vi.stubGlobal('fetch', fetchMock)
16+
})
17+
18+
afterEach(() => {
19+
vi.unstubAllGlobals()
20+
vi.resetAllMocks()
21+
})
22+
23+
it('excludes sdk reference paths from the merged sitemap', async () => {
24+
fetchMock.mockImplementation(async (input) => {
25+
const url = input instanceof URL ? input.toString() : String(input)
26+
27+
if (url === `https://${LANDING_PAGE_DOMAIN}/sitemap.xml`) {
28+
return new Response(
29+
`
30+
<urlset>
31+
<url>
32+
<loc>https://${LANDING_PAGE_DOMAIN}/pricing</loc>
33+
</url>
34+
</urlset>
35+
`,
36+
{
37+
status: 200,
38+
headers: {
39+
'Content-Type': 'application/xml',
40+
},
41+
}
42+
)
43+
}
44+
45+
if (url === `https://${DOCUMENTATION_DOMAIN}/sitemap.xml`) {
46+
return new Response(
47+
`
48+
<urlset>
49+
<url>
50+
<loc>https://${DOCUMENTATION_DOMAIN}/docs/quickstart</loc>
51+
</url>
52+
<url>
53+
<loc>https://${DOCUMENTATION_DOMAIN}/docs/sdk-reference</loc>
54+
</url>
55+
<url>
56+
<loc>https://${DOCUMENTATION_DOMAIN}/docs/sdk-reference/python</loc>
57+
</url>
58+
</urlset>
59+
`,
60+
{
61+
status: 200,
62+
headers: {
63+
'Content-Type': 'application/xml',
64+
},
65+
}
66+
)
67+
}
68+
69+
throw new Error(`Unexpected sitemap fetch: ${url}`)
70+
})
71+
72+
const sitemap = await constructSitemap()
73+
const urls = sitemap.map((entry) => entry.url)
74+
75+
expect(urls).toContain('https://e2b.dev/pricing')
76+
expect(urls).toContain('https://e2b.dev/docs/quickstart')
77+
expect(urls).not.toContain('https://e2b.dev/docs/sdk-reference')
78+
expect(urls).not.toContain('https://e2b.dev/docs/sdk-reference/python')
79+
})
80+
})

src/app/(rewrites)/[[...slug]]/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export async function GET(request: NextRequest): Promise<Response> {
4343
const { config, rule } = getRewriteForPath(url.pathname, 'route')
4444

4545
if (config) {
46-
if (rule && rule.pathPreprocessor) {
46+
if (rule?.pathPreprocessor) {
4747
url.pathname = rule.pathPreprocessor(url.pathname)
4848
}
4949
updateUrlHostname(config.domain)
@@ -131,7 +131,7 @@ export async function generateStaticParams() {
131131
if (isIndex && rule.path === '/') {
132132
return true
133133
}
134-
if (pathname === rule.path || pathname.startsWith(rule.path + '/')) {
134+
if (pathname === rule.path || pathname.startsWith(`${rule.path}/`)) {
135135
return true
136136
}
137137
return false

src/app/sitemap.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,8 @@
1010
import { XMLParser } from 'fast-xml-parser'
1111
import type { MetadataRoute } from 'next'
1212
import { ALLOW_SEO_INDEXING } from '@/configs/flags'
13-
import {
14-
LANDING_PAGE_DOMAIN,
15-
ROUTE_REWRITE_CONFIG,
16-
SDK_REFERENCE_DOMAIN,
17-
} from '@/configs/rewrites'
13+
import { LANDING_PAGE_DOMAIN, ROUTE_REWRITE_CONFIG } from '@/configs/rewrites'
14+
import { SITEMAP_EXCLUDE_CONFIG } from '@/configs/sitemap'
1815
import type { DomainConfig } from '@/types/rewrites.types'
1916
import { DOCUMENTATION_DOMAIN } from '../../next.config.mjs'
2017

@@ -56,12 +53,6 @@ const sites: Site[] = [
5653
changeFrequency: 'weekly',
5754
baseUrl: 'https://e2b.dev',
5855
},
59-
{
60-
sitemapUrl: `https://${SDK_REFERENCE_DOMAIN}/sitemap.xml`,
61-
priority: 0.7,
62-
changeFrequency: 'weekly',
63-
baseUrl: 'https://e2b.dev',
64-
},
6556
{
6657
sitemapUrl: `https://${DOCUMENTATION_DOMAIN}/sitemap.xml`,
6758
priority: 0.9,
@@ -119,6 +110,10 @@ async function getXmlData(url: string): Promise<Sitemap> {
119110
}
120111
}
121112

113+
function matchesPathPrefix(path: string, prefix: string): boolean {
114+
return path === prefix || path.startsWith(`${prefix}/`)
115+
}
116+
122117
/**
123118
* Finds the corresponding rewrite configuration for a given site based on its sitemap URL domain.
124119
*
@@ -138,6 +133,22 @@ function findRewriteConfig(site: Site): DomainConfig | undefined {
138133
}
139134
}
140135

136+
function findExcludedPathPrefixes(site: Site): string[] {
137+
try {
138+
const siteDomain = new URL(site.sitemapUrl).hostname
139+
return (
140+
SITEMAP_EXCLUDE_CONFIG.find((config) => config.domain === siteDomain)
141+
?.excludedPathPrefixes ?? []
142+
)
143+
} catch (error) {
144+
console.error(
145+
`Error parsing sitemapUrl ${site.sitemapUrl} to find sitemap exclusions:`,
146+
error
147+
)
148+
return []
149+
}
150+
}
151+
141152
/**
142153
* Processes a site's sitemap and converts it to Next.js sitemap format
143154
* Applies path preprocessing based on ROUTE_REWRITE_CONFIG.
@@ -148,6 +159,7 @@ function findRewriteConfig(site: Site): DomainConfig | undefined {
148159
async function getSitemap(site: Site): Promise<MetadataRoute.Sitemap> {
149160
const data = await getXmlData(site.sitemapUrl)
150161
const rewriteConfig = findRewriteConfig(site) // Find the rewrite config for this site
162+
const excludedPathPrefixes = findExcludedPathPrefixes(site)
151163

152164
if (!data || !site.baseUrl) {
153165
// Ensure baseUrl is defined, as it's crucial for constructing final URLs
@@ -170,6 +182,14 @@ async function getSitemap(site: Site): Promise<MetadataRoute.Sitemap> {
170182
const rewrittenPathname = originalUrl.pathname
171183
let finalPathname = rewrittenPathname // Default to the path from the sitemap
172184

185+
if (
186+
excludedPathPrefixes.some((prefix) =>
187+
matchesPathPrefix(rewrittenPathname, prefix)
188+
)
189+
) {
190+
return null
191+
}
192+
173193
// Find the corresponding original path if a preprocessor was involved
174194
if (rewriteConfig) {
175195
for (const rule of rewriteConfig.rules) {

src/configs/rewrites.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import type { DomainConfig } from '@/types/rewrites.types'
22

33
export const LANDING_PAGE_DOMAIN = 'www.e2b-landing-page.com'
4-
export const SDK_REFERENCE_DOMAIN = 'e2b-docs.vercel.app'
54

6-
// NOTE: DOCUMENTATION_DOMAIN has to be defined in next.config.mjs, such that we are able to use it there
75
import { DOCUMENTATION_DOMAIN } from '../../next.config.mjs'
86

97
// Currently we have two locations for rewrites to happen.
@@ -56,10 +54,6 @@ export const ROUTE_REWRITE_CONFIG: DomainConfig[] = [
5654
* control over the request/response cycle.
5755
*/
5856
export const MIDDLEWARE_REWRITE_CONFIG: DomainConfig[] = [
59-
{
60-
domain: SDK_REFERENCE_DOMAIN,
61-
rules: [{ path: '/docs/sdk-reference' }],
62-
},
6357
{
6458
domain: DOCUMENTATION_DOMAIN,
6559
rules: [

src/configs/sitemap.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { DOCUMENTATION_DOMAIN } from '../../next.config.mjs'
2+
3+
export const SITEMAP_EXCLUDE_CONFIG: Array<{
4+
domain: string
5+
excludedPathPrefixes: string[]
6+
}> = [
7+
{
8+
domain: DOCUMENTATION_DOMAIN,
9+
excludedPathPrefixes: ['/docs/sdk-reference'],
10+
},
11+
]

src/lib/utils/rewrites.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ function getRewriteForPath(
2020

2121
const matchingRule = domainConfig.rules.find((rule) => {
2222
if (isIndex && rule.path === '/') {
23-
return rule
23+
return true
2424
}
2525

2626
if (
2727
rule.path !== '/' &&
28-
(path === rule.path || path.startsWith(rule.path + '/'))
28+
(path === rule.path || path.startsWith(`${rule.path}/`))
2929
) {
30-
return rule
30+
return true
3131
}
32+
33+
return false
3234
})
3335

3436
if (matchingRule) {

0 commit comments

Comments
 (0)