Skip to content

Commit

Permalink
Failed Job view
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisToxz committed Aug 8, 2023
1 parent 512a606 commit 3c09de2
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 38 deletions.
7 changes: 5 additions & 2 deletions app/Enums/SlipStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
use BenSampo\Enum\Enum;

/**
* @method static static STATUS_PENDING()
* @method static static STATUS_QUEUED()
* @method static static STATUS_STARTING()
* @method static static STATUS_PROCESSING()
* @method static static STATUS_FINISHED()
* @method static static STATUS_FAILED()
*/
final class SlipStatus extends Enum
{
const PENDING = 'pending';
const QUEUED = 'queued';

const STARTING = 'starting';
const PROCESSING = 'processing';
const FINISHED = 'finished';
const FAILED = 'failed';
Expand Down
10 changes: 3 additions & 7 deletions app/Events/SlipProcessFinished.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
use App\Models\Slip;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
Expand All @@ -21,16 +18,15 @@ class SlipProcessFinished implements ShouldBroadcastNow
*/
public function __construct(
public Slip $slip,
public bool $failed = false
)
{
public bool $success = true
) {
//
}

/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
* @return array<int, Channel>
*/
public function broadcastOn(): array
{
Expand Down
43 changes: 43 additions & 0 deletions app/Http/Controllers/JobController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace App\Http\Controllers;

use App\Enums\SlipStatus;
use App\Models\Slip;
use Illuminate\Queue\Failed\FailedJobProviderInterface;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;

class JobController extends Controller
{

/* TODO: Optimize this */
public function requeue(Slip $slip)
{

$failedJobProvider = app(FailedJobProviderInterface::class);
$failedJob = $failedJobProvider->find($slip->job_uuid);


Queue::pushRaw($failedJob->payload);
$slip->setStatus(SlipStatus::QUEUED);

// Forget previous failed job
$failedJobProvider->forget($slip->job_uuid);

return Redirect()->back();
}

public function destroy(Slip $slip)
{
$failedJobProvider = app(FailedJobProviderInterface::class);

$failedJob = $failedJobProvider->find($slip->job_uuid);
$tmpPath = unserialize(json_decode($failedJob->payload)->data->command)->tmpPath;
Storage::disk('local')->delete($tmpPath);
$failedJobProvider->forget($slip->uuid);

$slip->delete();
return Redirect()->back();
}
}
14 changes: 9 additions & 5 deletions app/Jobs/CreateSlip.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function __construct(
public string $tmpPath,
public string $type
) {

}

/**
Expand All @@ -44,6 +44,8 @@ public function __construct(
*/
public function handle(): void
{
$this->slip->setStatus(SlipStatus::PROCESSING);

$output = new ConsoleOutput();

$streamHash = Str::random(40);
Expand Down Expand Up @@ -137,24 +139,26 @@ function ($media) use ($quality) {

public function before()
{
$this->slip->setStatus(SlipStatus::PROCESSING());
$this->slip->setJobUuid($this->job->uuid());

$this->slip->setStatus(SlipStatus::STARTING);
SlipProcessUpdate::dispatch($this->slip->token, 'Starting', 0);
}

public function after()
{
SlipProcessUpdate::dispatch($this->slip->token, 'Deleting temp file', 100);
Storage::disk('local')->delete($this->tmpPath);
$this->slip->setStatus(SLipStatus::FINISHED());
$this->slip->setStatus(SLipStatus::FINISHED);
SlipProcessFinished::dispatch($this->slip);
}

public function failed(Throwable $exception): void
{
// TODO: Make proper log of failed job including debug information
// maybe spatie/laravel-activitylog?
$this->slip->setStatus(SlipStatus::FAILED());
SlipProcessFinished::dispatch($this->slip, true);
$this->slip->setStatus(SlipStatus::FAILED);
SlipProcessFinished::dispatch($this->slip, false);
SlipProcessUpdate::dispatch($this->slip->token, 'Failed', 0);
}

Expand Down
42 changes: 26 additions & 16 deletions app/Models/Slip.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
namespace App\Models;

use App\Enums\SlipStatus;
use Eloquent;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Carbon;
use Storage;
use Str;

/**
Expand All @@ -17,20 +21,20 @@
* @property string $thumb
* @property string $mediable_type
* @property int $mediable_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|Slip newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Slip newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Slip query()
* @method static \Illuminate\Database\Eloquent\Builder|Slip whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Slip whereDescription($value)
* @method static \Illuminate\Database\Eloquent\Builder|Slip whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Slip whereMediableId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Slip whereMediableType($value)
* @method static \Illuminate\Database\Eloquent\Builder|Slip whereThumb($value)
* @method static \Illuminate\Database\Eloquent\Builder|Slip whereTitle($value)
* @method static \Illuminate\Database\Eloquent\Builder|Slip whereUpdatedAt($value)
* @mixin \Eloquent
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @method static Builder|Slip newModelQuery()
* @method static Builder|Slip newQuery()
* @method static Builder|Slip query()
* @method static Builder|Slip whereCreatedAt($value)
* @method static Builder|Slip whereDescription($value)
* @method static Builder|Slip whereId($value)
* @method static Builder|Slip whereMediableId($value)
* @method static Builder|Slip whereMediableType($value)
* @method static Builder|Slip whereThumb($value)
* @method static Builder|Slip whereTitle($value)
* @method static Builder|Slip whereUpdatedAt($value)
* @mixin Eloquent
*/
class Slip extends Model
{
Expand All @@ -54,10 +58,16 @@ public function mediable()
protected function thumb(): Attribute
{
return new Attribute(
get: fn() => \Storage::disk('slips')->url($this->token . '/thumb.jpg')
get: fn() => Storage::disk('slips')->url($this->token.'/thumb.jpg')
);
}

public function setJobUuid($uuid)
{
$this->job_uuid = $uuid;
$this->save();
}


public function setStatus($status)
{
Expand Down
4 changes: 3 additions & 1 deletion database/migrations/2023_06_27_192912_create_slips_table.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use App\Enums\SlipStatus;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Expand All @@ -18,8 +19,9 @@ public function up(): void
$table->string('description')->nullable();
// TODO: Make it not nullable, just for testing. Hmm it should be nullable because if first creates a token
$table->nullableMorphs('mediable');
$table->uuid('job_uuid')->nullable();

$table->string('status')->default('pending');
$table->string('status')->default(SlipStatus::QUEUED);

$table->timestamps();
});
Expand Down
36 changes: 31 additions & 5 deletions resources/js/Components/Dashboard/VideoCard.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup>
import {Link} from '@inertiajs/vue3'
import {Link, router} from '@inertiajs/vue3'
import {computed, ref} from 'vue'
import DeleteSlipModal from '@/Components/Dashboard/DeleteSlipDialog.vue'
import EditSlipModal from '@/Components/Dashboard/EditSlipModal.vue'
Expand All @@ -11,6 +11,8 @@ import iconType from '@/Composables/useIconType.js'
import Settings from '~icons/ic/baseline-video-settings'
import Download from '~icons/ion/download'
import Trash from '~icons/mdi/trash'
import PrimaryButton from '@/Components/UI/PrimaryButton.vue'
import WarningButton from '@/Components/UI/WarningButton.vue'
const hoverEffect = ref(false)
const hover = ref(false)
Expand Down Expand Up @@ -45,6 +47,14 @@ window.Echo.channel(`slip.${slip.value.token}`).listen('SlipProcessUpdate', (e)
status.value = e.status
})
const requeue = () => {
status.value = 'Queued'
router.post(route('job.requeue', props.slip))
}
const deleteJob = () => {
router.delete(route('job.destroy', props.slip))
}
</script>

<template>
Expand Down Expand Up @@ -104,10 +114,26 @@ window.Echo.channel(`slip.${slip.value.token}`).listen('SlipProcessUpdate', (e)
</div>
</div>
</div>
<div v-if="slip.status != 'finished'" class="z-2 absolute w-full h-full bg-[rgba(0,0,0,0.6)] flex flex-col justify-between items-center">
<ProgressBar :percentage="percentage" />
<p class="text-gray-200 pt-2">{{ percentage }}%</p>
<p class="text-gray-200 pb-2">{{ status ?? slip.status }} - {{ slip.title }}</p>
<div v-if="slip.status !== 'finished'" class="z-2 absolute w-full h-full bg-[rgba(0,0,0,0.6)]">
<div v-if="slip.status !== 'failed'" class="flex flex-col justify-between items-center h-full">
<ProgressBar :percentage="percentage" />
<div>
<p class="text-gray-200 pt-2">{{ percentage }}%</p>
</div>
<div>
<p class="text-gray-200 pb-2">{{ status ?? slip.status }} - {{ slip.title }}</p>
</div>
</div>
<div v-if="slip.status === 'failed'" class="flex flex-col justify-between space-y-4">
<div class="text-gray-200 text-center mt-2">
<p class="text-2xl font-bold">Oops, processing this slip have been failed!</p>
<p>Please check logs for more detailed information</p>
</div>
<div class="flex flex-row gap-3 jusitfy-between items-center mx-10">
<PrimaryButton @click="requeue()">Retry</PrimaryButton>
<WarningButton @click="deleteJob()">Delete</WarningButton>
</div>
</div>
</div>
<span
@mouseover="hover = true"
Expand Down
2 changes: 1 addition & 1 deletion resources/js/Components/UI/WarningButton.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<button class="bg-red-800 py-2 w-full rounded-lg transition-all duration-300 ease hover:-translate-y-0.5 hover:bg-red-900">
<button class="bg-red-800 py-2 w-full rounded-lg transition-all duration-300 ease hover:-translate-y-0.5 hover:bg-red-900 text-gray-200">
<slot />
</button>
</template>
2 changes: 1 addition & 1 deletion resources/js/Composables/useSlipSockets.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const useSlipSockets = (snackbar) => {
only: ['slips'],
onSuccess: (page) => {
//I had to move this to success, so toastr doesn't appear double
if (!e.failed) {
if (e.success) {
$snackbar.add({
type: 'success',
text: 'Slip successfully processed',
Expand Down
5 changes: 5 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use App\Http\Controllers\JobController;
use App\Http\Controllers\SettingsController;
use App\Http\Controllers\SlipController;
use Illuminate\Support\Facades\Route;
Expand Down Expand Up @@ -30,6 +31,10 @@
Route::post('/settings', [SettingsController::class, 'store'])->name('settings.store');
route::get('/settings/storage', [SettingsController::class, 'storageUsage'])->name('settings.storage');
route::post('/settings/clear-tmp', [SettingsController::class, 'clearTmp'])->name('settings.clear-tmp');

/* Jobs */
Route::post('/job/{slip}', [JobController::class, 'requeue'])->name('job.requeue');
Route::delete('/job/{slip}', [JobController::class, 'destroy'])->name('job.destroy');
});


Expand Down

0 comments on commit 3c09de2

Please sign in to comment.