Skip to content

Commit e115d76

Browse files
Merge pull request #4299 from opral/fix-cors
add cors
2 parents f929530 + 8752c33 commit e115d76

File tree

1 file changed

+78
-1
lines changed

1 file changed

+78
-1
lines changed

packages/website-v2/src/routes/[_]rpc.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ const RPC_BASE_URL = "https://rpc.inlang.com";
1818
const RPC_PATH = "/_rpc";
1919

2020
async function proxyRpcRequest(request: Request) {
21+
const corsHeaders = buildCorsHeaders(request);
22+
if (corsHeaders === null) {
23+
return new Response("CORS origin denied", { status: 403 });
24+
}
25+
26+
if (request.method === "OPTIONS") {
27+
return new Response(null, {
28+
status: 204,
29+
headers: corsHeaders,
30+
});
31+
}
32+
2133
const upstreamUrl = `${RPC_BASE_URL}${RPC_PATH}`;
2234
const headers = new Headers(request.headers);
2335

@@ -34,9 +46,74 @@ async function proxyRpcRequest(request: Request) {
3446
redirect: "manual",
3547
});
3648

49+
const responseHeaders = new Headers(response.headers);
50+
for (const [key, value] of corsHeaders.entries()) {
51+
if (key.toLowerCase() === "vary") {
52+
mergeVaryHeader(responseHeaders, value);
53+
continue;
54+
}
55+
responseHeaders.set(key, value);
56+
}
57+
3758
return new Response(response.body, {
3859
status: response.status,
3960
statusText: response.statusText,
40-
headers: response.headers,
61+
headers: responseHeaders,
4162
});
4263
}
64+
65+
function buildCorsHeaders(request: Request) {
66+
const headers = new Headers();
67+
const origin = request.headers.get("origin");
68+
if (!origin) {
69+
return headers;
70+
}
71+
72+
if (!isAllowedOrigin(origin)) {
73+
return null;
74+
}
75+
76+
headers.set("access-control-allow-origin", origin);
77+
headers.set("access-control-allow-credentials", "true");
78+
headers.set("access-control-allow-methods", "POST,OPTIONS");
79+
headers.set(
80+
"access-control-allow-headers",
81+
request.headers.get("access-control-request-headers") ?? "content-type"
82+
);
83+
headers.set("vary", "origin");
84+
return headers;
85+
}
86+
87+
function mergeVaryHeader(headers: Headers, value: string) {
88+
const existing = headers.get("vary");
89+
if (!existing) {
90+
headers.set("vary", value);
91+
return;
92+
}
93+
94+
const existingValues = existing
95+
.split(",")
96+
.map((entry) => entry.trim().toLowerCase())
97+
.filter(Boolean);
98+
const additions = value
99+
.split(",")
100+
.map((entry) => entry.trim().toLowerCase())
101+
.filter(Boolean);
102+
const merged = Array.from(new Set([...existingValues, ...additions]));
103+
headers.set("vary", merged.join(", "));
104+
}
105+
106+
function isAllowedOrigin(origin: string) {
107+
let hostname = "";
108+
try {
109+
hostname = new URL(origin).hostname;
110+
} catch {
111+
return false;
112+
}
113+
114+
if (hostname === "localhost") {
115+
return true;
116+
}
117+
118+
return hostname === "inlang.com" || hostname.endsWith(".inlang.com");
119+
}

0 commit comments

Comments
 (0)