Web application for modeling professional salary progression, with authentication, onboarding, employment history management, and versioned APIs.
Implemented today:
- Authentication with Better Auth (email/password, GitHub, and Google).
- Session management in frontend and backend (cookie and JWT bearer for API).
- Step-up verification for
/comparisonusing email OTP (Resend). - Initial onboarding (
/onboarding). - Companies and salary events module (
/companies). - User profile (
/profile). - Admin users panel (
/admin/users). - REST API at
/api/v1/*with typed contracts. - Turso/libSQL database + Drizzle with migrations in
/drizzle.
Pending or visual placeholder:
/explore/personal-path/comparison(feature content remains placeholder, access is OTP-protected)/settings/admin/users/[userId]
- Next.js 16 (App Router)
- React 19 + TypeScript
- Better Auth
- Drizzle ORM + drizzle-kit
- Turso/libSQL
- TanStack Query v4
- Tailwind CSS + internal UI components
- Sonner (notifications)
- Vitest
- Node.js 20+
- pnpm
- Accessible Turso database
- Install dependencies:
pnpm install- Create environment variables:
cp .env.example .env-
Complete
.envwith your actual credentials. -
Run migrations:
pnpm db:migrate- Start the app:
pnpm devThe app runs at http://localhost:3001.
pnpm dev: local environment on port3001.pnpm build: runs migrations (prebuild) and compiles for production.pnpm start: starts server in production mode.pnpm typecheck: TypeScript validation.pnpm lint: project linting.pnpm test: unit tests with Vitest.pnpm db:generate: generates migrations from schema.pnpm db:migrate: runs DB preflight and then applies pending migrations.pnpm db:preflight-empty-repair: if DB is empty but__drizzle_migrationsstill has rows, clears the ledger so migrations can be replayed.
Defined in .env.example:
BETTER_AUTH_SECRETBETTER_AUTH_URLBETTER_AUTH_TRUSTED_ORIGINSBETTER_AUTH_JWT_ISSUERBETTER_AUTH_JWT_AUDIENCEBETTER_AUTH_JWT_EXPIRATIONNEXT_PUBLIC_BETTER_AUTH_URLGOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETGITHUB_CLIENT_IDGITHUB_CLIENT_SECRETRESEND_API_KEYRESEND_FROM_EMAILRESEND_REPLY_TO(optional)ADMIN_EMAILSTURSO_DATABASE_URLTURSO_AUTH_TOKEN
Public:
/sign-in/explore
Protected:
/onboarding/personal-path/companies/comparison/profile/settings
Admin:
/admin/users/admin/users/[userId]
- Base URL:
/api/v1 - Response envelope:
- Success:
{ success: true, data: ... } - Error:
{ success: false, error: { status, code, message, details? } }
- Success:
- Auth API:
- Better Auth session cookie.
- Or
Authorization: Bearer <jwt>for external consumers.
- Route access step-up API:
GET /route-access/status?routeKey=comparisonPOST /route-access/email-otp/sendPOST /route-access/email-otp/verify
Endpoint details: see /docs/api-v1.md.
- Drizzle configuration:
drizzle.config.ts - Schema:
app/lib/db/schema/* - SQL migrations:
/drizzle pnpm buildalready runspnpm db:migrateautomatically before compiling.- Vercel build command is pinned in
vercel.jsontopnpm build.
- General index:
/docs/README.md - Previous README audit:
/docs/readme-audit.md - Original README copy (template):
/docs/readme-original-next-template.md - Project status review:
/docs/project-review.md