Energy Recruitment Hub

Energy Recruitment Hub (ERH) is a full stack, multi role job portal and headless CMS built for the energy and recruitment sector. It connects job seekers, recruiters, and internal staff in one marketplace with AI powered CV scoring, career intelligence, Stripe subscriptions, and role based dashboards.

Live preview
Energy Recruitment Hub project preview

Project overview

Energy Recruitment Hub is a production grade recruitment platform connecting three sides of a hiring marketplace: job seekers who browse jobs, apply, and use AI CV scoring and career intelligence; recruiters and employers who post jobs, manage companies, unlock applicants, and pay via Stripe subscriptions; and internal staff (Admin and VA) who moderate content, manage users, and run training workflows.

The project is a monorepo with a Strapi 5 TypeScript backend (headless CMS, REST API, business logic) and a Next.js 16 frontend (public marketing site plus four role based dashboards). Production runs at energyrecruitmenthub.com with the frontend on Vercel and Strapi hosted separately.

The platform includes 99 frontend pages and 23 backend API modules, with monetization through four Stripe subscription tiers (Access, Recruit, Hire, Partner), usage limits, publish gates, and webhook driven entitlement sync.

Your role

  • Full stack development across Strapi 5 backend and Next.js 16 frontend
  • Backend: custom auth, two layer RBAC, Stripe billing with webhooks, OpenAI integrations, S3 CV uploads, PostgreSQL
  • Frontend: App Router architecture, Redux state, Tailwind v4 and shadcn/ui, SEO and Core Web Vitals optimization
  • Custom Strapi content types, controllers, services, and permission bootstrap
  • Recruiter subscription billing, job lifecycle approval workflow, and applicant unlock flows
  • AI CV checker, career intelligence, interview prep, and AI assisted job drafts
  • BFF auth routes with HttpOnly JWT cookies and three layer route gating
  • Sentry observability and documentation driven feature workflow

Tech stack used

  • Frontend: Next.js 16, React 19, Tailwind CSS 4, shadcn/ui, Redux Toolkit, TipTap 3, Framer Motion
  • Forms & HTTP: React Hook Form, Axios (JWT cookie + localStorage fallback), lucide-react
  • Backend: Strapi 5 (TypeScript), @strapi/plugin-users-permissions, PostgreSQL
  • Payments & AI: Stripe 17 (Checkout, Portal, webhooks), OpenAI 4 (CV scoring, job drafts, career intelligence)
  • Storage & Parsing: AWS S3 (CV uploads), pdf-parse, mammoth (DOCX), xlsx (bulk import), fuse.js
  • Observability: Sentry (@sentry/node, @strapi/plugin-sentry), nodemailer
  • SEO: next-sitemap, JSON-LD, canonical domains, PWA manifest

Key features

  • Public job board with search, filters, split pane layout, and direct to recruiter applications
  • AI CV checker with deterministic rule based matching and LLM relevance gate
  • AI interview prep with per job cached questions and career context prep
  • Career Intelligence: role matching, skills gap analysis, salary insights, career journey dashboard
  • Recruiter Stripe subscriptions with 4 plans, usage limits, publish gates, and Customer Portal
  • Job lifecycle workflow: draft, pending, active with admin approve/reject and billing gates
  • AI assisted job drafts and Excel bulk job import
  • Multi role dashboards: Admin, Recruiter, VA, and Job seeker with scoped pages
  • Auth: email/password, Google OAuth, OTP, email verification, persona switching
  • Companies, Projects, and Training modules with ownership rules
  • SEO and PWA: structured data, sitemap, robots, web manifest, llms.txt

Platform modules

Public

  • /, /jobs, /projects, /companies, /career-intelligence
  • /cv-checker, /blog, /training, /pricing, /about, /contact

Auth

  • /auth/login, register, verify-email
  • Forgot/reset password, OAuth callback

Job Seeker

  • /job-seeker/dashboard, applied-jobs, saved-jobs
  • Projects, training, profile, membership

Recruiter

  • /recruiter/dashboard, jobs, companies, applications
  • Membership, profile, job create and status flows

VA (Virtual Assistant)

  • /va/jobs, all-jobs, companies, projects
  • Applicants, training, dashboard

Admin

  • /admin/dashboard, users, jobs, companies, projects
  • Applicants, CVs, training, settings (permissions, email templates)

Backend API (23 modules)

  • auth, job, job-application, company, project, recruiter
  • stripe, membership, career-intelligence, training-course
  • analytics, upload, bulk-import, preview-score, and more

User roles

Job Seeker

  • Browse jobs, apply, CV feedback, and career guidance
  • Persona: job-seeker with security role authenticated

