This file provides guidance to AI agents when working with code in this repository.
This is a Next.js application called "Half-Baked" that displays a personal book collection on a visual bookshelf. It uses Prisma with PostgreSQL for data management and implements a static site generation approach with revalidation.
npm run dev # Start development server
npm run build # Build for production
npm run start # Start production servernpx prisma migrate dev # Run migrations in development
npx prisma migrate deploy # Run migrations in production
npx prisma generate # Generate Prisma Client
npx prisma studio # Open Prisma Studio GUIThe application uses Prisma ORM with PostgreSQL. The schema defines two main models:
- Book: Stores book information (title, coverUrl, finishedOn date)
- Author: Stores author information (name)
- Relationship: Many-to-many between Book and Author
Important: The Prisma client (lib/prisma.ts) uses a conditional initialization pattern to only instantiate on the backend (typeof window === "undefined"). In development, it uses global caching to prevent multiple instances during hot reloading. Never import or use Prisma client in client-side code.
-
pages/index.tsx: Main page with book display and date filtering
- Uses
getStaticPropswith ISR (revalidate: 10 seconds) - Implements responsive shelf layout that adapts to screen width
- Date picker for filtering books by completion date
- Default date range is current year (Jan 1 - Dec 31)
- Uses
-
pages/p/[id]/index.tsx: Admin dashboard (protected)
- Full CRUD operations for books
- Uses dynamic route with server-side validation
- Only accessible at
/p/{ADMIN_PATH}where path matches env var
-
pages/p/[id]/login.tsx: Admin login page
- Password-based authentication
- Sets HTTP-only cookie on successful login
- components/Book: Renders individual book with cover image using Next.js Image component
- components/Shelf: 3D shelf visualization using SCSS modules
- Exports
shelfSideSpace(48px) andinterBookSpace(48px) constants - These constants are critical for shelf layout calculations in index.tsx
- Exports
Uses a combination of:
- Tailwind CSS for utility classes
- SCSS modules for complex 3D effects (shelf rendering)
- CSS variables for theming (defined in global styles)
TypeScript path aliases are configured in tsconfig.json:
@/lib/*→./lib/*@/components/*→./components/*@/models/*→./models/*@/styles/*→./styles/*
Always use these aliases for imports instead of relative paths.
Next.js Image component is configured to allow images from:
halfbaked-media.s3.amazonaws.com
Add new domains to next.config.mjs remotePatterns if needed.
Required in .env:
DATABASE_URL: PostgreSQL connection string for PrismaADMIN_PATH: Secret URL path segment for admin access (required, no default)ADMIN_PASSWORD: Password for admin authentication (required)AUTH_SECRET: HMAC-SHA256 signing secret for authentication tokens (required, min 32 chars recommended; generate withopenssl rand -hex 32)
The renderShelves function in pages/index.tsx implements dynamic shelf layout:
- Calculates available shelf width using resize observer
- Determines books per shelf based on book width (96px) + inter-book spacing
- Splits books array into shelves
- Renders each shelf with books
This algorithm depends on constants exported from components/Shelf/index.tsx.
The date picker implementation:
- Keeps picker open if only one date is selected (allows range selection)
- Closes after both dates selected or picker cleared
- Shows all books when date range is not fully set
The admin interface uses a defense-in-depth security approach with multiple layers:
-
Secret URL Path: The admin is only accessible at
/p/{ADMIN_PATH}whereADMIN_PATHis an environment variable. There are no hardcoded paths like/adminin the codebase. -
Middleware Enforcement (
middleware.ts): Intercepts all/p/*and/api/p/*requests, validates the path segment againstADMIN_PATH, and checks authentication tokens before allowing access. -
Server-Side Validation (
getServerSideProps): Pages atpages/p/[id]/re-validate the path parameter, providing defense-in-depth even if middleware is bypassed. -
API Route Validation: API endpoints at
pages/api/p/[id]/and protected routes inpages/api/books/validate authentication before processing write operations.
- HMAC-SHA256 Signed Tokens: Auth tokens are signed using
AUTH_SECRET(seelib/auth.ts). Token format:{timestamp}.{signature}. - 24-Hour Expiry: Tokens expire after 24 hours (
COOKIE_MAX_AGE). Future timestamps are also rejected. - HTTP-only Cookies: Tokens are stored in HTTP-only cookies with
SameSite=Strictto prevent XSS and CSRF attacks.
All password and token validations use crypto.timingSafeEqual to prevent timing attacks:
validatePassword()inlib/auth.tsfor loginvalidateAuthToken()inlib/auth.tsfor session validationtimingSafeCompare()inmiddleware.tsfor Edge runtime
middleware.ts- Route protection and token validation (Edge runtime)lib/auth.ts- Token generation, validation, and password checkingpages/p/[id]/index.tsx- Admin dashboard withgetServerSidePropsauth checkpages/p/[id]/login.tsx- Login pagepages/api/p/[id]/auth.ts- Login API endpointpages/api/p/[id]/logout.ts- Logout API endpoint
Why this approach: The source code is public, so we can't hardcode secret paths. By using environment variables and dynamic routes with server-side validation:
- Scanning the codebase reveals nothing about the admin URL
- Brute-forcing
/p/{random}returns 404 for incorrect guesses - API enumeration doesn't reveal admin-related endpoints
- Even finding the path requires knowing the password
Important: ADMIN_PATH, ADMIN_PASSWORD, and AUTH_SECRET have no fallback values. The app will error if they're not set, ensuring secure defaults.