diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..538af3f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +.wrangler/ +.dev.vars +*.log diff --git a/worker.js b/worker.js new file mode 100644 index 0000000..49dd0f5 --- /dev/null +++ b/worker.js @@ -0,0 +1,298 @@ +// MAKE A MILLY OR IT'S EMBARRASSING +// Horse race: King Omar vs Basic Rhys vs Pussy vs Patty +// Last to $1M is confirmed gay + +const GOAL = 1_000_000; +const STRIPE_KEY_SECRET = null; // Omar's revenue comes from KV metrics + +export default { + async fetch(request, env) { + const url = new URL(request.url); + + // API: update a competitor's revenue (each person calls this with their own secret) + if (url.pathname === '/update' && request.method === 'POST') { + const auth = request.headers.get('Authorization'); + const { competitor, revenue } = await request.json(); + const validKeys = { + rhys: env.RHYS_SECRET, + pussy: env.PUSSY_SECRET, + patty: env.PATTY_SECRET, + omar: env.OMAR_SECRET, + }; + if (!validKeys[competitor] || auth !== `Bearer ${validKeys[competitor]}`) { + return new Response('Unauthorized', { status: 401 }); + } + const comps = JSON.parse(await env.MILLY_METRICS.get('competitors') || '{}'); + if (comps[competitor]) comps[competitor].revenue = parseFloat(revenue) || 0; + await env.MILLY_METRICS.put('competitors', JSON.stringify(comps)); + return new Response('OK'); + } + + // Pull Omar's live revenue from metrics KV (updated by weekly agent) + const [compsRaw, metricsRaw, baselineRaw] = await Promise.all([ + env.MILLY_METRICS.get('competitors'), + env.MILLY_METRICS.get('metrics'), + env.MILLY_METRICS.get('baseline'), + ]); + + const competitors = compsRaw ? JSON.parse(compsRaw) : {}; + const metrics = metricsRaw ? JSON.parse(metricsRaw) : {}; + const baseline = baselineRaw ? JSON.parse(baselineRaw).amount : 0; + + // Omar's revenue comes from the live Stripe metrics + if (competitors.omar) { + competitors.omar.revenue = metrics.total_revenue || 0; + } + + return new Response(render(competitors), { + headers: { 'Content-Type': 'text/html;charset=UTF-8', 'Cache-Control': 'no-store' } + }); + } +}; + +function pct(revenue) { + return Math.min((revenue / GOAL) * 100, 100); +} + +function fmt(n) { + if (n >= 1e6) return `$${(n/1e6).toFixed(3)}M`; + if (n >= 1000) return `$${(n/1000).toFixed(1)}K`; + return `$${n.toFixed(0)}`; +} + +function render(competitors) { + const sorted = Object.entries(competitors).sort((a, b) => b[1].revenue - a[1].revenue); + const leader = sorted[0]; + const loser = sorted[sorted.length - 1]; + + // Build horse positions + const horses = Object.entries(competitors).map(([id, c]) => ({ + id, ...c, + pct: pct(c.revenue), + done: c.revenue >= GOAL + })); + + return ` + +
+ + +