Merge branch '6.x.x' into Warnings-System

This commit is contained in:
HDVinnie
2022-09-21 06:56:35 -04:00
322 changed files with 3726 additions and 4237 deletions
+17 -12
View File
@@ -1,17 +1,22 @@
name: Lint
name: PHP Linting (Pint)
on:
- push
workflow_dispatch:
push:
branches-ignore:
- 'dependabot/npm_and_yarn/*'
jobs:
lint:
name: Lint
phplint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
- uses: actions/checkout@v3
with:
php-version: '8.1'
tools: phplint
- name: Check syntax
run: phplint .
fetch-depth: 2
- name: "laravel-pint"
uses: aglipanci/laravel-pint-action@0.1.0
with:
preset: psr12
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: PSR12 Linting
skip_fetch: true
-30
View File
@@ -1,30 +0,0 @@
php:
preset: recommended
risky: true
version: 8.1
enabled:
- method_argument_space
- not_operator_with_successor_space
- no_useless_else
- ternary_to_null_coalescing
disabled:
- method_argument_space_strict
- psr4
finder:
exclude:
- node_modules
- storage
- vendor
- .github
not-name:
- index.php
- server.php
name:
- "*.php"
- "*.blade.php"
js:
finder:
not-name:
- webpack.mix.js
css: true
vue: true
+1 -1
View File
@@ -8,7 +8,7 @@
<a href="http://laravel.com"><img src="https://img.shields.io/badge/Laravel-9-f4645f.svg" /></a>
<a href="https://github.com/HDInnovations/UNIT3D/blob/master/LICENSE"><img src="https://img.shields.io/badge/License-AGPL%20v3.0-yellow.svg" /></a>
<br>
<a href="https://github.styleci.io/repos/113471037"><img src="https://github.styleci.io/repos/113471037/shield?style=flat&branch=master" alt="StyleCI"></a>
<a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/lint.yml/badge.svg"><img src="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/lint.yml/badge.svg" /></a>
<a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/phpunit-test.yml/badge.svg"><img src="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/phpunit-test.yml/badge.svg" /></a>
<a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/compile-assets-test.yml/badge.svg"><img src="https://github.com/HDInnovations/UNIT3D-Community-Edition/actions/workflows/compile-assets-test.yml/badge.svg" /></a>
<br>
@@ -57,7 +57,7 @@ class AutoDisableInactiveUsers extends Command
->all();
foreach ($users as $user) {
if ($user->getSeeding() === 0) {
if ($user->seedingTorrents()->count() === 0) {
$user->group_id = $disabledGroup[0];
$user->can_upload = 0;
$user->can_download = 0;
+2 -2
View File
@@ -96,7 +96,7 @@ class AutoGroup extends Command
}
// Seeder Seedsize >= 5TiB and account 1 month old and seedtime average 30 days or better
if ($user->getTotalSeedSize() >= $byteUnits->bytesFromUnit('5TiB') && $user->getRatio() >= \config('other.ratio') && \round($user->getTotalSeedTime() / \max(1, $hiscount)) > 2_592_000 && $user->created_at < $current->copy()->subDays(30)->toDateTimeString() && $user->group_id != UserGroups::SEEDER) {
if ($user->seedingTorrents()->sum('size') >= $byteUnits->bytesFromUnit('5TiB') && $user->getRatio() >= \config('other.ratio') && \round($user->history()->sum('seedtime') / \max(1, $hiscount)) > 2_592_000 && $user->created_at < $current->copy()->subDays(30)->toDateTimeString() && $user->group_id != UserGroups::SEEDER) {
$user->group_id = UserGroups::SEEDER;
$user->save();
}
@@ -108,7 +108,7 @@ class AutoGroup extends Command
}
// Archivist Seedsize >= 10TiB and account 3 month old and seedtime average 60 days or better
if ($user->getTotalSeedSize() >= $byteUnits->bytesFromUnit('10TiB') && $user->getRatio() >= \config('other.ratio') && \round($user->getTotalSeedTime() / \max(1, $hiscount)) > 2_592_000 * 2 && $user->created_at < $current->copy()->subDays(90)->toDateTimeString() && $user->group_id != UserGroups::ARCHIVIST) {
if ($user->seedingTorrents()->sum('size') >= $byteUnits->bytesFromUnit('10TiB') && $user->getRatio() >= \config('other.ratio') && \round($user->history()->sum('seedtime') / \max(1, $hiscount)) > 2_592_000 * 2 && $user->created_at < $current->copy()->subDays(90)->toDateTimeString() && $user->group_id != UserGroups::ARCHIVIST) {
$user->group_id = UserGroups::ARCHIVIST;
$user->save();
}
+2 -1
View File
@@ -51,7 +51,8 @@ class AutoHighspeedTag extends Command
->select('torrent_id')
->whereRaw("ip IN ('".$seedboxIps->implode("','")."')"),
'highspeed_torrents',
fn ($join) => $join->on('torrents.id', '=', 'highspeed_torrents.torrent_id'))
fn ($join) => $join->on('torrents.id', '=', 'highspeed_torrents.torrent_id')
)
->update([
'highspeed' => DB::raw('CASE WHEN highspeed_torrents.torrent_id IS NOT NULL THEN 1 ELSE 0 END'),
]);
+2 -1
View File
@@ -39,7 +39,8 @@ class AutoStatsClients extends Command
public function handle(): void
{
$clients = Peer::selectRaw('agent, count(*) as count')
->fromSub(fn ($sub) => $sub
->fromSub(
fn ($sub) => $sub
->select(['agent', 'user_id'])
->from('peers')
->groupBy('agent', 'user_id'),
+4 -2
View File
@@ -346,8 +346,10 @@ class GitUpdater extends Command
}
} elseif (\is_file(\base_path($path)) && \dirname($path) !== '.') {
$path = \dirname($path);
if (! \is_dir(\storage_path(\sprintf('gitupdate/%s', $path))) && ! \mkdir($concurrentDirectory = \storage_path(\sprintf('gitupdate/%s',
$path)), 0775, true) && ! \is_dir($concurrentDirectory)) {
if (! \is_dir(\storage_path(\sprintf('gitupdate/%s', $path))) && ! \mkdir($concurrentDirectory = \storage_path(\sprintf(
'gitupdate/%s',
$path
)), 0775, true) && ! \is_dir($concurrentDirectory)) {
throw new \RuntimeException(\sprintf('Directory "%s" was not created', $concurrentDirectory));
}
}
+39 -58
View File
@@ -27,10 +27,9 @@ class BBCodeConverter
*/
protected function replaceSize(): void
{
$this->text = \preg_replace_callback('#\[size=([\W\D\w\s]*?)\]([\W\D\w\s]*?)\[/size\]#iu',
$this->text = \preg_replace_callback(
'#\[size=([\W\D\w\s]*?)\]([\W\D\w\s]*?)\[/size\]#iu',
fn ($matches) => '<div style="font-size: '.\trim($matches[1], '').';">'.\trim($matches[1], '').'</span>',
$this->text
);
}
@@ -40,10 +39,9 @@ class BBCodeConverter
*/
protected function replaceCenter(): void
{
$this->text = \preg_replace_callback('#\[center\]([\W\D\w\s]*?)\[/center\]#iu',
$this->text = \preg_replace_callback(
'#\[center\]([\W\D\w\s]*?)\[/center\]#iu',
fn ($matches) => '<div class="text-center">'.\trim($matches[1], ' ').'</div>',
$this->text
);
}
@@ -53,10 +51,9 @@ class BBCodeConverter
*/
protected function replaceBold(): void
{
$this->text = \preg_replace_callback('#\[b\]([\W\D\w\s]*?)\[/b\]#iu',
$this->text = \preg_replace_callback(
'#\[b\]([\W\D\w\s]*?)\[/b\]#iu',
fn ($matches) => '**'.\trim($matches[1], ' ').'**',
$this->text
);
}
@@ -66,10 +63,9 @@ class BBCodeConverter
*/
protected function replaceItalic(): void
{
$this->text = \preg_replace_callback('#\[i\]([\W\D\w\s]*?)\[/i\]#iu',
$this->text = \preg_replace_callback(
'#\[i\]([\W\D\w\s]*?)\[/i\]#iu',
fn ($matches) => '*'.\trim($matches[1], ' ').'*',
$this->text
);
}
@@ -79,10 +75,9 @@ class BBCodeConverter
*/
protected function replaceUnderline(): void
{
$this->text = \preg_replace_callback('#\[u\]([\W\D\w\s]*?)\[/u\]#iu',
$this->text = \preg_replace_callback(
'#\[u\]([\W\D\w\s]*?)\[/u\]#iu',
fn ($matches) => '_'.\trim($matches[1], ' ').'_',
$this->text
);
}
@@ -92,10 +87,9 @@ class BBCodeConverter
*/
protected function replaceStrikethrough(): void
{
$this->text = \preg_replace_callback('#\[s\]([\W\D\w\s]*?)\[/s\]#iu',
$this->text = \preg_replace_callback(
'#\[s\]([\W\D\w\s]*?)\[/s\]#iu',
fn ($matches) => '~~'.\trim($matches[1], ' ').'~~',
$this->text
);
}
@@ -105,8 +99,8 @@ class BBCodeConverter
*/
protected function replaceLists(): void
{
$this->text = \preg_replace_callback('#\[list(?P<type>=1)?\](?P<items>[\W\D\w\s]*?)\[/list\]#iu',
$this->text = \preg_replace_callback(
'#\[list(?P<type>=1)?\](?P<items>[\W\D\w\s]*?)\[/list\]#iu',
function ($matches) {
$buffer = '';
@@ -140,7 +134,6 @@ class BBCodeConverter
return $buffer;
},
$this->text
);
}
@@ -200,8 +193,8 @@ class BBCodeConverter
*/
protected function replaceUrls(): void
{
$this->text = \preg_replace_callback('#\[url\s*=\s*("(?:[^"]*")|\A[^\']*\Z|(?:[^\'">\]\s]+))\s*(?:[^]\s]*)\]([\W\D\w\s]*?)\[/url\]#iu',
$this->text = \preg_replace_callback(
'#\[url\s*=\s*("(?:[^"]*")|\A[^\']*\Z|(?:[^\'">\]\s]+))\s*(?:[^]\s]*)\]([\W\D\w\s]*?)\[/url\]#iu',
function ($matches) {
if (isset($matches[1], $matches[2])) {
return '['.$matches[2].']('.$matches[1].')';
@@ -209,7 +202,6 @@ class BBCodeConverter
throw new \RuntimeException('Text has malformed BBCode urls');
},
$this->text
);
}
@@ -219,10 +211,9 @@ class BBCodeConverter
*/
protected function replaceImage(): void
{
$this->text = \preg_replace_callback('#\[img\]([\W\D\w\s]*?)\[/img\]#iu',
$this->text = \preg_replace_callback(
'#\[img\]([\W\D\w\s]*?)\[/img\]#iu',
fn ($matches) => PHP_EOL.'![]'.'('.$matches[1].')'.PHP_EOL,
$this->text
);
}
@@ -232,10 +223,9 @@ class BBCodeConverter
*/
protected function replaceImages(): void
{
$this->text = \preg_replace_callback('#\[img\s*=\s*("(?:[^"]*")|\A[^\']*\Z|(?:[^\'">\]\s]+))\s*(?:[^]\s]*)\[/img\]#iu',
fn ($matches) => PHP_EOL.'!['.$matches[2].']'.'('.$matches[1].')'.PHP_EOL,
$this->text = \preg_replace_callback(
'#\[img\s*=\s*("(?:[^"]*")|\A[^\']*\Z|(?:[^\'">\]\s]+))\s*(?:[^]\s]*)\[/img\]#iu',
fn ($matches) => PHP_EOL.'!['.$matches[2].']'.'('.$matches[1].')'.PHP_EOL,
$this->text
);
}
@@ -249,14 +239,13 @@ class BBCodeConverter
$this->text = \preg_replace('#\G(?<!^)(?>(\[quote\b[^]]*](?>[^[]++|\[(?!/?quote)|(?1))*\[/quote])|(?<!\[)(?>[^[]++|\[(?!/?quote))+\K)|\[quote\b[^]]*]\K#', '', $this->text);
// Replaces all the remaining quotes with '> ' characters.
$this->text = \preg_replace_callback('#\[quote\b[^]]*\]((?>[^[]++|\[(?!/?quote))*)\[/quote\]#i',
$this->text = \preg_replace_callback(
'#\[quote\b[^]]*\]((?>[^[]++|\[(?!/?quote))*)\[/quote\]#i',
function ($matches) {
$quote = \preg_replace('#^\s*#mu', '', \trim($matches[1]));
return '> '.$quote.PHP_EOL.PHP_EOL;
},
$this->text
);
}
@@ -266,8 +255,8 @@ class BBCodeConverter
*/
protected function replaceSnippets(): void
{
$this->text = \preg_replace_callback('#\[code\s*=?(?P<language>\w*)\](?P<snippet>[\W\D\w\s]*?)\[\/code\]#iu',
$this->text = \preg_replace_callback(
'#\[code\s*=?(?P<language>\w*)\](?P<snippet>[\W\D\w\s]*?)\[\/code\]#iu',
function ($matches) {
if (isset($matches['snippet'])) {
$language = \strtolower($matches['language']);
@@ -303,7 +292,6 @@ class BBCodeConverter
throw new \RuntimeException('Text has malformed BBCode snippet.');
},
$this->text
);
}
@@ -313,10 +301,9 @@ class BBCodeConverter
*/
protected function replaceSpoilers(): void
{
$this->text = \preg_replace_callback('#\[spoiler\](.*?)\[\/spoiler\]#ius',
$this->text = \preg_replace_callback(
'#\[spoiler\](.*?)\[\/spoiler\]#ius',
fn ($matches) => '<p><details class="label label-primary"><summary>Spoiler</summary><pre><code>'.\trim($matches[1]).'</code></pre></details></p>',
$this->text
);
}
@@ -326,10 +313,9 @@ class BBCodeConverter
*/
protected function replaceNamedSpoilers(): void
{
$this->text = \preg_replace_callback('#\[spoiler\=(.*?)\](.*?)\[\/spoiler\]#ius',
$this->text = \preg_replace_callback(
'#\[spoiler\=(.*?)\](.*?)\[\/spoiler\]#ius',
fn ($matches) => '<p><details class="label label-primary"><summary>'.\trim($matches[1]).'</summary><pre><code>'.\trim($matches[2]).'</code></pre></details></p>',
$this->text
);
}
@@ -339,10 +325,9 @@ class BBCodeConverter
*/
protected function replaceColor(): void
{
$this->text = \preg_replace_callback('#\[color=([\W\D\w\s]*?)\]([\W\D\w\s]*?)\[/color\]#iu',
$this->text = \preg_replace_callback(
'#\[color=([\W\D\w\s]*?)\]([\W\D\w\s]*?)\[/color\]#iu',
fn ($matches) => '<span style="color: '.\trim($matches[1], '').';">'.\trim($matches[2], '').'</span>',
$this->text
);
}
@@ -352,10 +337,9 @@ class BBCodeConverter
*/
protected function replaceVideo(): void
{
$this->text = \preg_replace_callback('#\[video=[^\]]*.([\W\D\w\s][^\[]*)\[/video]#iu',
$this->text = \preg_replace_callback(
'#\[video=[^\]]*.([\W\D\w\s][^\[]*)\[/video]#iu',
fn ($matches) => '<iframe src="https://www.youtube-nocookie.com/embed/'.\trim($matches[1], '').'?rel=0" width="640" height="480" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>',
$this->text
);
}
@@ -365,10 +349,9 @@ class BBCodeConverter
*/
protected function replaceYoutube(): void
{
$this->text = \preg_replace_callback('#\[youtube\]([\W\D\w\s]*?)\[/youtube\]#iu',
$this->text = \preg_replace_callback(
'#\[youtube\]([\W\D\w\s]*?)\[/youtube\]#iu',
fn ($matches) => '<iframe src="https://www.youtube-nocookie.com/embed/'.\trim($matches[1], '').'?rel=0" width="640" height="480" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>',
$this->text
);
}
@@ -378,10 +361,9 @@ class BBCodeConverter
*/
protected function replaceAlert(): void
{
$this->text = \preg_replace_callback('#\[alert\]([\W\D\w\s]*?)\[/alert\]#iu',
$this->text = \preg_replace_callback(
'#\[alert\]([\W\D\w\s]*?)\[/alert\]#iu',
fn ($matches) => '<div class="bbcode-alert">'.\trim($matches[1], '').'</div>',
$this->text
);
}
@@ -391,10 +373,9 @@ class BBCodeConverter
*/
protected function replaceNote(): void
{
$this->text = \preg_replace_callback('#\[note\]([\W\D\w\s]*?)\[/note\]#iu',
$this->text = \preg_replace_callback(
'#\[note\]([\W\D\w\s]*?)\[/note\]#iu',
fn ($matches) => '<div class="bbcode-note">'.\trim($matches[1], '').'</div>',
$this->text
);
}
+4 -4
View File
@@ -22,26 +22,26 @@ class BackupEncryption
*
* @var string
*/
public final const ENCRYPTION_DEFAULT = ZipArchive::EM_AES_128;
final public const ENCRYPTION_DEFAULT = ZipArchive::EM_AES_128;
/**
* AES-128 encryption contants.
*
* @var string
*/
public final const ENCRYPTION_WINZIP_AES_128 = ZipArchive::EM_AES_128;
final public const ENCRYPTION_WINZIP_AES_128 = ZipArchive::EM_AES_128;
/**
* AES-192 encryption contants.
*
* @var string
*/
public final const ENCRYPTION_WINZIP_AES_192 = ZipArchive::EM_AES_192;
final public const ENCRYPTION_WINZIP_AES_192 = ZipArchive::EM_AES_192;
/**
* AES-256 encryption contants.
*
* @var string
*/
public final const ENCRYPTION_WINZIP_AES_256 = ZipArchive::EM_AES_256;
final public const ENCRYPTION_WINZIP_AES_256 = ZipArchive::EM_AES_256;
}
+10 -5
View File
@@ -165,7 +165,8 @@ class Markdown
foreach ($lines as $line) {
if (\rtrim($line) === '') {
if (isset($CurrentBlock)) {
$CurrentBlock['interrupted'] = (isset($CurrentBlock['interrupted'])
$CurrentBlock['interrupted'] = (
isset($CurrentBlock['interrupted'])
? $CurrentBlock['interrupted'] + 1 : 1
);
}
@@ -798,8 +799,10 @@ class Markdown
}
if (
(! \str_contains((string) $Block['element']['handler']['argument'], '|') && ! \str_contains((string) $Line['text'], '|') && ! \str_contains((string) $Line['text'],
':')) || \str_contains((string) $Block['element']['handler']['argument'], "\n")
(! \str_contains((string) $Block['element']['handler']['argument'], '|') && ! \str_contains((string) $Line['text'], '|') && ! \str_contains(
(string) $Line['text'],
':'
)) || \str_contains((string) $Block['element']['handler']['argument'], "\n")
) {
return;
}
@@ -1020,7 +1023,8 @@ class Markdown
$Elements = [];
$nonNestables = (empty($nonNestables)
$nonNestables = (
empty($nonNestables)
? []
: \array_combine($nonNestables, $nonNestables)
);
@@ -1580,7 +1584,8 @@ class Markdown
continue;
}
$autoBreakNext = ($Element['autobreak'] ?? isset($Element['name'])
$autoBreakNext = (
$Element['autobreak'] ?? isset($Element['name'])
);
// (autobreak === false) covers both sides of an element
$autoBreak = $autoBreak ? $autoBreakNext : $autoBreak;
+5 -5
View File
@@ -15,15 +15,15 @@ namespace App\Helpers;
class StringHelper
{
public final const KIB = 1_024;
final public const KIB = 1_024;
public final const MIB = 1_024 * 1_024;
final public const MIB = 1_024 * 1_024;
public final const GIB = 1_024 * 1_024 * 1_024;
final public const GIB = 1_024 * 1_024 * 1_024;
public final const TIB = 1_024 * 1_024 * 1_024 * 1_024;
final public const TIB = 1_024 * 1_024 * 1_024 * 1_024;
public final const PIB = 1_024 * 1_024 * 1_024 * 1_024 * 1_024;
final public const PIB = 1_024 * 1_024 * 1_024 * 1_024 * 1_024;
/**
* @var string
+16 -8
View File
@@ -282,13 +282,17 @@ class TorrentController extends BaseController
if ($free >= 1 && $featured == 0) {
if ($torrent->fl_until === null) {
$this->chatRepository->systemMessage(
\sprintf('Ladies and Gents, [url=%s/torrents/',
$appurl).$torrent->id.']'.$torrent->name.'[/url] has been granted '.$free.'% FreeLeech! Grab It While You Can! :fire:'
\sprintf(
'Ladies and Gents, [url=%s/torrents/',
$appurl
).$torrent->id.']'.$torrent->name.'[/url] has been granted '.$free.'% FreeLeech! Grab It While You Can! :fire:'
);
} else {
$this->chatRepository->systemMessage(
\sprintf('Ladies and Gents, [url=%s/torrents/',
$appurl).$torrent->id.']'.$torrent->name.'[/url] has been granted '.$free.'% FreeLeech for '.$request->input('fl_until').' days. :stopwatch:'
\sprintf(
'Ladies and Gents, [url=%s/torrents/',
$appurl
).$torrent->id.']'.$torrent->name.'[/url] has been granted '.$free.'% FreeLeech for '.$request->input('fl_until').' days. :stopwatch:'
);
}
}
@@ -296,13 +300,17 @@ class TorrentController extends BaseController
if ($doubleup == 1 && $featured == 0) {
if ($torrent->du_until === null) {
$this->chatRepository->systemMessage(
\sprintf('Ladies and Gents, [url=%s/torrents/',
$appurl).$torrent->id.']'.$torrent->name.'[/url] has been granted Double Upload! Grab It While You Can! :fire:'
\sprintf(
'Ladies and Gents, [url=%s/torrents/',
$appurl
).$torrent->id.']'.$torrent->name.'[/url] has been granted Double Upload! Grab It While You Can! :fire:'
);
} else {
$this->chatRepository->systemMessage(
\sprintf('Ladies and Gents, [url=%s/torrents/',
$appurl).$torrent->id.']'.$torrent->name.'[/url] has been granted Double Upload for '.$request->input('du_until').' days. :stopwatch:'
\sprintf(
'Ladies and Gents, [url=%s/torrents/',
$appurl
).$torrent->id.']'.$torrent->name.'[/url] has been granted Double Upload for '.$request->input('du_until').' days. :stopwatch:'
);
}
}
+94 -46
View File
@@ -20,6 +20,7 @@ namespace App\Http\Controllers;
use App\Exceptions\TrackerException;
use App\Helpers\Bencode;
use App\Jobs\ProcessAnnounce;
use App\Models\BlacklistClient;
use App\Models\Group;
use App\Models\Peer;
use App\Models\Torrent;
@@ -146,17 +147,22 @@ class AnnounceController extends Controller
|| $request->header('want-digest'), new TrackerException(122));
$userAgent = $request->header('User-Agent');
$clientBlacklist = BlacklistClient::all()->pluck('name')->toArray();
// Should also block User-Agent strings that are too long. (For Database reasons)
\throw_if(\strlen((string) $userAgent) > 64, new TrackerException(123));
// Block Browser by checking the User-Agent
\throw_if(\preg_match('/(Mozilla|Browser|Chrome|Safari|AppleWebKit|Opera|Links|Lynx|Bot|Unknown)/i',
(string) $userAgent), new TrackerException(121));
\throw_if(\preg_match(
'/(Mozilla|Browser|Chrome|Safari|AppleWebKit|Opera|Links|Lynx|Bot|Unknown)/i',
(string) $userAgent
), new TrackerException(121));
// Block Blacklisted Clients
\throw_if(\in_array($request->header('User-Agent'), \config('client-blacklist.clients')),
new TrackerException(128, [':ua' => $request->header('User-Agent')]));
\throw_if(
\in_array($request->header('User-Agent'), $clientBlacklist),
new TrackerException(128, [':ua' => $request->header('User-Agent')])
);
}
/**
@@ -171,12 +177,16 @@ class AnnounceController extends Controller
\throw_if($passkey === null, new TrackerException(130, [':attribute' => 'passkey']));
// If Passkey Length Is Wrong
\throw_if(\strlen((string) $passkey) !== 32,
new TrackerException(132, [':attribute' => 'passkey', ':rule' => 32]));
\throw_if(
\strlen((string) $passkey) !== 32,
new TrackerException(132, [':attribute' => 'passkey', ':rule' => 32])
);
// If Passkey Format Is Wrong
\throw_if(\strspn(\strtolower($passkey), 'abcdef0123456789') !== 32,
new TrackerException(131, [':attribute' => 'passkey', ':reason' => 'Passkey format is incorrect']));
\throw_if(
\strspn(\strtolower($passkey), 'abcdef0123456789') !== 32,
new TrackerException(131, [':attribute' => 'passkey', ':reason' => 'Passkey format is incorrect'])
);
}
/**
@@ -202,14 +212,18 @@ class AnnounceController extends Controller
}
foreach (['info_hash', 'peer_id'] as $item) {
\throw_if(\strlen((string) $queries[$item]) !== 20,
new TrackerException(133, [':attribute' => $item, ':rule' => 20]));
\throw_if(
\strlen((string) $queries[$item]) !== 20,
new TrackerException(133, [':attribute' => $item, ':rule' => 20])
);
}
foreach (['uploaded', 'downloaded', 'left'] as $item) {
$itemData = $queries[$item];
\throw_if(! \is_numeric($itemData) || $itemData < 0,
new TrackerException(134, [':attribute' => $item]));
\throw_if(
! \is_numeric($itemData) || $itemData < 0,
new TrackerException(134, [':attribute' => $item])
);
}
// Part.2 Extract optional announce fields
@@ -223,25 +237,33 @@ class AnnounceController extends Controller
}
foreach (['numwant', 'corrupt'] as $item) {
\throw_if(! \is_numeric($queries[$item]) || $queries[$item] < 0,
new TrackerException(134, [':attribute' => $item]));
\throw_if(
! \is_numeric($queries[$item]) || $queries[$item] < 0,
new TrackerException(134, [':attribute' => $item])
);
}
\throw_if(! \in_array(\strtolower($queries['event']), ['started', 'completed', 'stopped', 'paused', '']),
new TrackerException(136, [':event' => \strtolower($queries['event'])]));
\throw_if(
! \in_array(\strtolower($queries['event']), ['started', 'completed', 'stopped', 'paused', '']),
new TrackerException(136, [':event' => \strtolower($queries['event'])])
);
// Part.3 check Port is Valid and Allowed
/**
* Normally , the port must in 1 - 65535 , that is ( $port > 0 && $port < 0xffff )
* However, in some case , When `&event=stopped` the port may set to 0.
*/
\throw_if($queries['port'] === 0 && \strtolower($queries['event']) !== 'stopped',
new TrackerException(137, [':event' => \strtolower($queries['event'])]));
\throw_if(
$queries['port'] === 0 && \strtolower($queries['event']) !== 'stopped',
new TrackerException(137, [':event' => \strtolower($queries['event'])])
);
\throw_if(! \is_numeric($queries['port']) || $queries['port'] < 0 || $queries['port'] > 0xFFFF
|| \in_array($queries['port'],
|| \in_array(
$queries['port'],
self::BLACK_PORTS,
true), new TrackerException(135, [':port' => $queries['port']]));
true
), new TrackerException(135, [':port' => $queries['port']]));
// Part.4 Get User Ip Address
$queries['ip-address'] = $request->getClientIp();
@@ -267,12 +289,18 @@ class AnnounceController extends Controller
protected function checkUser($passkey, $queries): object
{
// Caached System Required Groups
$bannedGroup = \cache()->rememberForever('banned_group',
fn () => Group::where('slug', '=', 'banned')->pluck('id'));
$validatingGroup = \cache()->rememberForever('validating_group',
fn () => Group::where('slug', '=', 'validating')->pluck('id'));
$disabledGroup = \cache()->rememberForever('disabled_group',
fn () => Group::where('slug', '=', 'disabled')->pluck('id'));
$bannedGroup = \cache()->rememberForever(
'banned_group',
fn () => Group::where('slug', '=', 'banned')->pluck('id')
);
$validatingGroup = \cache()->rememberForever(
'validating_group',
fn () => Group::where('slug', '=', 'validating')->pluck('id')
);
$disabledGroup = \cache()->rememberForever(
'disabled_group',
fn () => Group::where('slug', '=', 'disabled')->pluck('id')
);
// Check Passkey Against Users Table
$user = User::with('group')
@@ -284,20 +312,28 @@ class AnnounceController extends Controller
\throw_if($user === null, new TrackerException(140));
// If User Account Is Unactivated/Validating Return Error to Client
\throw_if($user->active === 0 || $user->group->id === $validatingGroup[0],
new TrackerException(141, [':status' => 'Unactivated/Validating']));
\throw_if(
$user->active === 0 || $user->group->id === $validatingGroup[0],
new TrackerException(141, [':status' => 'Unactivated/Validating'])
);
// If User Download Rights Are Disabled Return Error to Client
\throw_if($user->can_download === 0 && $queries['left'] !== '0',
new TrackerException(142));
\throw_if(
$user->can_download === 0 && $queries['left'] !== '0',
new TrackerException(142)
);
// If User Is Banned Return Error to Client
\throw_if($user->group->id === $bannedGroup[0],
new TrackerException(141, [':status' => 'Banned']));
\throw_if(
$user->group->id === $bannedGroup[0],
new TrackerException(141, [':status' => 'Banned'])
);
// If User Is Disabled Return Error to Client
throw_if($user->group->id === $disabledGroup[0],
new TrackerException(141, [':status' => 'Disabled']));
throw_if(
$user->group->id === $disabledGroup[0],
new TrackerException(141, [':status' => 'Disabled'])
);
return $user;
}
@@ -321,16 +357,22 @@ class AnnounceController extends Controller
\throw_if($torrent === null, new TrackerException(150));
// If Torrent Is Pending Moderation Return Error to Client
\throw_if($torrent->status === self::PENDING,
new TrackerException(151, [':status' => 'PENDING In Moderation']));
\throw_if(
$torrent->status === self::PENDING,
new TrackerException(151, [':status' => 'PENDING In Moderation'])
);
// If Torrent Is Rejected Return Error to Client
\throw_if($torrent->status === self::REJECTED,
new TrackerException(151, [':status' => 'REJECTED In Moderation']));
\throw_if(
$torrent->status === self::REJECTED,
new TrackerException(151, [':status' => 'REJECTED In Moderation'])
);
// If Torrent Is Postponed Return Error to Client
\throw_if($torrent->status === self::POSTPONED,
new TrackerException(151, [':status' => 'POSTPONED In Moderation']));
\throw_if(
$torrent->status === self::POSTPONED,
new TrackerException(151, [':status' => 'POSTPONED In Moderation'])
);
return $torrent;
}
@@ -368,9 +410,11 @@ class AnnounceController extends Controller
->first();
$setMin = \config('announce.min_interval.interval') ?? self::MIN;
$randomMinInterval = random_int($setMin, $setMin * 2);
\throw_if($prevAnnounce && $prevAnnounce->updated_at->greaterThan(now()->subSeconds($randomMinInterval))
\throw_if(
$prevAnnounce && $prevAnnounce->updated_at->greaterThan(now()->subSeconds($randomMinInterval))
&& \strtolower($queries['event']) !== 'completed' && \strtolower($queries['event']) !== 'stopped',
new TrackerException(162, [':min' => $randomMinInterval]));
new TrackerException(162, [':min' => $randomMinInterval])
);
}
/**
@@ -388,8 +432,10 @@ class AnnounceController extends Controller
->count();
// If Users Peer Count On A Single Torrent Is Greater Than X Return Error to Client
\throw_if($connections > \config('announce.rate_limit'),
new TrackerException(138, [':limit' => \config('announce.rate_limit')]));
\throw_if(
$connections > \config('announce.rate_limit'),
new TrackerException(138, [':limit' => \config('announce.rate_limit')])
);
}
/**
@@ -409,8 +455,10 @@ class AnnounceController extends Controller
->where('seeder', '=', 0)
->count();
\throw_if($count >= $max,
new TrackerException(164, [':max' => $max]));
\throw_if(
$count >= $max,
new TrackerException(164, [':max' => $max])
);
}
}
-733
View File
@@ -1,733 +0,0 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Controllers;
use App\Achievements\UserMade100Comments;
use App\Achievements\UserMade200Comments;
use App\Achievements\UserMade300Comments;
use App\Achievements\UserMade400Comments;
use App\Achievements\UserMade500Comments;
use App\Achievements\UserMade50Comments;
use App\Achievements\UserMade600Comments;
use App\Achievements\UserMade700Comments;
use App\Achievements\UserMade800Comments;
use App\Achievements\UserMade900Comments;
use App\Achievements\UserMadeComment;
use App\Achievements\UserMadeTenComments;
use App\Models\Article;
use App\Models\Collection;
use App\Models\Comment;
use App\Models\Playlist;
use App\Models\Ticket;
use App\Models\Torrent;
use App\Models\TorrentRequest;
use App\Models\User;
use App\Notifications\NewComment;
use App\Repositories\ChatRepository;
use App\Repositories\TaggedUserRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
/**
* @see \Tests\Feature\Http\Controllers\CommentControllerTest
*/
class CommentController extends Controller
{
public $tag;
/**
* CommentController Constructor.
*/
public function __construct(private readonly TaggedUserRepository $taggedUserRepository, private readonly ChatRepository $chatRepository)
{
}
/**
* Add A Comment To A Collection.
*/
public function collection(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$collection = Collection::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('collection-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('collection.show', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('collection-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('collection.show', ['id' => $collection->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->collection_id = $collection->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'collection_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('collection.show', ['id' => $collection->id])
->withErrors($v->errors());
}
$comment->save();
$collectionUrl = \href_collection($collection);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on collection [url=%s]%s[/url]', $profileUrl, $user->username, $collectionUrl, $collection->name)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on collection [url=%s]%s[/url]', $collectionUrl, $collection->name)
);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$collection->comments()->get()->each(function ($c, $v) use ($users) {
$users->push($c->user);
});
$this->tag->messageCommentUsers(
'collection',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'collection',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('mediahub.collections.show', ['id' => $collection->id, 'hash' => '#comments'])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Article.
*/
public function article(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$article = Article::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('article-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('articles.show', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('article-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('articles.show', ['id' => $article->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->article_id = $article->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'article_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('articles.show', ['id' => $article->id])
->withErrors($v->errors());
}
$comment->save();
$articleUrl = \href_article($article);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on article [url=%s]%s[/url]', $profileUrl, $user->username, $articleUrl, $article->title)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on article [url=%s]%s[/url]', $articleUrl, $article->title)
);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$article->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
'article',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'article',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('articles.show', ['id' => $article->id])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Playlist.
*/
public function playlist(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$playlist = Playlist::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('playlist-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('playlists.show', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('playlist-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('playlists.show', ['id' => $playlist->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->playlist_id = $playlist->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'playlist_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('playlists.show', ['id' => $playlist->id])
->withErrors($v->errors());
}
$comment->save();
$playlistUrl = \href_playlist($playlist);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on playlist [url=%s]%s[/url]', $profileUrl, $user->username, $playlistUrl, $playlist->name)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on playlist [url=%s]%s[/url]', $playlistUrl, $playlist->name)
);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$playlist->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
'playlist',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'playlist',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('playlists.show', ['id' => $playlist->id, 'hash' => '#comments'])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Torrent.
*/
public function torrent(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$torrent = Torrent::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('torrent-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('torrent-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->torrent_id = $torrent->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'torrent_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors($v->errors());
}
$comment->save();
//Notification
if ($user->id != $torrent->user_id) {
$torrent->notifyUploader('comment', $comment);
}
$torrentUrl = \href_torrent($torrent);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on Torrent [url=%s]%s[/url]', $profileUrl, $user->username, $torrentUrl, $torrent->name)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on torrent [url=%s]%s[/url]', $torrentUrl, $torrent->name)
);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$torrent->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
'torrent',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'torrent',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('torrent', ['id' => $torrent->id, 'hash' => '#comments'])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Request.
*/
public function request(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$tr = TorrentRequest::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('request-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('request', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('request-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('request', ['id' => $tr->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = $request->input('anonymous');
$comment->user_id = $user->id;
$comment->requests_id = $tr->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'requests_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('request', ['id' => $tr->id])
->withErrors($v->errors());
}
$comment->save();
$trUrl = \href_request($tr);
$profileUrl = \href_profile($user);
// Auto Shout
if ($comment->anon == 0) {
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on Request [url=%s]%s[/url]', $profileUrl, $user->username, $trUrl, $tr->name)
);
} else {
$this->chatRepository->systemMessage(
\sprintf('An anonymous user has left a comment on Request [url=%s]%s[/url]', $trUrl, $tr->name)
);
}
//Notification
if ($user->id != $tr->user_id) {
$tr->notifyRequester('comment', $comment);
}
if ($this->taggedUserRepository->hasTags($request->input('content'))) {
if ($this->taggedUserRepository->contains($request->input('content'), '@here') && $user->group->is_modo) {
$users = \collect([]);
$tr->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
'request',
$users,
$user,
'Staff',
$comment
);
} else {
$sender = $comment->anon ? 'Anonymous' : $user->username;
$this->taggedUserRepository->messageTaggedCommentUsers(
'request',
$request->input('content'),
$user,
$sender,
$comment
);
}
}
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
return \to_route('request', ['id' => $tr->id, 'hash' => '#comments'])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Request.
*/
public function ticket(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$ticket = Ticket::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('ticket-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('tickets.show', ['id' => $id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('ticket-comment:'.$user->id);
$comment = new Comment();
$comment->content = $request->input('content');
$comment->anon = 0;
$comment->user_id = $user->id;
$comment->ticket_id = $ticket->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'ticket_id' => 'required',
'anon' => 'required',
]);
if ($v->fails()) {
return \to_route('tickets.show', ['id' => $id])
->withErrors($v->errors());
}
if ($user->id != $ticket->user_id) {
$ticket->user_read = 0;
}
if ($user->id == $ticket->user_id) {
$ticket->staff_read = 0;
}
$comment->save();
$ticket->save();
return \to_route('tickets.show', ['id' => $ticket->id])
->withSuccess(\trans('comment.added'));
}
/**
* Store A New Comment To A Torrent Via Quick Thanks.
*/
public function quickthanks(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$torrent = Torrent::findOrFail($id);
$user = $request->user();
if (RateLimiter::tooManyAttempts('torrent-comment:'.$user->id, \config('unit3d.comment-rate-limit'))) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors(\trans('comment.slow-down'));
}
RateLimiter::hit('torrent-comment:'.$user->id);
if ($user->can_comment == 0) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors(\trans('comment.rights-revoked'));
}
$comment = new Comment();
if ($torrent->anon === 1) {
$thankArray = [
'Thanks for the upload! :thumbsup_tone2:',
'Time and effort is much appreciated :thumbsup_tone2:',
'Great upload! :fire:', 'Thank you :smiley:',
];
} else {
$uploader = User::where('id', '=', $torrent->user_id)->first();
$uploaderUrl = \href_profile($uploader);
$thankArray = [
\sprintf('Thanks for the upload [url=%s][color=%s][b]%s[/b][/color][/url] :vulcan_tone2:', $uploaderUrl, $uploader->group->color, $uploader->username),
\sprintf('Beautiful upload [url=%s][color=%s][b]%s[/b][/color][/url] :fire:', $uploaderUrl, $uploader->group->color, $uploader->username),
\sprintf('Cheers [url=%s][color=%s][b]%s[/b][/color][/url] for the upload :beers:', $uploaderUrl, $uploader->group->color, $uploader->username),
];
}
$selected = random_int(0, (\is_countable($thankArray) ? \count($thankArray) : 0) - 1);
$comment->content = $thankArray[$selected];
$comment->user_id = $user->id;
$comment->torrent_id = $torrent->id;
$v = \validator($comment->toArray(), [
'content' => 'required',
'user_id' => 'required',
'torrent_id' => 'required',
]);
if ($v->fails()) {
return \to_route('torrent', ['id' => $torrent->id])
->withErrors($v->errors());
}
$comment->save();
// Achievements
if ($comment->anon == 0) {
$user->unlock(new UserMadeComment(), 1);
$user->addProgress(new UserMadeTenComments(), 1);
$user->addProgress(new UserMade50Comments(), 1);
$user->addProgress(new UserMade100Comments(), 1);
$user->addProgress(new UserMade200Comments(), 1);
$user->addProgress(new UserMade300Comments(), 1);
$user->addProgress(new UserMade400Comments(), 1);
$user->addProgress(new UserMade500Comments(), 1);
$user->addProgress(new UserMade600Comments(), 1);
$user->addProgress(new UserMade700Comments(), 1);
$user->addProgress(new UserMade800Comments(), 1);
$user->addProgress(new UserMade900Comments(), 1);
}
//Notification
if ($user->id != $torrent->user_id) {
User::find($torrent->user_id)->notify(new NewComment('torrent', $comment));
}
// Auto Shout
$torrentUrl = \href_torrent($torrent);
$profileUrl = \href_profile($user);
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has left a comment on Torrent [url=%s]%s[/url]', $profileUrl, $user->username, $torrentUrl, $torrent->name)
);
return \to_route('torrent', ['id' => $torrent->id])
->withSuccess(\trans('comment.added'));
}
/**
* Edit A Comment.
*/
public function editComment(Request $request, int $commentId): \Illuminate\Http\RedirectResponse
{
$user = $request->user();
$comment = Comment::findOrFail($commentId);
\abort_unless($user->group->is_modo || $user->id == $comment->user_id, 403);
$comment->content = $request->input('comment-edit');
$v = \validator($comment->toArray(), [
'content' => 'required',
]);
if ($v->fails()) {
return \redirect()->back()
->withErrors($v->errors());
}
$comment->save();
return \redirect()->back()->withSuccess(\trans('comment.edited'));
}
/**
* Delete A Comment.
*/
public function deleteComment(Request $request, int $commentId): \Illuminate\Http\RedirectResponse
{
$user = $request->user();
$comment = Comment::findOrFail($commentId);
\abort_unless($user->group->is_modo || $user->id == $comment->user_id, 403);
$comment->delete();
return \redirect()->back()->withSuccess(\trans('comment.deleted'));
}
}
+3 -3
View File
@@ -36,7 +36,7 @@ class FollowController extends Controller
->withErrors(\trans('user.follow-yourself'));
}
if (! $request->user()->isFollowing($user->id)) {
if ($request->user()->follows()->following($user->id)->doesntExist()) {
$follow = new Follow();
$follow->user_id = $request->user()->id;
$follow->target_id = $user->id;
@@ -60,8 +60,8 @@ class FollowController extends Controller
{
$user = User::where('username', '=', $username)->firstOrFail();
if ($request->user()->isFollowing($user->id)) {
$follow = $request->user()->follows()->where('target_id', '=', $user->id)->first();
if ($request->user()->follows()->following($user->id)->exists()) {
$follow = $request->user()->follows()->following($user->id)->first();
$follow->delete();
if ($user->acceptsNotification($request->user(), $user, 'account', 'show_account_unfollow')) {
$user->notify(new NewUnfollow('user', $request->user(), $user, $follow));
+1 -1
View File
@@ -109,7 +109,7 @@ class HomeController extends Controller
$groups = \cache()->remember('user-groups', $expiresAt, fn () => Group::select(['name', 'color', 'effect', 'icon'])->oldest('position')->get());
// Featured Torrents Block
$featured = \cache()->remember('latest_featured', $expiresAt, fn () => FeaturedTorrent::with('torrent', 'user')->get());
$featured = \cache()->remember('latest_featured', $expiresAt, fn () => FeaturedTorrent::with('torrent', 'torrent.resolution', 'torrent.type', 'torrent.category', 'user', 'user.group')->get());
// Latest Poll Block
$poll = \cache()->remember('latest_poll', $expiresAt, fn () => Poll::latest()->first());
+15 -6
View File
@@ -13,6 +13,7 @@
namespace App\Http\Controllers;
use App\Models\BlacklistClient;
use App\Models\Group;
use App\Models\Internal;
use App\Models\Page;
@@ -47,7 +48,12 @@ class PageController extends Controller
*/
public function staff(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
$staff = Group::with('users:id,username,group_id,title')->where('is_modo', '=', 1)->orWhere('is_admin', '=', 1)->get()->sortByDesc('position');
$staff = Group::query()
->with('users:id,username,group_id,title')
->where('is_modo', '=', 1)
->orWhere('is_admin', '=', 1)
->get()
->sortByDesc('position');
return \view('page.staff', ['staff' => $staff]);
}
@@ -57,19 +63,22 @@ class PageController extends Controller
*/
public function internal(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
$internals = Internal::with('users')->get()->sortBy('name');
$internals = Internal::query()
->with('users')
->get()
->sortBy('name');
return \view('page.internal', ['internals' => $internals]);
}
/**
* Show Blacklist Page.
* Show Client-Blacklist Page.
*/
public function blacklist(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
public function clientblacklist(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
$clients = \config('client-blacklist.clients', []);
$clients = BlacklistClient::all();
return \view('page.blacklist', ['clients' => $clients]);
return \view('page.blacklist.client', ['clients' => $clients]);
}
/**
+33 -28
View File
@@ -156,7 +156,8 @@ class RssController extends Controller
$rss = Rss::query()
->where('id', '=', $id)
->where(fn ($query) => $query
->where(
fn ($query) => $query
->where('user_id', '=', $user->id)
->orWhere('is_private', '=', 0)
)
@@ -164,33 +165,37 @@ class RssController extends Controller
$search = $rss->object_torrent;
$torrents = Torrent::with('user', 'category', 'type', 'resolution')
->when($search->search !== null, fn ($query) => $query->ofName($search->search))
->when($search->description !== null, fn ($query) => $query->ofDescription($search->description)->orWhere->ofMediainfo($search->description))
->when($search->uploader !== null, fn ($query) => $query->ofUploader($search->uploader))
->when($search->categories !== null, fn ($query) => $query->ofCategory($search->categories))
->when($search->types !== null, fn ($query) => $query->ofType($search->types))
->when($search->resolutions !== null, fn ($query) => $query->ofResolution($search->resolutions))
->when($search->genres !== null, fn ($query) => $query->ofGenre($search->genres))
->when($search->tmdb !== null, fn ($query) => $query->ofTmdb((int) $search->tmdb))
->when($search->imdb !== null, fn ($query) => $query->ofImdb((int) (\preg_match('/tt0*(?=(\d{7,}))/', $search->imdb, $matches) ? $matches[1] : $search->imdb)))
->when($search->tvdb !== null, fn ($query) => $query->ofTvdb((int) $search->tvdb))
->when($search->mal !== null, fn ($query) => $query->ofMal((int) $search->mal))
->when($search->freeleech !== null, fn ($query) => $query->ofFreeleech([25, 50, 75, 100]))
->when($search->doubleupload !== null, fn ($query) => $query->doubleup())
->when($search->featured !== null, fn ($query) => $query->featured())
->when($search->stream !== null, fn ($query) => $query->streamOptimized())
->when($search->sd !== null, fn ($query) => $query->sd())
->when($search->highspeed !== null, fn ($query) => $query->highspeed())
->when($search->bookmark !== null, fn ($query) => $query->bookmarkedBy($user))
->when($search->internal !== null, fn ($query) => $query->internal())
->when($search->personalrelease !== null, fn ($query) => $query->personalRelease())
->when($search->alive !== null, fn ($query) => $query->alive())
->when($search->dying !== null, fn ($query) => $query->dying())
->when($search->dead !== null, fn ($query) => $query->dead())
->orderByDesc('bumped_at')
->take(50)
->get();
$cacheKey = \md5(\sprintf('%s.%s.%s', $rss->id, $user->id, $user->rsskey));
$torrents = \cache()->remember($cacheKey, 300, function () use ($search, $user) {
return Torrent::with('user', 'category', 'type', 'resolution')
->when($search->search !== null, fn ($query) => $query->ofName($search->search))
->when($search->description !== null, fn ($query) => $query->ofDescription($search->description)->orWhere->ofMediainfo($search->description))
->when($search->uploader !== null, fn ($query) => $query->ofUploader($search->uploader))
->when($search->categories !== null, fn ($query) => $query->ofCategory($search->categories))
->when($search->types !== null, fn ($query) => $query->ofType($search->types))
->when($search->resolutions !== null, fn ($query) => $query->ofResolution($search->resolutions))
->when($search->genres !== null, fn ($query) => $query->ofGenre($search->genres))
->when($search->tmdb !== null, fn ($query) => $query->ofTmdb((int) $search->tmdb))
->when($search->imdb !== null, fn ($query) => $query->ofImdb((int) (\preg_match('/tt0*(?=(\d{7,}))/', $search->imdb, $matches) ? $matches[1] : $search->imdb)))
->when($search->tvdb !== null, fn ($query) => $query->ofTvdb((int) $search->tvdb))
->when($search->mal !== null, fn ($query) => $query->ofMal((int) $search->mal))
->when($search->freeleech !== null, fn ($query) => $query->ofFreeleech([25, 50, 75, 100]))
->when($search->doubleupload !== null, fn ($query) => $query->doubleup())
->when($search->featured !== null, fn ($query) => $query->featured())
->when($search->stream !== null, fn ($query) => $query->streamOptimized())
->when($search->sd !== null, fn ($query) => $query->sd())
->when($search->highspeed !== null, fn ($query) => $query->highspeed())
->when($search->bookmark !== null, fn ($query) => $query->bookmarkedBy($user))
->when($search->internal !== null, fn ($query) => $query->internal())
->when($search->personalrelease !== null, fn ($query) => $query->personalRelease())
->when($search->alive !== null, fn ($query) => $query->alive())
->when($search->dying !== null, fn ($query) => $query->dying())
->when($search->dead !== null, fn ($query) => $query->dead())
->orderByDesc('bumped_at')
->take(50)
->get();
});
return \response()->view('rss.show', [
'torrents' => $torrents,
@@ -0,0 +1,124 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @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;
use App\Models\BlacklistClient;
use Illuminate\Http\Request;
/**
* @see \Tests\Feature\Http\Controllers\Staff\GroupControllerTest
*/
class BlacklistClientController extends Controller
{
/**
* Display All Blacklisted Clients.
*/
public function index(Request $request): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
\abort_unless($request->user()->group->is_modo, 403);
$clients = BlacklistClient::latest()->get();
return \view('Staff.blacklist.clients.index', ['clients' => $clients]);
}
/**
* Blacklisted Client Edit Form.
*/
public function edit(Request $request, int $id): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
{
\abort_unless($request->user()->group->is_modo, 403);
$client = BlacklistClient::findOrFail($id);
return \view('Staff.blacklist.clients.edit', ['client' => $client]);
}
/**
* Edit A Blacklisted Client.
*/
public function update(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
\abort_unless($request->user()->group->is_modo, 403);
$client = BlacklistClient::findOrFail($id);
$client->name = $request->input('name');
$client->reason = $request->input('reason');
$v = \validator($client->toArray(), [
'name' => 'required|string',
'reason' => 'sometimes|string',
]);
if ($v->fails()) {
return \to_route('staff.blacklists.clients.index')
->withErrors($v->errors());
}
$client->save();
return \to_route('staff.blacklists.clients.index')
->withSuccess('Blacklisted Client Was Updated Successfully!');
}
/**
* Blacklisted Client Add Form.
*/
public function create(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
return \view('Staff.blacklist.clients.create');
}
/**
* Store A New Blacklisted Client.
*/
public function store(Request $request): \Illuminate\Http\RedirectResponse
{
\abort_unless($request->user()->group->is_admin, 403);
$client = new BlacklistClient();
$client->name = $request->input('name');
$client->reason = $request->input('reason');
$v = \validator($client->toArray(), [
'name' => 'required|string|unique:blacklist_clients',
'reason' => 'sometimes|string',
]);
if ($v->fails()) {
return \to_route('staff.blacklists.clients.index')
->withErrors($v->errors());
}
$client->save();
return \to_route('staff.blacklists.clients.index')
->withSuccess('Blacklisted Client Stored Successfully!');
}
/**
* Delete A Blacklisted Client.
*/
public function destroy(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
\abort_unless($request->user()->group->is_admin, 403);
$client = BlacklistClient::findOrFail($id);
$client->delete();
return \to_route('staff.blacklists.clients.index')
->withSuccess('Blacklisted Client Destroyed Successfully!');
}
}
@@ -14,8 +14,8 @@
namespace App\Http\Controllers\Staff;
use App\Http\Controllers\Controller;
use App\Models\History;
use Illuminate\Support\Facades\DB;
use App\Models\Group;
use App\Models\User;
/**
* @see \Tests\Feature\Http\Controllers\Staff\CheaterControllerTest
@@ -27,20 +27,19 @@ class CheaterController extends Controller
*/
public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
$cheaters = History::with('user')
->select(['*'])
->join(
DB::raw('(SELECT MAX(id) AS id FROM history GROUP BY history.user_id) AS unique_history'),
function ($join) {
$join->on('history.id', '=', 'unique_history.id');
}
)
->where('seeder', '=', 0)
->where('active', '=', 0)
->where('seedtime', '=', 0)
->where('actual_downloaded', '=', 0)
->where('actual_uploaded', '=', 0)
->whereNull('completed_at')
$bannedGroup = \cache()->rememberForever('banned_group', fn () => Group::where('slug', '=', 'banned')->pluck('id'));
$cheaters = User::query()
->whereHas('history', function ($query) {
$query->where('seeder', '=', 0);
$query->where('active', '=', 0);
$query->where('seedtime', '=', 0);
$query->where('actual_downloaded', '=', 0);
$query->where('actual_uploaded', '=', 0);
$query->whereNull('completed_at');
})
->where('group_id', '!=', $bannedGroup[0]) // Banned Users
->where('id', '!=', 1) // System
->latest()
->paginate(25);
+22 -16
View File
@@ -35,26 +35,32 @@ class HomeController extends Controller
// User Info
$bannedGroup = \cache()->rememberForever('banned_group', fn () => Group::where('slug', '=', 'banned')->pluck('id'));
$validatingGroup = \cache()->rememberForever('validating_group', fn () => Group::where('slug', '=', 'validating')->pluck('id'));
$users = DB::table('users')
->selectRaw('count(*) as total')
->selectRaw(\sprintf('count(case when group_id = %s then 1 end) as banned', $bannedGroup[0]))
->selectRaw(\sprintf('count(case when group_id = %s then 1 end) as validating', $validatingGroup[0]))
->first();
$users = \cache()->remember('dashboard_users', 300, function () use ($bannedGroup, $validatingGroup) {
return DB::table('users')
->selectRaw('count(*) as total')
->selectRaw(\sprintf('count(case when group_id = %s then 1 end) as banned', $bannedGroup[0]))
->selectRaw(\sprintf('count(case when group_id = %s then 1 end) as validating', $validatingGroup[0]))
->first();
});
// Torrent Info
$torrents = DB::table('torrents')
->selectRaw('count(*) as total')
->selectRaw('count(case when status = 0 then 1 end) as pending')
->selectRaw('count(case when status = 2 then 1 end) as rejected')
->selectRaw('count(case when status = 3 then 1 end) as postponed')
->first();
$torrents = \cache()->remember('dashboard_torrents', 300, function () {
return DB::table('torrents')
->selectRaw('count(*) as total')
->selectRaw('count(case when status = 0 then 1 end) as pending')
->selectRaw('count(case when status = 2 then 1 end) as rejected')
->selectRaw('count(case when status = 3 then 1 end) as postponed')
->first();
});
// Peers Info
$peers = DB::table('peers')
->selectRaw('count(*) as total')
->selectRaw('count(case when seeder = 0 then 1 end) as leechers')
->selectRaw('count(case when seeder = 1 then 1 end) as seeders')
->first();
$peers = \cache()->remember('dashboard_peers', 300, function () {
return DB::table('peers')
->selectRaw('count(*) as total')
->selectRaw('count(case when seeder = 0 then 1 end) as leechers')
->selectRaw('count(case when seeder = 1 then 1 end) as seeders')
->first();
});
// Reports Info
$reports = DB::table('reports')
@@ -39,7 +39,7 @@ class SubscriptionController extends Controller
$params = ['id' => $topic->id];
}
if (! $request->user()->isSubscribed('topic', $topic->id)) {
if ($request->user()->subscriptions()->ofTopic($topic->id)->doesntExist()) {
$subscription = new Subscription();
$subscription->user_id = $request->user()->id;
$subscription->topic_id = $topic->id;
@@ -69,8 +69,8 @@ class SubscriptionController extends Controller
$params = ['id' => $topic->id];
}
if ($request->user()->isSubscribed('topic', $topic->id)) {
$subscription = $request->user()->subscriptions()->where('topic_id', '=', $topic->id)->first();
if ($request->user()->subscriptions()->ofTopic($topic->id)->exists()) {
$subscription = $request->user()->subscriptions()->ofTopic($topic->id)->first();
$subscription->delete();
return \to_route($logger, $params)
@@ -97,7 +97,7 @@ class SubscriptionController extends Controller
$params = ['id' => $forum->id];
}
if (! $request->user()->isSubscribed('forum', $forum->id)) {
if ($request->user()->subscriptions()->ofForum($forum->id)->doesntExist()) {
$subscription = new Subscription();
$subscription->user_id = $request->user()->id;
$subscription->forum_id = $forum->id;
@@ -127,8 +127,8 @@ class SubscriptionController extends Controller
$params = ['id' => $forum->id];
}
if ($request->user()->isSubscribed('forum', $forum->id)) {
$subscription = $request->user()->subscriptions()->where('forum_id', '=', $forum->id)->first();
if ($request->user()->subscriptions()->ofForum($forum->id)->exists()) {
$subscription = $request->user()->subscriptions()->ofForum($forum->id)->first();
$subscription->delete();
return \to_route($logger, $params)
+12 -4
View File
@@ -58,7 +58,9 @@ class UserEarningController extends Controller
$dying = $distinctSeeds
->clone()
->whereHas('torrent', fn ($query) => $query
->whereHas(
'torrent',
fn ($query) => $query
->where('seeders', '=', 1)
->where('times_completed', '>=', 3)
)
@@ -71,7 +73,9 @@ class UserEarningController extends Controller
$old = $distinctSeeds
->clone()
->whereHas('torrent', fn ($query) => $query
->whereHas(
'torrent',
fn ($query) => $query
->where('created_at', '<', Carbon::now()->subMonths(6)->toDateTimeString())
->where('created_at', '>', Carbon::now()->subYear()->toDateTimeString()),
)
@@ -84,7 +88,9 @@ class UserEarningController extends Controller
$large = $distinctSeeds
->clone()
->whereHas('torrent', fn ($query) => $query
->whereHas(
'torrent',
fn ($query) => $query
->where('size', '>=', $this->byteUnits->bytesFromUnit('25GiB'))
->where('size', '<', $this->byteUnits->bytesFromUnit('100GiB'))
)
@@ -92,7 +98,9 @@ class UserEarningController extends Controller
$regular = $distinctSeeds
->clone()
->whereHas('torrent', fn ($query) => $query
->whereHas(
'torrent',
fn ($query) => $query
->where('size', '>=', $this->byteUnits->bytesFromUnit('1GiB'))
->where('size', '<', $this->byteUnits->bytesFromUnit('25GiB'))
)
+4 -2
View File
@@ -42,7 +42,8 @@ class UserGiftController extends Controller
$gifttransactions = BonTransactions::query()
->with(['senderObj', 'receiverObj'])
->where(fn ($query) => $query
->where(
fn ($query) => $query
->where('sender', '=', $user->id)
->orwhere('receiver', '=', $user->id)
)
@@ -118,7 +119,8 @@ class UserGiftController extends Controller
}
$this->chatRepository->systemMessage(
\sprintf('[url=%s]%s[/url] has gifted %s BON to [url=%s]%s[/url]',
\sprintf(
'[url=%s]%s[/url] has gifted %s BON to [url=%s]%s[/url]',
\href_profile($user),
$user->username,
$value,
+2 -1
View File
@@ -39,7 +39,8 @@ class UserTipController extends Controller
$userbon = $user->getSeedbonus();
$bontransactions = BonTransactions::query()
->with(['senderObj', 'receiverObj'])
->where(fn ($query) => $query
->where(
fn ($query) => $query
->where('sender', '=', $user->id)
->orwhere('receiver', '=', $user->id)
)
+190
View File
@@ -0,0 +1,190 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Livewire;
use App\Achievements\UserMade100Comments;
use App\Achievements\UserMade200Comments;
use App\Achievements\UserMade300Comments;
use App\Achievements\UserMade400Comments;
use App\Achievements\UserMade500Comments;
use App\Achievements\UserMade50Comments;
use App\Achievements\UserMade600Comments;
use App\Achievements\UserMade700Comments;
use App\Achievements\UserMade800Comments;
use App\Achievements\UserMade900Comments;
use App\Achievements\UserMadeComment;
use App\Achievements\UserMadeTenComments;
use App\Models\User;
use App\Notifications\NewComment;
use App\Repositories\TaggedUserRepository;
use Livewire\Component;
use voku\helper\AntiXSS;
class Comment extends Component
{
public $comment;
public $anon = false;
private TaggedUserRepository $taggedUserRepository;
public \Illuminate\Contracts\Auth\Authenticatable|\App\Models\User $user;
protected $listeners = [
'refresh' => '$refresh',
];
protected $validationAttributes = [
'replyState.content' => 'reply',
];
public $isReplying = false;
public $replyState = [
'content' => '',
];
public $isEditing = false;
public $editState = [
'content' => '',
];
final public function mount(TaggedUserRepository $taggedUserRepository): void
{
$this->taggedUserRepository = $taggedUserRepository;
$this->user = \auth()->user();
}
final public function updatedIsEditing($isEditing): void
{
if (! $isEditing) {
return;
}
$this->editState = [
'content' => $this->comment->content,
];
}
final public function editComment(): void
{
if (\auth()->user()->id !== $this->comment->user_id || ! \auth()->user()->group->is_modo) {
$this->dispatchBrowserEvent('error', ['type' => 'error', 'message' => 'Permission Denied!']);
return;
}
$this->comment->update((new AntiXSS())->xss_clean($this->editState));
$this->isEditing = false;
}
final public function deleteComment(): void
{
if (\auth()->user()->id !== $this->comment->user_id || ! \auth()->user()->group->is_modo) {
$this->dispatchBrowserEvent('error', ['type' => 'error', 'message' => 'Permission Denied!']);
return;
}
$this->comment->delete();
$this->emitUp('refresh');
}
final public function postReply(): void
{
if (\auth()->user()->can_comment === 0) {
$this->dispatchBrowserEvent('error', ['type' => 'error', 'message' => \trans('comment.rights-revoked')]);
return;
}
if (! $this->comment->isParent()) {
return;
}
$this->validate([
'replyState.content' => 'required',
]);
$reply = $this->comment->children()->make((new AntiXSS())->xss_clean($this->replyState));
$reply->user()->associate(\auth()->user());
$reply->commentable()->associate($this->comment->commentable);
$reply->anon = $this->anon;
$reply->save();
$this->replyState = [
'content' => '',
];
// Achievements
if ($reply->anon === 0) {
$this->user->unlock(new UserMadeComment(), 1);
$this->user->addProgress(new UserMadeTenComments(), 1);
$this->user->addProgress(new UserMade50Comments(), 1);
$this->user->addProgress(new UserMade100Comments(), 1);
$this->user->addProgress(new UserMade200Comments(), 1);
$this->user->addProgress(new UserMade300Comments(), 1);
$this->user->addProgress(new UserMade400Comments(), 1);
$this->user->addProgress(new UserMade500Comments(), 1);
$this->user->addProgress(new UserMade600Comments(), 1);
$this->user->addProgress(new UserMade700Comments(), 1);
$this->user->addProgress(new UserMade800Comments(), 1);
$this->user->addProgress(new UserMade900Comments(), 1);
}
//Notification
if ($this->user->id !== $this->comment->user_id) {
User::find($this->comment->user_id)->notify(new NewComment($this->comment, $reply));
}
// Tagging
if ($this->taggedUserRepository->hasTags($this->replyState)) {
if ($this->user->group->is_modo && $this->taggedUserRepository->contains($this->replyState, '@here')) {
$users = \collect([]);
$this->comment->children()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
$this->comment,
$users,
$this->user,
'Staff',
$reply
);
} else {
$sender = $reply->anon !== 0 ? $this->user->username : 'Anonymous';
$this->taggedUserRepository->messageTaggedCommentUsers(
$this->comment,
$this->replyState[],
$this->user,
$sender,
$reply
);
}
}
$this->isReplying = false;
$this->emitSelf('refresh');
}
final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application
{
return \view('livewire.comment');
}
}
+160
View File
@@ -0,0 +1,160 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Livewire;
use App\Achievements\UserMade100Comments;
use App\Achievements\UserMade200Comments;
use App\Achievements\UserMade300Comments;
use App\Achievements\UserMade400Comments;
use App\Achievements\UserMade500Comments;
use App\Achievements\UserMade50Comments;
use App\Achievements\UserMade600Comments;
use App\Achievements\UserMade700Comments;
use App\Achievements\UserMade800Comments;
use App\Achievements\UserMade900Comments;
use App\Achievements\UserMadeComment;
use App\Achievements\UserMadeTenComments;
use App\Models\User;
use App\Notifications\NewComment;
use App\Repositories\TaggedUserRepository;
use Livewire\Component;
use Livewire\WithPagination;
use voku\helper\AntiXSS;
class Comments extends Component
{
use WithPagination;
private TaggedUserRepository $taggedUserRepository;
public \Illuminate\Contracts\Auth\Authenticatable|\App\Models\User $user;
public $model;
public $anon = false;
public int $perPage = 10;
protected $listeners = [
'refresh' => '$refresh',
];
public $newCommentState = [
'content' => '',
];
protected $validationAttributes = [
'newCommentState.content' => 'comment',
];
final public function mount(TaggedUserRepository $taggedUserRepository): void
{
$this->taggedUserRepository = $taggedUserRepository;
$this->user = \auth()->user();
}
final public function loadMore()
{
$this->perPage += 10;
}
final public function postComment(): void
{
if ($this->user->can_comment === 0) {
$this->dispatchBrowserEvent('error', ['type' => 'error', 'message' => \trans('comment.rights-revoked')]);
return;
}
$this->validate([
'newCommentState.content' => 'required',
]);
$comment = $this->model->comments()->make((new AntiXSS())->xss_clean($this->newCommentState));
$comment->user()->associate($this->user);
$comment->anon = $this->anon;
$comment->save();
$this->newCommentState = [
'content' => '',
];
// Achievements
if ($comment->anon === 0) {
$this->user->unlock(new UserMadeComment(), 1);
$this->user->addProgress(new UserMadeTenComments(), 1);
$this->user->addProgress(new UserMade50Comments(), 1);
$this->user->addProgress(new UserMade100Comments(), 1);
$this->user->addProgress(new UserMade200Comments(), 1);
$this->user->addProgress(new UserMade300Comments(), 1);
$this->user->addProgress(new UserMade400Comments(), 1);
$this->user->addProgress(new UserMade500Comments(), 1);
$this->user->addProgress(new UserMade600Comments(), 1);
$this->user->addProgress(new UserMade700Comments(), 1);
$this->user->addProgress(new UserMade800Comments(), 1);
$this->user->addProgress(new UserMade900Comments(), 1);
}
//Notification
if ($this->user->id !== $this->model->user_id) {
User::find($this->model->user_id)->notify(new NewComment($this->model, $comment));
}
// Tagging
if ($this->taggedUserRepository->hasTags($this->newCommentState)) {
if ($this->user->group->is_modo && $this->taggedUserRepository->contains($this->newCommentState, '@here')) {
$users = \collect([]);
$this->model->comments()->get()->each(function ($c) use ($users) {
$users->push($c->user);
});
$this->taggedUserRepository->messageCommentUsers(
$this->model,
$users,
$this->user,
'Staff',
$comment
);
} else {
$sender = $comment->anon !== 0 ? $this->user->username : 'Anonymous';
$this->taggedUserRepository->messageTaggedCommentUsers(
$this->model,
$this->newCommentState[],
$this->user,
$sender,
$comment
);
}
}
$this->gotoPage(1);
}
final public function getCommentsProperty(): \Illuminate\Contracts\Pagination\LengthAwarePaginator
{
return $this->model
->comments()
->with('user', 'children.user', 'children.children')
->parent()
->latest()
->paginate($this->perPage);
}
final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application
{
return \view('livewire.comments', [
'comments' => $this->comments,
]);
}
}
+3 -1
View File
@@ -99,7 +99,9 @@ class UserActive extends Component
)
->selectRaw('(1 - (peers.left / NULLIF(torrents.size, 0))) AS progress')
->where('peers.user_id', '=', $this->user->id)
->when($this->name, fn ($query) => $query
->when(
$this->name,
fn ($query) => $query
->where('name', 'like', '%'.str_replace(' ', '%', $this->name).'%')
)
->when($this->ip !== '', fn ($query) => $query->where('ip', '=', $this->ip))
+17 -7
View File
@@ -92,9 +92,10 @@ class UserTorrents extends Component
final public function getHistoryProperty(): \Illuminate\Contracts\Pagination\LengthAwarePaginator
{
return History::query()
->join('torrents', fn ($join) => $join
->join(
'torrents',
fn ($join) => $join
->on('history.torrent_id', '=', 'torrents.id')
->where('history.created_at', '>=', $this->user->created_at) // Unneeded, but increases performance
->where('history.user_id', '=', $this->user->id)
)
->select(
@@ -126,18 +127,27 @@ class UserTorrents extends Component
->selectRaw('TIMESTAMPDIFF(SECOND, history.created_at, history.completed_at) AS leechtime')
->selectRaw('CAST(history.uploaded AS float) / CAST((history.downloaded + 1) AS float) AS ratio')
->selectRaw('CAST(history.actual_uploaded AS float) / CAST((history.actual_downloaded + 1) AS float) AS actual_ratio')
->when($this->name, fn ($query) => $query
->when(
$this->name,
fn ($query) => $query
->where('name', 'like', '%'.str_replace(' ', '%', $this->name).'%')
)
->when(\config('hitrun.enabled') === true, fn ($query) => $query
->when($this->unsatisfied === 'exclude', fn ($query) => $query
->where(fn ($query) => $query
->when(
\config('hitrun.enabled') === true,
fn ($query) => $query
->when(
$this->unsatisfied === 'exclude',
fn ($query) => $query
->where(
fn ($query) => $query
->where('seedtime', '>', \config('hitrun.seedtime'))
->orWhere('immune', '=', 1)
->orWhereRaw('actual_downloaded < (torrents.size * ? / 100)', [\config('hitrun.buffer')])
)
)
->when($this->unsatisfied === 'include', fn ($query) => $query
->when(
$this->unsatisfied === 'include',
fn ($query) => $query
->where('seedtime', '<', \config('hitrun.seedtime'))
->where('immune', '=', 0)
->whereRaw('actual_downloaded > (torrents.size * ? / 100)', [\config('hitrun.buffer')])
+3 -1
View File
@@ -75,7 +75,9 @@ class UserUploads extends Component
->withAnyStatus()
->where('created_at', '>=', $this->user->created_at) // Unneeded, but increases performances
->where('user_id', '=', $this->user->id)
->when($this->name, fn ($query) => $query
->when(
$this->name,
fn ($query) => $query
->where('name', 'like', '%'.str_replace(' ', '%', $this->name).'%')
)
->when(! empty($this->status), fn ($query) => $query->whereIntegerInRaw('status', $this->status))
+1 -1
View File
@@ -291,7 +291,7 @@ class ProcessAnnounce implements ShouldQueue
$this->user->uploaded += $modUploaded;
$this->user->downloaded += $modDownloaded;
$this->user->save();
// End User Update
// End User Update
}
$peerCount = DB::table('peers')
+4 -5
View File
@@ -22,6 +22,10 @@ use Illuminate\Queue\SerializesModels;
class ProcessMassPM implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/**
* @var mixed
*/
@@ -32,11 +36,6 @@ class ProcessMassPM implements ShouldQueue
*/
public $receiver_id;
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/**
* ProcessMassPM Constructor.
*/
+2 -5
View File
@@ -36,12 +36,9 @@ class Article extends Model
]);
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class);
return $this->morphMany(Comment::class, 'commentable');
}
/**
+20
View File
@@ -0,0 +1,20 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class BlacklistClient extends Model
{
}
+2 -5
View File
@@ -23,12 +23,9 @@ class Collection extends Model
protected $table = 'collection';
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class, 'collection_id');
return $this->morphMany(Comment::class, 'commentable');
}
public function movie(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
+18 -37
View File
@@ -17,15 +17,24 @@ use App\Events\TicketWentStale;
use App\Helpers\Bbcode;
use App\Helpers\Linkify;
use App\Traits\Auditable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use voku\helper\AntiXSS;
class Comment extends Model
{
use HasFactory;
use Auditable;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'content',
'user_id',
'anon',
];
/**
* Belongs To A User.
*/
@@ -37,52 +46,24 @@ class Comment extends Model
]);
}
/**
* Belongs To A Torrent.
*/
public function torrent(): \Illuminate\Database\Eloquent\Relations\BelongsTo
public function commentable(): \Illuminate\Database\Eloquent\Relations\MorphTo
{
return $this->belongsTo(Torrent::class);
return $this->morphTo();
}
/**
* Belongs To A Article.
*/
public function article(): \Illuminate\Database\Eloquent\Relations\BelongsTo
public function children(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->belongsTo(Article::class);
return $this->hasMany(__CLASS__, 'parent_id')->oldest();
}
/**
* Belongs To A Request.
*/
public function request(): \Illuminate\Database\Eloquent\Relations\BelongsTo
public function isParent(): bool
{
return $this->belongsTo(TorrentRequest::class, 'requests_id', 'id');
return is_null($this->parent_id);
}
/**
* Belongs To A Playlist.
*/
public function playlist(): \Illuminate\Database\Eloquent\Relations\BelongsTo
public function scopeParent(Builder $builder): void
{
return $this->belongsTo(Playlist::class);
}
/**
* Belongs To A Ticket.
*/
public function ticket(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Ticket::class);
}
/**
* Set The Comments Content After Its Been Purified.
*/
public function setContentAttribute(?string $value): void
{
$this->attributes['content'] = \htmlspecialchars((new AntiXSS())->xss_clean($value), ENT_NOQUOTES);
$builder->whereNull('parent_id');
}
/**
+9
View File
@@ -14,6 +14,7 @@
namespace App\Models;
use App\Traits\Auditable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
@@ -45,4 +46,12 @@ class Follow extends Model
'id' => '1',
]);
}
/**
* Only included follows following a user
*/
public function scopeFollowing($query, $user_id): Builder
{
return $query->where('target_id', '=', $user_id);
}
}
+2 -5
View File
@@ -41,11 +41,8 @@ class Playlist extends Model
return $this->hasMany(PlaylistTorrent::class);
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class, 'playlist_id');
return $this->morphMany(Comment::class, 'commentable');
}
}
+17
View File
@@ -14,6 +14,7 @@
namespace App\Models;
use App\Traits\Auditable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@@ -48,4 +49,20 @@ class Subscription extends Model
{
return $this->belongsTo(Forum::class);
}
/**
* Only include subscriptions of a forum
*/
public function scopeOfForum($query, $forum_id): Builder
{
return $query->where('forum_id', '=', $forum_id);
}
/**
* Only include subscriptions of a topic
*/
public function scopeOfTopic($query, $topic_id): Builder
{
return $query->where('topic_id', '=', $topic_id);
}
}
+2 -5
View File
@@ -95,11 +95,8 @@ class Ticket extends Model
return $this->hasMany(TicketAttachment::class);
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class);
return $this->morphMany(Comment::class, 'commentable');
}
}
+2 -5
View File
@@ -182,12 +182,9 @@ class Torrent extends Model
return $this->hasMany(TorrentFile::class);
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class);
return $this->morphMany(Comment::class, 'commentable');
}
/**
+2 -5
View File
@@ -108,12 +108,9 @@ class TorrentRequest extends Model
return $this->belongsTo(Torrent::class, 'filled_hash', 'info_hash');
}
/**
* Has Many Comments.
*/
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function comments(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->hasMany(Comment::class, 'requests_id', 'id');
return $this->morphMany(Comment::class, 'commentable');
}
/**
+9 -169
View File
@@ -22,7 +22,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use voku\helper\AntiXSS;
@@ -128,6 +127,15 @@ class User extends Authenticatable
}
/**
* Belongs To Many Seeding Torrents.
*/
public function leechingTorrents(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(Torrent::class, 'history')->wherePivot('active', '=', 0);
}
/**
* Has Many Messages.
*/
public function messages(): \Illuminate\Database\Eloquent\Relations\HasMany
@@ -604,26 +612,6 @@ class User extends Authenticatable
return true;
}
/**
* Does Subscription Exist.
*/
public function isSubscribed(string $type, int $topicId): bool
{
if ($type === 'topic') {
return (bool) $this->subscriptions()->where('topic_id', '=', $topicId)->first(['id']);
}
return (bool) $this->subscriptions()->where('forum_id', '=', $topicId)->first(['id']);
}
/**
* Get All Followers Of A User.
*/
public function isFollowing(int $targetId): bool
{
return (bool) $this->follows()->where('target_id', '=', $targetId)->first(['id']);
}
/**
* Return Upload In Human Format.
*/
@@ -767,152 +755,4 @@ class User extends Authenticatable
{
return \number_format($this->seedbonus, 0, '.', ',');
}
/**
* @method getSeeding
*
* Gets the amount of torrents a user seeds
*/
public function getSeeding(): int
{
return Peer::where('user_id', '=', $this->id)
->where('seeder', '=', '1')
->distinct('torrent_id')
->count();
}
/**
* @method getLast30Uploads
*
* Gets the amount of torrents a user seeds
*/
public function getLast30Uploads(): int
{
$current = Carbon::now();
return Torrent::withAnyStatus()
->where('user_id', '=', $this->id)
->where('created_at', '>', $current->copy()->subDays(30)->toDateTimeString())
->count();
}
/**
* @method getUploads
*
* Gets the amount of torrents a user seeds
*/
public function getUploads(): int
{
return Torrent::withAnyStatus()
->where('user_id', '=', $this->id)
->count();
}
/**
* @method getLeeching
*
* Gets the amount of torrents a user seeds
*/
public function getLeeching(): int
{
return Peer::where('user_id', '=', $this->id)
->where('left', '>', '0')
->distinct('torrent_id')
->count();
}
/**
* @method getWarning
*
* Gets count on users active warnings
*/
public function getWarning(): int
{
return Warning::where('user_id', '=', $this->id)
->whereNotNull('torrent')
->where('active', '=', '1')
->count();
}
/**
* @method getTotalSeedTime
*
* Gets the users total seedtime
*/
public function getTotalSeedTime(): int
{
return History::where('user_id', '=', $this->id)
->sum('seedtime');
}
/**
* @method getTotalSeedSize
*
* Gets the users total seedsoze
*/
public function getTotalSeedSize(): int
{
$peers = Peer::where('user_id', '=', $this->id)->where('seeder', '=', 1)->pluck('torrent_id');
return Torrent::whereIntegerInRaw('id', $peers)->sum('size');
}
/**
* @method getCompletedSeeds
*
* Gets the users satisfied torrent count.
*/
public function getCompletedSeeds(): int
{
return History::where('user_id', '=', $this->id)->where('seedtime', '>=', \config('hitrun.seedtime'))->count();
}
/**
* @method getSpecialSeedingSize
*
* Gets the seeding size of torrents with at least 15 days seedtime in the past 30 days.
*/
public function getSpecialSeedingSize(): int
{
$current = Carbon::now();
$seeding = History::where('user_id', '=', $this->id)
->where('completed_at', '<=', $current->copy()->subDays(30)->toDateTimeString())
->where('active', '=', 1)
->where('seeder', '=', 1)
->where('seedtime', '>=', 1_296_000)
->pluck('torrent_id');
return Torrent::whereIntergerIn('id', $seeding)->sum('size');
}
/**
* Gets the seeding size of torrents that are connectable.
*/
public function getConnectableSeedsizeAttribute(): int
{
if (\config('announce.connectable_check')) {
$unconnectablePeers = Peer::query()
->select('ip', 'port', 'agent')
->distinct()
->where('user_id', '=', 3)
->get()
->filter(fn ($peer) => ! cache()->get('peers:connectable:'.$peer->ip.'-'.$peer->port.'-'.$peer->agent, false));
return Torrent::whereHas('peers', function ($query) use ($unconnectablePeers) {
$query->where('user_id', '=', $this->id);
foreach ($unconnectablePeers as $peer) {
$query->whereNot(function ($query) use ($peer) {
$query
->where('ip', '=', $peer->ip)
->where('port', '=', $peer->port)
->where('agent', '=', $peer->agent);
});
}
})
->sum('size');
} else {
return 0;
}
}
}
+9
View File
@@ -14,6 +14,7 @@
namespace App\Models;
use App\Traits\Auditable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -64,4 +65,12 @@ class Warning extends Model
'id' => '1',
]);
}
/**
* Active Warnings
*/
public function scopeActive($query): Builder
{
return $query->where('active', '=', 1);
}
}
+3 -3
View File
@@ -63,8 +63,8 @@ class RouteServiceProvider extends ServiceProvider
*/
protected function configureRateLimiting(): void
{
RateLimiter::for('api', fn (Request $request) => Limit::perMinute(5)->by($request->ip()));
RateLimiter::for('announce', fn (Request $request) => Limit::perMinute(2000)->by($request->ip()));
RateLimiter::for('rss', fn (Request $request) => Limit::perMinute(2)->by($request->ip()));
RateLimiter::for('api', fn (Request $request) => Limit::perMinute(30)->by($request->ip()));
RateLimiter::for('announce', fn (Request $request) => Limit::perMinute(500)->by($request->ip()));
RateLimiter::for('rss', fn (Request $request) => Limit::perMinute(30)->by($request->ip()));
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ class Collection
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+1 -1
View File
@@ -19,7 +19,7 @@ class Company
public ?int $page = null;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+1 -1
View File
@@ -17,7 +17,7 @@ class Episode
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+1 -1
View File
@@ -17,7 +17,7 @@ class FindMovie
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+1 -1
View File
@@ -17,7 +17,7 @@ class FindTv
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+1 -1
View File
@@ -17,7 +17,7 @@ class Genre
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+1 -1
View File
@@ -17,7 +17,7 @@ class Movie
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.themoviedb.org/3/';
final public const API_BASE_URI = 'https://api.themoviedb.org/3/';
public $data;
+1 -1
View File
@@ -17,7 +17,7 @@ class Network
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+1 -1
View File
@@ -17,7 +17,7 @@ class Person
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+1 -1
View File
@@ -17,7 +17,7 @@ class Season
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+1 -1
View File
@@ -17,7 +17,7 @@ class TV
{
public \GuzzleHttp\Client $client;
public final const API_BASE_URI = 'https://api.TheMovieDB.org/3';
final public const API_BASE_URI = 'https://api.TheMovieDB.org/3';
public $data;
+6 -4
View File
@@ -29,13 +29,12 @@ use Illuminate\Support\Str;
class TMDBScraper implements ShouldQueue
{
use SerializesModels;
/**
* @var mixed|array|string|null
*/
public $id;
use SerializesModels;
public function __construct(Request $request = null)
{
if ($request != null) {
@@ -97,8 +96,11 @@ class TMDBScraper implements ShouldQueue
\preg_match($re, (string) $movie['title'], $matches);
$year = (new DateTime($movie['release_date']))->format('Y');
$titleSort = \addslashes(\str_replace(['The ', 'An ', 'A ', '"'], [''],
Str::limit($matches['namesort'] ? $matches['namesort'].' '.$year : $movie['title'], 100)));
$titleSort = \addslashes(\str_replace(
['The ', 'An ', 'A ', '"'],
[''],
Str::limit($matches['namesort'] ? $matches['namesort'].' '.$year : $movie['title'], 100)
));
$array = [
'adult' => $movie['adult'] ?? 0,
+39 -21
View File
@@ -26,26 +26,29 @@ trait TorrentFilter
{
public function scopeOfName(Builder $query, string $name, bool $isRegex = false): Builder
{
return $query->when($isRegex,
fn ($query) => $query->where('name', 'REGEXP', \substr($name, 1, -1)),
fn ($query) => $query->where('name', 'LIKE', '%'.\str_replace(' ', '%', $name).'%')
);
return $query->when(
$isRegex,
fn ($query) => $query->where('name', 'REGEXP', \substr($name, 1, -1)),
fn ($query) => $query->where('name', 'LIKE', '%'.\str_replace(' ', '%', $name).'%')
);
}
public function scopeOfDescription(Builder $query, string $description, bool $isRegex = false): Builder
{
return $query->when($isRegex,
fn ($query) => $query->where('description', 'REGEXP', \substr($description, 1, -1)),
fn ($query) => $query->where('description', 'LIKE', '%'.$description.'%')
);
return $query->when(
$isRegex,
fn ($query) => $query->where('description', 'REGEXP', \substr($description, 1, -1)),
fn ($query) => $query->where('description', 'LIKE', '%'.$description.'%')
);
}
public function scopeOfMediainfo(Builder $query, string $mediainfo, bool $isRegex = false): Builder
{
return $query->when($isRegex,
fn ($query) => $query->where('mediainfo', 'REGEXP', \substr($mediainfo, 1, -1)),
fn ($query) => $query->where('mediainfo', 'LIKE', '%'.$mediainfo.'%')
);
return $query->when(
$isRegex,
fn ($query) => $query->where('mediainfo', 'REGEXP', \substr($mediainfo, 1, -1)),
fn ($query) => $query->where('mediainfo', 'LIKE', '%'.$mediainfo.'%')
);
}
public function scopeOfUploader(Builder $query, string $username): Builder
@@ -88,12 +91,15 @@ trait TorrentFilter
public function scopeOfGenre(Builder $query, array $genres): Builder
{
return $query
->where(fn ($query) => $query
->where(fn ($query) => $query
->where(
fn ($query) => $query
->where(
fn ($query) => $query
->whereIn('category_id', Category::select('id')->where('movie_meta', '=', 1))
->whereIn('tmdb', DB::table('genre_movie')->select('movie_id')->whereIn('genre_id', $genres))
)
->orWhere(fn ($query) => $query
->orWhere(
fn ($query) => $query
->whereIn('category_id', Category::select('id')->where('tv_meta', '=', 1))
->whereIn('tmdb', DB::table('genre_tv')->select('tv_id')->whereIn('genre_id', $genres))
)
@@ -212,7 +218,9 @@ trait TorrentFilter
public function scopeNotDownloadedBy(Builder $query, User $user): Builder
{
return $query
->whereDoesntHave('history', fn ($query) => $query
->whereDoesntHave(
'history',
fn ($query) => $query
->where('user_id', '=', $user->id)
);
}
@@ -220,7 +228,9 @@ trait TorrentFilter
public function scopeDownloadedBy(Builder $query, User $user): Builder
{
return $query
->whereHas('history', fn (Builder $query) => $query
->whereHas(
'history',
fn (Builder $query) => $query
->where('user_id', '=', $user->id)
);
}
@@ -228,7 +238,9 @@ trait TorrentFilter
public function scopeSeededBy(Builder $query, User $user): Builder
{
return $query
->whereHas('history', fn ($query) => $query
->whereHas(
'history',
fn ($query) => $query
->where('user_id', '=', $user->id)
->where('active', '=', 1)
->where('seeder', '=', 1)
@@ -238,7 +250,9 @@ trait TorrentFilter
public function scopeLeechedby(Builder $query, User $user): Builder
{
return $query
->whereHas('history', fn ($query) => $query
->whereHas(
'history',
fn ($query) => $query
->where('user_id', '=', $user->id)
->where('active', '=', 1)
->where('seeder', '=', 0)
@@ -248,7 +262,9 @@ trait TorrentFilter
public function scopeUncompletedBy(Builder $query, User $user): Builder
{
return $query
->whereHas('history', fn ($query) => $query
->whereHas(
'history',
fn ($query) => $query
->where('user_id', '=', $user->id)
->where('active', '=', 0)
->where('seeder', '=', 0)
@@ -259,7 +275,9 @@ trait TorrentFilter
public function scopeOfFilename(Builder $query, string $filename): Builder
{
return $query
->whereHas('files', fn ($query) => $query
->whereHas(
'files',
fn ($query) => $query
->where('name', $filename)
);
}
+1
View File
@@ -32,6 +32,7 @@
"league/flysystem-sftp-v3": "^3.0",
"livewire/livewire": "^2.0",
"marcreichel/igdb-laravel": "^3.1.2",
"opcodesio/log-viewer": "^1.3",
"paragonie/constant_time_encoding": "^2.0",
"predis/predis": "^2.0",
"spatie/laravel-backup": "^8.0.1",
Generated
+543 -568
View File
File diff suppressed because it is too large Load Diff
+109
View File
@@ -0,0 +1,109 @@
<?php
use Opcodes\LogViewer\Level;
return [
/*
|--------------------------------------------------------------------------
| Log Viewer Route
|--------------------------------------------------------------------------
| Log Viewer will be available under this URL.
|
*/
'route_path' => 'dashboard/laravel-log-viewer',
/*
|--------------------------------------------------------------------------
| Back to system URL
|--------------------------------------------------------------------------
| When set, displays a link to easily get back to this URL.
| Set to `null` to hide this link.
|
| Optional label to display for the above URL.
|
*/
'back_to_system_url' => config('app.url', null),
'back_to_system_label' => null, // Displayed by default: "Back to {{ app.name }}"
/*
|--------------------------------------------------------------------------
| Log Viewer route middleware.
|--------------------------------------------------------------------------
| The middleware should enable session and cookies support in order for the Log Viewer to work.
| The 'web' middleware will be applied automatically if empty.
|
*/
'middleware' => ['web', 'owner'],
/*
|--------------------------------------------------------------------------
| Include file patterns
|--------------------------------------------------------------------------
|
*/
'include_files' => [
'*.log',
],
/*
|--------------------------------------------------------------------------
| Exclude file patterns.
|--------------------------------------------------------------------------
| This will take precedence over included files.
|
*/
'exclude_files' => [
//'my_secret.log'
],
/*
|--------------------------------------------------------------------------
| Shorter stack trace filters.
|--------------------------------------------------------------------------
| Lines containing any of these strings will be excluded from the full log.
| This setting is only active when the function is enabled via the user interface.
|
*/
'shorter_stack_trace_excludes' => [
'/vendor/symfony/',
'/vendor/laravel/framework/',
'/vendor/barryvdh/laravel-debugbar/',
],
/*
|--------------------------------------------------------------------------
| Log matching patterns
|--------------------------------------------------------------------------
| Regexes for matching log files
|
*/
'patterns' => [
'laravel' => [
'log_matching_regex' => '/^\[(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}\.?(\d{6}([\+-]\d\d:\d\d)?)?)\].*/',
/**
* This pattern, used for processing Laravel logs, returns these results:
* $matches[0] - the full log line being tested.
* $matches[1] - full timestamp between the square brackets (includes microseconds and timezone offset)
* $matches[2] - timestamp microseconds, if available
* $matches[3] - timestamp timezone offset, if available
* $matches[4] - contents between timestamp and the severity level
* $matches[5] - environment (local, production, etc)
* $matches[6] - log severity (info, debug, error, etc)
* $matches[7] - the log text, the rest of the text.
*/
'log_parsing_regex' => '/^\[(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}\.?(\d{6}([\+-]\d\d:\d\d)?)?)\](.*?(\w+)\.|.*?)('
.implode('|', array_filter(Level::caseValues()))
.')?: (.*?)( in [\/].*?:[0-9]+)?$/is',
],
],
];
+2 -2
View File
@@ -22,7 +22,7 @@ return [
|
*/
'powered-by' => 'Powered By UNIT3D Community Edition v6.4.0',
'powered-by' => 'Powered By UNIT3D Community Edition v6.4.1',
/*
|--------------------------------------------------------------------------
@@ -44,7 +44,7 @@ return [
|
*/
'version' => 'v6.4.0',
'version' => 'v6.4.1',
/*
|--------------------------------------------------------------------------
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -13,7 +13,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -13,7 +13,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -13,7 +13,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -13,7 +13,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -13,7 +13,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -13,7 +13,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -13,7 +13,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/
@@ -14,7 +14,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class() extends Migration {
return new class () extends Migration {
/**
* Run the migrations.
*/

Some files were not shown because too many files have changed in this diff Show More