Recruiter / Employer

  • Post jobs, unlock applicants, Stripe subscription customer
  • Can switch persona to job seeker without re registration

VA (Virtual Assistant)

  • Create and moderate jobs, companies, projects, and training
  • Staff workflow on behalf of recruiters

Admin

  • Full platform control: users, approvals, settings, permissions
  • Two layer RBAC: security role (role.type) + UX persona (user_type)

Target audience

  • Energy sector job seekers seeking roles, CV feedback, and career guidance
  • Recruiters and employers posting jobs and unlocking applicants via paid plans
  • Virtual assistants moderating jobs, companies, and training content
  • Platform admins managing users, approvals, and system settings

Code structure & architecture

job_portal_cms/  (monorepo)
├── backend/                    # Strapi 5 (TypeScript)
│   ├── src/api/                # 23 API route groups
│   ├── src/services/           # recruiter-billing, ai-cv-matcher, stripe/*
│   ├── src/middlewares/
│   └── src/index.ts            # Bootstrap: roles, permissions, patches
└── frontend/                   # Next.js 16 App Router
    ├── app/                    # 99 pages, role prefixed segments
    ├── app/api/auth/           # BFF layer (HttpOnly strapi_jwt cookies)
    └── store/                  # Redux Toolkit slices

Request flow:
Browser → Next.js → BFF /api/auth → Strapi REST /api → PostgreSQL
                              ├→ AWS S3 (CVs)
                              ├→ Stripe (billing)
                              └→ OpenAI (AI features)

Performance optimizations

  • LCP fixes: optimized next/image with responsive sizes on hero and banner assets
  • Home page below the fold sections moved to next/dynamic lazy loading
  • Google Analytics deferred via next/script strategy afterInteractive
  • System font stack (font-sans), no render blocking next/font
  • Core Web Vitals targets: Performance > 80, LCP < 2.5s, CLS < 0.1, INP < 200ms
  • AI hot path uses deterministic CV scoring; OpenAI gated behind relevance check
  • Interview prep questions cached per job and reused for later applicants
  • NODE_OPTIONS max old space size cap in production start
  • Usage counters synced from DB after publish to avoid drift
  • Mobile responsiveness audit: tablet nav, body scroll lock, touch targets
  • Thin controllers, fat services: domain logic in backend/src/services/
  • One way dependency chains: resume-detection → cv-relevance-gate → ai-cv-matcher
  • Two layer RBAC: canPerform(securityRole, persona, action) requires both layers
  • Ownership stamping: created_by_user_id immutable from request body on update
  • Strapi 5 draft/publish dedupe helpers (dedupeJobsByDocumentId) on list endpoints
  • Redux Toolkit slices: auth, jobs, companies, recruiter, jobSeeker, admin, projects
  • Three layer auth: Next.js middleware → Strapi permissions → BFF cookie handling
  • Documentation driven development with feature docs in docs/features/*

Challenges & solutions

  • Strapi 5 documentId duplication from draft/publish creating multiple rows.

    Shared dedupe helpers (dedupeJobsByDocumentId, dedupeRecruiterJobsByIdentity) on all list endpoints.

  • Two authorization needs: hard role ceiling vs UX persona.

    Two layer RBAC with role.type for security and user_type persona; canPerform() requires both.

  • Recruiter and job seeker switching without re registration.

    Persona switch updates user_profiles.user_type only, keeping Strapi security role intact.

  • Stripe billing complexity with limits, usage, and publish gates.

    Strapi owns limits and gates; Stripe owns subscriptions; webhook sync with idempotency table.

  • Irrelevant or non resume uploads skewing AI scores.

    detectResumeLikeDocument and evaluateCvRelevanceGate run before deterministic scoring.

  • Circular service imports across CV and Stripe modules.

    Refactored into one way dependency chains without behavior change.

  • Core Web Vitals on image heavy marketing pages.

    Image optimization, dynamic imports, and deferred third party scripts.

  • SQLite to PostgreSQL migration.

    Postgres only config, Docker compose, import script, and removal of SQLite artifacts.

Results

  • Production grade platform with 99 frontend pages and 23 backend API modules at energyrecruitmenthub.com.
  • Monetization ready with 4 Stripe subscription tiers, usage limits, publish gates, and Customer Portal.
  • AI differentiation: CV scoring, career intelligence, job drafts, and cached interview prep with cost controlled LLM usage.
  • Secure role aware system with three layer auth, frozen permission matrix, and ownership enforcement.
  • SEO and performance optimized: Core Web Vitals targets met, structured data, canonical domains, PWA manifest.
  • Maintainable and observable with Sentry, documentation sync workflow, and audit reports.