Repository navigation
Expand file tree
/
Copy pathcloudflare-worker.js
More file actions
110 lines (94 loc) · 3.33 KB
/
Copy pathcloudflare-worker.js
File metadata and controls
110 lines (94 loc) · 3.33 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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Cloudflare Worker — PlayHQ CORS proxy + SquadLogic state sync
// Deployment steps:
// 1. Go to https://dash.cloudflare.com (free account, no credit card needed)
// 2. Left sidebar → Workers & Pages → Create → Create Worker
// 3. Replace all the default code with this file's contents
// 4. Click "Deploy"
// 5. Copy the worker URL shown (e.g. https://afl-proxy.yourname.workers.dev)
// 6. Paste that URL into index.html where it says REPLACE_WITH_YOUR_WORKER_URL
//
// Cloud save setup:
// 1. Create a Workers KV namespace
// 2. Bind it to this Worker as SQUADLOGIC_STATE
// 3. Re-deploy the Worker
export default {
async fetch(request, env) {
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, X-SquadLogic-Auth',
};
// CORS preflight
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: corsHeaders,
});
}
const url = new URL(request.url);
if (url.pathname.startsWith('/state/')) {
return handleStateRequest(request, env, corsHeaders, url);
}
if (request.method !== 'POST') {
return json({ error: 'Use POST for PlayHQ proxy requests.' }, 405, corsHeaders);
}
const body = await request.text();
const upstream = await fetch('https://api.playhq.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'tenant': 'afl',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'Origin': 'https://www.playhq.com',
'Referer': 'https://www.playhq.com/',
},
body,
});
const data = await upstream.text();
return new Response(data, {
status: upstream.status,
headers: {
'Content-Type': 'application/json',
...corsHeaders,
},
});
},
};
async function handleStateRequest(request, env, corsHeaders, url) {
if (!env.SQUADLOGIC_STATE) {
return json({ error: 'Cloud save KV binding SQUADLOGIC_STATE is not configured.' }, 501, corsHeaders);
}
const key = decodeURIComponent(url.pathname.slice('/state/'.length)).trim();
if (!/^[a-z0-9._:-]{3,160}$/i.test(key)) {
return json({ error: 'Invalid state key.' }, 400, corsHeaders);
}
if (request.method === 'GET') {
const stored = await env.SQUADLOGIC_STATE.get(key, 'json');
return json({ value: stored || null }, 200, corsHeaders);
}
if (request.method === 'PUT') {
let payload;
try {
payload = await request.json();
} catch {
return json({ error: 'Invalid JSON body.' }, 400, corsHeaders);
}
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
return json({ error: 'State payload must be a JSON object.' }, 400, corsHeaders);
}
await env.SQUADLOGIC_STATE.put(key, JSON.stringify(payload), {
expirationTtl: 60 * 60 * 24 * 21,
});
return json({ ok: true }, 200, corsHeaders);
}
return json({ error: 'Use GET or PUT for state requests.' }, 405, corsHeaders);
}
function json(value, status, headers) {
return new Response(JSON.stringify(value), {
status,
headers: {
'Content-Type': 'application/json',
...headers,
},
});
}