Skip to content

Commit

Permalink
WiP: Pardon control from web feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Xinecraft committed Nov 16, 2024
1 parent ed1d0fb commit caa3734
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 2 deletions.
26 changes: 26 additions & 0 deletions app/Http/Controllers/BanWardenController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Http\Controllers;

use App\Jobs\GeneratePunishmentInsightsJob;
use App\Jobs\PardonPlayerPunishmentJob;
use App\Models\Country;
use App\Models\MinecraftPlayerSession;
use App\Models\Player;
Expand Down Expand Up @@ -136,6 +137,7 @@ public function show(PlayerPunishment $playerPunishment, Request $request)
if (!$canShowMaskedIp) {
$playerPunishment->makeHidden('masked_ip_address');
}
$allowControlFromWeb = config('minetrax.banwarden.allow_control_from_web');
$response = [
'punishment' => $playerPunishment,
'permissions' => [
Expand All @@ -146,6 +148,7 @@ public function show(PlayerPunishment $playerPunishment, Request $request)
'canCreateEvidence' => Gate::allows('createEvidence', PlayerPunishment::class)
&& $playerPunishment->evidences->count() < config('minetrax.banwarden.evidence_max_count'),
'canDeleteEvidence' => Gate::allows('deleteEvidence', PlayerPunishment::class),
'canPardon' => $allowControlFromWeb && Gate::allows('delete', $playerPunishment),
],
];

Expand Down Expand Up @@ -317,4 +320,27 @@ public function deleteEvidence(PlayerPunishment $playerPunishment, $evidence)
return redirect()->back()
->with(['toast' => ['type' => 'success', 'title' => __('Delete Successful'), 'body' => __('Evidence deleted successfully')]]);
}

public function pardon(PlayerPunishment $playerPunishment, Request $request)
{
$request->validate([
'reason' => 'nullable|string|max:255',
]);

$this->authorize('delete', $playerPunishment);

try {
PardonPlayerPunishmentJob::dispatchSync($playerPunishment, $request->input('reason'));
} catch (\Exception $e) {
\Log::error($e);
return redirect()->back()
->with(['toast' => ['type' => 'error', 'title' => __('Pardon Failed'), 'body' => 'Failed to execute pardon job to due webquery issue.']]);
}

$playerPunishment->removed_by = auth()->id();
$playerPunishment->save();

return redirect()->back()
->with(['toast' => ['type' => 'success', 'title' => __('Pardon Successful'), 'body' => __('Reloading in 5 seconds to reflect changes'), 'milliseconds' => 5000]]);
}
}
52 changes: 52 additions & 0 deletions app/Jobs/PardonPlayerPunishmentJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace App\Jobs;

use App\Models\PlayerPunishment;
use App\Models\Server;
use App\Utils\MinecraftQuery\MinecraftWebQuery;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class PardonPlayerPunishmentJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public function __construct(public PlayerPunishment $playerPunishment, public $reason)
{
//
}

public function handle(): void
{
// Decide which server to connect.
// 1. If we have origin_server_id & that server has webquery, use that server.
// 2. else, send to all servers that have webquery.
if ($this->playerPunishment->origin_server_id && $this->playerPunishment->originServer?->webquery_port) {
$server = [$this->playerPunishment->originServer];
} else {
$servers = Server::select(['id', 'name', 'hostname'])->whereNotNull('webquery_port')->get();
}


foreach ($servers as $server) {
$this->pardonPlayerPunishment($server);
}
}

