A Laravel 12 monolith for a simple job marketplace with Job Seeker and Employer roles. Includes authentication via Fortify, role/permission management via Spatie, AI-assisted resume and job draft generation (stubbed), recommendations, applications, notifications, and basic dashboards.
- Auth (Fortify): registration, login, reset password, 2FA views configured in
app/Providers/FortifyServiceProvider.php. - Roles & Permissions: via
spatie/laravel-permissionwith gates inapp/Providers/AuthServiceProvider.phpand example routes restricting dashboards. - Jobs & Applications: browse jobs, view details, and apply (authenticated). See routes in
routes/web.phpand controllers inapp/Http/Controllers. - AI Draft Generators (stub): interfaces in
app/Services/AIbound toProviderClientinAppServiceProvider. - Rate Limiting: custom limiters for AI and apply actions in
AppServiceProvider. - Notifications: stored in DB (see migration
2025_09_15_090000_create_notifications_table.php) and mailed. - Dashboards: role-based redirects for
admin,employer,job_seeker.
- PHP 8.2+, Laravel 12
- Fortify (auth), Spatie Permission (roles)
- Pest (tests), Pint (formatting)
- Vite, Node 20+
- PHP 8.2+
- Composer
- Node.js 20+ and npm
- SQLite (default) or another DB supported by Laravel
# Clone and enter
git clone <your-repo-url> jobs && cd jobs
# Install PHP deps
composer install
# Copy env and app key
cp .env.example .env
php artisan key:generate
# Use SQLite by default
# Create the DB file if it doesn't exist
php -r "file_exists('database/database.sqlite') || touch('database/database.sqlite');"
# Configure your .env
# DB_CONNECTION=sqlite (uncomment) or set up MySQL/PostgreSQL
# MAIL_*, QUEUE_CONNECTION=database (optional)
# Run migrations
php artisan migrate --graceful --ansi
# Seed roles, demo data, and an admin user
php artisan db:seed --ansi
# Demo data seeder (optional but recommended)
php artisan db:seed --class=DemoSeeder --ansi
# Install JS deps and build assets
npm install
npm run build # or: npm run dev for hot reload# Start Laravel HTTP server and Vite in separate terminals
php artisan serve
npm run dev- Admin:
admin@example.com/password - Employer:
employer@example.com/password - Job Seeker:
seeker@example.com/password
These are created by the seeders in database/seeders.
# Clear cached config to avoid env drift
php artisan config:clear --ansi
# Run the test suite (Pest)
./vendor/bin/pest
# Or via composer script
composer test# Run concurrent dev processes (HTTP server, queue listener, logs, Vite)
composer dev
# Lint/format with Pint
./vendor/bin/pint- Auth Views: registered in
FortifyServiceProviderand rendered fromresources/views/auth/*. - Roles/Gates:
is-admin,is-employer,is-job-seekerdefined inAuthServiceProvider. - Dashboards:
GET /dashboardredirects to role dashboards; seeroutes/web.php. - AI Providers:
AIResumeGeneratorServiceandAIJobGeneratorServiceare bound toProviders/ProviderClient(stub implementation) inAppServiceProvider. Replace bindings to integrate a real AI provider. - Rate Limits: named limiters
aiandapplyinAppServiceProvider. - Mail/Queues: configure
MAIL_*andQUEUE_CONNECTIONas needed.
APP_NAME="Simple Job Site"
APP_ENV=local
APP_DEBUG=true
APP_URL=http://127.0.0.1:8000
# SQLite (default)
DB_CONNECTION=sqlite
# For MySQL/Postgres, fill in DB_* accordingly
# Fortify / Session basics
SESSION_DRIVER=file
SESSION_LIFETIME=120
# Mail (optional for notifications)
MAIL_MAILER=log
MAIL_FROM_ADDRESS="no-reply@example.com"
MAIL_FROM_NAME="Simple Job Site"
# Queue (optional)
QUEUE_CONNECTION=sync- dev: runs server, queue:listen, pail logs, and Vite together.
- test: clears config cache and runs
artisan test(Pest powered).
app/Services/AI/*: AI interfaces and provider client.app/Providers/*: service providers, Fortify, gates, rate limiting.routes/web.php: homepage, job views, application routes, dashboards.database/seeders/*: roles, demo data, admin user.tests/*: Pest tests (Feature, Integration, Contract, Unit).
Implement real providers by creating a concrete client that implements:
App\Services\AI\AIResumeGeneratorService::generateResumeDraft(array $input): array
App\Services\AI\AIJobGeneratorService::generateJobDraft(array $input): arrayThen bind it in AppServiceProvider::register() instead of ProviderClient.
MIT