Skip to content

Repository files navigation

RoleReach

RoleReach is a MERN-based opportunity discovery application that opens LinkedIn in a visible Playwright-controlled browser, searches recent hiring posts for one or more job roles, extracts public recruiter details, stores the results in MongoDB, and presents them in a polished responsive dashboard.

This is an assignment/demo implementation. LinkedIn may request login, OTP, CAPTCHA or temporarily restrict automated activity. RoleReach never bypasses those controls; the user completes verification manually in visible Chrome.

Live demo

Component URL Status
Frontend role-reach-delta.vercel.app Live on Vercel
API health Railway API health Live on Railway
Remote browser Railway noVNC workspace Authenticated access is initiated from RoleReach

The hosted frontend, email authentication, API, MongoDB persistence and remote browser are available for review. LinkedIn search automation is resource-intensive: Chromium can exceed Railway Trial's 1 GB memory limit and crash on the hosted instance. Run the Docker setup below for the complete and most reliable demonstration.

Features

  • Multiple job-role input and configurable 1–30 day search window
  • Visible Chrome automation with a persistent LinkedIn browser session
  • Password-protected noVNC workspace for remote login, OTP and CAPTCHA handling
  • Manual LinkedIn login/verification with a real pause-and-resume workflow
  • Post content, author name, designation, profile URL and post URL extraction
  • Canonical LinkedIn permalink matching that rejects ambiguous nested/re-shared post links
  • Public email and phone extraction from post text
  • MongoDB persistence with owner-scoped duplicate protection
  • Passwordless email OTP authentication through Brevo
  • Persistent JWT session, sign-out, saved-post shortlist and search activity history
  • One-click, one-profile LinkedIn connection request without a note, with persistent status
  • Target-profile-scoped Connect actions with post-click Pending/Sent confirmation
  • JWT-protected APIs and one-active-browser protection
  • API and authentication rate limiting, Helmet, CORS and input limits
  • Responsive dark/light interface with loading, empty and error states
  • Frontend and backend automated tests
  • Dockerfiles, Docker Compose and GitHub Actions CI/CD

Stack

Layer Technology
Frontend React 19, TypeScript, Vinext, Tailwind/CSS
Backend Node.js, Express 5
Database MongoDB, Mongoose
Browser automation Playwright, visible Chromium/Chrome
Authentication Email OTP, Brevo Transactional Email, JWT
Security Helmet, CORS, express-rate-limit
Testing Node test runner, rendered frontend tests
Remote browser Xvfb, x11vnc, noVNC, websockify
Delivery Docker, Docker Compose, GitHub Actions, Vercel, Railway

Architecture

React dashboard → Express API → Playwright visible Chrome → LinkedIn
       │                 ↓                    ↑
       │              MongoDB          noVNC workspace
       │                 ↑                    ↑
       └────────── Brevo email OTP      Human verification

The frontend starts a search and polls its status. The API launches a persistent Playwright context locally, waits for manual login when required, searches each role, extracts public fields, and upserts every result immediately so progress survives an interrupted search. A user can also deliberately click Connect on one result; RoleReach opens that author's profile in the same visible session and selects Send without a note. This action is limited to five attempts per day and is never run in bulk.

Why visible Chrome instead of headless

RoleReach deliberately uses a full visible browser. LinkedIn may require login, OTP, CAPTCHA or another verification step, and a visible browser lets the user complete that step manually. When Docker runs on another machine, x11vnc shares Playwright's virtual X11 display and noVNC makes it controllable in a normal web browser. Set PLAYWRIGHT_CHANNEL=chrome to use an installed Google Chrome build; leave it empty to use Playwright Chromium. Headless mode is not used because it would hide verification screens and prevent safe human intervention.

Local setup

Requirements

  • Node.js 22+
  • MongoDB 7/8, local or Atlas
  • A verified Brevo sender and API key
  • A desktop environment capable of showing Chrome

1. Install

npm install
cd server
npm install
npx playwright install chromium

2. Configure

Copy server/.env.example to server/.env and provide:

