Skip to content

Refactor environment validation, admin auth, and error handling#1

Open
onwp wants to merge 1 commit intomainfrom
claude/harden-nextjs-production-9zui8
Open

Refactor environment validation, admin auth, and error handling#1
onwp wants to merge 1 commit intomainfrom
claude/harden-nextjs-production-9zui8

Conversation

@onwp
Copy link
Copy Markdown
Owner

@onwp onwp commented Apr 17, 2026

Summary

This PR introduces centralized environment variable validation, refactors admin authentication to use HMAC-signed sessions, improves error handling across the application, and adds comprehensive test coverage for critical utilities.

Key Changes

Environment & Configuration

  • New src/lib/env.ts: Centralized environment validation using Zod that runs at startup, ensuring all required variables are present and properly formatted before the app starts
  • All environment-dependent modules now import from env() instead of directly accessing process.env
  • Added validation for database URL, admin credentials, secrets, R2 storage, Postmark, and Lemon Squeezy configuration

Admin Authentication

  • New src/lib/admin-session.ts: Replaced simple session verification with HMAC-SHA256 signed cookies using Web Crypto API
  • Works in both Node.js and Edge runtimes (middleware-compatible)
  • Removed dependency on better-auth for admin sessions; now uses separate ADMIN_SESSION_SECRET
  • Updated src/app/api/auth/admin-login/route.ts with proper rate limiting and validation

Rate Limiting

  • New src/lib/rate-limit.ts: In-memory sliding-window rate limiter for single-node deployments
  • Applied to admin login (5 attempts per 60s) and user registration (10 per hour)
  • Includes automatic garbage collection to prevent memory leaks

Error Handling & Resilience

  • Pages now use force-dynamic mode with proper error boundaries (src/app/error.tsx, src/app/admin/error.tsx)
  • Database failures no longer silently fall back to sample data; instead, routes render on-demand with real data or 404
  • Removed try-catch fallbacks that masked configuration issues
  • Added fire-and-forget view count tracking with error logging
  • Improved error messages in server actions with proper typing

Database & Schema

  • Migrated users table to user (better-auth standard naming)
  • Added boolean type import to schema
  • Updated Lemon Squeezy webhook handler with Zod validation and proper status mapping
  • Improved query builders with better type safety

UI & Components

  • New src/components/roast/roast-card.tsx: Extracted reusable card component for roast gallery
  • New src/components/admin/action-button.tsx: Simplified action button for non-destructive operations
  • Updated ConfirmButton to use useTransition for better UX
  • Improved form validation and error display across auth pages

Testing

  • Added comprehensive unit tests:
    • tests/unit/env.test.ts: Environment validation
    • tests/unit/admin-session.test.ts: HMAC session creation/verification
    • tests/unit/rate-limit.test.ts: Rate limiter behavior
    • tests/unit/auth-utils.test.ts: UUID and domain validation
    • tests/unit/lemonsqueezy.test.ts: Webhook signature verification
    • tests/unit/scrapers.test.ts: Scraper source validation

Data Fetching

  • Refactored getPublishedPosts to getPublishedPostsWithThumbnails with proper screenshot handling
  • Added getPostScreenshots query for hero image selection
  • Improved parallel data fetching with Promise.all()
  • Better handling of missing/optional data (pros, cons, suggestions now always arrays)

Documentation

  • Updated README with accurate auth description (better-auth + custom admin cookie)
  • Clarified environment variable requirements and validation
  • Added instructions for generating secure secrets

Notable Implementation Details

  • Web Crypto API: Admin session signing uses crypto.subtle for cross-runtime compatibility
  • Zod Schemas: All user input validated with strict schemas (registration, webhook payloads)
  • Type Safety: Improved TypeScript coverage with proper Result types for server actions
  • Graceful Degradation: R2 and Postmark are optional; missing config returns "not-configured" status in health checks
  • Build-time Hints: generateStaticParams returns empty list when DB unavailable,

https://claude.ai/code/session_015tozJhgdCwsq2cibcqQX8h

…schema, proper boundaries

Critical fixes:
- Add zod env validation (src/lib/env.ts); all required vars parsed at boot
- Fix admin session: sign with dedicated ADMIN_SESSION_SECRET (min 32 chars)
  instead of falling back to ADMIN_PASSWORD
- Rewrite admin-session helpers on Web Crypto so proxy.ts (edge) works
- Add real better-auth schema tables (user/session/account/verification)
  and wire drizzleAdapter to them; consolidate Lemon Squeezy fields onto user
- Remove all mock/sample data (SAMPLE_ROASTS, SAMPLE_ROAST, placeholder
  screenshot divs, stale TODO); pages now show real empty/error states
- Wire up actual screenshot rendering on home, category, and roast pages
  via next/image with thumbnails joined in a single extra query
- Fix seed source whitelist: scraper sourceName was "seed-saaslandingpage"
  but allowlist expected "seed-saaslandingpage.com"; add regression test
- Swap Google Fonts for local `geist` package (already in deps)

Quality:
- Add error.tsx + loading.tsx at root and /admin
- Rewrite webhook with zod, tolerant of string/number id/customer_id
- Admin buttons now surface server action errors inline (ActionButton /
  ConfirmButton using useTransition)
- Health page does real reachability probes (DB SELECT 1, R2 HeadBucket,
  Postmark /server, Lemon Squeezy /users/me) with 3s timeouts
- Shared in-memory rate limiter (createRateLimiter); applied to
  admin-login + /api/auth/register
- Pricing CTA hooked up to LEMONSQUEEZY_CHECKOUT_URL env var; disabled
  button when not configured
- Remove unused CategoryPill, drop unused imports; eliminate `any` in
  postmark.ts; tighten types across actions

Tests:
- admin-session (round-trip, tamper, expiry, wrong secret)
- rate-limit (window, eviction, IP extraction)
- env validation (required, URL format, optional fields)
- auth utils (UUID, escapeHtml, domain allow/deny list)
- lemonsqueezy signature verify
- scrapers source whitelist regression

Docs:
- Updated README with architecture notes, script reference, env table
- Updated .env.example with ADMIN_SESSION_SECRET and LEMONSQUEEZY_CHECKOUT_URL

https://claude.ai/code/session_015tozJhgdCwsq2cibcqQX8h
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants