Complete Phase 1: MVC, DB migrations, Auth, RBAC, Security, and Views

This commit is contained in:
Hamza-Ayed
2026-06-05 00:56:41 +03:00
parent 7ffbc8bafa
commit bed7624ae9
51 changed files with 3295 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
CREATE TABLE roles (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
code VARCHAR(100) NOT NULL UNIQUE,
description TEXT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE permissions (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
code VARCHAR(100) NOT NULL UNIQUE,
description TEXT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE role_permissions (
role_id BIGINT UNSIGNED NOT NULL,
permission_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (role_id, permission_id),
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,20 @@
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
INDEX idx_users_email (email),
INDEX idx_users_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE user_roles (
user_id BIGINT UNSIGNED NOT NULL,
role_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,69 @@
CREATE TABLE organizations (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
domain VARCHAR(255) NULL UNIQUE,
description TEXT NULL,
type VARCHAR(50) NOT NULL, -- 'vc', 'angel', 'accelerator', 'incubator', 'venture_studio', 'partner'
country VARCHAR(100) NULL,
city VARCHAR(100) NULL,
website_url VARCHAR(2048) NULL,
linkedin_url VARCHAR(2048) NULL,
logo_url VARCHAR(2048) NULL,
employee_count INT NULL,
funding_stage VARCHAR(100) NULL,
crm_status VARCHAR(50) NOT NULL DEFAULT 'New', -- 'New', 'Researching', 'Contacted', 'Follow Up', 'Meeting Scheduled', 'Interested', 'Rejected', 'Invested'
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
INDEX idx_orgs_type (type),
INDEX idx_orgs_crm_status (crm_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE investors (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
organization_id BIGINT UNSIGNED NOT NULL UNIQUE,
min_ticket_size DECIMAL(15,2) NULL,
max_ticket_size DECIMAL(15,2) NULL,
investment_stages JSON NULL, -- e.g. ['Pre-Seed', 'Seed']
target_geographies JSON NULL, -- e.g. ['KSA', 'Egypt']
active_portfolio_count INT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE accelerators (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
organization_id BIGINT UNSIGNED NOT NULL UNIQUE,
cohort_size INT NULL,
program_duration_weeks INT NULL,
equity_taken_percent DECIMAL(5,2) NULL,
funding_provided DECIMAL(15,2) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE incubators (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
organization_id BIGINT UNSIGNED NOT NULL UNIQUE,
program_duration_weeks INT NULL,
facilities_offered JSON NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE venture_studios (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
organization_id BIGINT UNSIGNED NOT NULL UNIQUE,
focus_areas JSON NULL,
resources_provided JSON NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,32 @@
CREATE TABLE contacts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
organization_id BIGINT UNSIGNED NULL,
first_name VARCHAR(150) NOT NULL,
last_name VARCHAR(150) NOT NULL,
email VARCHAR(255) NULL,
phone VARCHAR(50) NULL,
title VARCHAR(150) NULL,
linkedin_url VARCHAR(2048) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE SET NULL,
INDEX idx_contacts_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE crm_activities (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
organization_id BIGINT UNSIGNED NOT NULL,
contact_id BIGINT UNSIGNED NULL,
type VARCHAR(50) NOT NULL, -- 'meeting', 'email', 'note', 'follow_up'
details TEXT NOT NULL,
scheduled_at TIMESTAMP NULL,
completed_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE SET NULL,
INDEX idx_crm_act_type (type),
INDEX idx_crm_act_org (organization_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,82 @@
CREATE TABLE opportunities (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
type VARCHAR(50) NOT NULL, -- 'grant', 'competition', 'demo_day', 'event', 'partnership', 'investment'
organization_id BIGINT UNSIGNED NULL,
url VARCHAR(2048) NULL,
deadline TIMESTAMP NULL,
amount DECIMAL(15, 2) NULL,
location VARCHAR(255) NULL,
status VARCHAR(50) NOT NULL DEFAULT 'active',
score INT NOT NULL DEFAULT 0,
raw_data JSON NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE SET NULL,
INDEX idx_opps_type (type),
INDEX idx_opps_status (status),
INDEX idx_opps_score (score)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE tags (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE,
slug VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE opportunity_tags (
opportunity_id BIGINT UNSIGNED NOT NULL,
tag_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (opportunity_id, tag_id),
FOREIGN KEY (opportunity_id) REFERENCES opportunities(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE applications (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
opportunity_id BIGINT UNSIGNED NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'draft', -- 'draft', 'submitted', 'under_review', 'accepted', 'rejected'
submission_date TIMESTAMP NULL,
notes TEXT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (opportunity_id) REFERENCES opportunities(id) ON DELETE CASCADE,
INDEX idx_applications_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE partnerships (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT NULL,
partner_organization_id BIGINT UNSIGNED NOT NULL,
target_organization_id BIGINT UNSIGNED NULL,
terms TEXT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'open', -- 'open', 'signed', 'cancelled'
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (partner_organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (target_organization_id) REFERENCES organizations(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE startup_events (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
organization_id BIGINT UNSIGNED NULL,
title VARCHAR(255) NOT NULL,
description TEXT NULL,
start_date TIMESTAMP NULL,
end_date TIMESTAMP NULL,
location_type VARCHAR(50) NOT NULL, -- 'online', 'in_person', 'hybrid'
location_address VARCHAR(500) NULL,
event_url VARCHAR(2048) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE SET NULL,
INDEX idx_events_start (start_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,19 @@
CREATE TABLE sources (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
url VARCHAR(2048) NOT NULL,
type VARCHAR(50) NOT NULL, -- 'rss', 'api', 'website'
status VARCHAR(50) NOT NULL DEFAULT 'active',
last_crawled_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
INDEX idx_sources_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE source_categories (
source_id BIGINT UNSIGNED NOT NULL,
category VARCHAR(100) NOT NULL,
PRIMARY KEY (source_id, category),
FOREIGN KEY (source_id) REFERENCES sources(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,56 @@
CREATE TABLE ai_reports (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content JSON NOT NULL,
generation_date DATE NOT NULL,
report_type VARCHAR(50) NOT NULL DEFAULT 'daily', -- 'daily', 'weekly', 'custom'
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_ai_reports_date (generation_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE email_reports (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
ai_report_id BIGINT UNSIGNED NOT NULL,
recipient_email VARCHAR(255) NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'pending', -- 'pending', 'sent', 'failed'
sent_at TIMESTAMP NULL,
error_message TEXT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (ai_report_id) REFERENCES ai_reports(id) ON DELETE CASCADE,
INDEX idx_email_rep_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE activity_logs (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NULL,
action VARCHAR(255) NOT NULL,
description TEXT NULL,
ip_address VARCHAR(45) NULL,
user_agent VARCHAR(500) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE notifications (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
title VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
read_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_notifications_unread (user_id, read_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE settings (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`key` VARCHAR(100) NOT NULL UNIQUE,
value TEXT NULL,
description TEXT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_settings_key (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,154 @@
<?php
namespace Database\Seeds;
use App\Services\Database\Connection;
use PDO;
class DatabaseSeeder
{
private PDO $pdo;
public function __construct(Connection $connection)
{
$this->pdo = $connection->getPdo();
}
/**
* Seed all defaults.
*/
public function seed(): void
{
echo "Seeding roles and permissions...\n";
$this->seedRolesAndPermissions();
echo "Seeding default settings...\n";
$this->seedSettings();
echo "Seeding sample crawler sources...\n";
$this->seedSources();
echo "Seeding default administrator...\n";
$this->seedDefaultAdmin();
echo "Seeding completed successfully!\n";
}
private function seedRolesAndPermissions(): void
{
// Insert Roles
$roles = [
['name' => 'Administrator', 'code' => 'admin', 'description' => 'Full administrative access to the platform.'],
['name' => 'Member', 'code' => 'member', 'description' => 'Access to CRM, Opportunities and Reports.'],
];
foreach ($roles as $role) {
$stmt = $this->pdo->prepare("INSERT IGNORE INTO roles (name, code, description) VALUES (?, ?, ?)");
$stmt->execute([$role['name'], $role['code'], $role['description']]);
}
// Insert Permissions
$permissions = [
['name' => 'View Dashboard', 'code' => 'view_dashboard', 'description' => 'Access the admin dashboard metrics.'],
['name' => 'Manage CRM', 'code' => 'manage_crm', 'description' => 'Create/update contacts and log interactions.'],
['name' => 'View Opportunities', 'code' => 'view_opportunities', 'description' => 'Browse VCs, grants and accelerators.'],
['name' => 'Run Collectors', 'code' => 'run_collectors', 'description' => 'Manually trigger crawler collections.'],
['name' => 'Generate Reports', 'code' => 'generate_reports', 'description' => 'Trigger AI daily summaries.'],
['name' => 'Manage Settings', 'code' => 'manage_settings', 'description' => 'Update application parameters.'],
['name' => 'Manage Users', 'code' => 'manage_users', 'description' => 'Manage roles and accounts of users.'],
];
foreach ($permissions as $permission) {
$stmt = $this->pdo->prepare("INSERT IGNORE INTO permissions (name, code, description) VALUES (?, ?, ?)");
$stmt->execute([$permission['name'], $permission['code'], $permission['description']]);
}
// Fetch Role IDs and Permission IDs
$roleIds = $this->pdo->query("SELECT id, code FROM roles")->fetchAll(PDO::FETCH_KEY_PAIR);
$permissionIds = $this->pdo->query("SELECT id, code FROM permissions")->fetchAll(PDO::FETCH_KEY_PAIR);
// Map Permissions to Admin (All permissions)
foreach ($permissionIds as $pId => $pCode) {
$stmt = $this->pdo->prepare("INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?, ?)");
$stmt->execute([$roleIds['admin'], $pId]);
}
// Map Permissions to Member (Subset of permissions)
$memberPermissions = ['view_dashboard', 'manage_crm', 'view_opportunities', 'generate_reports'];
foreach ($permissionIds as $pId => $pCode) {
if (in_array($pCode, $memberPermissions)) {
$stmt = $this->pdo->prepare("INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?, ?)");
$stmt->execute([$roleIds['member'], $pId]);
}
}
}
private function seedSettings(): void
{
$settings = [
['key' => 'system_email', 'value' => 'info@scoutiq.intaleqapp.com', 'description' => 'Primary sender email address.'],
['key' => 'crawler_enabled', 'value' => 'true', 'description' => 'Global toggle for data collection crawlers.'],
['key' => 'crawler_interval_hours', 'value' => '24', 'description' => 'Delay between crawler runs.'],
];
foreach ($settings as $setting) {
$stmt = $this->pdo->prepare("INSERT IGNORE INTO settings (`key`, value, description) VALUES (?, ?, ?)");
$stmt->execute([$setting['key'], $setting['value'], $setting['description']]);
}
}
private function seedSources(): void
{
$sources = [
['name' => 'TechCrunch Mobility Feed', 'url' => 'https://techcrunch.com/category/transportation/feed/', 'type' => 'rss'],
['name' => 'VentureBeat AI Feed', 'url' => 'https://venturebeat.com/category/ai/feed/', 'type' => 'rss'],
['name' => 'YC Directory API', 'url' => 'https://api.ycombinator.com/v1/companies', 'type' => 'api'],
];
foreach ($sources as $source) {
$stmt = $this->pdo->prepare("SELECT id FROM sources WHERE url = ?");
$stmt->execute([$source['url']]);
if (!$stmt->fetch()) {
$stmt = $this->pdo->prepare("INSERT INTO sources (name, url, type, status) VALUES (?, ?, ?, 'active')");
$stmt->execute([$source['name'], $source['url'], $source['type']]);
$sourceId = (int)$this->pdo->lastInsertId();
$category = str_contains($source['name'], 'Mobility') ? 'mobility' : 'ai';
$stmt = $this->pdo->prepare("INSERT INTO source_categories (source_id, category) VALUES (?, ?)");
$stmt->execute([$sourceId, $category]);
}
}
}
private function seedDefaultAdmin(): void
{
$email = 'admin@scoutiq.com';
$stmt = $this->pdo->prepare("SELECT id FROM users WHERE email = ?");
$stmt->execute([$email]);
if (!$stmt->fetch()) {
$passwordHash = password_hash('AdminScout123!', PASSWORD_BCRYPT);
$this->pdo->beginTransaction();
try {
$stmt = $this->pdo->prepare("INSERT INTO users (name, email, password_hash, status) VALUES (?, ?, ?, 'active')");
$stmt->execute(['ScoutIQ Admin', $email, $passwordHash]);
$userId = (int)$this->pdo->lastInsertId();
$stmt = $this->pdo->prepare("SELECT id FROM roles WHERE code = 'admin'");
$stmt->execute();
$roleId = $stmt->fetchColumn();
if ($roleId) {
$stmt = $this->pdo->prepare("INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)");
$stmt->execute([$userId, $roleId]);
}
$this->pdo->commit();
echo "Admin User Created: {$email} / AdminScout123!\n";
} catch (\Exception $e) {
$this->pdo->rollBack();
echo "Failed to create default admin: " . $e->getMessage() . "\n";
}
}
}
}