small fixes

This commit is contained in:
Doar Mihai
2026-04-04 18:52:19 +03:00
parent 7c09846bd6
commit feb964f3f6
16 changed files with 717 additions and 14 deletions
+260
View File
@@ -0,0 +1,260 @@
# Cron Job Setup for JobifyGarden
Since your hosting provider doesn't have Supervisor, you'll use cron jobs to handle scheduled tasks and queue processing.
## Prerequisites
1. SSH access to your server
2. Access to crontab configuration
3. Verify your application path (likely `/home/jobifyg/public_html` or similar)
## Setup Instructions
### Step 1: Make Queue Worker Script Executable
```bash
cd /home/jobifyg/public_html
chmod +x queue-worker.sh
```
### Step 2: Configure Crontab
Edit your crontab:
```bash
crontab -e
```
Add these lines (replace `/home/jobifyg/public_html` with your actual path):
```bash
# =============================================================================
# JobifyGarden Cron Jobs
# =============================================================================
# Laravel Scheduler - REQUIRED
# Runs every minute to process scheduled commands
* * * * * cd /home/jobifyg/public_html && php artisan schedule:run >> /dev/null 2>&1
# Queue Worker - REQUIRED
# Option 1: Using the helper script (recommended)
* * * * * /home/jobifyg/public_html/queue-worker.sh
# Queue Worker - Option 2: Direct command (if script doesn't work)
# * * * * * cd /home/jobifyg/public_html && php artisan queue:work --max-time=50 --stop-when-empty --sleep=3 --tries=3 >> storage/logs/queue.log 2>&1
# Queue Restart - OPTIONAL but RECOMMENDED
# Restarts queue workers every hour to prevent memory leaks
0 * * * * cd /home/jobifyg/public_html && php artisan queue:restart >> /dev/null 2>&1
# Clear old failed jobs - OPTIONAL
# Clears failed jobs older than 7 days, runs daily at 4 AM
0 4 * * * cd /home/jobifyg/public_html && php artisan queue:flush >> /dev/null 2>&1
```
### Step 3: Verify Cron Jobs Are Running
After saving your crontab, verify it's active:
```bash
# List your cron jobs
crontab -l
# Check if cron jobs are running (wait a few minutes)
tail -f /home/jobifyg/public_html/storage/logs/laravel.log
# Check queue worker logs
tail -f /home/jobifyg/public_html/storage/logs/queue-worker.log
```
## What Each Cron Job Does
### 1. Laravel Scheduler (`* * * * * ... schedule:run`)
**Frequency:** Every minute
**Purpose:** Checks and runs scheduled tasks defined in `bootstrap/app.php`
**Scheduled Tasks:**
- `jobs:fetch` - Fetches new jobs from APIs every 2 hours
- `jobs:enrich` - Auto-categorizes and enhances jobs every 6 hours
- `jobs:clean` - Removes old jobs (45+ days) daily at 2 AM
- `jobs:deduplicate` - Removes duplicate jobs daily at 3 AM
- `cache:prune-stale-tags` - Cleans cache hourly
For detailed explanations and customization options, see [SCHEDULED_TASKS.md](SCHEDULED_TASKS.md)
### 2. Queue Worker (`* * * * * ... queue:work`)
**Frequency:** Every minute
**Purpose:** Processes background jobs from the queue
**Jobs Processed:**
- Fetching jobs from external APIs
- Processing individual job listings
- Email notifications (if configured)
- Cache warming
- Other background tasks
**Options Explained:**
- `--max-time=50` - Worker runs for max 50 seconds then exits
- `--stop-when-empty` - Exits if no jobs are in queue
- `--sleep=3` - Waits 3 seconds between jobs
- `--tries=3` - Retries failed jobs up to 3 times
### 3. Queue Restart (`0 * * * * ... queue:restart`)
**Frequency:** Every hour
**Purpose:** Gracefully restarts queue workers to prevent memory leaks
### 4. Queue Flush (`0 4 * * * ... queue:flush`)
**Frequency:** Daily at 4 AM
**Purpose:** Clears old failed jobs from the database
## Troubleshooting
### Cron jobs not running?
1. **Check crontab is saved:**
```bash
crontab -l
```
2. **Check PHP path:**
```bash
which php
# Use the full path in crontab if needed: /usr/bin/php artisan ...
```
3. **Check application path:**
```bash
pwd
# Make sure you're using the correct path in crontab
```
4. **Check file permissions:**
```bash
ls -la /home/jobifyg/public_html/artisan
chmod +x /home/jobifyg/public_html/artisan
```
### Jobs not being processed?
1. **Check queue worker logs:**
```bash
tail -f storage/logs/queue-worker.log
```
2. **Check Laravel logs:**
```bash
tail -f storage/logs/laravel.log
```
3. **Manually run queue worker to see errors:**
```bash
php artisan queue:work --verbose
```
4. **Check failed jobs:**
```bash
php artisan queue:failed
```
5. **Retry failed jobs:**
```bash
php artisan queue:retry all
```
### Jobs are fetched but not appearing?
1. **Check if job fetch is scheduled:**
```bash
php artisan schedule:list
```
2. **Manually test job fetching:**
```bash
php artisan jobs:fetch --limit=5
```
3. **Check queue:**
```bash
php artisan queue:work --once --verbose
```
## Performance Tuning
### Low Traffic Site
If your site has low traffic, you can reduce frequency:
```bash
# Run queue worker every 5 minutes instead
*/5 * * * * cd /home/jobifyg/public_html && php artisan queue:work --max-time=240 --stop-when-empty --sleep=3 --tries=3 >> storage/logs/queue.log 2>&1
```
### High Traffic Site
If you need faster job processing:
```bash
# Run multiple queue workers
* * * * * cd /home/jobifyg/public_html && php artisan queue:work --max-time=50 --stop-when-empty --queue=high,default --sleep=1 --tries=3 >> storage/logs/queue-worker-1.log 2>&1
* * * * * cd /home/jobifyg/public_html && php artisan queue:work --max-time=50 --stop-when-empty --queue=default --sleep=1 --tries=3 >> storage/logs/queue-worker-2.log 2>&1
```
## Monitoring
### Check Cron Execution
Your hosting control panel may have a cron job log. Check there for execution history.
### Check Application Health
```bash
# Visit this URL in your browser (Laravel health check)
https://jobifygarden.com/up
# Check recent jobs
php artisan tinker
>>> \App\Models\Job::latest()->take(5)->get(['id', 'title', 'created_at']);
```
### Monitor Logs
```bash
# Watch all logs in real-time
tail -f storage/logs/*.log
# Check for errors
grep -i error storage/logs/laravel.log | tail -20
```
## Important Notes
1. **Queue Worker Must Keep Running**: The queue worker cron ensures jobs are processed continuously. Without it, jobs will queue but never execute.
2. **Schedule Must Run Every Minute**: The scheduler cron must run every minute. Laravel internally determines which scheduled tasks should run.
3. **Time Zone**: Make sure your server's timezone matches your Laravel app timezone (check `config/app.php`).
4. **Testing**: After setup, wait a few minutes and check logs to verify everything is working.
5. **Production**: Make sure `APP_ENV=production` and `APP_DEBUG=false` in your `.env` file.
## Quick Test
After setting up cron, test immediately:
```bash
# 1. Manually run scheduler
php artisan schedule:run
# 2. Queue a test job
php artisan jobs:fetch Remotive --limit=2
# 3. Process the queue
php artisan queue:work --once --verbose
# 4. Check if jobs were created
php artisan tinker
>>> \App\Models\Job::count();
```
## Support
If you encounter issues:
1. Check the troubleshooting section above
2. Review logs in `storage/logs/`
3. Ensure Redis is working: `php artisan tinker` then `Cache::get('test')`
4. Verify database connection works
+4 -1
View File
@@ -220,7 +220,7 @@ The application uses queues for background job processing. You must run a queue
./vendor/bin/sail artisan queue:work --sleep=3 --tries=3 --timeout=90 ./vendor/bin/sail artisan queue:work --sleep=3 --tries=3 --timeout=90
``` ```
**Using Supervisor (Production):** **Using Supervisor (Production with Supervisor):**
Create `/etc/supervisor/conf.d/jobifygarden-worker.conf`: Create `/etc/supervisor/conf.d/jobifygarden-worker.conf`:
```ini ```ini
[program:jobifygarden-worker] [program:jobifygarden-worker]
@@ -244,6 +244,9 @@ sudo supervisorctl update
sudo supervisorctl start jobifygarden-worker:* sudo supervisorctl start jobifygarden-worker:*
``` ```
**Using Cron (Production without Supervisor):**
If your hosting provider doesn't have Supervisor, see [CRON_SETUP.md](CRON_SETUP.md) for detailed cron-based setup instructions.
### Cron Jobs (Required) ### Cron Jobs (Required)
Laravel's task scheduler must run every minute. Add this to your crontab: Laravel's task scheduler must run every minute. Add this to your crontab:
+367
View File
@@ -0,0 +1,367 @@
# Scheduled Tasks Guide for JobifyGarden
This document explains the optimal scheduling intervals for all automated tasks in JobifyGarden and the reasoning behind them.
## Overview
All scheduled tasks are configured in `bootstrap/app.php` and run automatically via the Laravel scheduler (cron job running every minute).
## Scheduled Tasks
### 1. Job Fetching (`jobs:fetch`)
**Interval:** Every 2 hours
**Command:** `php artisan jobs:fetch --limit=50`
**Why this interval:**
- **Freshness:** Most job boards update their listings every few hours
- **API Rate Limits:** Spacing out requests prevents hitting rate limits
- **Resource Management:** Prevents overwhelming your queue and database
- **User Experience:** 2-hour intervals ensure users see fresh content without excessive API calls
**Customization Options:**
```php
// More frequent (hourly) - for high-traffic sites
->hourly()
// Less frequent (every 4 hours) - for lower traffic or to save resources
->everyFourHours()
// Peak hours only (every 2 hours from 6 AM to 6 PM)
->everyTwoHours()->between('6:00', '18:00')
```
**Limit Parameter:**
- `--limit=50` fetches up to 50 jobs per source
- Adjust based on your API quotas and database capacity
- Recommended: 20-100 jobs per fetch cycle
---
### 2. Job Enrichment (`jobs:enrich`)
**Interval:** Every 6 hours
**Command:** `php artisan jobs:enrich`
**Why this interval:**
- **Processing Time:** Enrichment can be resource-intensive (AI/ML for categorization)
- **Batch Efficiency:** Allows multiple fetch cycles to accumulate jobs before enriching
- **Priority:** Less urgent than fetching; can run in background
- **Resource Balance:** Doesn't compete with peak fetch times
**What it does:**
- Auto-categorizes jobs based on title/description
- Detects experience levels (entry, mid, senior, etc.)
- Normalizes job data
- Enhances job metadata
**Customization Options:**
```php
// More frequent (every 4 hours) - if categorization is critical
->everyFourHours()
// Less frequent (twice daily) - to save resources
->twiceDaily(1, 13) // 1 AM and 1 PM
// Only during off-peak hours
->dailyAt('03:00')
```
---
### 3. Job Deduplication (`jobs:deduplicate`)
**Interval:** Daily at 3:00 AM
**Command:** `php artisan jobs:deduplicate`
**Why this interval:**
- **Daily Cleanup:** Removes duplicates that accumulate from multiple sources
- **Off-Peak Hours:** Runs at 3 AM when traffic is lowest
- **Post-Fetch:** Runs after fetch cycles have completed
- **Database Health:** Keeps database clean without running too frequently
**What it does:**
- Identifies duplicate jobs based on title, company, and location
- Removes or merges duplicate entries
- Updates job fingerprints
- Maintains data integrity
**Customization Options:**
```php
// More frequent (every 12 hours) - if duplicates are a major issue
->twiceDaily(3, 15) // 3 AM and 3 PM
// Less frequent (weekly) - if duplicates are rare
->weeklyOn(0, '03:00') // Sunday at 3 AM
// After major fetch cycles
->dailyAt('04:00') // After enrichment completes
```
---
### 4. Job Cleanup (`jobs:clean`)
**Interval:** Daily at 2:00 AM
**Command:** `php artisan jobs:clean --days=45`
**Why this interval:**
- **Daily Maintenance:** Removes expired jobs every day
- **Off-Peak Hours:** 2 AM has minimal user activity
- **Before Deduplication:** Runs before deduplication to remove stale data first
- **Database Performance:** Prevents database bloat
**What it does:**
- Removes jobs older than 45 days
- Deletes expired job listings
- Cleans up soft-deleted jobs
- Optimizes database performance
**Retention Period Options:**
```php
// Shorter retention (30 days) - for high-volume sites
'jobs:clean --days=30'
// Longer retention (60-90 days) - for SEO or archival
'jobs:clean --days=60'
// Remove only expired jobs
'jobs:clean --expired-only'
```
**Customization Options:**
```php
// Weekly cleanup instead of daily
->weeklyOn(0, '02:00')
// Twice daily (aggressive cleanup)
->twiceDaily(2, 14)
// Monthly cleanup (for archival sites)
->monthlyOn(1, '02:00')
```
---
### 5. Cache Pruning (`cache:prune-stale-tags`)
**Interval:** Hourly
**Command:** `php artisan cache:prune-stale-tags`
**Why this interval:**
- **Cache Health:** Removes stale cache entries regularly
- **Memory Management:** Prevents Redis/cache bloat
- **Frequent Cleanup:** Cache tags can accumulate quickly
- **Low Impact:** Fast operation, doesn't affect performance
---
## Recommended Schedules by Site Size
### Small Site (<1,000 jobs, <1,000 visitors/day)
```php
$schedule->command('jobs:fetch --limit=30')
->everyFourHours()
->withoutOverlapping();
$schedule->command('jobs:enrich')
->twiceDaily(2, 14);
$schedule->command('jobs:deduplicate')
->weeklyOn(0, '03:00');
$schedule->command('jobs:clean --days=60')
->weekly();
```
### Medium Site (1,000-10,000 jobs, 1,000-10,000 visitors/day)
```php
// Current default configuration in bootstrap/app.php
$schedule->command('jobs:fetch --limit=50')
->everyTwoHours()
->withoutOverlapping();
$schedule->command('jobs:enrich')
->everySixHours();
$schedule->command('jobs:deduplicate')
->dailyAt('03:00');
$schedule->command('jobs:clean --days=45')
->dailyAt('02:00');
```
### Large Site (10,000+ jobs, 10,000+ visitors/day)
```php
$schedule->command('jobs:fetch --limit=100')
->hourly()
->withoutOverlapping()
->onOneServer()
->runInBackground();
$schedule->command('jobs:enrich')
->everyFourHours()
->runInBackground();
$schedule->command('jobs:deduplicate')
->twiceDaily(3, 15)
->runInBackground();
$schedule->command('jobs:clean --days=30')
->dailyAt('02:00')
->runInBackground();
// Add sitemap regeneration
$schedule->command('sitemap:generate')
->dailyAt('04:00')
->runInBackground();
```
---
## Best Practices
### 1. Monitoring
Always monitor your scheduled tasks:
```bash
# View scheduled tasks
php artisan schedule:list
# Test schedule (simulates next minute)
php artisan schedule:test
# Run schedule manually
php artisan schedule:run
# Check logs
tail -f storage/logs/laravel.log | grep -i "schedule\|fetch\|enrich\|deduplicate\|clean"
```
### 2. Resource Management
- Use `->runInBackground()` for heavy tasks to prevent blocking
- Use `->withoutOverlapping()` to prevent concurrent runs
- Use `->onOneServer()` if running multiple app servers
### 3. Error Handling
Add notifications for failures:
```php
$schedule->command('jobs:fetch')
->everyTwoHours()
->onFailure(function () {
// Log error, send notification, etc.
Log::error('Job fetch failed!');
});
```
### 4. Performance Optimization
```php
// Run during off-peak hours only
$schedule->command('jobs:fetch')
->everyTwoHours()
->between('6:00', '22:00'); // Only 6 AM to 10 PM
// Avoid peak hours
$schedule->command('jobs:deduplicate')
->dailyAt('03:00')
->unlessBetween('08:00', '18:00'); // Not during business hours
```
---
## Task Dependencies
The order matters! Run tasks in this sequence:
```
1. jobs:fetch (2 AM, 4 AM, 6 AM, ...)
↓
2. jobs:clean (2 AM) - Remove old/expired first
↓
3. jobs:deduplicate (3 AM) - Then remove duplicates
↓
4. jobs:enrich (6 AM, 12 PM, 6 PM, 12 AM) - Finally enrich
```
This ensures:
- Old jobs are removed before deduplication
- Deduplication happens before enrichment
- Enrichment works on clean, deduplicated data
---
## Troubleshooting
### Jobs not being fetched?
1. Check if scheduler is running:
```bash
php artisan schedule:list
```
2. Verify cron job is active:
```bash
crontab -l
```
3. Test manual fetch:
```bash
php artisan jobs:fetch --limit=5
```
4. Check API credentials in `.env`
### Duplicates still appearing?
1. Run deduplication manually:
```bash
php artisan jobs:deduplicate --force
```
2. Check fingerprint generation logic
3. Consider running deduplication more frequently
### Database growing too large?
1. Reduce retention period:
```php
'jobs:clean --days=30'
```
2. Run cleanup more frequently:
```php
->twiceDaily(2, 14)
```
3. Check for failed deletions:
```bash
php artisan jobs:clean --verbose
```
---
## Summary
**Optimal Schedule (Default):**
- **Fetch:** Every 2 hours
- **Enrich:** Every 6 hours
- **Deduplicate:** Daily at 3 AM
- **Clean:** Daily at 2 AM (45-day retention)
This provides a good balance of:
✅ Fresh content
✅ Resource efficiency
✅ Database health
✅ User experience
✅ API compliance
Adjust based on your specific needs, traffic patterns, and resource constraints.
+5
View File
@@ -0,0 +1,5 @@
We need a tags system for the jobs.
Also, on the jobs table we need a is_curated bool column.
a command that gets jobs that were not curated, does an POST api call that i will give later to you with the job name and description and the response will be an array of tags.
we should save those tags and associate it with the job.
we should also display the tags in the job page,
+49 -10
View File
@@ -16,20 +16,59 @@ return Application::configure(basePath: dirname(__DIR__))
// //
}) })
->withSchedule(function (Schedule $schedule): void { ->withSchedule(function (Schedule $schedule): void {
// Fetch jobs from all sources hourly // =========================================================================
$schedule->command('jobs:fetch --limit=100') // Job Fetching - Fetch fresh jobs from external APIs
->hourly() // =========================================================================
// Fetch jobs every 2 hours to keep listings fresh
// Limit per source to avoid rate limits and keep queue manageable
$schedule->command('jobs:fetch --limit=50')
->everyTwoHours()
->withoutOverlapping() ->withoutOverlapping()
->onOneServer(); ->onOneServer()
->runInBackground();
// Clean up old jobs daily at 2 AM // =========================================================================
$schedule->command('jobs:clean --days=30') // Job Enrichment - Auto-categorize and enhance job data
->dailyAt('02:00') // =========================================================================
->onOneServer();
// Deduplicate jobs weekly on Sunday at 3 AM // Enrich new jobs every 6 hours (categorization, experience level detection)
// This runs after jobs have been fetched to categorize uncategorized jobs
$schedule->command('jobs:enrich')
->everySixHours()
->withoutOverlapping()
->onOneServer()
->runInBackground();
// =========================================================================
// Job Deduplication - Remove duplicate job listings
// =========================================================================
// Deduplicate jobs daily at 3 AM (after fetch cycles have completed)
// Daily is better than weekly to keep listings clean
$schedule->command('jobs:deduplicate') $schedule->command('jobs:deduplicate')
->weeklyOn(0, '03:00') ->dailyAt('03:00')
->onOneServer()
->runInBackground();
// =========================================================================
// Job Cleanup - Remove expired and old jobs
// =========================================================================
// Clean up expired and old jobs daily at 2 AM
// Keeps database lean and removes stale listings
$schedule->command('jobs:clean --days=45')
->dailyAt('02:00')
->onOneServer()
->runInBackground();
// =========================================================================
// Cache Management - Keep caches fresh
// =========================================================================
// Prune stale cache tags hourly
$schedule->command('cache:prune-stale-tags')
->hourly()
->onOneServer(); ->onOneServer();
}) })
->withExceptions(function (Exceptions $exceptions): void { ->withExceptions(function (Exceptions $exceptions): void {
Generated
+3 -3
View File
@@ -9221,12 +9221,12 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": {},
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "^8.3" "php": "^8.3"
}, },
"platform-dev": [], "platform-dev": {},
"plugin-api-version": "2.2.0" "plugin-api-version": "2.9.0"
} }
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
This favicon was generated using the following font:
- Font Title: Leckerli One
- Font Author: undefined
- Font Source: https://fonts.gstatic.com/s/leckerlione/v21/V8mCoQH8VCsNttEnxnGQ-1itLZxcBtItFw.ttf
- Font License: undefined)
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+1
View File
@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
#
# Queue Worker Script for JobifyGarden
# This script runs the queue worker and ensures it doesn't run if already running
#
# Get the directory where this script is located
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$DIR"
# Check if queue worker is already running
if pgrep -f "artisan queue:work" > /dev/null; then
echo "Queue worker already running, skipping..."
exit 0
fi
# Run the queue worker
# --max-time=50 ensures it runs for max 50 seconds
# --stop-when-empty exits when queue is empty
# --sleep=3 waits 3 seconds between jobs
# --tries=3 retries failed jobs 3 times
php artisan queue:work --max-time=50 --stop-when-empty --sleep=3 --tries=3 >> storage/logs/queue-worker.log 2>&1