private function pardonPlayerPunishment(Server $server): void
{
$webQuery = new MinecraftWebQuery($server->ip_address, $server->webquery_port);

$victim = $this->playerPunishment->uuid ?? $this->playerPunishment->ip_address;
$webQuery->banwardenPardon(
$this->playerPunishment->type->value,
$victim,
$this->reason,
);
}
}
3 changes: 2 additions & 1 deletion app/Policies/PlayerPunishmentPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ public function update(User $user, PlayerPunishment $playerPunishment): bool
*/
public function delete(User $user, PlayerPunishment $playerPunishment): bool
{
if ($user->can('delete banwarden_punishments')) {
$allowControlFromWeb = config('minetrax.banwarden.allow_control_from_web');
if ($allowControlFromWeb && $user->can('delete banwarden_punishments')) {
return true;
}

Expand Down
77 changes: 76 additions & 1 deletion resources/default/js/Pages/BanWarden/ShowPunishment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import { EyeIcon, DocumentMagnifyingGlassIcon, PlusSmallIcon } from '@heroicons/
import ArrayTable from '@/Components/DataTable/ArrayTable.vue';
import Chart from '@/Components/Dashboard/Chart.vue';
import LoadingSpinner from '@/Components/LoadingSpinner.vue';
import JetDialogModal from '@/Jetstream/DialogModal.vue';
import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue';
import { ref } from 'vue';
import { XCircleIcon } from '@heroicons/vue/24/solid';
import XInput from '@/Components/Form/XInput.vue';
import LoadingButton from '@/Components/LoadingButton.vue';
const { __ } = useTranslations();
const { formatTimeAgoToNow, formatToDayDateString, secondsToHMS } = useHelpers();
Expand Down Expand Up @@ -221,14 +225,25 @@ const handleEvidenceUpload = (event) => {
uploadEvidenceForm.post(route('player.punishment.evidence.store', props.punishment.id));
};
const showingPardonForm = ref(false);
const pardonForm = useForm({
reason: '',
});
function reloadPageWithTimeout() {
setTimeout(() => {
location.reload();
}, 5000);
}
</script>
<template>
<AppLayout>
<AppHead :title="__('Punishments')" />
<div class="px-2 py-4 mx-auto md:py-12 md:px-10 max-w-7xl">
<div class="flex justify-between mb-8">
<div class="flex flex-col md:flex-row justify-between mb-8">
<div class="flex items-center">
<h1 class="text-lg font-bold text-gray-500 md:text-3xl dark:text-gray-300">
{{ __("Punishment #:id", {
Expand All @@ -245,6 +260,22 @@ const handleEvidenceUpload = (event) => {
/>
</span>
</div>
<div class="flex space-x-2">
<button
v-if="permissions['canPardon'] && punishment.is_active"
class="inline-flex items-center px-4 py-2 bg-green-500 dark:bg-cool-green-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-green-600 active:bg-green-700 focus:outline-none focus:border-green-800 focus:shadow-outline-green transition ease-in-out duration-150"
@click="showingPardonForm = true"
>
<span>{{ __("Pardon") }}</span>
</button>
<Link
:href="route('player.punishment.index')"
class="hidden md:inline-flex items-center px-4 py-2 bg-gray-400 dark:bg-cool-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-500 active:bg-gray-600 focus:outline-none focus:border-gray-500 focus:shadow-outline-gray transition ease-in-out duration-150"
>
<span>{{ __("Back") }}</span>
</Link>
</div>
</div>
<div class="flex flex-col space-y-8 text-gray-800 dark:text-gray-300">
<!-- Details Start -->
Expand Down Expand Up @@ -1309,5 +1340,49 @@ const handleEvidenceUpload = (event) => {
<!-- Possible Alts End -->
</div>
</div>
<JetDialogModal
:show="showingPardonForm"
@close="showingPardonForm = false"
>
<template #title>
<h3 class="text-lg font-bold">
{{ __("Pardon") }}
</h3>
</template>
<template #content>
<XInput
v-model="pardonForm.reason"
:label="__('Please provide a reason..')"
:error="pardonForm.errors.reason"
/>
</template>
<template #footer>
<JetSecondaryButton
class="mr-2"
@click="showingPardonForm = false"
>
{{ __("Cancel") }}
</JetSecondaryButton>
<LoadingButton
:loading="pardonForm.processing"
class="inline-flex items-center px-4 py-2 text-xs font-semibold tracking-widest text-white uppercase transition duration-150 ease-in-out bg-green-500 border border-transparent rounded-md hover:bg-green-700 active:bg-green-900 focus:outline-none focus:border-green-900 focus:shadow-outline-gray"
@click="() => {
pardonForm.delete(route('player.punishment.pardon', {
playerPunishment: punishment.id,
}), {
onSuccess: () => {
showingPardonForm = false;
reloadPageWithTimeout();
},
})
}"
>
{{ __("Pardon") }}
</LoadingButton>
</template>
</JetDialogModal>
</AppLayout>
</template>
1 change: 1 addition & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
Route::post('applications/{recruitment:slug}/submissions/{submission}/messages', [\App\Http\Controllers\RecruitmentSubmissionController::class, 'postMessage'])->name('recruitment-submission.message.store')->middleware('throttle:chat');

// BanWarden (Authenticated)
Route::delete('player/punishments/{playerPunishment:id}', [\App\Http\Controllers\BanWardenController::class, 'pardon'])->name('player.punishment.pardon');
Route::post('player/punishments/{playerPunishment:id}/evidence', [\App\Http\Controllers\BanWardenController::class, 'createEvidence'])->name('player.punishment.evidence.store');
Route::delete('player/punishments/{playerPunishment:id}/evidence/{evidence}', [\App\Http\Controllers\BanWardenController::class, 'deleteEvidence'])->name('player.punishment.evidence.delete');
});
Expand Down

0 comments on commit caa3734

Please sign in to comment.