78 lines
3.2 KiB
PHP
78 lines
3.2 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use App\Enums\RoleName;
|
||
use App\Models\EarnApiToken;
|
||
use App\Models\EarnTaskAssignment;
|
||
use App\Models\User;
|
||
use Illuminate\Http\JsonResponse;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\DB;
|
||
use RuntimeException;
|
||
|
||
/**
|
||
* Kodular istemcisine cihazla eşleştirilmiş JSON görev akışı sağlar.
|
||
*
|
||
* @author Gökhan Engin - İletişim : https://bit.ly/YazılımUzmanı
|
||
*/
|
||
class TestTaskController extends Controller
|
||
{
|
||
/**
|
||
* Yapılandırılmış görevleri gerçek müşteri sahibiyle doğrulayıp token ve cihaza tekil olarak atar.
|
||
*
|
||
* @author Gökhan Engin - İletişim : https://bit.ly/YazılımUzmanı
|
||
*/
|
||
public function index(Request $request): JsonResponse
|
||
{
|
||
$allowedHosts = config('test_tasks.allowed_hosts', []);
|
||
$tasks = config('test_tasks.tasks', []);
|
||
$customerEmail = config('test_tasks.customer_email');
|
||
$token = $request->attributes->get('earn_api_token');
|
||
$deviceHash = $request->attributes->get('earn_device_hash');
|
||
|
||
if (! $token instanceof EarnApiToken || ! is_string($deviceHash) || ! is_string($customerEmail)) {
|
||
throw new RuntimeException('Test task ownership is not configured.');
|
||
}
|
||
|
||
$customer = User::query()->where('email', $customerEmail)->first();
|
||
if (! $customer?->hasRole(RoleName::Customer)) {
|
||
throw new RuntimeException('Test task customer is invalid.');
|
||
}
|
||
|
||
$payload = [];
|
||
foreach ($tasks as $task) {
|
||
$url = $task['url'] ?? null;
|
||
|
||
if (! is_string($url) || parse_url($url, PHP_URL_SCHEME) !== 'https' || ! in_array(parse_url($url, PHP_URL_HOST), $allowedHosts, true)) {
|
||
throw new RuntimeException('Test task configuration contains a URL outside the HTTPS allowlist.');
|
||
}
|
||
|
||
// Görevi ilk istemciye atomik olarak kilitler.
|
||
// Yazar: Gökhan Engin - İletişim : https://bit.ly/YazılımUzmanı
|
||
$assignment = DB::transaction(function () use ($task, $customer, $token, $deviceHash): EarnTaskAssignment {
|
||
return EarnTaskAssignment::query()->lockForUpdate()->firstOrCreate(
|
||
['task_id' => $task['task_id']],
|
||
['user_id' => $customer->id, 'earn_member_id' => $token->tokenable_id,
|
||
'personal_access_token_id' => $token->id, 'device_id_hash' => $deviceHash,
|
||
'url' => $task['url'], 'duration_seconds' => $task['duration_seconds'], 'assigned_at' => now()],
|
||
);
|
||
}, 3);
|
||
|
||
if ($assignment->personal_access_token_id !== $token->id
|
||
|| ! hash_equals($assignment->device_id_hash, $deviceHash)
|
||
|| $assignment->result()->exists()) {
|
||
continue;
|
||
}
|
||
|
||
$payload[] = ['CID' => $assignment->user_id, 'task_id' => $assignment->task_id,
|
||
'url' => $assignment->url, 'duration_seconds' => $assignment->duration_seconds,
|
||
'completed' => false, 'responseCode' => null, 'error' => null];
|
||
}
|
||
|
||
return response()
|
||
->json(['tasks' => $payload])
|
||
->header('Cache-Control', 'no-store');
|
||
}
|
||
}
|