cms-renderer 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/proxy.js +1 -14
- package/dist/lib/proxy.js.map +1 -1
- package/package.json +1 -1
package/dist/lib/proxy.js
CHANGED
|
@@ -50,20 +50,7 @@ async function proxyToUpstream(request, pathname, upstream) {
|
|
|
50
50
|
const newLocation = `${currentOrigin}${locationUrl.pathname}${locationUrl.search}`;
|
|
51
51
|
responseHeaders.set(key, newLocation);
|
|
52
52
|
} else {
|
|
53
|
-
|
|
54
|
-
const redirectUri = locationUrl.searchParams.get("redirect_uri");
|
|
55
|
-
if (redirectUri) {
|
|
56
|
-
try {
|
|
57
|
-
const redirectUriUrl = new URL(redirectUri);
|
|
58
|
-
if (redirectUriUrl.host === upstreamUrlObj.host) {
|
|
59
|
-
const newRedirectUri = `${currentOrigin}${redirectUriUrl.pathname}${redirectUriUrl.search}`;
|
|
60
|
-
locationUrl.searchParams.set("redirect_uri", newRedirectUri);
|
|
61
|
-
finalLocation = locationUrl.toString();
|
|
62
|
-
}
|
|
63
|
-
} catch {
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
responseHeaders.set(key, finalLocation);
|
|
53
|
+
responseHeaders.set(key, value);
|
|
67
54
|
}
|
|
68
55
|
} catch {
|
|
69
56
|
responseHeaders.set(key, value);
|
package/dist/lib/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../lib/proxy.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\n\n/**\n * Configuration options for the CMS proxy.\n */\nexport interface ProxyConfig {\n /**\n * The upstream CMS server URL (e.g., 'https://cms.example.com').\n * Defaults to ADMIN_UPSTREAM_ORIGIN environment variable.\n */\n upstream?: string;\n /**\n * Additional path prefixes to proxy (beyond /admin, /api, /auth).\n */\n additionalPaths?: string[];\n}\n\n// Static file extensions to proxy to upstream\nconst STATIC_FILE_REGEX =\n /\\.(css|js|map|png|jpg|jpeg|gif|svg|ico|webp|avif|woff|woff2|ttf|eot|txt|xml)$/;\n\n/**\n * Check if the request originates from an admin page (via Referer header).\n */\nfunction isFromAdminPage(request: NextRequest): boolean {\n const referer = request.headers.get('referer');\n if (!referer) return false;\n\n try {\n const refererUrl = new URL(referer);\n return refererUrl.pathname.startsWith('/admin');\n } catch {\n return false;\n }\n}\n\n/**\n * Proxy a request to the upstream CMS server with proper cookie handling.\n */\nasync function proxyToUpstream(\n request: NextRequest,\n pathname: string,\n upstream: string\n): Promise<NextResponse> {\n const upstreamUrl = new URL(pathname, upstream);\n upstreamUrl.search = request.nextUrl.search;\n\n // Clone all headers from the request\n const headers = new Headers(request.headers);\n\n // Keep the original host header so the upstream app knows the real origin\n // This is important for auth redirects (WorkOS) to use the correct domain\n // The x-forwarded-* headers provide additional context\n headers.set('x-forwarded-host', request.headers.get('host') ?? '');\n headers.set('x-forwarded-proto', request.nextUrl.protocol.replace(':', ''));\n headers.set('x-forwarded-for', request.headers.get('x-forwarded-for') ?? '');\n\n const response = await fetch(upstreamUrl.toString(), {\n method: request.method,\n headers,\n body: request.body,\n // @ts-expect-error - duplex is required for streaming bodies\n duplex: 'half',\n redirect: 'manual', // Don't follow redirects, let the client handle them\n });\n\n // Create response with proper header handling\n const responseHeaders = new Headers();\n\n const upstreamUrlObj = new URL(upstream);\n const upstreamOrigin = upstreamUrlObj.origin;\n const currentOrigin = request.nextUrl.origin;\n\n // Copy headers from upstream response\n response.headers.forEach((value, key) => {\n const lowerKey = key.toLowerCase();\n\n // Handle Set-Cookie specially - rewrite domain to current host\n if (lowerKey === 'set-cookie') {\n let modifiedCookie = value;\n\n // Remove Domain attribute so cookie defaults to current host\n modifiedCookie = modifiedCookie.replace(/;\\s*Domain=[^;]*/gi, '');\n\n // Ensure Path is set (usually /admin or /)\n if (!/;\\s*Path=/i.test(modifiedCookie)) {\n modifiedCookie += '; Path=/';\n }\n\n // For secure cookies in production, ensure SameSite is appropriate\n if (!/;\\s*SameSite=/i.test(modifiedCookie)) {\n modifiedCookie += '; SameSite=Lax';\n }\n\n responseHeaders.append(key, modifiedCookie);\n }\n // Handle Location header - rewrite upstream URLs to current host\n else if (lowerKey === 'location') {\n try {\n // Parse the location (handles both absolute and relative URLs)\n const locationUrl = new URL(value, upstream);\n\n // If redirect points to upstream, rewrite to current origin\n if (locationUrl.origin === upstreamOrigin) {\n const newLocation = `${currentOrigin}${locationUrl.pathname}${locationUrl.search}`;\n responseHeaders.set(key, newLocation);\n } else {\n // External redirect (e.g., to WorkOS)\n // Rewrite redirect_uri parameter if it points to upstream\n let finalLocation = value;\n\n // Check if this is a WorkOS/OAuth redirect with redirect_uri parameter\n const redirectUri = locationUrl.searchParams.get('redirect_uri');\n if (redirectUri) {\n try {\n const redirectUriUrl = new URL(redirectUri);\n // If redirect_uri points to upstream, rewrite to current origin\n if (redirectUriUrl.host === upstreamUrlObj.host) {\n const newRedirectUri = `${currentOrigin}${redirectUriUrl.pathname}${redirectUriUrl.search}`;\n locationUrl.searchParams.set('redirect_uri', newRedirectUri);\n finalLocation = locationUrl.toString();\n }\n } catch {\n // If redirect_uri parsing fails, keep original\n }\n }\n\n responseHeaders.set(key, finalLocation);\n }\n } catch {\n // If URL parsing fails, keep original\n responseHeaders.set(key, value);\n }\n }\n // Skip headers that cause issues after fetch decompresses the body\n else if (\n lowerKey !== 'transfer-encoding' &&\n lowerKey !== 'content-encoding' &&\n lowerKey !== 'content-length'\n ) {\n responseHeaders.set(key, value);\n }\n });\n\n // Add debug header to verify middleware is running\n responseHeaders.set('x-proxied-by', 'cms-proxy');\n\n // For HTML responses, rewrite upstream URLs in the body\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('text/html') && response.body) {\n let text = await response.text();\n\n // Get the upstream host for more comprehensive replacement\n const upstreamHost = upstreamUrlObj.host;\n\n // Replace full origin (https://cms.example.com)\n text = text.replaceAll(upstreamOrigin, currentOrigin);\n\n // Replace protocol-relative URLs (//cms.example.com)\n text = text.replaceAll(`//${upstreamHost}`, `//${request.nextUrl.host}`);\n\n // Replace any remaining absolute URLs with the upstream host\n // This catches cases where protocol might differ\n text = text.replaceAll(`https://${upstreamHost}`, currentOrigin);\n text = text.replaceAll(`http://${upstreamHost}`, currentOrigin);\n\n return new NextResponse(text, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n }\n\n return new NextResponse(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n}\n\n/**\n * Creates a proxy middleware function for Next.js.\n *\n * @example\n * ```ts\n * // middleware.ts\n * import { createCmsProxy } from 'cms-renderer/lib/proxy';\n *\n * const cmsProxy = createCmsProxy({\n * upstream: process.env.ADMIN_UPSTREAM_ORIGIN,\n * });\n *\n * export async function middleware(request: NextRequest) {\n * return cmsProxy(request);\n * }\n *\n * export const config = {\n * matcher: cmsProxyMatcher,\n * };\n * ```\n */\nexport function createCmsProxy(config: ProxyConfig = {}) {\n const upstream = (config.upstream ?? process.env.ADMIN_UPSTREAM_ORIGIN ?? '').replace(/\\/$/, '');\n const additionalPaths = config.additionalPaths ?? [];\n\n if (!upstream) {\n console.warn(\n '[cms-proxy] No upstream URL configured. Set ADMIN_UPSTREAM_ORIGIN or pass upstream option.'\n );\n }\n\n return async function cmsProxy(request: NextRequest): Promise<NextResponse> {\n if (!upstream) {\n return NextResponse.next();\n }\n\n const { pathname } = request.nextUrl;\n\n // Proxy /admin routes to the upstream CMS\n if (pathname.startsWith('/admin')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy /api routes to the upstream CMS\n if (pathname.startsWith('/api')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy auth routes to the upstream CMS (WorkOS callbacks, signin, etc.)\n if (pathname.startsWith('/auth')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy additional custom paths\n for (const pathPrefix of additionalPaths) {\n if (pathname.startsWith(pathPrefix)) {\n return proxyToUpstream(request, pathname, upstream);\n }\n }\n\n // Only proxy /_next and static files if the request comes from an admin page\n // This prevents breaking the web app's own assets\n if (isFromAdminPage(request)) {\n if (pathname.startsWith('/_next') || STATIC_FILE_REGEX.test(pathname)) {\n return proxyToUpstream(request, pathname, upstream);\n }\n }\n\n return NextResponse.next();\n };\n}\n\n/**\n * Default matcher configuration for the CMS proxy middleware.\n * Use this in your middleware.ts config export.\n *\n * @example\n * ```ts\n * export const config = {\n * matcher: cmsProxyMatcher,\n * };\n * ```\n */\nexport const cmsProxyMatcher = [\n '/admin',\n '/admin/:path*',\n '/api/:path*',\n '/auth/:path*',\n '/_next/:path*',\n '/((?:.*\\\\.(?:css|js|map|png|jpg|jpeg|gif|svg|ico|webp|avif|woff|woff2|ttf|eot|txt|xml))$)',\n];\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../lib/proxy.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\n\n/**\n * Configuration options for the CMS proxy.\n */\nexport interface ProxyConfig {\n /**\n * The upstream CMS server URL (e.g., 'https://cms.example.com').\n * Defaults to ADMIN_UPSTREAM_ORIGIN environment variable.\n */\n upstream?: string;\n /**\n * Additional path prefixes to proxy (beyond /admin, /api, /auth).\n */\n additionalPaths?: string[];\n}\n\n// Static file extensions to proxy to upstream\nconst STATIC_FILE_REGEX =\n /\\.(css|js|map|png|jpg|jpeg|gif|svg|ico|webp|avif|woff|woff2|ttf|eot|txt|xml)$/;\n\n/**\n * Check if the request originates from an admin page (via Referer header).\n */\nfunction isFromAdminPage(request: NextRequest): boolean {\n const referer = request.headers.get('referer');\n if (!referer) return false;\n\n try {\n const refererUrl = new URL(referer);\n return refererUrl.pathname.startsWith('/admin');\n } catch {\n return false;\n }\n}\n\n/**\n * Proxy a request to the upstream CMS server with proper cookie handling.\n */\nasync function proxyToUpstream(\n request: NextRequest,\n pathname: string,\n upstream: string\n): Promise<NextResponse> {\n const upstreamUrl = new URL(pathname, upstream);\n upstreamUrl.search = request.nextUrl.search;\n\n // Clone all headers from the request\n const headers = new Headers(request.headers);\n\n // Keep the original host header so the upstream app knows the real origin\n // This is important for auth redirects (WorkOS) to use the correct domain\n // The x-forwarded-* headers provide additional context\n headers.set('x-forwarded-host', request.headers.get('host') ?? '');\n headers.set('x-forwarded-proto', request.nextUrl.protocol.replace(':', ''));\n headers.set('x-forwarded-for', request.headers.get('x-forwarded-for') ?? '');\n\n const response = await fetch(upstreamUrl.toString(), {\n method: request.method,\n headers,\n body: request.body,\n // @ts-expect-error - duplex is required for streaming bodies\n duplex: 'half',\n redirect: 'manual', // Don't follow redirects, let the client handle them\n });\n\n // Create response with proper header handling\n const responseHeaders = new Headers();\n\n const upstreamUrlObj = new URL(upstream);\n const upstreamOrigin = upstreamUrlObj.origin;\n const currentOrigin = request.nextUrl.origin;\n\n // Copy headers from upstream response\n response.headers.forEach((value, key) => {\n const lowerKey = key.toLowerCase();\n\n // Handle Set-Cookie specially - rewrite domain to current host\n if (lowerKey === 'set-cookie') {\n let modifiedCookie = value;\n\n // Remove Domain attribute so cookie defaults to current host\n modifiedCookie = modifiedCookie.replace(/;\\s*Domain=[^;]*/gi, '');\n\n // Ensure Path is set (usually /admin or /)\n if (!/;\\s*Path=/i.test(modifiedCookie)) {\n modifiedCookie += '; Path=/';\n }\n\n // For secure cookies in production, ensure SameSite is appropriate\n if (!/;\\s*SameSite=/i.test(modifiedCookie)) {\n modifiedCookie += '; SameSite=Lax';\n }\n\n responseHeaders.append(key, modifiedCookie);\n }\n // Handle Location header - rewrite upstream URLs to current host\n else if (lowerKey === 'location') {\n try {\n // Parse the location (handles both absolute and relative URLs)\n const locationUrl = new URL(value, upstream);\n\n // If redirect points to upstream, rewrite to current origin\n if (locationUrl.origin === upstreamOrigin) {\n const newLocation = `${currentOrigin}${locationUrl.pathname}${locationUrl.search}`;\n responseHeaders.set(key, newLocation);\n } else {\n // External redirect (e.g., to WorkOS/authkit).\n // Do NOT rewrite the redirect_uri parameter. The CMS middleware encodes\n // the customer origin in the WorkOS `state` parameter (via x-forwarded-host)\n // and handles the return-to-customer-domain flow through a short-lived\n // handoff token at /api/auth/workos/complete. The redirect_uri must remain\n // pointing to the CMS host so WorkOS accepts it as a registered callback URI.\n responseHeaders.set(key, value);\n }\n } catch {\n // If URL parsing fails, keep original\n responseHeaders.set(key, value);\n }\n }\n // Skip headers that cause issues after fetch decompresses the body\n else if (\n lowerKey !== 'transfer-encoding' &&\n lowerKey !== 'content-encoding' &&\n lowerKey !== 'content-length'\n ) {\n responseHeaders.set(key, value);\n }\n });\n\n // Add debug header to verify middleware is running\n responseHeaders.set('x-proxied-by', 'cms-proxy');\n\n // For HTML responses, rewrite upstream URLs in the body\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('text/html') && response.body) {\n let text = await response.text();\n\n // Get the upstream host for more comprehensive replacement\n const upstreamHost = upstreamUrlObj.host;\n\n // Replace full origin (https://cms.example.com)\n text = text.replaceAll(upstreamOrigin, currentOrigin);\n\n // Replace protocol-relative URLs (//cms.example.com)\n text = text.replaceAll(`//${upstreamHost}`, `//${request.nextUrl.host}`);\n\n // Replace any remaining absolute URLs with the upstream host\n // This catches cases where protocol might differ\n text = text.replaceAll(`https://${upstreamHost}`, currentOrigin);\n text = text.replaceAll(`http://${upstreamHost}`, currentOrigin);\n\n return new NextResponse(text, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n }\n\n return new NextResponse(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n}\n\n/**\n * Creates a proxy middleware function for Next.js.\n *\n * @example\n * ```ts\n * // middleware.ts\n * import { createCmsProxy } from 'cms-renderer/lib/proxy';\n *\n * const cmsProxy = createCmsProxy({\n * upstream: process.env.ADMIN_UPSTREAM_ORIGIN,\n * });\n *\n * export async function middleware(request: NextRequest) {\n * return cmsProxy(request);\n * }\n *\n * export const config = {\n * matcher: cmsProxyMatcher,\n * };\n * ```\n */\nexport function createCmsProxy(config: ProxyConfig = {}) {\n const upstream = (config.upstream ?? process.env.ADMIN_UPSTREAM_ORIGIN ?? '').replace(/\\/$/, '');\n const additionalPaths = config.additionalPaths ?? [];\n\n if (!upstream) {\n console.warn(\n '[cms-proxy] No upstream URL configured. Set ADMIN_UPSTREAM_ORIGIN or pass upstream option.'\n );\n }\n\n return async function cmsProxy(request: NextRequest): Promise<NextResponse> {\n if (!upstream) {\n return NextResponse.next();\n }\n\n const { pathname } = request.nextUrl;\n\n // Proxy /admin routes to the upstream CMS\n if (pathname.startsWith('/admin')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy /api routes to the upstream CMS\n if (pathname.startsWith('/api')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy auth routes to the upstream CMS (WorkOS callbacks, signin, etc.)\n if (pathname.startsWith('/auth')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy additional custom paths\n for (const pathPrefix of additionalPaths) {\n if (pathname.startsWith(pathPrefix)) {\n return proxyToUpstream(request, pathname, upstream);\n }\n }\n\n // Only proxy /_next and static files if the request comes from an admin page\n // This prevents breaking the web app's own assets\n if (isFromAdminPage(request)) {\n if (pathname.startsWith('/_next') || STATIC_FILE_REGEX.test(pathname)) {\n return proxyToUpstream(request, pathname, upstream);\n }\n }\n\n return NextResponse.next();\n };\n}\n\n/**\n * Default matcher configuration for the CMS proxy middleware.\n * Use this in your middleware.ts config export.\n *\n * @example\n * ```ts\n * export const config = {\n * matcher: cmsProxyMatcher,\n * };\n * ```\n */\nexport const cmsProxyMatcher = [\n '/admin',\n '/admin/:path*',\n '/api/:path*',\n '/auth/:path*',\n '/_next/:path*',\n '/((?:.*\\\\.(?:css|js|map|png|jpg|jpeg|gif|svg|ico|webp|avif|woff|woff2|ttf|eot|txt|xml))$)',\n];\n"],"mappings":"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","names":[]}
|