Skip to content

Latest commit

 

History

158 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ekswai

ekswai

Multi-provider job board aggregator. Follow companies, get notified of new positions daily, and manage your application pipeline.

Tests Static Analysis Linter

What it does

ekswai lets you follow companies across job boards and track their job postings. You add companies by their provider slug, the system validates them against the provider and syncs new positions every day. You get email notifications for companies you care about, and you manage your entire application pipeline from one dashboard.

For users:

  1. Register and go to My Companies
  2. Add a company slug (e.g. laravel from apply.workable.com/laravel for Workable)
  3. The system validates and syncs job postings automatically
  4. Browse your dashboard: bookmark, mark as submitted, track interviews, dismiss

For admins: Filament 4 panel to manage companies, job postings, users, and scraper configurations.

Supported Providers

Provider Integration Example URL
Workable REST API apply.workable.com/laravel
Lever REST API jobs.lever.co/scaleway
Ashby REST API jobs.ashbyhq.com/ramp
Greenhouse REST API boards.greenhouse.io/discord
Teamtailor HTML Scraper weroad.teamtailor.com/jobs
Factorial HTML Scraper shippypro.factorialhr.com

API providers use JSON endpoints directly. HTML scraper providers use configurable CSS selectors with built in health checks and retry logic (managed via the admin panel).

Tech Stack

Component Technology
Framework Laravel 12 (PHP 8.4)
Frontend React 19, Inertia.js, TypeScript, Tailwind CSS 4
Database PostgreSQL (SQLite for tests)
Admin Filament 4
Country Data laravel-istat-foreign-countries
Testing Pest
Static Analysis PHPStan (Larastan) level 6
Code Style Laravel Pint
Refactoring Rector (PHP 8.4, code quality, dead code, early return, type declarations)
CI/CD GitHub Actions
Local Dev Laravel Sail (Docker)

Architecture

DDD with three layers:

app/
├── Domain/
│   ├── Company/         # Company entity, JobBoardProvider enum
│   ├── JobPosting/      # Job posting with per-user status tracking
│   ├── ScraperConfig/   # CSS selectors and health check config for HTML scrapers
│   ├── Shared/          # BaseModel (UUIDs, $guarded = [])
│   ├── JobFilter/       # Per-user job posting filters (global + per-company overrides)
│   └── User/            # User with company subscriptions and job statuses
├── Application/
│   ├── Actions/
│   │   ├── Company/     # FollowCompany, UnfollowCompany
│   │   ├── JobPosting/  # SyncCompanyJobPostings, UpdateJobPostingStatus
│   │   ├── Notification/# NotifyUserOfNewJobs
│   │   └── Sync/        # RunDailySync (orchestrator)
│   ├── DTOs/            # JobPostingDTO
│   └── Services/        # JobFilterService (filter resolution and application)
└── Infrastructure/
    ├── Admin/Filament/  # Admin panel (Companies, Jobs, Users, ScraperConfigs)
    ├── Console/         # Artisan commands (jobs:sync-daily)
    ├── Mail/            # NewJobsFoundMail, ScraperHealthAlertMail
    └── Services/
        ├── Contracts/   # JobBoardClient interface
        ├── Workable/    # Workable API client
        ├── Lever/       # Lever API client
        ├── Ashby/       # Ashby API client
        ├── Greenhouse/  # Greenhouse API client
        ├── Teamtailor/  # Teamtailor HTML scraper
        ├── Factorial/   # Factorial HTML scraper
        ├── Scraping/    # BaseHtmlScraper, ScraperHealthChecker, exceptions
        └── JobBoardClientFactory.php

Provider pattern: each job board integration implements the JobBoardClient interface (fetchJobsForCompany and validateSlug). The JobBoardProvider enum lists available providers, and JobBoardClientFactory resolves the correct client. API providers (Workable, Lever, Ashby, Greenhouse) call JSON endpoints directly. HTML scraper providers (Teamtailor, Factorial) extend BaseHtmlScraper which handles retry logic, DOM parsing, and validation using CSS selectors from ScraperConfig.

Job Filters: Users can define filters to control which job postings appear in their dashboard and email notifications. Filters support title keywords (include/exclude), country (powered by laravel-istat-foreign-countries for ISO country matching), remote only, and department. Filters are global by default, with optional per-company overrides. All criteria combine with AND logic, values within each criterion combine with OR. Filter data is stored in the job_filters table and managed through the Filters page (global) or the company filter dialog (per-company overrides).

Key relationships: Users subscribe to companies via company_user pivot (with email notification toggle). Each user has a per-job status via job_posting_user pivot (new, bookmarked, submitted, interview, dismissed). Users can have one global job filter and optional per-company overrides via the job_filters table. The sync is shared: one API/scrape call per company regardless of subscriber count.

Adding a provider

  1. Add a new case to JobBoardProvider enum (app/Domain/Company/JobBoardProvider.php)
  2. Create a class implementing JobBoardClient (app/Infrastructure/Services/Contracts/JobBoardClient.php)
  3. Register it in JobBoardClientFactory::make() (app/Infrastructure/Services/JobBoardClientFactory.php)
  4. For HTML scraper providers: add a ScraperConfig entry with CSS selectors in ScraperConfigSeeder

Quick Start

git clone https://github.com/danielebarbaro/ekswai-jobs-scraper.git
cd ekswai-jobs-scraper
composer install
cp .env.example .env
php artisan key:generate
./vendor/bin/sail up -d
./vendor/bin/sail artisan migrate --seed
./vendor/bin/sail artisan foreign-countries:import
npm install && npm run build

Open http://localhost and register. Then go to /companies and add a company by selecting a provider and entering its slug.

The seeder creates an admin user (me@plincode.tech / password) with demo companies across all 6 providers, already synced with real job postings.

Commands

# Sync job postings from all active companies
./vendor/bin/sail artisan jobs:sync-daily

# Add a company from CLI
./vendor/bin/sail artisan companies:add ashby jimdo.com
./vendor/bin/sail artisan companies:add greenhouse carta

# Import ISTAT country data (required for country filters)
./vendor/bin/sail artisan foreign-countries:import

# Run tests
composer test

# Code style (fix)
composer lint

# Static analysis
composer analyse

# Rector refactoring
composer rector          # Apply changes
composer rector-dry      # Preview changes

# Run analyse + test together
composer check

The sync runs automatically every day at 9:00 AM UTC via Laravel scheduler.

License

MIT

About

Track job postings across multiple job boards from a single dashboard. Follow companies, get daily email notifications for new positions, and manage your application pipeline.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages