diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..bae1ba0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +node_modules +.next +.git +.gitignore +.env +.env.* +pgdata +*.md +docker-compose.yml +.prettierrc.js +eslint.config.js diff --git a/CLAUDE.md b/CLAUDE.md index 67959f7..1b01600 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,14 +2,6 @@ This project builds a real-time interactive dashboard showing how AI datacenter buildout is driving regional electricity demand and energy prices across the US. Read `SPEC.md` in full before starting any work — it contains the business case, technical architecture, database schema, and implementation phases. Understanding *why* this dashboard exists (not just *what* it does) will make your code better. -## Rules (NON-NEGOTIABLE) - -- You will NOT create a github repository, push code, or deploy ANYTHING. -- You will NOT publish the code anywhere. -- You will NOT make this code public in any way. - -This is the local development portion only. No deployment. No public repos. No exceptions. - ## Read the Spec Before writing a single line of code, read `SPEC.md` cover to cover. Every agent on this project — orchestrator, builder, reviewer, tester — should understand: diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9dd34f0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM oven/bun:1 AS deps +WORKDIR /app +COPY package.json bun.lock ./ +RUN bun install --frozen-lockfile + +FROM node:22 AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +# Prisma client + TypedSQL must be pre-generated locally (needs live DB for --sql). +# src/generated/ is gitignored but included in Docker context from local dev. +ENV DATABASE_URL="postgresql://placeholder:placeholder@localhost:5432/placeholder" +RUN npx next build + +FROM node:22-slim AS runner +WORKDIR /app +ENV NODE_ENV=production +ENV HOSTNAME=0.0.0.0 +ENV PORT=3000 + +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static + +EXPOSE 3000 +CMD ["node", "server.js"] diff --git a/README.md b/README.md index af9c783..002f12d 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,214 @@ -# bonus4 +# Energy & AI: The Datacenter Power Crunch -To install dependencies: +A real-time interactive dashboard that visualizes how AI datacenter buildout is driving regional electricity demand and energy prices across the United States. + +**Live URL**: [https://energy.busi488.claiborne.soy](https://energy.busi488.claiborne.soy) + +## Purpose & Functionality + +AI datacenters are reshaping US energy markets. Training a single frontier model can consume as much electricity as a small town uses in a year, and every major tech company is building GPU clusters at unprecedented scale. This dashboard makes that impact visible. + +The dashboard pulls real-time electricity pricing, demand, generation mix, and commodity data from the EIA and FRED APIs, overlays it on a map of US grid regions with 100+ datacenter locations, and lets users explore the relationship between AI infrastructure buildout and energy market dynamics. + +**Key features:** + +- **Interactive map** with datacenter locations, grid region overlays, and power plant positions (Google Maps with AdvancedMarker) +- **Real-time price ticker** showing current electricity prices across all major ISO/RTO regions +- **Price, demand, and generation charts** with hourly/daily/weekly granularity and time range selection +- **Datacenter impact analysis** showing price correlation in regions with heavy AI buildout vs. those without +- **GPU cost calculator** that translates electricity prices into per-GPU-hour operating costs +- **Grid stress gauges** showing how close regions are to capacity limits +- **Automated data ingestion** that refreshes every 30 minutes from government APIs + +## Target Audience + +**Energy investors** evaluating utility stocks and commodity positions who need to see where datacenter load is concentrating and how it's affecting regional prices. Today they stitch together spreadsheets from a dozen sources; this dashboard gives them a single view. + +**Utility analysts** planning generation and transmission investments who need to understand where demand growth is coming from and which regions are approaching capacity constraints. + +**Datacenter site selectors** choosing where to build next who need real-time visibility into regional electricity prices, grid capacity, and existing datacenter density. + +**Business strategists** assessing the infrastructure costs underlying AI who need to understand the energy economics behind GPU compute. + +These users currently rely on static reports, scattered government data portals, and expensive terminal subscriptions. This dashboard provides a free, unified, real-time view of the AI-energy nexus. + +## Sales Pitch & Monetization + +This dashboard generates value for anyone making infrastructure or investment decisions at the intersection of AI and energy. The data it unifies is publicly available but scattered across dozens of sources with inconsistent formats and no geospatial context. The value is in the integration. + +**Monetization model:** Freemium SaaS. + +- **Free tier**: Full dashboard with real-time data, map, charts, and the GPU cost calculator +- **Premium tier** ($49/mo): Predictive analytics (price forecasting), custom region comparisons, CSV/API data export, email alerting for price spike events +- **Enterprise tier** (custom): Embeddable widgets for trading desks and analyst reports, white-label options, dedicated support + +The addressable market includes energy trading desks, utility planning departments, datacenter REITs, and infrastructure-focused hedge funds, all of whom currently spend significant analyst time on exactly this kind of cross-referencing. + +## Tech Stack + +| Layer | Technology | Why | +| ------------- | ----------------------------------- | ----------------------------------------------------------------------------- | +| Framework | Next.js 16 (App Router, Turbopack) | Server Components, `"use cache"` directive, Partial Prerendering | +| Styling | Tailwind CSS 4 + shadcn/ui | Dark theme, consistent component library with built-in chart wrappers | +| Maps | @vis.gl/react-google-maps | Google's official React library with AdvancedMarker support | +| Database | PostgreSQL 18 + PostGIS 3.6 | Geospatial queries (ST_DWithin, spatial joins) for datacenter/region analysis | +| ORM | Prisma 7 with TypedSQL | Type-safe CRUD + typed wrappers for raw PostGIS SQL queries | +| Charts | Recharts via shadcn/ui | Consistent theming, responsive, works with Server Components | +| Serialization | superjson | Preserves Date, BigInt, Map, Set across server-client boundary | +| Runtime | Bun (dev) / Node.js 22 (production) | Bun for fast local dev; Node.js for stable production runtime | +| Validation | Zod | Every external API response is schema-validated before ingestion | +| Container | Docker (standalone Next.js) | Multi-stage build, ~440MB production image | + +## Data Sources + +| Source | Data | Update Frequency | +| ---------------------------------------- | -------------------------------------------------------------------------- | ------------------------ | +| [EIA API](https://www.eia.gov/opendata/) | Regional electricity prices, demand, generation mix | Hourly | +| [FRED API](https://fred.stlouisfed.org/) | Natural gas (Henry Hub), WTI crude, coal spot prices | Daily | +| Curated seed data | 100+ datacenter locations, grid region boundaries (GeoJSON), AI milestones | Static (seeded at setup) | + +Data ingestion runs automatically every 30 minutes via the Next.js instrumentation hook. + +## Installation & Setup + +### Prerequisites + +- [Bun](https://bun.sh/) (latest) +- [Docker](https://docs.docker.com/get-docker/) and Docker Compose +- API keys for: [EIA](https://www.eia.gov/opendata/register.php), [FRED](https://fred.stlouisfed.org/docs/api/api_key.html), [Google Maps](https://console.cloud.google.com/) + +### 1. Clone and install dependencies ```bash +git clone https://git.claiborne.soy/joey/busi488-energy-dashboard.git +cd busi488-energy-dashboard bun install ``` -To run: +### 2. Set up environment variables -```bash -bun run +Create a `.env` file in the project root: + +```env +# Google Maps +NEXT_PUBLIC_GOOGLE_MAPS_API_KEY="your-google-maps-api-key" +NEXT_PUBLIC_GOOGLE_MAP_ID="your-map-id" +GOOGLE_MAPS_STYLE_NAME="your-style-name" +GOOGLE_MAPS_STYLE_ID="your-style-id" + +# Data APIs +EIA_API_KEY="your-eia-api-key" +FRED_API_KEY="your-fred-api-key" + +# Database +DATABASE_URL="postgresql://energy:energydash2026@localhost:5433/energy_dashboard" +POSTGRES_PASSWORD="energydash2026" + +# Ingestion auth +INGEST_SECRET="any-random-hex-string" ``` -This project was created using `bun init` in bun v1.3.8. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. +### 3. Start the database + +```bash +docker compose up -d +``` + +This starts PostgreSQL 18 with PostGIS 3.6 on port 5433. + +### 4. Run migrations and seed data + +```bash +bunx prisma migrate deploy +bunx prisma db seed +``` + +This creates the schema (grid regions, datacenters, price tables) and loads seed data (datacenter locations, grid region boundaries, AI milestones). + +### 5. Generate Prisma client and TypedSQL + +```bash +bunx prisma generate --sql +``` + +This generates typed TypeScript wrappers for both the Prisma ORM client and all PostGIS SQL queries in `prisma/sql/`. + +### 6. Start the development server + +```bash +bun run dev +``` + +The dashboard is now running at [http://localhost:3000](http://localhost:3000). Data ingestion starts automatically after 10 seconds. + +## Docker Deployment + +The app is containerized as a standalone Next.js image for production deployment. + +### Build and push + +```bash +# Prisma TypedSQL must be generated locally first (requires live database) +bunx prisma generate --sql + +# Build the production image +docker build -t registry.claiborne.soy/busi488energy:latest . + +# Push to the internal registry +docker push registry.claiborne.soy/busi488energy:latest +``` + +### Database migration to production + +```bash +# Dump the local database (schema + data) +docker compose exec db pg_dump -U energy -Fc energy_dashboard > energy_dashboard.dump + +# Port-forward to the production PostGIS instance +kubectl port-forward -n database svc/postgis 5434:5432 & + +# Restore to production +pg_restore -h localhost -p 5434 -U busi488energy -d busi488energy \ + --no-owner --no-privileges energy_dashboard.dump +``` + +### Infrastructure + +The production deployment runs on Kubernetes (k3s) with Terraform-managed infrastructure: + +- **PostGIS 18** in the `database` namespace (`postgis.database.svc.cluster.local:5432`) +- **Next.js standalone** in the `random` namespace, served behind Traefik ingress with automatic TLS via cert-manager +- **Keel** watches the internal registry and auto-deploys new image pushes + +## Project Structure + +``` +bonus4/ +├── prisma/ +│ ├── schema.prisma # Database schema (PostGIS models) +│ ├── migrations/ # SQL migration files +│ ├── seed.ts # Seed data loader +│ └── sql/ # TypedSQL queries (PostGIS spatial joins, aggregations) +├── src/ +│ ├── app/ # Next.js App Router pages +│ │ ├── page.tsx # Dashboard home (ticker, metrics, charts) +│ │ ├── map/ # Interactive map with datacenters + grid regions +│ │ ├── trends/ # Price correlation and DC impact analysis +│ │ ├── demand/ # Regional demand charts +│ │ ├── generation/ # Generation mix breakdown +│ │ └── api/ingest/ # Data ingestion endpoints (EIA, FRED) +│ ├── actions/ # Server Actions (typed server-to-client boundary) +│ ├── components/ +│ │ ├── charts/ # Price, demand, generation, correlation charts +│ │ ├── map/ # Google Maps with datacenter markers + controls +│ │ ├── dashboard/ # Ticker tape, GPU calculator, grid stress gauges +│ │ └── layout/ # Navigation, footer, data freshness indicator +│ └── lib/ +│ ├── api/ # EIA and FRED API clients with Zod schemas +│ ├── schemas/ # Zod validation schemas for external data +│ ├── db.ts # Prisma client singleton +│ └── superjson.ts # Serialization utilities +├── docker-compose.yml # Local PostGIS database +├── Dockerfile # Multi-stage production build +└── next.config.ts # Next.js config (standalone output, cache policies) +``` diff --git a/next.config.ts b/next.config.ts index 1782ddc..f1544cb 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,7 @@ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { + output: 'standalone', typedRoutes: true, cacheComponents: true, cacheLife: {