Skip to content

Deploy Natali Proxy Worker #11

Deploy Natali Proxy Worker

Deploy Natali Proxy Worker #11

Workflow file for this run

name: Deploy Natali Proxy Worker
on:
workflow_dispatch:
permissions:
contents: read
jobs:
deploy-proxy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy Natali Proxy Worker
env:
CF_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: |
set -x
ZONE_ID="5b3f8237bfa2518bff375eb8163623a5"
WORKER_NAME="natalie-proxy"
cat > /tmp/worker.js << 'WORKEREOF'
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
try {
const url = new URL(request.url);
const path = url.pathname + url.search;
// Debug: capture request info
let debugInfo = {
path: path,
headers: Object.fromEntries(request.headers),
};
// Try fetch with the GH Pages domain
const ghUrl = 'https://spivanatalie64.github.io' + path;
// Set the host header to the GH Pages domain to avoid custom domain redirect
const reqHeaders = new Headers(request.headers);
reqHeaders.set('Host', 'spivanatalie64.github.io');
let response = await fetch(ghUrl, {
method: request.method,
headers: reqHeaders,
body: request.body,
redirect: 'manual',
});
debugInfo.firstStatus = response.status;
debugInfo.firstLocation = response.headers.get('Location');
// If we got a redirect, try following it with the custom domain host
if (response.status >= 300 && response.status < 400) {
const location = response.headers.get('Location');
if (location && location.includes('natalie.acreetionos.org')) {
// Replace our domain with gh pages domain to avoid loop
const locUrl = new URL(location);
locUrl.hostname = 'spivanatalie64.github.io';
const followHeaders = new Headers(request.headers);
followHeaders.set('Host', 'natalie.acreetionos.org');
followHeaders.set('Accept', request.headers.get('Accept') || 'text/html');
const followResponse = await fetch(locUrl.toString(), {
method: 'GET',
headers: followHeaders,
redirect: 'manual',
});
debugInfo.secondStatus = followResponse.status;
debugInfo.secondLocation = followResponse.headers.get('Location');
debugInfo.secondContentType = followResponse.headers.get('Content-Type');
// If the follow succeeded, return its content
if (followResponse.status === 200 || followResponse.status === 404 || followResponse.status === 304) {
const newHeaders = new Headers(followResponse.headers);
newHeaders.set('X-Proxied-By', 'natalie-worker');
newHeaders.set('X-Debug', JSON.stringify(debugInfo));
return new Response(followResponse.body, {
status: followResponse.status,
statusText: followResponse.statusText,
headers: newHeaders,
});
}
// If still a redirect and it's back to natalie, give up and return the content-type info
// Try one more time with raw.githubusercontent.com
const rawUrl = 'https://raw.githubusercontent.com/spivanatalie64/spivanatalie64.github.io/gh-pages' + path;
const rawResponse = await fetch(rawUrl, {
headers: { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' },
redirect: 'manual',
});
debugInfo.rawStatus = rawResponse.status;
debugInfo.rawContentType = rawResponse.headers.get('Content-Type');
if (rawResponse.status === 200) {
// Force the content type to text/html for HTML files
const newHeaders = new Headers(rawResponse.headers);
newHeaders.set('Content-Type', 'text/html; charset=utf-8');
newHeaders.set('X-Proxied-By', 'natalie-worker');
newHeaders.set('X-Debug', JSON.stringify(debugInfo));
return new Response(rawResponse.body, {
status: 200,
statusText: 'OK',
headers: newHeaders,
});
}
}
}
// Return the response as-is with debug info
const newHeaders = new Headers(response.headers);
newHeaders.set('X-Proxied-By', 'natalie-worker');
newHeaders.set('X-Debug', JSON.stringify(debugInfo));
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
} catch (err) {
return new Response('Worker Error: ' + err.message + '\nStack: ' + (err.stack || ''), {
status: 500,
headers: { 'Content-Type': 'text/plain' },
});
}
}
WORKEREOF
cat > /tmp/metadata.json << 'METADATAEOF'
{
"body_part": "script",
"bindings": [],
"compatibility_date": "2026-06-20",
"compatibility_flags": []
}
METADATAEOF
curl -s -X PUT \
"https://api.cloudflare.com/client/v4/accounts/6a46b418508c26278ae2d3373f41da81/workers/scripts/$WORKER_NAME" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-F "metadata=@/tmp/metadata.json;type=application/json" \
-F "script=@/tmp/worker.js;type=application/javascript" | jq .
EXISTING_ROUTES=$(curl -s -H "Authorization: Bearer $CF_API_TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/workers/routes")
NATALIE_ROUTE_ID=$(echo "$EXISTING_ROUTES" | jq -r '.result[] | select(.pattern == "natalie.acreetionos.org/*") | .id')
curl -s -X PUT \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/workers/routes/$NATALIE_ROUTE_ID" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"pattern\": \"natalie.acreetionos.org/*\",
\"script\": \"$WORKER_NAME\"
}" | jq .
echo "=== Done ==="