PORT=5000
CLIENT_URL=http://localhost:3000
MONGODB_URI=mongodb://localhost:27017/rolereach
JWT_SECRET=use-a-long-random-secret
BREVO_API_KEY=xkeysib-your-key
BREVO_SENDER_EMAIL=your-verified-sender@example.com
PLAYWRIGHT_PROFILE_DIR=./.linkedin-profiles
PLAYWRIGHT_SLOW_MO=100
NOVNC_PASSWORD=use-a-strong-remote-browser-password

In Brevo, open Settings → Senders & IP → Senders, verify the sender address, create an API v3 key, and place both values in the environment file. RoleReach uses Brevo directly—Nodemailer is not used.

3. Run

Terminal 1:

cd server
npm run dev

Terminal 2:

npm run dev

Open the displayed frontend URL, verify your email, enter roles, and start a live search. During direct desktop development, a visible browser opens locally. Complete LinkedIn login, OTP or CAPTCHA manually if requested.

Docker

cp server/.env.example server/.env
docker compose up --build

Before starting Docker, set a unique NOVNC_PASSWORD in server/.env. Then:

  1. Open http://localhost:3000 for RoleReach.
  2. Click Open LinkedIn browser.
  3. RoleReach requests authenticated browser access and embeds the noVNC workspace inside the application; the reviewer is not shown a separate tab or password prompt.
  4. Sign in to LinkedIn inside the embedded Chrome workspace.
  5. Minimize the browser workspace, start the search, and reopen it whenever manual verification is needed.

MongoDB data and per-user LinkedIn browser profiles persist in named Docker volumes. docker compose down stops the stack without deleting those volumes.

Live deployment: Vercel + Railway

The current assignment deployment uses Vercel for the frontend, MongoDB Atlas for persistent application data and Railway for the Dockerized Express, Playwright and noVNC service.

Railway backend

Create a Railway service from this repository and configure:

  • Root Directory: /server
  • Builder: the existing server/Dockerfile
  • API public domain target port: the port printed by the application (8080 in the current deployment)
  • noVNC public domain target port: 6080
  • Persistent volume mount: /app/.linkedin-profiles

Add these runtime variables in Railway. Never commit their real values:

NODE_ENV=production
CLIENT_URL=https://your-project.vercel.app
MONGODB_URI=mongodb+srv://user:password@cluster/rolereach
JWT_SECRET=use-a-long-random-secret
BREVO_API_KEY=xkeysib-your-key
BREVO_SENDER_EMAIL=your-verified-sender@example.com
PLAYWRIGHT_PROFILE_DIR=/app/.linkedin-profiles
PLAYWRIGHT_SLOW_MO=100
NOVNC_PASSWORD=use-a-strong-remote-browser-password
SCREEN_WIDTH=1100
SCREEN_HEIGHT=700
NODE_OPTIONS=--max-old-space-size=256
MALLOC_ARENA_MAX=2

Do not manually set PORT; Railway injects it. MongoDB Atlas must allow the Railway service to connect. For a short-lived demo with dynamic outbound IPs, an Atlas IP access-list entry of 0.0.0.0/0 can be used only with a strong, unique database password. Restrict network access further for production.

Vercel frontend

Import the repository into Vercel with:

  • Framework Preset: Next.js
  • Root Directory: repository root
  • Install Command: npm ci
  • Build Command: npx next build

Set these build-time variables:

NEXT_PUBLIC_API_URL=https://your-api.up.railway.app/api
NEXT_PUBLIC_NOVNC_URL=https://your-browser.up.railway.app/vnc.html?autoconnect=true&resize=scale

After Vercel assigns the production domain, set Railway's CLIENT_URL to that exact HTTPS origin without a trailing slash and redeploy the backend.

Hosted-demo limitation

Railway Trial currently provides 1 GB RAM. LinkedIn's modern web client and visible Chromium can approach or exceed that limit even with low-memory browser flags, causing Playwright to report Page crashed. This is a hosting-resource constraint rather than an API, database or frontend failure. The Vercel UI, Brevo OTP login, Express API, MongoDB persistence and noVNC workspace remain independently testable. For the complete search workflow, use local Docker or a server with at least 2 GB RAM. The noVNC workspace is intentionally a single-operator demo workspace; do not use it concurrently from multiple browsers.

Tests

