A production-ready web scraping dashboard built with Next.js 14
Scrape any website β including client-side rendered apps β and instantly view titles, headings, meta descriptions, and visible body text. All results are stored, searchable, and displayed in a beautiful authenticated dashboard.
- π Authentication β Email/password auth with NextAuth.js (credentials + JWT)
- π€ Puppeteer Scraping β Headless Chromium renders JavaScript before extracting data β works on SPAs, React, and Next.js sites
- π Dashboard β Stats cards, paginated logs table, detail modals, real-time feed
- ποΈ Persistent Storage β All scrape results saved to Neon PostgreSQL via Prisma ORM
- β‘ Real-time Updates β Socket.IO broadcasts new scrape logs instantly
- π Dark Mode β Toggle between light and dark themes
- π‘οΈ Validation β Zod schemas for URL, registration, and login inputs
- π± Responsive β Mobile-first UI with Tailwind CSS
| Layer | Technology |
|---|---|
| Framework | Next.js 14 (App Router) |
| Language | TypeScript 5 |
| Styling | Tailwind CSS 3 |
| Auth | NextAuth.js v4 (Credentials + JWT) |
| Scraping | Puppeteer (headless Chromium) |
| Database | PostgreSQL (Neon) |
| ORM | Prisma 7 |
| Validation | Zod 4 |
| Real-time | Socket.IO 4 |
| Password Hashing | bcrypt.js |
webscope/
βββ prisma/
β βββ schema.prisma # Database models (User, RequestLog, ScrapedData)
βββ src/
β βββ app/
β β βββ api/
β β β βββ auth/
β β β β βββ [...nextauth]/route.ts # NextAuth handler
β β β β βββ register/route.ts # POST /api/auth/register
β β β βββ logs/
β β β β βββ [id]/route.ts # DELETE /api/logs/:id
β β β β βββ route.ts # GET /api/logs
β β β βββ scrape/route.ts # POST /api/scrape
β β β βββ stats/route.ts # GET /api/stats
β β βββ auth/
β β β βββ login/page.tsx # Login page
β β β βββ register/page.tsx # Register page
β β βββ dashboard/page.tsx # Protected dashboard
β β βββ layout.tsx # Root layout + SessionProvider
β β βββ page.tsx # Landing page
β β βββ globals.css # Global styles + animations
β βββ components/
β β βββ Button.tsx # Reusable button (variants, loading)
β β βββ Card.tsx # Glass-effect card wrapper
β β βββ DetailModal.tsx # Scrape result detail modal
β β βββ EmptyState.tsx # Empty state illustration
β β βββ LoadingSpinner.tsx # Spinner component
β β βββ LogsTable.tsx # Paginated logs table
β β βββ Navbar.tsx # Sticky nav with auth + theme toggle
β β βββ Pagination.tsx # Page navigation controls
β β βββ RequestHistory.tsx # Request history list
β β βββ ResponseTimeBadge.tsx# Color-coded response time badge
β β βββ ScrapeResultCard.tsx # Quick result preview card
β β βββ SessionProvider.tsx # NextAuth session wrapper
β β βββ SkeletonLoader.tsx # Loading skeleton
β β βββ StatsCards.tsx # Stats overview (3-column grid)
β β βββ StatusBadge.tsx # HTTP status code badge
β β βββ ThemeToggle.tsx # Dark/light mode toggle
β βββ lib/
β β βββ auth.ts # NextAuth config (credentials provider)
β β βββ prisma.ts # Prisma client singleton
β β βββ socket.ts # Socket.IO server init
β β βββ validators.ts # Zod schemas (URL, register, login)
β βββ pages/api/
β β βββ socketio.ts # Socket.IO handshake endpoint
β βββ types/
β β βββ index.ts # Shared TypeScript interfaces
β β βββ next-auth.d.ts # NextAuth type augmentation
β βββ middleware.ts # Protects /dashboard routes
βββ .env # Environment variables
βββ package.json
βββ tailwind.config.ts
βββ tsconfig.json
βββ next.config.mjs
model User {
id String @id @default(uuid())
name String?
email String @unique
password String
logs RequestLog[]
createdAt DateTime @default(now())
}
model RequestLog {
id String @id @default(uuid())
url String
method String
statusCode Int
responseTime Int
userId String
user User @relation(fields: [userId], references: [id])
scrapedData ScrapedData[]
createdAt DateTime @default(now())
}
model ScrapedData {
id String @id @default(uuid())
requestId String
request RequestLog @relation(fields: [requestId], references: [id], onDelete: Cascade)
title String
headings String[]
meta String?
bodyText String?
createdAt DateTime @default(now())
}| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/auth/register |
No | Create new user account |
POST |
/api/auth/[...nextauth] |
No | NextAuth sign-in / sign-out |
POST |
/api/scrape |
Yes | Scrape a URL with Puppeteer |
GET |
/api/logs?page=1&pageSize=10 |
Yes | Paginated scrape history |
DELETE |
/api/logs/:id |
Yes | Delete a log (ownership verified) |
GET |
/api/stats |
Yes | Dashboard stats (total, avg time, success %) |
- Node.js 18+
- PostgreSQL database (or a Neon free tier)
git clone https://github.com/Tushardevx01/webscope.git
cd webscopenpm installCreate a .env file in the project root:
# PostgreSQL connection string (Neon or local)
DATABASE_URL="postgresql://user:password@host:5432/dbname?sslmode=require"
# NextAuth
NEXTAUTH_SECRET="your-random-secret-here"
NEXTAUTH_URL="http://localhost:3000"
# Gemini API (server-side only)
GEMINI_API_KEY="your_key_here"Generate a secret:
openssl rand -base64 32
npx prisma db pushnpm run devOpen http://localhost:3000 β register an account and start scraping.
npm run build # Runs prisma generate + next build
npm start # Starts production server- User submits a URL from the dashboard
- Server validates the URL with Zod
- Puppeteer launches headless Chromium
- Navigates to the page, waits for
networkidle2(JavaScript fully rendered) - Extracts from the live DOM:
document.title- All
<h1>and<h2>elements <meta name="description">contentdocument.body.innerText(truncated to 5000 chars)
- Records HTTP status code and response time
- Saves everything to PostgreSQL via Prisma
- Broadcasts the new log via Socket.IO
- Returns structured JSON to the frontend
Why Puppeteer? Traditional HTTP scrapers (Axios + Cheerio) only see the raw HTML. Modern sites built with React, Next.js, or Vue render content via JavaScript β Puppeteer runs a real browser to capture the fully-rendered page.
- Gradient backgrounds β
gray-50 β blue-50(light) /gray-950 β indigo-950(dark) - Glass-effect cards β
bg-white/70 backdrop-blur-xlwith soft borders - Indigo primary accent with emerald success and rose error badges
- Smooth transitions β fade-in animations, hover scale effects
- Skeleton loaders during data fetching
- Empty state illustration when no scrapes exist
- Fully responsive β works on mobile, tablet, and desktop
- Passwords hashed with bcrypt (12 salt rounds)
- JWT-based sessions via NextAuth
- Middleware protects
/dashboardroutes - API routes verify session before any DB operation
- Log deletion checks ownership (
userId) - Rate limiting on scrape endpoint (1 request per 3 seconds per user)
- Input validation with Zod on all endpoints
- Puppeteer runs with
--no-sandbox --disable-dev-shm-usagefor containerized environments
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
NEXTAUTH_SECRET |
Yes | Random string for JWT signing |
NEXTAUTH_URL |
Yes | Base URL of the app (http://localhost:3000) |
GEMINI_API_KEY |
No* | Google Gemini API key for Website Animal Spirit generation (used server-side only) |
* If omitted, WebScope uses deterministic fallback animal spirits instead of AI-generated output.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is open source and available under the MIT License.
Built with β€οΈ by Tushar