-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathworker.js
More file actions
91 lines (85 loc) · 3.64 KB
/
Copy pathworker.js
File metadata and controls
91 lines (85 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* Cloudflare Worker: routes bloomlibrary.org API traffic between Supabase and Azure.
*
* Bloom Library is migrating its API function-by-function from Azure Functions to
* Supabase Edge Functions (see ../MIGRATION-PLAN.md). This worker is the routing
* layer for that migration:
*
* - Requests whose function name (the first path segment after /v1/) is listed in
* SUPABASE_FUNCTIONS are PROXIED — not redirected — to the Supabase project,
* rewriting /v1/<fn>/... to /functions/v1/<fn>/...
* - Everything else goes to the Azure Functions app: to the host named by the
* ORIGIN_HOST variable if set (staging), otherwise straight through to the
* zone's configured origin for the request hostname (production).
*
* The same script is deployed twice (see wrangler.toml):
* - bloom-api-router-staging on staging-api.bloomlibrary.org/v1/* → staging Supabase
* - bloom-api-router on api.bloomlibrary.org/v1/* → production Supabase
*
* Proxying (rather than the 302 "forwarding" page rule it replaces) is required
* because redirects break CORS preflights, convert POSTs to GETs, and drop auth
* headers on cross-origin hops — see MIGRATION-PLAN.md §2a.
*
* To migrate a function: add its name to SUPABASE_FUNCTIONS and redeploy.
* To roll back a function: remove its name and redeploy.
*/
// Function names that have been migrated to Supabase.
// NOTE: this is the URL path segment, which is not always the repo folder name
// (e.g. the "subscriptions" function is reached at /v1/subscriptionInfo).
const SUPABASE_FUNCTIONS = new Set([
"fs",
"social",
]);
// Fetch `url`, carrying over the incoming request's method, headers, and
// (streaming) body. When `publicUrl` is given, it is passed along as
// X-Bloom-Public-Url so the function can reconstruct the public URL in links
// it generates (e.g. og:url). A custom header is used rather than
// X-Forwarded-Host because Supabase's edge strips proxy-managed forwarding
// headers before the function sees them.
async function proxy(url, request, publicUrl) {
try {
const proxied = new Request(url, request);
if (publicUrl) {
proxied.headers.set("X-Bloom-Public-Url", publicUrl);
}
return await fetch(proxied);
} catch (err) {
return new Response(`Error proxying to ${url.hostname}: ${err.message}`, {
status: 502,
headers: { "content-type": "text/plain" },
});
}
}
export default {
async fetch(request, env) {
const url = new URL(request.url);
url.protocol = "https:";
url.port = "";
// The public URL the client used (normalized to https), captured before we
// rewrite the host/path to the Supabase target below.
const publicUrl = url.toString();
const match = url.pathname.match(/^\/v1\/([^/]+)(\/.*)?$/);
if (match && SUPABASE_FUNCTIONS.has(match[1])) {
// Migrated: rewrite https://<host>/v1/<fn>/<rest>?<query>
// to https://<project>.supabase.co/functions/v1/<fn>/<rest>?<query>
url.hostname = env.SUPABASE_FUNCTIONS_HOST;
url.pathname = `/functions/v1/${match[1]}${match[2] ?? ""}`;
return proxy(url, request, publicUrl);
}
// Not migrated: send to the Azure Functions app.
if (env.ORIGIN_HOST) {
url.hostname = env.ORIGIN_HOST;
return proxy(url, request);
}
// No ORIGIN_HOST configured (production): fall through to the zone's
// existing origin for this hostname, exactly as if the worker weren't here.
try {
return await fetch(request);
} catch (err) {
return new Response(`Error fetching from origin: ${err.message}`, {
status: 502,
headers: { "content-type": "text/plain" },
});
}
},
};