This commit is contained in:
46
app/Bots/BashScript.php
Normal file
46
app/Bots/BashScript.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Bots;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class BashScript implements BotContract
|
||||||
|
{
|
||||||
|
protected array $config;
|
||||||
|
|
||||||
|
public function __construct(array $config = [])
|
||||||
|
{
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
if (!empty($this->config['script'])) {
|
||||||
|
// Execute the bash script
|
||||||
|
$script = $this->config['script'];
|
||||||
|
$output = [];
|
||||||
|
$returnVar = null;
|
||||||
|
exec($script, $output, $returnVar);
|
||||||
|
|
||||||
|
if ($returnVar !== 0) {
|
||||||
|
// Log error if the script failed
|
||||||
|
\Illuminate\Support\Facades\Log::error("Bash script execution failed: " . implode("\n", $output));
|
||||||
|
} else {
|
||||||
|
Log::info("Bash script executed successfully: " . implode("\n", $output));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function configSchema(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'script' => [
|
||||||
|
'type' => 'textarea',
|
||||||
|
'label' => 'Bash Script',
|
||||||
|
'rules' => [
|
||||||
|
'required',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,12 +3,13 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class DashboardController extends Controller
|
class DashboardController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$bots = \App\Models\Bot::all();
|
$bots = Auth::user()->bots;
|
||||||
|
|
||||||
return view('dashboard', compact('bots'));
|
return view('dashboard', compact('bots'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\Bot;
|
use App\Models\Bot;
|
||||||
|
use App\Models\BotLog;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Queue\Queueable;
|
use Illuminate\Foundation\Queue\Queueable;
|
||||||
|
|
||||||
@@ -13,9 +14,7 @@ class RunBot implements ShouldQueue
|
|||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*/
|
*/
|
||||||
public function __construct(private Bot $bot)
|
public function __construct(private Bot $bot, private BotLog $log) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
@@ -24,6 +23,22 @@ class RunBot implements ShouldQueue
|
|||||||
{
|
{
|
||||||
$class = new $this->bot->class($this->bot->config ?? []);
|
$class = new $this->bot->class($this->bot->config ?? []);
|
||||||
|
|
||||||
$class->run();
|
try {
|
||||||
|
$class->run();
|
||||||
|
|
||||||
|
// Update the log entry on success
|
||||||
|
$this->log->update([
|
||||||
|
'finished_at' => now(),
|
||||||
|
'status' => 'success',
|
||||||
|
// 'output' => is_string($result) ? $result : json_encode($result, JSON_PRETTY_PRINT),
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// Log the error in the bot log
|
||||||
|
$this->log->update([
|
||||||
|
'finished_at' => now(),
|
||||||
|
'status' => 'failed',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
app/Livewire/BotLogs.php
Normal file
35
app/Livewire/BotLogs.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Models\BotLog;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\WithoutUrlPagination;
|
||||||
|
use Livewire\WithPagination;
|
||||||
|
|
||||||
|
class BotLogs extends Component
|
||||||
|
{
|
||||||
|
use WithPagination;
|
||||||
|
use WithoutUrlPagination;
|
||||||
|
|
||||||
|
// public function mount()
|
||||||
|
// {
|
||||||
|
// $this->getLogs();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public function getLogs(): void
|
||||||
|
// {
|
||||||
|
// $this->logs = Auth::user()->bots->pluck('logs')->flatten()->sortByDesc('created_at');
|
||||||
|
// }
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.bot-logs', [
|
||||||
|
'logs' => BotLog::whereHas('bot', fn($q) => $q->where('user_id', Auth::id()))
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->latest()
|
||||||
|
->paginate(10),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,11 +12,21 @@ class BotsList extends Component
|
|||||||
{
|
{
|
||||||
public $bots;
|
public $bots;
|
||||||
|
|
||||||
|
public string $query = '';
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->bots = Auth::user()->bots;
|
$this->bots = Auth::user()->bots;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function search()
|
||||||
|
{
|
||||||
|
$this->bots = Auth::user()->bots()
|
||||||
|
->where('name', 'like', '%' . $this->query . '%')
|
||||||
|
->orWhere('class', 'like', '%' . $this->query . '%')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
public function toggleBot($botId)
|
public function toggleBot($botId)
|
||||||
{
|
{
|
||||||
$bot = \App\Models\Bot::find($botId);
|
$bot = \App\Models\Bot::find($botId);
|
||||||
@@ -62,8 +72,13 @@ class BotsList extends Component
|
|||||||
|
|
||||||
public function confirmRunBot(Bot $bot): void
|
public function confirmRunBot(Bot $bot): void
|
||||||
{
|
{
|
||||||
|
$log = $bot->logs()->create([
|
||||||
|
'status' => 'pending',
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
// Dispatch the job to run the bot
|
// Dispatch the job to run the bot
|
||||||
dispatch(new RunBot($bot));
|
dispatch(new RunBot($bot, $log));
|
||||||
|
|
||||||
flash()->success("Bot '{$bot->name}' is being executed.");
|
flash()->success("Bot '{$bot->name}' is being executed.");
|
||||||
}
|
}
|
||||||
|
|||||||
38
app/Livewire/ViewBot.php
Normal file
38
app/Livewire/ViewBot.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Jobs\RunBot;
|
||||||
|
use App\Models\Bot;
|
||||||
|
use Jantinnerezo\LivewireAlert\Facades\LivewireAlert;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class ViewBot extends Component
|
||||||
|
{
|
||||||
|
public Bot $bot;
|
||||||
|
|
||||||
|
public function runBot()
|
||||||
|
{
|
||||||
|
LivewireAlert::title('Are you sure you want to run ' . $this->bot->name . '?')
|
||||||
|
->asConfirm()
|
||||||
|
->onConfirm('confirmRunBot')
|
||||||
|
->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirmRunBot(): void
|
||||||
|
{
|
||||||
|
$log = $this->bot->logs()->create([
|
||||||
|
'status' => 'pending',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Dispatch the job to run the bot
|
||||||
|
dispatch(new RunBot($this->bot, $log));
|
||||||
|
|
||||||
|
flash()->success("Bot '{$this->bot->name}' is being executed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.view-bot');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,4 +51,19 @@ class Bot extends Model
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function logs()
|
||||||
|
{
|
||||||
|
return $this->hasMany(BotLog::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function latestLog()
|
||||||
|
{
|
||||||
|
return $this->hasOne(BotLog::class)->latestOfMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function failedLogs()
|
||||||
|
{
|
||||||
|
return $this->hasMany(BotLog::class)->where('status', 'failed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
app/Models/BotLog.php
Normal file
30
app/Models/BotLog.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class BotLog extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'bot_id',
|
||||||
|
'started_at',
|
||||||
|
'finished_at',
|
||||||
|
'status',
|
||||||
|
'output',
|
||||||
|
'error',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'started_at' => 'datetime',
|
||||||
|
'finished_at' => 'datetime',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bot()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Bot::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace App\Services;
|
|||||||
|
|
||||||
use App\Models\Bot;
|
use App\Models\Bot;
|
||||||
use App\Bots\BotContract;
|
use App\Bots\BotContract;
|
||||||
|
use App\Jobs\RunBot;
|
||||||
use Cron\CronExpression;
|
use Cron\CronExpression;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
@@ -16,14 +17,29 @@ class BotService
|
|||||||
foreach ($bots as $bot) {
|
foreach ($bots as $bot) {
|
||||||
$cron = new CronExpression($bot->schedule);
|
$cron = new CronExpression($bot->schedule);
|
||||||
if ($cron->isDue()) {
|
if ($cron->isDue()) {
|
||||||
|
$log = $bot->logs()->create([
|
||||||
|
'status' => 'pending',
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$instance = app($bot->class, ['config' => $bot->config]);
|
$instance = app($bot->class, ['config' => $bot->config]);
|
||||||
|
|
||||||
if ($instance instanceof BotContract) {
|
if ($instance instanceof BotContract) {
|
||||||
$instance->run();
|
dispatch(RunBot::class, $bot, $log);
|
||||||
|
$log->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
'status' => 'running'
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
Log::error("Bot [{$bot->name}] failed: " . $e->getMessage());
|
Log::error("Bot [{$bot->name}] failed: " . $e->getMessage());
|
||||||
|
|
||||||
|
$log->update([
|
||||||
|
'finished_at' => now(),
|
||||||
|
'status' => 'failed',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info("Bot [{$bot->name}] executed successfully.");
|
Log::info("Bot [{$bot->name}] executed successfully.");
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('bot_logs', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('bot_id');
|
||||||
|
$table->timestamp('started_at')->nullable();
|
||||||
|
$table->timestamp('finished_at')->nullable();
|
||||||
|
$table->string('status')->default('pending'); // pending, running, success, failed
|
||||||
|
$table->text('output')->nullable(); // store raw response/log text
|
||||||
|
$table->text('error')->nullable(); // store error message/trace if failed
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('bot_logs');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -28,13 +28,16 @@
|
|||||||
<div class="p-4"></div>
|
<div class="p-4"></div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="relative aspect-video overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-700"
|
class="relative aspect-video overflow-scroll rounded-xl border border-neutral-200 dark:border-neutral-700"
|
||||||
>
|
>
|
||||||
<div class="p-4"></div>
|
<div class="p-4">Logs</div>
|
||||||
|
<div class="">
|
||||||
|
@livewire('bot-logs')
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="relative h-full flex-1 overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-700"
|
class="relative h-full flex-1 overflow-scroll rounded-xl border border-neutral-200 dark:border-neutral-700"
|
||||||
>
|
>
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<h2>All Bots</h2>
|
<h2>All Bots</h2>
|
||||||
|
|||||||
40
resources/views/livewire/bot-logs.blade.php
Normal file
40
resources/views/livewire/bot-logs.blade.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<div>
|
||||||
|
<table class="p-2" wire:poll.5s>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="px-4 py-2">Bot</th>
|
||||||
|
<th class="px-4 py-2">Status</th>
|
||||||
|
<th class="px-4 py-2">Started At</th>
|
||||||
|
<th class="px-4 py-2">Finished At</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="text-center">
|
||||||
|
@foreach ($logs as $log)
|
||||||
|
<tr>
|
||||||
|
<td class="px-4 py-2">
|
||||||
|
{{ $log->bot->name }}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
@class([
|
||||||
|
'text-red-400' => $log->status === 'failed',
|
||||||
|
'text-green-400' => $log->status === 'success',
|
||||||
|
'text-yellow-400' => $log->status === 'pending',
|
||||||
|
]),
|
||||||
|
class="px-4 py-2"
|
||||||
|
>
|
||||||
|
{{ $log->status }}
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2">
|
||||||
|
{{ $log->started_at ?? '' }}
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2">
|
||||||
|
{{ $log->finished_at ?? '' }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="mt-4">
|
||||||
|
{{ $logs->links() }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,13 +1,27 @@
|
|||||||
<div>
|
<div>
|
||||||
<table class="table w-full">
|
<div class="flex gap-2 my-auto mb-4">
|
||||||
|
<input
|
||||||
|
|
||||||
|
type="text"
|
||||||
|
wire:model="query"
|
||||||
|
wire:keydown.debounce.500ms="search"
|
||||||
|
class="border p-2 rounded"
|
||||||
|
placeholder="Search bots"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div wire:loading wire:target.delay.longer="search">
|
||||||
|
Searching
|
||||||
|
<i class="fas fa-spinner fa-spin"></i>
|
||||||
|
</div>
|
||||||
|
<table class="table w-full" wire:loading.remove>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="border px-4 py-2">Name</th>
|
<th class="px-4 py-2">Name</th>
|
||||||
<th class="border px-4 py-2">Schema</th>
|
<th class="px-4 py-2">Schema</th>
|
||||||
<th class="border px-4 py-2">Schedule</th>
|
<th class="px-4 py-2">Schedule</th>
|
||||||
<th class="border px-4 py-2">Next due</th>
|
<th class="px-4 py-2">Next due</th>
|
||||||
<th class="border px-4 py-2">Enabled</th>
|
<th class="px-4 py-2">Enabled</th>
|
||||||
<th class="border px-4 py-2">Actions</th>
|
<th class="px-4 py-2">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="text-center">
|
<tbody class="text-center">
|
||||||
@@ -16,7 +30,7 @@
|
|||||||
<td class="px-4 py-2">
|
<td class="px-4 py-2">
|
||||||
<a
|
<a
|
||||||
wire:navigate
|
wire:navigate
|
||||||
href="{{ route('bots.edit', $bot) }}"
|
href="{{ route('bots.show', $bot) }}"
|
||||||
class="text-blue-500"
|
class="text-blue-500"
|
||||||
>
|
>
|
||||||
{{ $bot->name }}
|
{{ $bot->name }}
|
||||||
@@ -37,7 +51,7 @@
|
|||||||
wire:click="toggleBot({{ $bot->id }})"
|
wire:click="toggleBot({{ $bot->id }})"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="text-xs">
|
||||||
<button
|
<button
|
||||||
wire:click="runBot({{ $bot->id }})"
|
wire:click="runBot({{ $bot->id }})"
|
||||||
class="ml-4 cursor-pointer"
|
class="ml-4 cursor-pointer"
|
||||||
|
|||||||
@@ -65,10 +65,19 @@
|
|||||||
>
|
>
|
||||||
Prettify
|
Prettify
|
||||||
</span>
|
</span>
|
||||||
|
@elseif ($meta['type'] === 'textarea')
|
||||||
|
<textarea
|
||||||
|
wire:model.live="config.{{ $field }}"
|
||||||
|
class="border rounded w-full"
|
||||||
|
name="config[{{ $field }}]"
|
||||||
|
rows="4"
|
||||||
|
>
|
||||||
|
{{ old("config.$field", $bot->config[$field] ?? '') }}
|
||||||
|
</textarea>
|
||||||
@else
|
@else
|
||||||
<input
|
<input
|
||||||
wire:model.live="config.{{ $field }}"
|
wire:model.live="config.{{ $field }}"
|
||||||
class="border p-2 rounded mb-2 w-full"
|
class="border p-2 rounded w-full"
|
||||||
type="text"
|
type="text"
|
||||||
name="config[{{ $field }}]"
|
name="config[{{ $field }}]"
|
||||||
value="{{ old("config.$field", $bot->config[$field] ?? '') }}"
|
value="{{ old("config.$field", $bot->config[$field] ?? '') }}"
|
||||||
|
|||||||
22
resources/views/livewire/view-bot.blade.php
Normal file
22
resources/views/livewire/view-bot.blade.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<div>
|
||||||
|
<div class="flex justify-between mb-4">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">
|
||||||
|
{{ $bot->name }}
|
||||||
|
</h1>
|
||||||
|
<div>
|
||||||
|
<button class="bg-green-600 p-2 text-white rounded-lg mr-2 cursor-pointer">
|
||||||
|
<i class="fas fa-play"></i>
|
||||||
|
<span wire:click="confirmRunBot({{ $bot->id }})" class="cursor-pointer">
|
||||||
|
{{ __('Run Bot') }}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="bg-gray-600 p-2 text-white rounded-lg cursor-pointer"
|
||||||
|
wire:navigate
|
||||||
|
href="{{ route('bots.edit', $bot) }}"
|
||||||
|
>
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
{{ __('Edit Bot') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -6,6 +6,7 @@ use App\Livewire\EditBot;
|
|||||||
use App\Livewire\Settings\Appearance;
|
use App\Livewire\Settings\Appearance;
|
||||||
use App\Livewire\Settings\Password;
|
use App\Livewire\Settings\Password;
|
||||||
use App\Livewire\Settings\Profile;
|
use App\Livewire\Settings\Profile;
|
||||||
|
use App\Livewire\ViewBot;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
@@ -19,6 +20,8 @@ Route::get('dashboard', [App\Http\Controllers\DashboardController::class, 'index
|
|||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
Route::get('bots/create', CreateEditBot::class)
|
Route::get('bots/create', CreateEditBot::class)
|
||||||
->name('bots.create');
|
->name('bots.create');
|
||||||
|
Route::get('bots/{bot}', ViewBot::class)
|
||||||
|
->name('bots.show')->middleware('can:view,bot');
|
||||||
Route::get('bots/edit/{bot}', CreateEditBot::class)
|
Route::get('bots/edit/{bot}', CreateEditBot::class)
|
||||||
->name('bots.edit')->middleware('can:view,bot');
|
->name('bots.edit')->middleware('can:view,bot');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user