Skip to content

Commit 625c81a

Browse files
authored
Merge pull request #58 from storviaio/refactor/enhancements
Refactor/enhancements
2 parents ba35508 + d5890f9 commit 625c81a

14 files changed

Lines changed: 184 additions & 538 deletions

phpstan-baseline.neon

Lines changed: 23 additions & 365 deletions
Large diffs are not rendered by default.

phpstan.neon.dist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ parameters:
1010
tmpDir: build/phpstan
1111
checkOctaneCompatibility: true
1212
checkModelProperties: true
13+
ignoreErrors:
14+
-
15+
message: '#^Parameter \#1 \$view of function view expects view\-string\|null, string given\.$#'
16+
reportUnmatched: false
1317

src/Http/Controllers/QueueMonitorController.php

Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Illuminate\Support\Facades\DB;
1010
use Illuminate\Support\Facades\Schema;
1111
use Storvia\Vantage\Models\VantageJob;
12+
use Storvia\Vantage\Support\JobRestorer;
1213
use Storvia\Vantage\Support\QueueDepthChecker;
1314
use Storvia\Vantage\Support\TagAggregator;
1415
use Storvia\Vantage\Support\VantageLogger;
@@ -424,7 +425,7 @@ public function retry($id)
424425
}
425426

426427
// Safely restore job from payload with security checks
427-
$job = $this->restoreJobFromPayload($run, $jobClass);
428+
$job = app(JobRestorer::class)->restore($run, $jobClass);
428429

429430
if (! $job) {
430431
return back()->with('error', 'Unable to restore job. Payload might be missing or corrupted.');
@@ -450,92 +451,6 @@ public function retry($id)
450451
}
451452
}
452453

453-
/**
454-
* Safely restore job from payload with security checks.
455-
*
456-
* @param VantageJob $run The job run record
457-
* @param string $expectedJobClass The expected job class name for validation
458-
* @return object|null The restored job object or null on failure
459-
*/
460-
protected function restoreJobFromPayload(VantageJob $run, string $expectedJobClass): ?object
461-
{
462-
if (! $run->payload) {
463-
return null;
464-
}
465-
466-
// Validate expected class exists and is a valid job class
467-
if (! class_exists($expectedJobClass)) {
468-
VantageLogger::warning('Vantage: Expected job class does not exist', [
469-
'run_id' => $run->id,
470-
'expected_class' => $expectedJobClass,
471-
]);
472-
473-
return null;
474-
}
475-
476-
try {
477-
$payload = is_array($run->payload) ? $run->payload : json_decode($run->payload, true);
478-
479-
if (! is_array($payload)) {
480-
VantageLogger::warning('Vantage: Invalid payload format', ['run_id' => $run->id]);
481-
482-
return null;
483-
}
484-
485-
// Get the serialized command from Laravel's raw payload
486-
$serialized = $payload['raw_payload']['data']['command'] ?? null;
487-
488-
// Fallback to old format if new format not available
489-
if (! $serialized) {
490-
$serialized = $payload['data']['command'] ?? null;
491-
}
492-
493-
if (! $serialized || ! is_string($serialized)) {
494-
VantageLogger::warning('Vantage: No serialized command in payload', ['run_id' => $run->id]);
495-
496-
return null;
497-
}
498-
499-
// Unserialize with security: only allow the expected job class
500-
$job = @unserialize($serialized, ['allowed_classes' => [$expectedJobClass]]);
501-
502-
if (! is_object($job)) {
503-
VantageLogger::warning('Vantage: Unserialize did not return object', [
504-
'run_id' => $run->id,
505-
'result_type' => gettype($job),
506-
]);
507-
508-
return null;
509-
}
510-
511-
// Double-check the class matches the expected class (security validation)
512-
if (! $job instanceof $expectedJobClass) {
513-
VantageLogger::warning('Vantage: Unserialized job class does not match expected class', [
514-
'run_id' => $run->id,
515-
'expected_class' => $expectedJobClass,
516-
'actual_class' => get_class($job),
517-
]);
518-
519-
return null;
520-
}
521-
522-
VantageLogger::info('Vantage: Successfully restored job', [
523-
'run_id' => $run->id,
524-
'job_class' => get_class($job),
525-
]);
526-
527-
return $job;
528-
529-
} catch (\Throwable $e) {
530-
VantageLogger::error('Vantage: Exception while restoring job from payload', [
531-
'run_id' => $run->id,
532-
'error' => $e->getMessage(),
533-
]);
534-
535-
return null;
536-
}
537-
}
538-
539454
/**
540455
* Get retry chain
541456
*/

