diff --git a/app/Http/Controllers/Staff/HistoryController.php b/app/Http/Controllers/Staff/HistoryController.php new file mode 100644 index 000000000..bcf2853ae --- /dev/null +++ b/app/Http/Controllers/Staff/HistoryController.php @@ -0,0 +1,27 @@ + + * @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0 + */ + +namespace App\Http\Controllers\Staff; + +use App\Http\Controllers\Controller; + +class HistoryController extends Controller +{ + /** + * Display All Pages. + */ + public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View + { + return view('Staff.history.index'); + } +} diff --git a/app/Http/Livewire/HistorySearch.php b/app/Http/Livewire/HistorySearch.php new file mode 100644 index 000000000..5d8ed9fe2 --- /dev/null +++ b/app/Http/Livewire/HistorySearch.php @@ -0,0 +1,174 @@ + + * @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0 + */ + +namespace App\Http\Livewire; + +use App\Models\History; +use App\Models\Torrent; +use App\Models\User; +use Illuminate\Support\Facades\DB; +use Livewire\Component; +use Livewire\WithPagination; + +/** + * @property \Illuminate\Contracts\Pagination\LengthAwarePaginator $histories + */ +class HistorySearch extends Component +{ + use WithPagination; + + public int $perPage = 25; + public string $agent = ''; + public string $torrent = ''; + public string $user = ''; + public string $seeder = 'any'; + public string $active = 'any'; + public string $groupBy = 'none'; + public string $sortField = ''; + public string $sortDirection = 'desc'; + + protected $queryString = [ + 'page' => ['except' => 1], + 'perPage' => ['except' => 25], + 'agent' => ['except' => ''], + 'torrent' => ['except' => ''], + 'user' => ['except' => ''], + 'seeder' => ['except' => 'any'], + 'active' => ['except' => 'any'], + 'groupBy' => ['except' => 'none'], + 'sortField' => ['except' => ''], + 'sortDirection' => ['except' => 'desc'], + ]; + + final public function updatedPage(): void + { + $this->emit('paginationChanged'); + } + + final public function updatingUser(): void + { + $this->resetPage(); + } + + final public function updatingAgent(): void + { + $this->resetPage(); + } + + final public function updatingTorrent(): void + { + $this->resetPage(); + } + + final public function updatingSeeder(): void + { + $this->resetPage(); + } + + final public function updatingActive(): void + { + $this->resetPage(); + } + + final public function updatingGroupBy(): void + { + $this->resetPage(); + } + + final public function getHistoriesProperty(): \Illuminate\Contracts\Pagination\LengthAwarePaginator + { + return History::query() + ->with('user', 'torrent:id,name') + ->when( + $this->groupBy === 'user_id', + fn ($query) => $query->groupBy('user_id') + ->select([ + 'user_id', + DB::raw('COUNT(*) AS torrent_count'), + DB::raw('SUM(uploaded) AS uploaded_sum'), + DB::raw('SUM(actual_uploaded) AS actual_uploaded_sum'), + DB::raw('SUM(client_uploaded) AS client_uploaded_sum'), + DB::raw('SUM(downloaded) AS downloaded_sum'), + DB::raw('SUM(actual_downloaded) AS actual_downloaded_sum'), + DB::raw('SUM(client_downloaded) AS client_downloaded_sum'), + DB::raw('SUM(refunded_download) AS refunded_download_sum'), + DB::raw('AVG(seedtime) AS seedtime_avg'), + DB::raw('MIN(created_at) AS created_at_min'), + DB::raw('MAX(updated_at) AS updated_at_max'), + DB::raw('SUM(active AND seeder) AS seeding_count'), + DB::raw('SUM(active AND NOT seeder) AS leeching_count'), + DB::raw('SUM(prewarn = 1) AS prewarn_count'), + DB::raw('SUM(hitrun = 1) AS hitrun_count'), + DB::raw('SUM(immune = 1) AS immune_count'), + ]) + ->withCasts([ + 'created_at_min' => 'datetime', + 'updated_at_max' => 'datetime', + ]), + fn ($query) => $query + ->select([ + 'user_id', + 'torrent_id', + 'uploaded', + 'actual_uploaded', + 'client_uploaded', + 'downloaded', + 'actual_downloaded', + 'client_downloaded', + 'refunded_download', + 'seedtime', + 'created_at', + 'updated_at', + 'completed_at', + DB::raw('active AND seeder AS seeding'), + DB::raw('active AND NOT seeder AS leeching '), + 'prewarn', + 'hitrun', + 'immune', + ]) + ) + ->when($this->torrent !== '', fn ($query) => $query->whereIn( + 'history.torrent_id', + Torrent::select('id')->where('name', 'LIKE', '%'.str_replace(' ', '%', $this->torrent).'%') + )) + ->when($this->user !== '', fn ($query) => $query->whereIn( + 'history.user_id', + User::select('id')->where('username', 'LIKE', $this->user) + )) + ->when($this->agent !== '', fn ($query) => $query->where('history.agent', 'LIKE', $this->agent.'%')) + ->when($this->active === 'include', fn ($query) => $query->where('active', '=', true)) + ->when($this->active === 'exclude', fn ($query) => $query->where('active', '=', false)) + ->when($this->seeder === 'include', fn ($query) => $query->where('seeder', '=', true)) + ->when($this->seeder === 'exclude', fn ($query) => $query->where('seeder', '=', false)) + ->when($this->sortField !== '', fn ($query) => $query->orderBy($this->sortField, $this->sortDirection)) + ->paginate($this->perPage); + } + + final public function sortBy($field): void + { + if ($this->sortField === $field) { + $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc'; + } else { + $this->sortDirection = 'asc'; + } + + $this->sortField = $field; + } + + final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application + { + return view('livewire.history-search', [ + 'histories' => $this->histories, + ]); + } +} diff --git a/resources/views/Staff/dashboard/index.blade.php b/resources/views/Staff/dashboard/index.blade.php index 7f14deedb..bacdd260c 100644 --- a/resources/views/Staff/dashboard/index.blade.php +++ b/resources/views/Staff/dashboard/index.blade.php @@ -194,6 +194,12 @@ Peers
++ + + Histories + +
diff --git a/resources/views/Staff/history/index.blade.php b/resources/views/Staff/history/index.blade.php
new file mode 100644
index 000000000..4a89e9121
--- /dev/null
+++ b/resources/views/Staff/history/index.blade.php
@@ -0,0 +1,22 @@
+@extends('layout.default')
+
+@section('title')
+ {{ __('common.search') }}
+ Histories
+
+
+
+ @break
+ @default
+
+
+
+
+ @foreach ($histories as $history)
+
+ {{ __('user.user') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.user_id'])
+
+
+ {{ __('torrent.torrents') }}
+ @include('livewire.includes._sort-icon', ['field' => 'torrent_count'])
+
+
+ {{ __('user.credited-upload') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.uploaded_sum'])
+
+
+ {{ __('user.upload-true') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.actual_uploaded_sum'])
+
+
+ Client Upload
+ @include('livewire.includes._sort-icon', ['field' => 'histories.client_uploaded_sum'])
+
+
+ {{ __('user.credited-download') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.downloaded_sum'])
+
+
+ {{ __('user.download-true') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.actual_downloaded_sum'])
+
+
+ Client Download
+ @include('livewire.includes._sort-icon', ['field' => 'histories.client_downloaded_sum'])
+
+
+ {{ __('torrent.refunded') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.refunded_downloaded_sum'])
+
+
+ {{ __('torrent.started') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.created_at_min'])
+
+
+ Announced
+ @include('livewire.includes._sort-icon', ['field' => 'histories.updated_at_max'])
+
+
+ {{ __('user.avg-seedtime') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.seedtime_avg'])
+
+
+ {{ __('torrent.seeding') }}
+ @include('livewire.includes._sort-icon', ['field' => 'seeding_count'])
+
+
+ {{ __('torrent.leeching') }}
+ @include('livewire.includes._sort-icon', ['field' => 'leeching_count'])
+
+
+
+
+
+
+
+
+
+
+
+
+ @endforeach
+
+
+
+
+ {{ $history->torrent_count }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->uploaded_sum, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->actual_uploaded_sum, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->client_uploaded_sum, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->downloaded_sum, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->actual_downloaded_sum, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->client_downloaded_sum, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->refunded_downloaded_sum, 2) }}
+
+
+
+
+
+
+
+ @if ($history->seedtime < config('hitrun.seedtime'))
+
+ {{ $weeks = intdiv($history->seedtime_avg ?? 0, 3600 * 24 * 7) }}w {{ intdiv(($history->seedtime_avg ?? 0) - $weeks * 3600 * 24 * 7, 3600) }}h
+
+ @else
+
+ {{ $weeks = intdiv($history->seedtime_avg ?? 0, 3600 * 24 * 7) }}w {{ intdiv(($history->seedtime_avg ?? 0) - $weeks * 3600 * 24 * 7, 3600) }}h
+
+ @endif
+ {{ $history->seeding_count }}
+ {{ $history->leeching_count }}
+ {{ $history->immune_count }}
+ {{ $history->hitrun_count }}
+ {{ $history->prewarn_count }}
+
+
+
+ @endswitch
+ {{ $histories->links('partials.pagination') }}
+
+
+
+
+ @foreach ($histories as $history)
+
+ {{ __('user.user') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.user_id'])
+
+
+ {{ __('torrent.torrent') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.torrent_id'])
+
+
+ {{ __('torrent.agent') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.agent'])
+
+
+ {{ __('user.credited-upload') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.uploaded'])
+
+
+ {{ __('user.upload-true') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.actual_uploaded'])
+
+
+ Client Upload
+ @include('livewire.includes._sort-icon', ['field' => 'histories.client_uploaded'])
+
+
+ {{ __('user.credited-download') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.downloaded'])
+
+
+ {{ __('user.download-true') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.actual_downloaded'])
+
+
+ Client Download
+ @include('livewire.includes._sort-icon', ['field' => 'histories.client_downloaded'])
+
+
+ {{ __('torrent.refunded') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.refunded_downloaded'])
+
+
+ {{ __('torrent.started') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.created_at'])
+
+
+ Announced
+ @include('livewire.includes._sort-icon', ['field' => 'histories.updated_at'])
+
+
+ {{ __('torrent.completed_at') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.completed_at'])
+
+
+ {{ __('torrent.seedtime') }}
+ @include('livewire.includes._sort-icon', ['field' => 'histories.seedtime'])
+
+
+ {{ __('torrent.seeding') }}
+ @include('livewire.includes._sort-icon', ['field' => 'seeding'])
+
+
+ {{ __('torrent.leeching') }}
+ @include('livewire.includes._sort-icon', ['field' => 'leeching'])
+
+
+
+
+
+
+
+
+
+
+
+
+ @endforeach
+
+
+
+
+
+ {{ $history->torrent->name ?? '' }}
+
+
+ {{ $history->agent }}
+
+ {{ App\Helpers\StringHelper::formatBytes($history->uploaded, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->actual_uploaded, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->client_uploaded, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->downloaded, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->actual_downloaded, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->client_downloaded, 2) }}
+
+
+ {{ App\Helpers\StringHelper::formatBytes($history->refunded_downloaded, 2) }}
+
+
+
+
+
+
+
+
+
+
+ @if ($history->seedtime < config('hitrun.seedtime'))
+
+ {{ $weeks = intdiv($history->seedtime ?? 0, 3600 * 24 * 7) }}w {{ intdiv(($history->seedtime ?? 0) - $weeks * 3600 * 24 * 7, 3600) }}h
+
+ @else
+
+ {{ $weeks = intdiv($history->seedtime ?? 0, 3600 * 24 * 7) }}w {{ intdiv(($history->seedtime ?? 0) - $weeks * 3600 * 24 * 7, 3600) }}h
+
+ @endif
+
+ @if ($history->active)
+
+ @else
+
+ @endif
+
+
+ @if ($history->seeder)
+
+ @else
+
+ @endif
+
+
+ @if ($history->immune)
+
+ @else
+
+ @endif
+
+
+ @if ($history->hitrun)
+
+ @else
+
+ @endif
+
+
+ @if ($history->prewarn)
+
+ @else
+
+ @endif
+
+