How It's Built
This site is a monorepo built with Astro, deployed on Cloudflare Pages, with analytics powered by PostHog and Supabase. Here's how the pieces fit together.
🏗️ Architecture
Data flows from user action to live dashboard:
CLOUDFLARE
Pages (eeshans.com) Worker (/ingest/*)
↓
POSTHOG
Events → Webhook → Supabase destination
↓
SUPABASE
Edge Function → PostgreSQL → Views
↓
FRONTEND
PostgREST API → React island → Live stats
⚡ Tech Stack
| Layer | Tech | Why |
|---|---|---|
| Framework | Astro 4.x | Static-first, islands for interactivity |
| Styling | Tailwind CSS | Utility-first, dark mode support |
| Islands | React 19 | Interactive components (stats, game) |
| Analytics | PostHog | Events, session replay, feature flags |
| Database | Supabase (Postgres) | Views, PostgREST API, edge functions |
| Hosting | Cloudflare Pages | Fast, free, global CDN |
| Proxy | Cloudflare Worker | PostHog reverse proxy (bypass blockers) |
| Monorepo | pnpm workspaces | Shared components across packages |
📊 Analytics Pipeline
Real-time stats flow from user actions to the dashboard in ~5 seconds:
- User plays game → PostHog SDK captures
ab_test_decisionevent - PostHog webhook → Fires on event, sends JSON to Supabase Edge Function
- Edge Function → Validates, transforms, inserts into
posthog_eventstable - Postgres View →
ab_test_statsaggregates totals, unique users, last 24h - PostgREST → Frontend fetches
/rest/v1/ab_test_stats - StatsCard → React island renders live numbers with loading states
📁 Monorepo Structure
ds-apps-main/
src/
pages/ — Routes (index, projects, contribute, stack, writing)
content/post/ — MDX blog posts
components/ — Site-specific components
packages/
shared/ — ProjectCard, Timeline, Header, projects.yaml
ab-simulator/ — Standalone game app
analytics/
notebooks/ — Jupyter analysis
🔗 Key Files
- 📄 projects.yaml — Project metadata source of truth
- 📁 ab-simulator/public/js/ — Game logic modules
- 📁 Full repository
🚀 Adding a New Project
Bootstrap a new project in 8 steps:
- Add to projects.yaml — id, name, status, description, tags
- Create package —
packages/{project-name}/with Astro config - Add stats config — Supabase view name, display labels
- Create Supabase view — Aggregate events for stats
- Create hub page —
/projects/{id}.astro - Add aiStory — Bullets describing AI collaboration
- Write blog post — With
projectId: {id}frontmatter - Update Timeline — Move from coming-soon → in-progress → live
✍️ Writing Workflow
Blog posts live in src/content/post/ as MDX files:
frontmatter example:
title: "How I Built the A/B Simulator"
publishDate: 2025-11-30
tags: ["data-science", "analytics"]
projectId: "ab-simulator" ← links to hub
featured: true ← shows on home
Posts with projectId appear in the project hub's "Related Content" section via getPostsByProject().
🔮 What's Next
In Progress
Prompt Debugger
Visualize and iterate on LLM prompts
LLM APIsReact
Coming Soon
Data Story Generator
Turn SQL queries into narrative insights
SQLLLMsVisualization
Coming Soon
Marketing Mix Modeling Explorer
Interactive tool for understanding MMM
marketingmedia-mix-modelcausal inference