# Frontend production build and rendered-page tests
npm test

# Backend extraction tests
cd server && npm test

GitHub Actions runs frontend lint/tests, backend API/unit tests, and both Docker builds on pushes and pull requests. On main-branch pushes, the CD workflow publishes versioned frontend and backend images to GitHub Container Registry and optionally calls the configured deployment hooks.

Configure these repository secrets for automatic deployment:

  • FRONTEND_DEPLOY_HOOK: the frontend provider's deploy hook
  • BACKEND_DEPLOY_HOOK: the backend provider's deploy hook

Container publishing still completes when either hook is intentionally omitted.

Main API routes

Method Route Purpose
POST /api/auth/request-otp Send a Brevo email OTP
POST /api/auth/verify-otp Verify OTP and return JWT
POST /api/search Start an authenticated LinkedIn search
GET /api/search/:jobId/status Read live search status
POST /api/search/:jobId/resume Resume after manual verification
POST /api/search/:jobId/cancel Cancel while preserving collected posts
GET /api/posts List persisted user posts
PATCH /api/posts/:id/saved Add/remove a post from the shortlist
POST /api/posts/:id/connect Send one connection request without a note
DELETE /api/posts/:id Delete one saved post
GET /api/activity Read persistent search history
GET /api/auth/me Validate a persisted login session
POST /api/browser/access Authorize one-click access to the noVNC workspace
GET /api/health API health check

Security and privacy

  • LinkedIn credentials are never requested or stored.
  • Only the Playwright profile/cookies persist; keep that directory and its Docker volume private.
  • noVNC requires a separate strong password and should be served through HTTPS in production.
  • RoleReach returns that credential only after email authentication and places it in the noVNC URL fragment, so it is not sent in HTTP requests or access logs.
  • Public contact details are extracted only when present in the post text.
  • OTPs are SHA-256 hashed, expire after ten minutes and are cleared after use.
  • Search, post and authentication routes are rate-limited.
  • Connection requests require an explicit click, are limited to five per day and store a status to prevent repeats.
  • A click alone is never recorded as success; RoleReach stores Sent/Pending only after LinkedIn displays a visible confirmation for the target profile.
  • JWT secrets, Brevo keys and MongoDB credentials belong only in environment variables.

Known limitations

  • LinkedIn selectors and page structure can change.
  • Ambiguous cards containing different nested post permalinks are skipped instead of being saved with a mismatched source link.
  • Automation can trigger verification or account restrictions.
  • LinkedIn can hide Connect under More, mark a request Pending, or make the action unavailable.
  • Existing invitations shown as Pending/Withdraw invitation and existing relationships shown as Remove connection are mapped back to the correct persistent status.
  • Hashed LinkedIn variants also use a profile-heading-scoped visible-action fallback before Unavailable is recorded.
  • Connection state is profile-scoped, so every collected post from the same author displays the same Pending/Connected status.
  • Date labels shown by LinkedIn may be approximate.
  • The official LinkedIn API does not provide this project’s broad public post keyword-search capability.
  • No browser automation can guarantee uninterrupted operation for every account.
  • The free Railway deployment has a 1 GB memory ceiling; visible Chromium can crash under LinkedIn's workload. Local Docker or a 2 GB+ server is recommended for the full demonstration.
  • The included noVNC workspace is single-operator; production multi-user isolation would require one browser container/session per user.

RoleReach responds safely: it keeps already-saved results, reports the current status, and expects the user to complete any verification manually. It does not attempt to bypass LinkedIn security controls.

Project structure

app/                     React dashboard
server/src/index.js      Express routes, auth and rate limits
server/src/automation.js Visible Chrome, extraction and pause/resume workflow
server/start-novnc.sh   X11, VNC and noVNC process lifecycle
server/src/models.js     MongoDB models and indexes
server/src/email.js      Brevo OTP delivery
server/src/utils.js      Date, relevance, contact and URL helpers
server/tests/            Backend tests
tests/                   Frontend rendered-output tests
.github/workflows/       CI and CD pipelines
Dockerfile               Frontend container
server/Dockerfile        Backend/Playwright container
docker-compose.yml       Web, API and MongoDB stack

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages