.
This commit is contained in:
88
qwen/hack/src/Services/AuthService.php
Normal file
88
qwen/hack/src/Services/AuthService.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?hh // strict
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use League\OAuth2\Client\Provider\Google;
|
||||
use League\OAuth2\Client\Provider\Github;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class AuthService
|
||||
{
|
||||
private Google $googleProvider;
|
||||
private Github $githubProvider;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->googleProvider = new Google([
|
||||
'clientId' => $_ENV['GOOGLE_CLIENT_ID'] ?? '',
|
||||
'clientSecret' => $_ENV['GOOGLE_CLIENT_SECRET'] ?? '',
|
||||
'redirectUri' => $_ENV['APP_URL'] . '/auth/google/callback' ?? 'http://localhost:18000/auth/google/callback',
|
||||
]);
|
||||
|
||||
$this->githubProvider = new Github([
|
||||
'clientId' => $_ENV['GITHUB_CLIENT_ID'] ?? '',
|
||||
'clientSecret' => $_ENV['GITHUB_CLIENT_SECRET'] ?? '',
|
||||
'redirectUri' => $_ENV['APP_URL'] . '/auth/github/callback' ?? 'http://localhost:18000/auth/github/callback',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getGoogleAuthorizationUrl(): string
|
||||
{
|
||||
return $this->googleProvider->getAuthorizationUrl([
|
||||
'scope' => ['email', 'profile']
|
||||
]);
|
||||
}
|
||||
|
||||
public function getGithubAuthorizationUrl(): string
|
||||
{
|
||||
return $this->githubProvider->getAuthorizationUrl([
|
||||
'scope' => ['user:email']
|
||||
]);
|
||||
}
|
||||
|
||||
public function handleGoogleCallback(ServerRequestInterface $request): array
|
||||
{
|
||||
$code = $request->getQueryParams()['code'] ?? null;
|
||||
|
||||
if (!$code) {
|
||||
throw new \Exception('No authorization code received from Google');
|
||||
}
|
||||
|
||||
$token = $this->googleProvider->getAccessToken('authorization_code', [
|
||||
'code' => $code
|
||||
]);
|
||||
|
||||
$user = $this->googleProvider->getResourceOwner($token);
|
||||
|
||||
return [
|
||||
'provider' => 'google',
|
||||
'id' => $user->getId(),
|
||||
'email' => $user->getEmail(),
|
||||
'name' => $user->getName(),
|
||||
'avatar' => $user->getAvatar(),
|
||||
];
|
||||
}
|
||||
|
||||
public function handleGithubCallback(ServerRequestInterface $request): array
|
||||
{
|
||||
$code = $request->getQueryParams()['code'] ?? null;
|
||||
|
||||
if (!$code) {
|
||||
throw new \Exception('No authorization code received from GitHub');
|
||||
}
|
||||
|
||||
$token = $this->githubProvider->getAccessToken('authorization_code', [
|
||||
'code' => $code
|
||||
]);
|
||||
|
||||
$user = $this->githubProvider->getResourceOwner($token);
|
||||
|
||||
return [
|
||||
'provider' => 'github',
|
||||
'id' => $user->getId(),
|
||||
'email' => $user->getEmail(),
|
||||
'name' => $user->getName(),
|
||||
'avatar' => $user->getAvatarUrl(),
|
||||
];
|
||||
}
|
||||
}
|
||||
143
qwen/hack/src/Services/JobService.php
Normal file
143
qwen/hack/src/Services/JobService.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?hh // strict
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Job;
|
||||
use App\Models\Tenant;
|
||||
use PDO;
|
||||
|
||||
class JobService
|
||||
{
|
||||
private PDO $db;
|
||||
|
||||
public function __construct(PDO $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all jobs for the current tenant
|
||||
*/
|
||||
public function getAllJobs(?Tenant $tenant, ?array $filters = null): array
|
||||
{
|
||||
$tenantId = $tenant ? $tenant->getId() : 'default';
|
||||
|
||||
$sql = "SELECT * FROM jobs WHERE tenant_id = :tenant_id AND status = 'active'";
|
||||
|
||||
// Add filters if provided
|
||||
$params = [':tenant_id' => $tenantId];
|
||||
|
||||
if ($filters) {
|
||||
if (isset($filters['location'])) {
|
||||
$sql .= " AND location LIKE :location";
|
||||
$params[':location'] = '%' . $filters['location'] . '%';
|
||||
}
|
||||
|
||||
if (isset($filters['keywords'])) {
|
||||
$sql .= " AND (title LIKE :keywords OR description LIKE :keywords)";
|
||||
$params[':keywords'] = '%' . $filters['keywords'] . '%';
|
||||
}
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY created_at DESC";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
|
||||
$jobs = [];
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$jobs[] = new Job(
|
||||
id: (int)$row['id'],
|
||||
title: $row['title'],
|
||||
description: $row['description'],
|
||||
location: $row['location'],
|
||||
employmentType: $row['employment_type'],
|
||||
tenantId: $row['tenant_id']
|
||||
);
|
||||
}
|
||||
|
||||
return $jobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific job by ID for the current tenant
|
||||
*/
|
||||
public function getJobById(int $jobId, ?Tenant $tenant): ?Job
|
||||
{
|
||||
$tenantId = $tenant ? $tenant->getId() : 'default';
|
||||
|
||||
$sql = "SELECT * FROM jobs WHERE id = :id AND tenant_id = :tenant_id";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([
|
||||
':id' => $jobId,
|
||||
':tenant_id' => $tenantId
|
||||
]);
|
||||
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Job(
|
||||
id: (int)$row['id'],
|
||||
title: $row['title'],
|
||||
description: $row['description'],
|
||||
location: $row['location'],
|
||||
employmentType: $row['employment_type'],
|
||||
tenantId: $row['tenant_id']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new job posting for the tenant
|
||||
*/
|
||||
public function createJob(Job $job): bool
|
||||
{
|
||||
$sql = "INSERT INTO jobs (title, description, location, employment_type, tenant_id) VALUES (:title, :description, :location, :employment_type, :tenant_id)";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
|
||||
return $stmt->execute([
|
||||
':title' => $job->getTitle(),
|
||||
':description' => $job->getDescription(),
|
||||
':location' => $job->getLocation(),
|
||||
':employment_type' => $job->getEmploymentType(),
|
||||
':tenant_id' => $job->getTenantId()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing job for the tenant
|
||||
*/
|
||||
public function updateJob(Job $job): bool
|
||||
{
|
||||
$sql = "UPDATE jobs SET title = :title, description = :description, location = :location, employment_type = :employment_type, updated_at = NOW() WHERE id = :id AND tenant_id = :tenant_id";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
|
||||
return $stmt->execute([
|
||||
':id' => $job->getId(),
|
||||
':title' => $job->getTitle(),
|
||||
':description' => $job->getDescription(),
|
||||
':location' => $job->getLocation(),
|
||||
':employment_type' => $job->getEmploymentType(),
|
||||
':tenant_id' => $job->getTenantId()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a job for the tenant
|
||||
*/
|
||||
public function deleteJob(int $jobId, ?Tenant $tenant): bool
|
||||
{
|
||||
$tenantId = $tenant ? $tenant->getId() : 'default';
|
||||
|
||||
$sql = "DELETE FROM jobs WHERE id = :id AND tenant_id = :tenant_id";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
|
||||
$result = $stmt->execute([
|
||||
':id' => $jobId,
|
||||
':tenant_id' => $tenantId
|
||||
]);
|
||||
|
||||
return $result && $stmt->rowCount() > 0;
|
||||
}
|
||||
}
|
||||
55
qwen/hack/src/Services/TenantResolver.php
Normal file
55
qwen/hack/src/Services/TenantResolver.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?hh // strict
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Tenant;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class TenantResolver
|
||||
{
|
||||
/**
|
||||
* Resolve the tenant based on the request
|
||||
*/
|
||||
public function resolveTenant(ServerRequestInterface $request): ?Tenant
|
||||
{
|
||||
$host = $request->getUri()->getHost();
|
||||
$path = $request->getUri()->getPath();
|
||||
|
||||
// Try to extract tenant from subdomain
|
||||
// Format: tenant.merchantsofhope.org
|
||||
$hostParts = explode('.', $host);
|
||||
if (count($hostParts) >= 3) {
|
||||
$subdomain = $hostParts[0];
|
||||
return $this->findTenantBySubdomain($subdomain);
|
||||
}
|
||||
|
||||
// Alternatively, look for tenant in path
|
||||
// Format: merchantsofhope.org/tenant-name
|
||||
$pathParts = explode('/', trim($path, '/'));
|
||||
if (!empty($pathParts[0])) {
|
||||
return $this->findTenantBySubdomain($pathParts[0]);
|
||||
}
|
||||
|
||||
// Default to a global tenant if none found
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find tenant by subdomain in the database
|
||||
*/
|
||||
private function findTenantBySubdomain(string $subdomain): ?Tenant
|
||||
{
|
||||
// This would normally query the database
|
||||
// For now, we'll return a mock tenant if subdomain exists
|
||||
if (!empty($subdomain)) {
|
||||
return new Tenant(
|
||||
id: 'tenant-' . $subdomain,
|
||||
name: ucfirst($subdomain) . ' Tenant',
|
||||
subdomain: $subdomain,
|
||||
isActive: true
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user