Public marketing site + a role-gated CRM for Khalti Suraksha, built with Next.js (App Router), Prisma/Postgres, and a custom JWT-cookie auth (no third-party auth provider).
Design/content reference for this build lives in design-reference/ and
PROTOTYPE-README.md (the original static prototype — not part of the app).
Three fixed roles, one per user account:
- Admin — full access: website content, SEO, queries, and team/user management.
- SEO Editor — SEO & Open Graph metadata only.
- Query Viewer — customer form submissions only (view/filter/update status/delete).
Only one Admin account is seeded (see below). The Admin creates SEO Editor / Query Viewer accounts from CRM → Users.
Requires Node.js 20+ and PostgreSQL. On macOS:
brew install node postgresql@16
brew services start postgresql@16
createdb khaltisurakshaThen:
npm install
cp .env.example .env # fill in DATABASE_URL, SESSION_SECRET, ADMIN_EMAIL, ADMIN_PASSWORD
npx prisma migrate dev
npx prisma db seed
npm run devOpen http://localhost:3000 for the public site, and http://localhost:3000/login to sign in as the seeded Admin.
- Create a Postgres database on Neon or Supabase and copy its connection string.
- Push this repo to GitHub and import it into Vercel.
- Set the environment variables from
.env.examplein the Vercel project settings (DATABASE_URL= your Neon/Supabase string,SESSION_SECRET= output ofopenssl rand -base64 32,ADMIN_EMAIL/ADMIN_PASSWORD/ADMIN_NAME). - Run migrations and seed against the production database once, from your machine:
DATABASE_URL="<production connection string>" npx prisma migrate deploy DATABASE_URL="<production connection string>" npx prisma db seed
- Deploy. Log in at
/loginwith the seeded Admin credentials and change the password by resetting it from another Admin account, or re-seed with a newADMIN_PASSWORD.
app/page.tsx— public site (Server Component, reads content/SEO from the DB, realgenerateMetadata).app/login,app/actions/auth.ts,lib/session.ts,lib/dal.ts— auth (JWT session cookie viajose, bcrypt password hashes,requireRole()DAL helper).proxy.ts— role-based route protection for/crm/**(Next.js 16 renamed Middleware to Proxy).app/crm/**— CRM pages (content,seo,queries,users), each gated to its allowed roles.app/api/**— route handlers backing the CRM (all re-check role server-side, not just UI hiding).components/site/**— public site sections;components/crm/**— CRM editors.prisma/schema.prisma,prisma/seed.ts— data model and initial seed data.lib/defaults.ts— default content/SEO/sample-query data (used for seeding and "Reset to defaults").