src/Http/Middleware/AuthorizeVantage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class AuthorizeVantage
1313
/**
1414
* Handle an incoming request.
1515
*
16-
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
16+
* @param Closure(Request): (Response) $next
1717
*/
1818
public function handle(Request $request, Closure $next): Response
1919
{

src/Listeners/RecordJobSuccess.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Storvia\Vantage\Listeners;
44

55
use Illuminate\Queue\Events\JobProcessed;
6+
use Illuminate\Support\Str;
67
use Storvia\Vantage\Models\VantageJob;
78
use Storvia\Vantage\Support\JobPerformanceContext;
89
use Storvia\Vantage\Support\PayloadExtractor;

src/Models/VantageJob.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,36 @@
44

55
use Illuminate\Database\Eloquent\Factories\HasFactory;
66
use Illuminate\Database\Eloquent\Model;
7+
use Illuminate\Support\Carbon;
78
use Storvia\Vantage\Database\Factories\VantageJobFactory;
89

10+
/**
11+
* @property int $id
12+
* @property string $status
13+
* @property string $job_uuid
14+
* @property string $job_class
15+
* @property string $queue
16+
* @property string|null $connection
17+
* @property int $attempt
18+
* @property int|null $retried_from_id
19+
* @property array|null $payload
20+
* @property array|null $job_tags
21+
* @property string|null $exception_class
22+
* @property string|null $exception_message
23+
* @property string|null $stack
24+
* @property Carbon|null $started_at
25+
* @property Carbon|null $finished_at
26+
* @property int|null $duration_ms
27+
* @property int|null $memory_start_bytes
28+
* @property int|null $memory_end_bytes
29+
* @property int|null $memory_peak_start_bytes
30+
* @property int|null $memory_peak_end_bytes
31+
* @property int|null $memory_peak_delta_bytes
32+
* @property int|null $cpu_user_ms
33+
* @property int|null $cpu_sys_ms
34+
* @property Carbon|null $created_at
35+
* @property Carbon|null $updated_at
36+
*/
937
class VantageJob extends Model
1038
{
1139
use HasFactory;

src/Support/JobRestorer.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
namespace Storvia\Vantage\Support;
4+
5+
use Storvia\Vantage\Models\VantageJob;
6+
7+
class JobRestorer
8+
{
9+
/**
10+
* Safely restore a job instance from its stored payload.
11+
*
12+
* Performs the following security checks before returning an object:
13+
* - The expected class must exist in the application.
14+
* - The stored payload must be a valid JSON array.
15+
* - The serialized command must be present (new or legacy format).
16+
* - Unserialize is restricted to the expected class only.
17+
* - The resulting object must be an instance of the expected class.
18+
*/
19+
public function restore(VantageJob $job, string $expectedJobClass): ?object
20+
{
21+
if (! $job->payload) {
22+
return null;
23+
}
24+
25+
if (! class_exists($expectedJobClass)) {
26+
VantageLogger::warning('Vantage: Expected job class does not exist', [
27+
'job_id' => $job->id,
28+
'expected_class' => $expectedJobClass,
29+
]);
30+
31+
return null;
32+
}
33+
34+
try {
35+
$payload = is_array($job->payload) ? $job->payload : json_decode($job->payload, true);
36+
37+
if (! is_array($payload)) {
38+
VantageLogger::warning('Vantage: Invalid payload format', ['job_id' => $job->id]);
39+
40+
return null;
41+
}
42+
43+
// Support both the new format (from PayloadExtractor) and the legacy format.
44+
$serialized = $payload['raw_payload']['data']['command']
45+
?? $payload['data']['command']
46+
?? null;
47+
48+
if (! $serialized || ! is_string($serialized)) {
49+
VantageLogger::warning('Vantage: No serialized command in payload', ['job_id' => $job->id]);
50+
51+
return null;
52+
}
53+
54+
$command = @unserialize($serialized, ['allowed_classes' => [$expectedJobClass]]);
55+
56+
if (! is_object($command)) {
57+
VantageLogger::warning('Vantage: Unserialize did not return an object', [
58+
'job_id' => $job->id,
59+
'result_type' => gettype($command),
60+
]);
61+
62+
return null;
63+
}
64+
65+
if (! $command instanceof $expectedJobClass) {
66+
VantageLogger::warning('Vantage: Unserialized job class does not match expected class', [
67+
'job_id' => $job->id,
68+
'expected_class' => $expectedJobClass,
69+
'actual_class' => get_class($command),
70+
]);
71+
72+
return null;
73+
}
74+
75+
VantageLogger::info('Vantage: Successfully restored job from payload', [
76+
'job_id' => $job->id,
77+
'job_class' => get_class($command),
78+
]);
79+
80+
return $command;
81+
82+
} catch (\Throwable $e) {
83+
VantageLogger::error('Vantage: Exception while restoring job from payload', [
84+
'job_id' => $job->id,
85+
'error' => $e->getMessage(),
86+
]);
87+
88+
return null;
89+
}
90+
}
91+
}

src/Support/QueueDepthChecker.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Support\Facades\DB;
66
use Illuminate\Support\Facades\Queue;
77
use Illuminate\Support\Facades\Redis;
8+
use Storvia\Vantage\Models\VantageJob;
89

910
class QueueDepthChecker
1011
{
@@ -153,7 +154,7 @@ protected static function getFallbackQueueDepth(?string $queueName, string $driv
153154
// For unsupported drivers, we can still show what we know from job_runs
154155
// Count jobs that are processing or recently started (might be queued)
155156
try {
156-
$query = \Storvia\Vantage\Models\VantageJob::where('status', 'processing');
157+
$query = VantageJob::where('status', 'processing');
157158

158159
if ($queueName) {
159160
$query->where('queue', $queueName);

src/Support/TagAggregator.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Storvia\Vantage\Support;
44

5+
use Carbon\Carbon;
6+
use Illuminate\Database\Connection;
57
use Illuminate\Support\Collection;
68
use Illuminate\Support\Facades\DB;
79
use Illuminate\Support\Facades\Schema;
@@ -28,7 +30,7 @@ class TagAggregator
2830

2931
protected string $connectionName;
3032

31-
protected \Illuminate\Database\Connection $connection;
33+
protected Connection $connection;
3234

3335
protected string $jobsTable;
3436

@@ -47,7 +49,7 @@ public function __construct()
4749
/**
4850
* Get top tags with statistics, using the most efficient method available.
4951
*
50-
* @param \Carbon\Carbon $since Filter jobs created after this date
52+
* @param Carbon $since Filter jobs created after this date
5153
* @param int $limit Maximum number of tags to return
5254
* @return Collection Collection of tag statistics
5355
*/
@@ -65,7 +67,7 @@ public function getTopTags($since, int $limit = 10): Collection
6567
/**
6668
* Get detailed tag statistics including duration averages.
6769
*
68-
* @param \Carbon\Carbon $since Filter jobs created after this date
70+
* @param Carbon $since Filter jobs created after this date
6971
* @return array Associative array of tag => statistics
7072
*/
7173
public function getTagStats($since): array
@@ -602,7 +604,7 @@ protected function formatTagStatsResult($rows): array
602604
*
603605
* @param int $jobId The job ID
604606
* @param array $tags Array of tag strings
605-
* @param \Carbon\Carbon|null $createdAt The job's created_at timestamp
607+
* @param Carbon|null $createdAt The job's created_at timestamp
606608
*/
607609
public function insertJobTags(int $jobId, array $tags, $createdAt = null): void
608610
{
@@ -665,7 +667,7 @@ public function deleteJobTags(int $jobId): void
665667
* Delete old tags based on job created_at timestamp.
666668
* Used by the prune command.
667669
*
668-
* @param \Carbon\Carbon $before Delete tags for jobs created before this date
670+
* @param Carbon $before Delete tags for jobs created before this date
669671
* @return int Number of tags deleted
670672
*/
671673
public function pruneOldTags($before): int

0 commit comments

Comments
 (0)