A Next.js application for sharing dining hall points at UCSC, built with NextAuth.js, Prisma, and shadcn/ui.
Before you begin, ensure you have the following installed:
- Node.js (v20 or higher)
- npm or yarn or pnpm
- PostgreSQL database (or access to a PostgreSQL database)
git clone <repository-url>npm installCreate a .env file in the webserver directory with the following variables:
Required Environment Variables:
# Database Connection
DATABASE_URL="postgresql://user:password@localhost:5432/database_name?sslmode=require"
# NextAuth.js Secret
# Generate a secret with: openssl rand -base64 32
AUTH_SECRET="your-secret-key-here"
# Google OAuth (for Google sign-in)
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"Optional Environment Variables:
# Prisma Accelerate (if using)
PRISMA_DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/?api_key=your_api_key"
# Alternative PostgreSQL URL (if needed)
POSTGRES_URL="postgresql://user:password@localhost:5432/database_name?sslmode=require"If you need to generate a new AUTH_SECRET, run:
openssl rand -base64 32Copy the output and add it to your .env file.
If you have a database connection string, add it to your .env file as DATABASE_URL.
After setting up your database, run Prisma migrations:
npx prisma migrate devThis will:
- Create the database schema (User, Account, Session, VerificationToken, Points, Request tables)
- Generate the Prisma Client
Note: After schema changes, always run npx prisma generate to update TypeScript types.
npm run devOpen http://localhost:3000 in your browser.
webserver/
├── app/
│ ├── auth/
│ │ ├── login/ # Login page
│ │ └── signup/ # Sign up page
│ ├── dashboard/ # Protected dashboard page (shows points balance)
│ ├── requests/
│ │ ├── page.tsx # Requests list page
│ │ └── create/ # Create request page
│ ├── actions/ # Server actions
│ └── api/
│ ├── auth/ # NextAuth.js routes
│ ├── points/ # Points API (GET, POST)
│ ├── requests/ # Requests API (GET, POST)
│ │ └── [id]/
│ │ ├── accept/ # Accept request route
│ │ └── decline/# Decline request route
│ └── user/ # Current user API
├── components/
│ ├── ui/ # shadcn/ui components
│ ├── providers.tsx # Session provider
│ └── UpdatePointsForm.tsx # Points update form
├── lib/
│ ├── auth.ts # Auth utilities
│ ├── prisma.ts # Prisma client
│ ├── utils.ts # Utility functions
│ └── locations.ts # UCSC dining locations
├── prisma/
│ └── schema.prisma # Database schema
└── .env # Environment variables (create this)
- Framework: Next.js 16.1.1 (App Router)
- Authentication: NextAuth.js v5 (beta)
- Database: PostgreSQL with Prisma ORM
- UI Components: shadcn/ui
- Styling: Tailwind CSS v4
- TypeScript: v5
sequenceDiagram
participant U as User
participant FE as Frontend (GET Connect UI)
participant API as /api/get/connect
participant Parse as extractValidatedSessionId
participant GA as GET Adapter
participant DB as Prisma GetCredential
U->>FE: Paste validated GET URL/input
FE->>API: POST validatedInput
API->>Parse: Extract validated GET sessionId
Parse-->>API: validatedSessionId
API->>GA: generateDeviceId() + generatePin()
API->>GA: createPin(validatedSessionId, deviceId, pin)
API->>GA: authenticatePin(pin, deviceId)
GA-->>API: apiSessionId
API->>GA: verifyPin(apiSessionId, deviceId, pin)
API->>GA: retrieveAccounts(apiSessionId)
API->>DB: upsert deviceId + encryptedPin + linked metadata
API-->>FE: linked=true, status=linked
sequenceDiagram
participant FE as Frontend (Scan / Pull QR)
participant API as /api/get/pull-qr or /api/get/barcode
participant Server as getActiveGetSessionForUser
participant DB as Prisma GetCredential
participant Crypto as decryptSecret
participant GA as GET Adapter
participant GET as External GET API
FE->>API: Request QR payload
API->>DB: Load linked credential (deviceId, encryptedPin)
DB-->>API: linked credential
API->>Server: getActiveGetSessionForUser(userId)
Server->>Crypto: decrypt encryptedPin -> pin
Server->>GA: authenticatePin(pin, deviceId)
GA->>GET: authenticateWithPin(...)
GET-->>GA: sessionId (short-lived token)
Server->>GA: verifyPin(sessionId, deviceId, pin)
Server->>DB: Update lastValidatedAt/status
API->>GA: retrieveBarcodePayload(sessionId, ...)
GA->>GET: barcode method with sessionId
GET-->>GA: barcode payload + expiry
API-->>FE: payload for PDF417 rendering
Note over API,DB: deviceId + encryptedPin are persisted
Note over API,FE: session token is obtained server-side per request and never exposed as stored client secret
npm run dev- Start development servernpm run build- Build for productionnpm run start- Start production servernpm run lint- Run ESLintnpx prisma migrate dev- Run database migrationsnpx prisma generate- Generate Prisma Client (run after schema changes)npx prisma studio- Open Prisma Studio (database GUI)
- Credentials Provider - Email/password authentication
- Google OAuth Provider - Sign in with Google
- Prisma Adapter - Stores sessions and accounts in the database
- JWT Sessions - Token-based session management
- View current points balance on dashboard
- Update points balance manually
- Automatic points record creation on first access
- Create requests for dining points (location, amount, optional message)
- View all requests from other users
- View your own requests with status tracking
- Accept or decline requests from other users
- Atomic point transfers when accepting requests
- Validation: sufficient balance, pending status, prevent self-acceptance
- Automatic balance updates for both donor and requester
/- Home page (redirects to login)/auth/login- Login page/auth/signup- Sign up page/dashboard- Protected dashboard (shows points balance)/requests- All requests page (My Requests + Other Requests)/requests/create- Create new request page
/api/auth/*- NextAuth.js authentication routes/api/points- GET (fetch balance), POST (update balance)/api/requests- GET (all requests), POST (create request)/api/requests/[id]/accept- POST (accept request and transfer points)/api/requests/[id]/decline- POST (decline request)/api/user- GET (current user info)
The Prisma schema includes:
- User - User accounts
- Account - OAuth account connections
- Session - User sessions
- VerificationToken - Email verification tokens
- Points - User point balances (one row per user)
- Request - Point sharing requests (requester, donor, location, status)
If you need help setting up the project, please contact the project maintainer with:
- Your Node.js version (
node --version) - Your database setup (local PostgreSQL, cloud provider, etc.)
- Any error messages you're encountering
To enable Google sign-in:
- Go to Google Cloud Console
- Create a new project or select existing
- Enable Google+ API (APIs & Services > Library)
- Create OAuth credentials (APIs & Services > Credentials)
- Application type: Web application
- Authorized redirect URI:
http://localhost:3000/api/auth/callback/google
- Copy Client ID and Client Secret to
.envfile
- The
.envfile is gitignored - never commit it to version control - Make sure your database is running before starting the development server
- The
AUTH_SECRETshould be a long, random string (at least 32 characters) - For production, use a secure database connection with SSL enabled
- After modifying
prisma/schema.prisma, runnpx prisma generateto update TypeScript types - Run
npx prisma migrate devafter schema changes to apply migrations