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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
const CORS_HEADERS = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Content-Encoding', 'Access-Control-Max-Age': '86400', };
const json = (obj, status = 200) => new Response(JSON.stringify(obj), { status, headers: { 'content-type': 'application/json; charset=utf-8', ...CORS_HEADERS }, });
function hexDump(buf, n = 32) { const arr = new Uint8Array(buf).slice(0, n); return Array.from(arr).map(b => b.toString(16).padStart(2, '0')).join(' '); }
async function tryDecompress(buf, format) { try { const ds = new DecompressionStream(format); const stream = new Blob([buf]).stream().pipeThrough(ds); const decompressed = await new Response(stream).arrayBuffer(); return new TextDecoder('utf-8', { fatal: true }).decode(decompressed); } catch (e) { return null; } }
export default { async fetch(request, env) { const url = new URL(request.url); const path = url.pathname.replace(/\/+$/, '') || '/';
if (request.method === 'OPTIONS') { return new Response(null, { headers: CORS_HEADERS }); }
if (path === '/update' && request.method === 'POST') { console.log('=== /update ==='); console.log('Content-Type:', request.headers.get('content-type')); console.log('Content-Encoding:', request.headers.get('content-encoding'));
const buf = await request.arrayBuffer(); console.log('Body length:', buf.byteLength); console.log('First 32 bytes (hex):', hexDump(buf, 32));
let text = null; try { text = new TextDecoder('utf-8', { fatal: true }).decode(buf); console.log('Plain UTF-8 OK. First 200 chars:', text.slice(0, 200)); } catch { console.log('Not valid UTF-8, trying decompression...'); }
if (text === null) { for (const fmt of ['gzip', 'deflate', 'deflate-raw']) { const r = await tryDecompress(buf, fmt); if (r !== null) { console.log(`Decompressed with ${fmt}. First 200 chars:`, r.slice(0, 200)); text = r; break; } } }
if (text === null) { console.log('Could not decode body in any known format.'); return json({ action: 'error', msg: 'cannot decode body' }, 400); }
let body = null; try { body = JSON.parse(text); } catch (e) { try { body = Object.fromEntries(new URLSearchParams(text)); if (!body.uuid && !body.encrypted) body = null; } catch { body = null; } } if (!body) { console.log('Could not parse as JSON or urlencoded.'); return json({ action: 'error', msg: 'cannot parse body' }, 400); } console.log('Parsed keys:', Object.keys(body));
const uuid = body.uuid; const encrypted = body.encrypted || body.data; if (!uuid || !encrypted) { return json({ action: 'error', msg: 'uuid or encrypted missing' }, 400); }
if (env.ALLOWED_UUIDS) { const allow = env.ALLOWED_UUIDS.split(',').map((s) => s.trim()).filter(Boolean); if (!allow.includes(uuid)) { return json({ action: 'error', msg: 'uuid not allowed' }, 403); } }
if (typeof encrypted !== 'string' || encrypted.length > 5_000_000) { return json({ action: 'error', msg: 'payload too large' }, 413); }
await env.COOKIE_KV.put(uuid, encrypted); console.log('Stored OK. uuid:', uuid, 'encrypted len:', encrypted.length); return json({ action: 'done' }); }
const m = path.match(/^\/get\/([^/]+)$/); if (m && (request.method === 'GET' || request.method === 'POST')) { const uuid = m[1]; const encrypted = await env.COOKIE_KV.get(uuid); console.log('/get/' + uuid, '→', encrypted ? `len=${encrypted.length}` : 'empty'); if (!encrypted) return json({}, 404); return json({ encrypted }); }
if (path === '/') { return new Response( 'CookieCloud-compatible Worker is running.\n', { headers: { 'content-type': 'text/plain; charset=utf-8' } } ); }
return json({ action: 'error', msg: 'not found' }, 404); }, };
|