phase 1 and 2
This commit is contained in:
@@ -11,7 +11,7 @@ return new class extends Migration
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
Schema::create('queue_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('queue')->index();
|
||||
$table->longText('payload');
|
||||
@@ -50,7 +50,7 @@ return new class extends Migration
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('jobs');
|
||||
Schema::dropIfExists('queue_jobs');
|
||||
Schema::dropIfExists('job_batches');
|
||||
Schema::dropIfExists('failed_jobs');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('job_categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->text('description')->nullable();
|
||||
$table->string('icon')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('slug');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('job_categories');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('job_locations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('city')->nullable();
|
||||
$table->string('state')->nullable();
|
||||
$table->string('country');
|
||||
$table->string('country_code', 2);
|
||||
$table->decimal('latitude', 10, 7)->nullable();
|
||||
$table->decimal('longitude', 10, 7)->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['country', 'state', 'city']);
|
||||
$table->index('country_code');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('job_locations');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('job_sources', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('api_url');
|
||||
$table->text('api_key')->nullable();
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->integer('rate_limit')->default(100);
|
||||
$table->timestamp('last_fetched_at')->nullable();
|
||||
$table->json('configuration')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('is_active');
|
||||
$table->index('last_fetched_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('job_sources');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('external_id')->nullable();
|
||||
$table->string('title');
|
||||
$table->string('company');
|
||||
$table->text('description');
|
||||
$table->string('location')->nullable();
|
||||
$table->decimal('salary_min', 10, 2)->nullable();
|
||||
$table->decimal('salary_max', 10, 2)->nullable();
|
||||
$table->string('salary_currency', 3)->nullable()->default('USD');
|
||||
$table->enum('job_type', ['full-time', 'part-time', 'contract', 'temporary', 'internship', 'freelance'])->nullable();
|
||||
$table->foreignId('job_source_id')->constrained('job_sources')->onDelete('cascade');
|
||||
$table->string('source_url')->nullable();
|
||||
$table->string('apply_url')->nullable();
|
||||
$table->timestamp('posted_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->boolean('remote_allowed')->default(false);
|
||||
$table->enum('experience_level', ['entry', 'mid', 'senior', 'lead', 'executive'])->nullable();
|
||||
$table->foreignId('job_category_id')->nullable()->constrained('job_categories')->onDelete('set null');
|
||||
$table->foreignId('job_location_id')->nullable()->constrained('job_locations')->onDelete('set null');
|
||||
$table->string('fingerprint')->nullable()->unique();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->index(['job_source_id', 'external_id']);
|
||||
$table->index('posted_at');
|
||||
$table->index('expires_at');
|
||||
$table->index('job_type');
|
||||
$table->index('experience_level');
|
||||
$table->index('remote_allowed');
|
||||
$table->index('job_category_id');
|
||||
$table->index('job_location_id');
|
||||
$table->fullText(['title', 'company', 'description']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('jobs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_saved_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
|
||||
$table->foreignId('job_id')->constrained('jobs')->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['user_id', 'job_id']);
|
||||
$table->index('user_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_saved_jobs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_job_alerts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
|
||||
$table->string('keywords')->nullable();
|
||||
$table->string('location')->nullable();
|
||||
$table->foreignId('job_category_id')->nullable()->constrained('job_categories')->onDelete('set null');
|
||||
$table->enum('frequency', ['instant', 'daily', 'weekly'])->default('daily');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamp('last_sent_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('user_id');
|
||||
$table->index('is_active');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_job_alerts');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\JobSource;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class JobSourceSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$sources = [
|
||||
[
|
||||
'name' => 'Remotive',
|
||||
'api_url' => 'https://remotive.com/api',
|
||||
'api_key' => null, // No API key required
|
||||
'is_active' => true,
|
||||
'rate_limit' => 60, // requests per hour
|
||||
'configuration' => [
|
||||
'description' => 'Remote jobs API - Free, no authentication required',
|
||||
'docs' => 'https://remotive.com/api-documentation',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'The Muse',
|
||||
'api_url' => 'https://www.themuse.com/api/public',
|
||||
'api_key' => null, // Optional API key for higher limits
|
||||
'is_active' => true,
|
||||
'rate_limit' => 100, // requests per hour
|
||||
'configuration' => [
|
||||
'description' => 'The Muse job board API - Free tier available',
|
||||
'docs' => 'https://www.themuse.com/developers/api/v2',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Adzuna',
|
||||
'api_url' => 'https://api.adzuna.com/v1/api/jobs',
|
||||
'api_key' => env('ADZUNA_API_KEY'), // Format: app_id:app_key
|
||||
'is_active' => env('ADZUNA_API_KEY') ? true : false,
|
||||
'rate_limit' => 100, // requests per hour
|
||||
'configuration' => [
|
||||
'country' => 'us',
|
||||
'description' => 'Adzuna job search API - Requires free API key',
|
||||
'docs' => 'https://developer.adzuna.com/overview',
|
||||
'signup' => 'https://developer.adzuna.com/signup',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($sources as $source) {
|
||||
JobSource::updateOrCreate(
|
||||
['name' => $source['name']],
|
||||
$source
|
||||
);
|
||||
}
|
||||
|
||||
$this->command->info('Job sources seeded successfully!');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user