diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index 8c89fe3c..1819a456 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -85,8 +85,11 @@ class Test extends Command { $ip = '116.30.133.129'; // $ip = '240e:3a1:680c:bb11:211:32ff:fe2c:a603'; - $ipObj = IPBlock::create($ip); - $r = $ipObj->getFirstIp(); +// $ipObj = IPBlock::create($ip); +// $ipObj = IP::create($ip); +// $r = $ipObj->getVersion(); + $r = get_ip_location('116.30.133.129'); +// $r = get_ip_location_from_geoip('116.30.133.129'); dd($r); } diff --git a/app/Filament/Resources/System/SeedBoxRecordResource.php b/app/Filament/Resources/System/SeedBoxRecordResource.php index 2cbba422..893e2c08 100644 --- a/app/Filament/Resources/System/SeedBoxRecordResource.php +++ b/app/Filament/Resources/System/SeedBoxRecordResource.php @@ -5,6 +5,8 @@ namespace App\Filament\Resources\System; use App\Filament\Resources\System\SeedBoxRecordResource\Pages; use App\Filament\Resources\System\SeedBoxRecordResource\RelationManagers; use App\Models\SeedBoxRecord; +use App\Repositories\SeedBoxRepository; +use Filament\Facades\Filament; use Filament\Forms; use Filament\Resources\Form; use Filament\Resources\Resource; @@ -12,6 +14,7 @@ use Filament\Resources\Table; use Filament\Tables; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use phpDocumentor\Reflection\DocBlock\Tags\See; class SeedBoxRecordResource extends Resource { @@ -25,7 +28,7 @@ class SeedBoxRecordResource extends Resource protected static function getNavigationLabel(): string { - return __('admin.sidebar.seedbox_records'); + return __('admin.sidebar.seed_box_records'); } public static function getBreadcrumb(): string @@ -37,11 +40,11 @@ class SeedBoxRecordResource extends Resource { return $form ->schema([ - Forms\Components\TextInput::make('operator')->label(__('label.seedbox_record.operator')), - Forms\Components\TextInput::make('bandwidth')->label(__('label.seedbox_record.bandwidth'))->integer(), - Forms\Components\TextInput::make('ip_begin')->label(__('label.seedbox_record.ip_begin')), - Forms\Components\TextInput::make('ip_end')->label(__('label.seedbox_record.ip_end')), - Forms\Components\TextInput::make('ip')->label(__('label.seedbox_record.ip'))->helperText(__('label.seedbox_record.ip_help')), + Forms\Components\TextInput::make('operator')->label(__('label.seed_box_record.operator')), + Forms\Components\TextInput::make('bandwidth')->label(__('label.seed_box_record.bandwidth'))->integer(), + Forms\Components\TextInput::make('ip_begin')->label(__('label.seed_box_record.ip_begin')), + Forms\Components\TextInput::make('ip_end')->label(__('label.seed_box_record.ip_end')), + Forms\Components\TextInput::make('ip')->label(__('label.seed_box_record.ip'))->helperText(__('label.seed_box_record.ip_help')), Forms\Components\Textarea::make('comment')->label(__('label.comment')), ])->columns(1); } @@ -51,18 +54,45 @@ class SeedBoxRecordResource extends Resource return $table ->columns([ Tables\Columns\TextColumn::make('id'), - Tables\Columns\TextColumn::make('typeText')->label(__('label.seedbox_record.type')), - Tables\Columns\TextColumn::make('user.username')->label(__('label.username')), - Tables\Columns\TextColumn::make('operation')->label(__('label.seedbox_record.operator')), - Tables\Columns\TextColumn::make('bandwidth')->label(__('label.seedbox_record.bandwidth')), - Tables\Columns\TextColumn::make('ip')->label(__('label.seedbox_record.ip'))->formatStateUsing(fn ($record) => $record->ip ?: sprintf('%s ~ %s', $record->ip_begin, $record->ip_end)), + Tables\Columns\TextColumn::make('typeText')->label(__('label.seed_box_record.type')), + Tables\Columns\TextColumn::make('user.username')->label(__('label.username'))->searchable(), + Tables\Columns\TextColumn::make('operator')->label(__('label.seed_box_record.operator'))->searchable(), + Tables\Columns\TextColumn::make('bandwidth')->label(__('label.seed_box_record.bandwidth')), + Tables\Columns\TextColumn::make('ip') + ->label(__('label.seed_box_record.ip')) + ->searchable() + ->formatStateUsing(fn ($record) => $record->ip ?: sprintf('%s ~ %s', $record->ip_begin, $record->ip_end)), Tables\Columns\TextColumn::make('comment')->label(__('label.comment')), + Tables\Columns\BadgeColumn::make('status') + ->colors([ + 'success' => SeedBoxRecord::STATUS_ALLOWED, + 'warning' => SeedBoxRecord::STATUS_UNAUDITED, + 'danger' => SeedBoxRecord::STATUS_DENIED, + ]) + ->formatStateUsing(fn ($record) => $record->statusText) + ->label(__('label.seed_box_record.status')), ]) ->filters([ - // + Tables\Filters\SelectFilter::make('type')->options(SeedBoxRecord::listTypes('text'))->label(__('label.seed_box_record.type')), + Tables\Filters\SelectFilter::make('status')->options(SeedBoxRecord::listStatus('text'))->label(__('label.seed_box_record.status')), ]) ->actions([ Tables\Actions\EditAction::make(), + Tables\Actions\Action::make('audit') + ->label(__('admin.resources.seed_box_record.toggle_status')) + ->form([ + Forms\Components\Radio::make('status')->options(SeedBoxRecord::listStatus('text')) + ->inline()->label(__('label.seed_box_record.status'))->required() + ]) + ->action(function (SeedBoxRecord $record, array $data) { + $rep = new SeedBoxRepository(); + try { + $rep->updateStatus($record, $data['status']); + } catch (\Exception $exception) { + Filament::notify('danger', class_basename($exception)); + } + }) + , ]) ->bulkActions([ Tables\Actions\DeleteBulkAction::make(), diff --git a/app/Filament/Resources/System/SeedBoxRecordResource/Pages/CreateSeedBoxRecord.php b/app/Filament/Resources/System/SeedBoxRecordResource/Pages/CreateSeedBoxRecord.php index d9f52e4e..6356e6b0 100644 --- a/app/Filament/Resources/System/SeedBoxRecordResource/Pages/CreateSeedBoxRecord.php +++ b/app/Filament/Resources/System/SeedBoxRecordResource/Pages/CreateSeedBoxRecord.php @@ -13,23 +13,28 @@ class CreateSeedBoxRecord extends CreateRecord { protected static string $resource = SeedBoxRecordResource::class; - protected function mutateFormDataBeforeCreate(array $data): array + public function create(bool $another = false): void { + $data = $this->form->getState(); $data['uid'] = auth()->id(); $data['type'] = SeedBoxRecord::TYPE_ADMIN; - - return $data; - } - - protected function handleRecordCreation(array $data): Model - { - $seedBoxRep = new SeedBoxRepository(); + $data['status'] = SeedBoxRecord::STATUS_ALLOWED; + $rep = new SeedBoxRepository(); try { - return $seedBoxRep->store($data); + $this->record = $rep->store($data); + $this->notify('success', $this->getCreatedNotificationMessage()); + if ($another) { + // Ensure that the form record is anonymized so that relationships aren't loaded. + $this->form->model($this->record::class); + $this->record = null; + + $this->fillForm(); + + return; + } + $this->redirect($this->getResource()::getUrl('index')); } catch (\Exception $exception) { - //this wont work... $this->notify('danger', $exception->getMessage()); - die(); } } } diff --git a/app/Filament/Resources/System/SeedBoxRecordResource/Pages/EditSeedBoxRecord.php b/app/Filament/Resources/System/SeedBoxRecordResource/Pages/EditSeedBoxRecord.php index 27ee46a4..ed5a78ea 100644 --- a/app/Filament/Resources/System/SeedBoxRecordResource/Pages/EditSeedBoxRecord.php +++ b/app/Filament/Resources/System/SeedBoxRecordResource/Pages/EditSeedBoxRecord.php @@ -3,6 +3,7 @@ namespace App\Filament\Resources\System\SeedBoxRecordResource\Pages; use App\Filament\Resources\System\SeedBoxRecordResource; +use App\Repositories\SeedBoxRepository; use Filament\Pages\Actions; use Filament\Resources\Pages\EditRecord; @@ -16,4 +17,17 @@ class EditSeedBoxRecord extends EditRecord Actions\DeleteAction::make(), ]; } + + public function save(bool $shouldRedirect = true): void + { + $data = $this->form->getState(); + $rep = new SeedBoxRepository(); + try { + $this->record = $rep->update($data, $this->record->id); + $this->notify('success', $this->getSavedNotificationMessage()); + $this->redirect($this->getResource()::getUrl('index')); + } catch (\Exception $exception) { + $this->notify('danger', $exception->getMessage()); + } + } } diff --git a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php index e556f81b..502fcace 100644 --- a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php +++ b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php @@ -52,7 +52,7 @@ class EditSetting extends Page implements Forms\Contracts\HasForms ])->columns(2), Forms\Components\Tabs\Tab::make(__('label.setting.backup.tab_header')) ->schema([ - Forms\Components\Radio::make('backup.enabled')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.enabled'))->helperText(__('label.setting.backup.enabled_help')), + Forms\Components\Radio::make('backup.enabled')->options(self::$yesOrNo)->inline(true)->label(__('label.enabled'))->helperText(__('label.setting.backup.enabled_help')), Forms\Components\Radio::make('backup.frequency')->options(['daily' => 'daily', 'hourly' => 'hourly'])->inline(true)->label(__('label.setting.backup.frequency'))->helperText(__('label.setting.backup.frequency_help')), Forms\Components\Select::make('backup.hour')->options(range(0, 23))->label(__('label.setting.backup.hour'))->helperText(__('label.setting.backup.hour_help')), Forms\Components\Select::make('backup.minute')->options(range(0, 59))->label(__('label.setting.backup.minute'))->helperText(__('label.setting.backup.minute_help')), @@ -63,6 +63,13 @@ class EditSetting extends Page implements Forms\Contracts\HasForms Forms\Components\Radio::make('backup.via_ftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_ftp'))->helperText(__('label.setting.backup.via_ftp_help')), Forms\Components\Radio::make('backup.via_sftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_sftp'))->helperText(__('label.setting.backup.via_sftp_help')), ])->columns(2), + Forms\Components\Tabs\Tab::make(__('label.setting.seed_box.tab_header')) + ->schema([ + Forms\Components\Radio::make('seed_box.enabled')->options(self::$yesOrNo)->inline(true)->label(__('label.enabled'))->helperText(__('label.setting.seed_box.enabled_help')), + Forms\Components\TextInput::make('seed_box.not_seed_box_max_speed')->label(__('label.setting.seed_box.not_seed_box_max_speed'))->helperText(__('label.setting.seed_box.not_seed_box_max_speed_help'))->integer(), + Forms\Components\Radio::make('seed_box.no_promotion')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.seed_box.no_promotion'))->helperText(__('label.setting.seed_box.no_promotion_help')), + Forms\Components\TextInput::make('seed_box.max_uploaded')->label(__('label.setting.seed_box.max_uploaded'))->helperText(__('label.setting.seed_box.max_uploaded_help'))->integer(), + ])->columns(2), ]) ]; } diff --git a/app/Models/Message.php b/app/Models/Message.php index 8e27eaed..3800f3b2 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -2,6 +2,8 @@ namespace App\Models; +use Nexus\Database\NexusDB; + class Message extends NexusModel { protected $table = 'messages'; @@ -24,4 +26,11 @@ class Message extends NexusModel return $this->belongsTo(User::class, 'receiver'); } + public static function add(array $data): bool + { + NexusDB::cache_del('user_'.$data["receiver"].'_inbox_count'); + NexusDB::cache_del('user_'.$data["receiver"].'_unread_message_count'); + return self::query()->insert($data); + } + } diff --git a/app/Models/SeedBoxRecord.php b/app/Models/SeedBoxRecord.php index 9680b883..0aa2c4ec 100644 --- a/app/Models/SeedBoxRecord.php +++ b/app/Models/SeedBoxRecord.php @@ -6,22 +6,68 @@ use Illuminate\Database\Eloquent\Casts\Attribute; class SeedBoxRecord extends NexusModel { - protected $table = 'seedbox_records'; - - protected $fillable = ['type', 'uid', 'operator', 'bandwidth', 'ip', 'ip_begin', 'ip_end', 'ip_begin_numeric', 'ip_end_numeric', 'comment']; + protected $fillable = ['type', 'uid', 'status', 'operator', 'bandwidth', 'ip', 'ip_begin', 'ip_end', 'ip_begin_numeric', 'ip_end_numeric', 'comment', 'version']; public $timestamps = true; const TYPE_USER = 1; const TYPE_ADMIN = 2; + public static array $types = [ + self::TYPE_USER => ['text' => 'User'], + self::TYPE_ADMIN => ['text' => 'Administrator'], + ]; + + const STATUS_UNAUDITED = 0; + const STATUS_ALLOWED = 1; + const STATUS_DENIED = 2; + + public static array $status = [ + self::STATUS_UNAUDITED => ['text' => 'Unaudited'], + self::STATUS_ALLOWED => ['text' => 'Allowed'], + self::STATUS_DENIED => ['text' => 'Denied'], + ]; + protected function typeText(): Attribute { return new Attribute( - get: fn($value, $attributes) => __("seedbox.type_text." . $attributes['type']) + get: fn($value, $attributes) => nexus_trans("seed-box.type_text." . $attributes['type']) ); } + protected function statusText(): Attribute + { + return new Attribute( + get: fn($value, $attributes) => nexus_trans("seed-box.status_text." . $attributes['status']) + ); + } + + public static function listTypes($key = null): array + { + $result = self::$types; + $keyValues = []; + foreach ($result as $type => &$info) { + $info['text'] = nexus_trans("seed-box.type_text.$type"); + if ($key !== null) { + $keyValues[$type] = $info[$key]; + } + } + return $key === null ? $result : $keyValues; + } + + public static function listStatus($key = null): array + { + $result = self::$status; + $keyValues = []; + foreach ($result as $status => &$info) { + $info['text'] = nexus_trans("seed-box.status_text.$status"); + if ($key !== null) { + $keyValues[$status] = $info[$key]; + } + } + return $key === null ? $result : $keyValues; + } + public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(User::class, 'uid'); diff --git a/app/Repositories/SeedBoxRepository.php b/app/Repositories/SeedBoxRepository.php index ba7e4d29..1f8e2b0f 100644 --- a/app/Repositories/SeedBoxRepository.php +++ b/app/Repositories/SeedBoxRepository.php @@ -1,12 +1,16 @@ paginate(); } + /** + * @param array $params + * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model + */ public function store(array $params) + { + $params = $this->formatParams($params); + $seedBoxRecord = SeedBoxRecord::query()->create($params); + $this->clearCache(); + return $seedBoxRecord; + } + + private function formatParams(array $params): array { if (!empty($params['ip']) && empty($params['ip_begin']) && empty($params['ip_end'])) { - if (str_contains($params['ip'], '/')) { + try { $ipBlock = IPBlock::create($params['ip']); $params['ip_begin_numeric'] = $ipBlock->getFirstIp()->numeric(); $params['ip_end_numeric'] = $ipBlock->getLastIp()->numeric(); - } else { - $ip = IP::create($params['ip']); - $params['ip_begin_numeric'] = $ip->numeric(); - $params['ip_end_numeric'] = $ip->numeric(); + $params['version'] = $ipBlock->getVersion(); + } catch (\Exception $exception) { + do_log("[NOT_IP_BLOCK], {$params['ip']}" . $exception->getMessage()); } + if (empty($params['version'])) { + try { + $ip = IP::create($params['ip']); + $params['ip_begin_numeric'] = $ip->numeric(); + $params['ip_end_numeric'] = $ip->numeric(); + $params['version'] = $ip->getVersion(); + } catch (\Exception $exception) { + do_log("[NOT_IP], {$params['ip']}" . $exception->getMessage()); + } + } + if (empty($params['version'])) { + throw new \InvalidArgumentException("Invalid IPBlock or IP: " . $params['ip']); + } + } elseif (empty($params['ip']) && !empty($params['ip_begin']) && !empty($params['ip_end'])) { $ipBegin = IP::create($params['ip_begin']); $params['ip_begin_numeric'] = $ipBegin->numeric(); $ipEnd = IP::create($params['ip_end']); $params['ip_end_numeric'] = $ipEnd->numeric(); + if ($ipBegin->getVersion() != $ipEnd->getVersion()) { + throw new \InvalidArgumentException("ip_begin/ip_end must be the same version"); + } + $params['version'] = $ipEnd->getVersion(); } else { throw new \InvalidArgumentException("Require ip or ip_begin + ip_end"); } - return SeedBoxRecord::query()->create($params); + return $params; } public function update(array $params, $id) { - $model = Poll::query()->findOrFail($id); + $model = SeedBoxRecord::query()->findOrFail($id); + $params = $this->formatParams($params); $model->update($params); + $this->clearCache(); return $model; } @@ -60,7 +95,54 @@ class SeedBoxRepository extends BaseRepository public function delete($id, $uid) { + $this->clearCache(); return SeedBoxRecord::query()->whereIn('id', Arr::wrap($id))->where('uid', $uid)->delete(); } + public function updateStatus(SeedBoxRecord $seedBoxRecord, $status): bool + { + if (Auth::user()->class < User::CLASS_ADMINISTRATOR) { + throw new InsufficientPermissionException(); + } + if (!isset(SeedBoxRecord::$status[$status])) { + throw new \InvalidArgumentException("Invalid status: $status"); + } + if ($seedBoxRecord->status == $status) { + return true; + } + $message = [ + 'receiver' => $seedBoxRecord->uid, + 'subject' => nexus_trans('seed-box.status_change_message.subject'), + 'msg' => nexus_trans('seed-box.status_change_message.body', [ + 'id' => $seedBoxRecord->id, + 'operator' => Auth::user()->username, + 'old_status' => $seedBoxRecord->statusText, + 'new_status' => nexus_trans('seed-box.status_text.' . $status), + ]), + 'added' => now() + ]; + return NexusDB::transaction(function () use ($seedBoxRecord, $status, $message) { + $seedBoxRecord->status = $status; + $seedBoxRecord->save(); + $this->clearCache(); + return Message::add($message); + }); + } + + public function renderIcon($ip, $uid): string + { + $result = ''; + if ((isIPV4($ip) || isIPV6($ip)) && get_setting('seed_box.enabled') == 'yes' && isIPSeedBox($ip, $uid)) { + $result = ''; + } + return $result; + } + + private function clearCache() + { + NexusDB::redis()->del("nexus_is_ip_seed_box"); + } + + + } diff --git a/app/Repositories/TrackerRepository.php b/app/Repositories/TrackerRepository.php index cb6c5dd3..8e502c6d 100644 --- a/app/Repositories/TrackerRepository.php +++ b/app/Repositories/TrackerRepository.php @@ -88,16 +88,23 @@ class TrackerRepository extends BaseRepository } elseif ($isReAnnounce == self::ANNOUNCE_FIRST) { $this->checkMinInterval($peerSelf, $queries); } + + $snatch = Snatch::query() + ->where('torrentid', $torrent->id) + ->where('userid', $user->id) + ->orderBy('id', 'desc') + ->first(); /** * Note: Must get before update peer! */ - $dataTraffic = $this->getDataTraffic($torrent, $queries, $user, $peerSelf); + $dataTraffic = $this->getDataTraffic($torrent, $queries, $user, $peerSelf, $snatch); /** * Note: Only check in old session */ if ($peerSelf->exists) { - $this->checkCheater($torrent, $dataTraffic, $user, $peerSelf); + $this->checkCheater($torrent, $queries, $dataTraffic, $user, $peerSelf); + $this->checkSeedBox($torrent, $queries, $dataTraffic, $user, $peerSelf); } /** @@ -113,7 +120,7 @@ class TrackerRepository extends BaseRepository /** * Note: Must update snatch first, otherwise peer `last_action` already change */ - $snatch = $this->updateSnatch($peerSelf, $queries, $dataTraffic); + $snatch = $this->updateSnatch($peerSelf, $queries, $dataTraffic, $snatch); if ($queries['event'] == 'completed') { $this->handleHitAndRun($user, $torrent, $snatch); } @@ -420,6 +427,7 @@ class TrackerRepository extends BaseRepository if ($user->class >= User::CLASS_VIP) { return; } + $gigs = $user->downloaded / (1024*1024*1024); if ($gigs < 10) { return; @@ -485,7 +493,7 @@ class TrackerRepository extends BaseRepository } } - protected function checkCheater(Torrent $torrent, $dataTraffic, User $user, Peer $peer) + protected function checkCheater(Torrent $torrent, $queries, $dataTraffic, User $user, Peer $peer) { $settingSecurity = Setting::get('security'); $level = $settingSecurity['cheaterdet']; @@ -560,6 +568,33 @@ class TrackerRepository extends BaseRepository } + private function checkSeedBox(Torrent $torrent, $queries, $dataTraffic, User $user, Peer $peer) + { + if ($user->class >= User::CLASS_VIP || $user->isDonating()) { + return; + } + $isSeedBoxRuleEnabled = Setting::get('seed_box.enabled') == 'yes'; + if (!$isSeedBoxRuleEnabled) { + return; + } + $isIPSeedBox = isIPSeedBox($queries['ip'], $user->id); + if ($isIPSeedBox) { + return; + } + if (!$peer->isValidDate('last_action')) { + //no last action + return; + } + $duration = Carbon::now()->diffInSeconds($peer->last_action); + $upSpeedMbps = ($dataTraffic['uploaded_increment'] / $duration) * 8; + $notSeedBoxMaxSpeedMbps = Setting::get('seed_box.not_seed_box_max_speed'); + if ($upSpeedMbps > $notSeedBoxMaxSpeedMbps) { + $user->update(['downloadpos' => 'no']); + do_log("user: {$user->id} downloading privileges have been disabled! (over speed)", 'error'); + throw new TrackerException("Your downloading privileges have been disabled! (over speed)"); + } + } + private function createOrUpdateCheater(Torrent $torrent, User $user, array $createData) { $existsCheater = Cheater::query() @@ -671,7 +706,7 @@ class TrackerRepository extends BaseRepository return $real_annnounce_interval; } - private function getDataTraffic(Torrent $torrent, $queries, User $user, Peer $peer): array + private function getDataTraffic(Torrent $torrent, $queries, User $user, Peer $peer, $snatch): array { $log = sprintf( "torrent: %s, user: %s, peer: %s, queriesUploaded: %s, queriesDownloaded: %s", @@ -720,12 +755,32 @@ class TrackerRepository extends BaseRepository $downRatio = 0; $log .= ", [PEER_NOT_EXISTS], realUploaded: $realUploaded, realDownloaded: $realDownloaded, upRatio: $upRatio, downRatio: $downRatio"; } + $uploadedIncrementForUser = $realUploaded * $upRatio; + $downloadedIncrementForUser = $realDownloaded * $downRatio; + + /** + * check seed box rule + */ + $isSeedBoxRuleEnabled = Setting::get('seed_box.enabled') == 'yes'; + if ($isSeedBoxRuleEnabled) { + $isIPSeedBox = isIPSeedBox($queries['ip'], $user->id); + if ($isIPSeedBox) { + $uploadedIncrementForUser = $realUploaded; + $downloadedIncrementForUser = $realDownloaded; + $log .= ", isIPSeedBox, increment for user = real"; + $maxUploadedTimes = Setting::get('seed_box.max_uploaded'); + if ($snatch && $snatch->uploaded >= $torrent->size * $maxUploadedTimes) { + $log .= ", uploaded >= torrentSize * times($maxUploadedTimes), uploadedIncrementForUser = 0"; + $uploadedIncrementForUser = 0; + } + } + } $result = [ 'uploaded_increment' => $realUploaded, - 'uploaded_increment_for_user' => $realUploaded * $upRatio, + 'uploaded_increment_for_user' => $uploadedIncrementForUser, 'downloaded_increment' => $realDownloaded, - 'downloaded_increment_for_user' => $realDownloaded * $downRatio, + 'downloaded_increment_for_user' => $downloadedIncrementForUser, ]; do_log("$log, result: " . json_encode($result), 'info'); return $result; @@ -938,15 +993,10 @@ class TrackerRepository extends BaseRepository * @param $queries * @param $dataTraffic */ - private function updateSnatch(Peer $peer, $queries, $dataTraffic) + private function updateSnatch(Peer $peer, $queries, $dataTraffic, $snatch) { $nowStr = Carbon::now()->toDateTimeString(); - $snatch = Snatch::query() - ->where('torrentid', $peer->torrent) - ->where('userid', $peer->userid) - ->first(); - //torrentid, userid, ip, port, uploaded, downloaded, to_go, ,seedtime, leechtime, last_action, startdat, completedat, finished if (!$snatch) { $snatch = new Snatch(); diff --git a/composer.json b/composer.json index 75e22fd0..b0429985 100644 --- a/composer.json +++ b/composer.json @@ -78,6 +78,9 @@ ], "post-create-project-cmd": [ "@php artisan key:generate --ansi" + ], + "post-update-cmd": [ + "@php artisan filament:upgrade" ] }, "extra": { diff --git a/database/migrations/2022_07_20_194152_create_seedbox_records_table.php b/database/migrations/2022_07_20_194152_create_seedbox_records_table.php index 359f365c..be35c84d 100644 --- a/database/migrations/2022_07_20_194152_create_seedbox_records_table.php +++ b/database/migrations/2022_07_20_194152_create_seedbox_records_table.php @@ -13,10 +13,11 @@ return new class extends Migration */ public function up() { - Schema::create('seedbox_records', function (Blueprint $table) { + Schema::create('seed_box_records', function (Blueprint $table) { $table->id(); $table->integer('type'); $table->integer('uid'); + $table->integer('status')->default(0); $table->string('operator')->nullable(); $table->integer('bandwidth')->nullable(); $table->string('ip')->nullable(); @@ -24,6 +25,7 @@ return new class extends Migration $table->string('ip_end')->nullable(); $table->string('ip_begin_numeric', 128)->index(); $table->string('ip_end_numeric', 128)->index(); + $table->integer('version'); $table->string('comment')->nullable(); $table->timestamps(); }); @@ -36,6 +38,6 @@ return new class extends Migration */ public function down() { - Schema::dropIfExists('seedbox_records'); + Schema::dropIfExists('seed_box_records'); } }; diff --git a/include/constants.php b/include/constants.php index 943dd226..1389c04f 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ 'en', ]; $locale = $langMap[$lang] ?? $lang; - $result = []; - foreach (explode(',', $ip) as $__ip) { - if (empty($__ip)) { - continue; - } - $locationInfo = \Nexus\Database\NexusDB::remember("locations_{$__ip}", 3600, function () use ($locale, $__ip, $reader) { - $info = [ - 'ip' => $__ip, - 'version' => '', - 'country' => '', - 'city' => '', - ]; - try { - $record = $reader->city($__ip); - $countryName = $record->country->names[$locale] ?? $record->country->names['en'] ?? ''; - $cityName = $record->city->names[$locale] ?? $record->city->names['en'] ?? ''; - if (isIPV4($__ip)) { - $info['version'] = 4; - } elseif (isIPV6($__ip)) { - $info['version'] = 6; - } - $info['country'] = $countryName; - $info['city'] = $cityName; - } catch (\Exception $exception) { - do_log($exception->getMessage() . $exception->getTraceAsString(), 'error'); + $locationInfo = \Nexus\Database\NexusDB::remember("locations_{$ip}", 3600, function () use ($locale, $ip, $reader) { + $info = [ + 'ip' => $ip, + 'version' => '', + 'country' => '', + 'city' => '', + ]; + try { + $record = $reader->city($ip); + $countryName = $record->country->names[$locale] ?? $record->country->names['en'] ?? ''; + $cityName = $record->city->names[$locale] ?? $record->city->names['en'] ?? ''; + if (isIPV4($ip)) { + $info['version'] = 4; + } elseif (isIPV6($ip)) { + $info['version'] = 6; } - return $info; - }); - $result[] = $locationInfo; - } - usort($result, function ($a, $b) { - if ($a['version'] == $b['version']) { - return 0; + $info['country'] = $countryName; + $info['city'] = $cityName; + } catch (\Exception $exception) { + do_log($exception->getMessage() . $exception->getTraceAsString(), 'error'); } - return $a['version'] > $b['version'] ? 1 : -1; + return $info; }); - do_log("ip: $ip, locale: $locale, result: " . nexus_json_encode($result)); - $names = []; - foreach ($result as $item) { - $names[] = sprintf('%s[v%s]', $item['city'] ? ($item['city'] . "·" . $item['country']) : $item['country'], $item['version']); - } + do_log("ip: $ip, locale: $locale, result: " . nexus_json_encode($locationInfo)); + $name = sprintf('%s[v%s]', $locationInfo['city'] ? ($locationInfo['city'] . "·" . $locationInfo['country']) : $locationInfo['country'], $locationInfo['version']); return [ - 'name' => implode(" + ", $names), + 'name' => $name, 'location_main' => '', 'location_sub' => '', 'flagpic' => '', diff --git a/include/globalfunctions.php b/include/globalfunctions.php index 79d7de0b..c6888daf 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -767,4 +767,45 @@ function do_action($name, ...$args) return $hook->doAction(...func_get_args()); } - +function isIPSeedBox($ip, $uid = null, $withoutCache = false): bool +{ + $redis = \Nexus\Database\NexusDB::redis(); + $key = "nexus_is_ip_seed_box"; + $hashKey = "ip:$ip:uid:$uid"; + $cacheData = $redis->hGet($key, $hashKey); + if ($cacheData && !$withoutCache) { + $cacheDataOriginal = unserialize($cacheData); + if ($cacheDataOriginal['deadline'] > time()) { + do_log("$hashKey, get result from cache: " . json_encode($cacheDataOriginal)); + return $cacheDataOriginal['data']; + } + } + $ipObject = \PhpIP\IP::create($ip); + $ipNumeric = $ipObject->numeric(); + $ipVersion = $ipObject->getVersion(); + $checkSeedBoxAdminSql = sprintf( + 'select id from seed_box_records where `ip_begin_numeric` <= "%s" and `ip_end_numeric` >= "%s" and `type` = %s and `version` = %s and `status` = %s limit 1', + $ipNumeric, $ipNumeric, \App\Models\SeedBoxRecord::TYPE_ADMIN, $ipVersion, \App\Models\SeedBoxRecord::STATUS_ALLOWED + ); + $res = \Nexus\Database\NexusDB::select($checkSeedBoxAdminSql); + if (!empty($res)) { + $redis->hSet($key, $hashKey, serialize(['data' => true, 'deadline' => time() + 3600])); + do_log("$hashKey, get result from admin, true"); + return true; + } + if ($uid !== null) { + $checkSeedBoxUserSql = sprintf( + 'select id from seed_box_records where `ip_begin_numeric` <= "%s" and `ip_end_numeric` >= "%s" and `uid` = %s and `type` = %s and `version` = %s and `status` = %s limit 1', + $ipNumeric, $ipNumeric, $uid, \App\Models\SeedBoxRecord::TYPE_USER, $ipVersion, \App\Models\SeedBoxRecord::STATUS_ALLOWED + ); + $res = \Nexus\Database\NexusDB::select($checkSeedBoxUserSql); + if (!empty($res)) { + $redis->hSet($key, $hashKey, serialize(['data' => true, 'deadline' => time() + 3600])); + do_log("$hashKey, get result from user, true"); + return true; + } + } + $redis->hSet($key, $hashKey, serialize(['data' => false, 'deadline' => time() + 3600])); + do_log("$hashKey, no result, false"); + return false; +} diff --git a/nexus/Install/settings.default.php b/nexus/Install/settings.default.php index 2d4590b3..03cd5f54 100644 --- a/nexus/Install/settings.default.php +++ b/nexus/Install/settings.default.php @@ -407,4 +407,10 @@ return array ( 'ignore_when_ratio_reach' => '', 'ban_user_when_counts_reach' => '', ], + 'seed_box' => [ + 'enabled' => 'no', + 'no_promotion' => 'yes', + 'max_uploaded' => 3, + 'not_seed_box_max_speed' => 10240, + ], ); diff --git a/public/ajax.php b/public/ajax.php index 94dc2a9f..793db6b3 100644 --- a/public/ajax.php +++ b/public/ajax.php @@ -99,6 +99,7 @@ function addSeedBoxRecord($params) $rep = new \App\Repositories\SeedBoxRepository(); $params['uid'] = $CURUSER['id']; $params['type'] = \App\Models\SeedBoxRecord::TYPE_USER; + $params['status'] = \App\Models\SeedBoxRecord::STATUS_UNAUDITED; return $rep->store($params); } diff --git a/public/announce.php b/public/announce.php index 7fbf33e1..ea202f01 100644 --- a/public/announce.php +++ b/public/announce.php @@ -120,6 +120,7 @@ if (!$az) err("Invalid passkey! Re-download the .torrent from $BASEURL"); $userid = intval($az['id'] ?? 0); unset($GLOBALS['CURUSER']); $CURUSER = $GLOBALS["CURUSER"] = $az; +$isDonor = $az['donor'] == 'yes' && ($az['donoruntil'] === null || $az['donoruntil'] == '0000-00-00 00:00:00' || $az['donoruntil'] > date('Y-m-d H:i:s')); //3. CHECK IF CLIENT IS ALLOWED //$clicheck_res = check_client($peer_id,$agent,$client_familyid); @@ -152,7 +153,7 @@ elseif ($az['showclienterror'] == 'yes'){ } // check torrent based on info_hash -$checkTorrentSql = "SELECT id, owner, sp_state, seeders, leechers, UNIX_TIMESTAMP(added) AS ts, banned, hr, approval_status FROM torrents WHERE " . hash_where("info_hash", $info_hash); +$checkTorrentSql = "SELECT id, size, owner, sp_state, seeders, leechers, UNIX_TIMESTAMP(added) AS ts, banned, hr, approval_status FROM torrents WHERE " . hash_where("info_hash", $info_hash); if (!$torrent = $Cache->get_value('torrent_hash_'.$info_hash.'_content')){ $res = sql_query($checkTorrentSql); $torrent = mysql_fetch_array($res); @@ -284,6 +285,12 @@ if(isset($self) && empty($_GET['event']) && $self['prevts'] > (TIMENOW - $announ } +$isSeedBoxRuleEnabled = get_setting('seed_box.enabled') == 'yes'; +$isIPSeedBox = false; +if ($isSeedBoxRuleEnabled && !($az['class'] >= \App\Models\User::CLASS_VIP || $isDonor)) { + $isIPSeedBox = isIPSeedBox($ip, $userid); +} + // current peer_id, or you could say session with tracker not found in table peers if (!isset($self)) { @@ -296,7 +303,7 @@ if (!isset($self)) elseif ($az["parked"] == "yes") err("Your account is parked! (Read the FAQ)"); elseif ($az["downloadpos"] == "no") - err("Your downloading priviledges have been disabled! (Read the rules)"); + err("Your downloading privileges have been disabled! (Read the rules)"); if ($az["class"] < UC_VIP) { @@ -336,10 +343,18 @@ if (!isset($self)) } else // continue an existing session { + $snatchInfo = mysql_fetch_assoc(sql_query(sprintf('select * from snatched where torrentid = %s and userid = %s order by id desc limit 1', $torrentid, $userid))); $upthis = $trueupthis = max(0, $uploaded - $self["uploaded"]); $downthis = $truedownthis = max(0, $downloaded - $self["downloaded"]); $announcetime = ($self["seeder"] == "yes" ? "seedtime = seedtime + {$self['announcetime']}" : "leechtime = leechtime + {$self['announcetime']}"); $is_cheater = false; + $notSeedBoxMaxSpeedMbps = get_setting('seed_box.not_seed_box_max_speed'); + $upSpeedMbps = ($trueupthis / $self['announcetime']) * 8; + if ($isSeedBoxRuleEnabled && !$isIPSeedBox && $upSpeedMbps > $notSeedBoxMaxSpeedMbps) { + sql_query("update users set downloadpos = 'no' where id = $userid"); + do_log("user: $userid downloading privileges have been disabled! (over speed)", 'error'); + err("Your downloading privileges have been disabled! (over speed)"); + } if ($cheaterdet_security){ if ($az['class'] < $nodetect_security && $self['announcetime'] > 10) @@ -352,92 +367,106 @@ else // continue an existing session if (!$is_cheater && ($trueupthis > 0 || $truedownthis > 0)) { - $global_promotion_state = get_global_sp_state(); - if (isset($torrent['__ignore_global_sp_state']) && $torrent['__ignore_global_sp_state']) { - do_log("[IGNORE_GLOBAL_SP_STATE], sp_state: {$torrent['sp_state']}"); - $global_promotion_state = 1; - } - if($global_promotion_state == 1)// Normal, see individual torrent - { - if($torrent['sp_state']==3) //2X - { - $USERUPDATESET[] = "uploaded = uploaded + 2*$trueupthis"; - $USERUPDATESET[] = "downloaded = downloaded + $truedownthis"; - } - elseif($torrent['sp_state']==4) //2X Free - { - $USERUPDATESET[] = "uploaded = uploaded + 2*$trueupthis"; - } - elseif($torrent['sp_state']==6) //2X 50% - { - $USERUPDATESET[] = "uploaded = uploaded + 2*$trueupthis"; - $USERUPDATESET[] = "downloaded = downloaded + $truedownthis/2"; - } - else{ - if ($torrent['owner'] == $userid && $uploaderdouble_torrent > 0) - $upthis = $trueupthis * $uploaderdouble_torrent; + if ($isSeedBoxRuleEnabled && $isIPSeedBox) { + $tmpLog = "[SEED_BOX_RULE_ENABLED_AND_IS_IP_SEED_BOX]"; + $maxUploadedTimes = get_setting('seed_box.max_uploaded'); + $userUploadedIncrement = $trueupthis; + $userDownloadedIncrement = $truedownthis; + if (!empty($snatchInfo) && isset($torrent['size']) && $snatchInfo['uploaded'] >= $torrent['size'] * $maxUploadedTimes) { + $tmpLog .= ", uploaded >= torrentSize * times($maxUploadedTimes), userUploadedIncrement = 0"; + $userUploadedIncrement = 0; + } + $USERUPDATESET[] = "uploaded = uploaded + $userUploadedIncrement"; + $USERUPDATESET[] = "downloaded = downloaded + $userDownloadedIncrement"; + do_log($tmpLog); + } else { + $global_promotion_state = get_global_sp_state(); + if (isset($torrent['__ignore_global_sp_state']) && $torrent['__ignore_global_sp_state']) { + do_log("[IGNORE_GLOBAL_SP_STATE], sp_state: {$torrent['sp_state']}"); + $global_promotion_state = 1; + } + if($global_promotion_state == 1)// Normal, see individual torrent + { + if($torrent['sp_state']==3) //2X + { + $USERUPDATESET[] = "uploaded = uploaded + 2*$trueupthis"; + $USERUPDATESET[] = "downloaded = downloaded + $truedownthis"; + } + elseif($torrent['sp_state']==4) //2X Free + { + $USERUPDATESET[] = "uploaded = uploaded + 2*$trueupthis"; + } + elseif($torrent['sp_state']==6) //2X 50% + { + $USERUPDATESET[] = "uploaded = uploaded + 2*$trueupthis"; + $USERUPDATESET[] = "downloaded = downloaded + $truedownthis/2"; + } + else{ + if ($torrent['owner'] == $userid && $uploaderdouble_torrent > 0) + $upthis = $trueupthis * $uploaderdouble_torrent; - if($torrent['sp_state']==2) //Free - { - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - } - elseif($torrent['sp_state']==5) //50% - { - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - $USERUPDATESET[] = "downloaded = downloaded + $truedownthis/2"; - } - elseif($torrent['sp_state']==7) //30% - { - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - $USERUPDATESET[] = "downloaded = downloaded + $truedownthis*3/10"; - } - elseif($torrent['sp_state']==1) //Normal - { - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - $USERUPDATESET[] = "downloaded = downloaded + $truedownthis"; - } - } - } - elseif($global_promotion_state == 2) //Free - { - if ($torrent['owner'] == $userid && $uploaderdouble_torrent > 0) - $upthis = $trueupthis * $uploaderdouble_torrent; - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - } - elseif($global_promotion_state == 3) //2X - { - if ($uploaderdouble_torrent > 2 && $torrent['owner'] == $userid && $uploaderdouble_torrent > 0) - $upthis = $trueupthis * $uploaderdouble_torrent; - else $upthis = 2*$trueupthis; - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - $USERUPDATESET[] = "downloaded = downloaded + $truedownthis"; - } - elseif($global_promotion_state == 4) //2X Free - { - if ($uploaderdouble_torrent > 2 && $torrent['owner'] == $userid && $uploaderdouble_torrent > 0) - $upthis = $trueupthis * $uploaderdouble_torrent; - else $upthis = 2*$trueupthis; - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - } - elseif($global_promotion_state == 5){ // 50% - if ($torrent['owner'] == $userid && $uploaderdouble_torrent > 0) - $upthis = $trueupthis * $uploaderdouble_torrent; - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - $USERUPDATESET[] = "downloaded = downloaded + $truedownthis/2"; - } - elseif($global_promotion_state == 6){ //2X 50% - if ($uploaderdouble_torrent > 2 && $torrent['owner'] == $userid && $uploaderdouble_torrent > 0) - $upthis = $trueupthis * $uploaderdouble_torrent; - else $upthis = 2*$trueupthis; - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - $USERUPDATESET[] = "downloaded = downloaded + $truedownthis/2"; - } - elseif($global_promotion_state == 7){ //30% - if ($torrent['owner'] == $userid && $uploaderdouble_torrent > 0) - $upthis = $trueupthis * $uploaderdouble_torrent; - $USERUPDATESET[] = "uploaded = uploaded + $upthis"; - $USERUPDATESET[] = "downloaded = downloaded + $truedownthis*3/10"; - } + if($torrent['sp_state']==2) //Free + { + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + } + elseif($torrent['sp_state']==5) //50% + { + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + $USERUPDATESET[] = "downloaded = downloaded + $truedownthis/2"; + } + elseif($torrent['sp_state']==7) //30% + { + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + $USERUPDATESET[] = "downloaded = downloaded + $truedownthis*3/10"; + } + elseif($torrent['sp_state']==1) //Normal + { + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + $USERUPDATESET[] = "downloaded = downloaded + $truedownthis"; + } + } + } + elseif($global_promotion_state == 2) //Free + { + if ($torrent['owner'] == $userid && $uploaderdouble_torrent > 0) + $upthis = $trueupthis * $uploaderdouble_torrent; + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + } + elseif($global_promotion_state == 3) //2X + { + if ($uploaderdouble_torrent > 2 && $torrent['owner'] == $userid && $uploaderdouble_torrent > 0) + $upthis = $trueupthis * $uploaderdouble_torrent; + else $upthis = 2*$trueupthis; + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + $USERUPDATESET[] = "downloaded = downloaded + $truedownthis"; + } + elseif($global_promotion_state == 4) //2X Free + { + if ($uploaderdouble_torrent > 2 && $torrent['owner'] == $userid && $uploaderdouble_torrent > 0) + $upthis = $trueupthis * $uploaderdouble_torrent; + else $upthis = 2*$trueupthis; + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + } + elseif($global_promotion_state == 5){ // 50% + if ($torrent['owner'] == $userid && $uploaderdouble_torrent > 0) + $upthis = $trueupthis * $uploaderdouble_torrent; + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + $USERUPDATESET[] = "downloaded = downloaded + $truedownthis/2"; + } + elseif($global_promotion_state == 6){ //2X 50% + if ($uploaderdouble_torrent > 2 && $torrent['owner'] == $userid && $uploaderdouble_torrent > 0) + $upthis = $trueupthis * $uploaderdouble_torrent; + else $upthis = 2*$trueupthis; + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + $USERUPDATESET[] = "downloaded = downloaded + $truedownthis/2"; + } + elseif($global_promotion_state == 7){ //30% + if ($torrent['owner'] == $userid && $uploaderdouble_torrent > 0) + $upthis = $trueupthis * $uploaderdouble_torrent; + $USERUPDATESET[] = "uploaded = uploaded + $upthis"; + $USERUPDATESET[] = "downloaded = downloaded + $truedownthis*3/10"; + } + } } } @@ -476,23 +505,14 @@ elseif(isset($self)) // $updateset[] = ($seeder == "yes" ? "seeders = seeders + 1, leechers = leechers - 1" : "seeders = seeders - 1, leechers = leechers + 1"); $hasChangeSeederLeecher = true; } - $snatchInfo = \App\Models\Snatch::query() - ->where('torrentid', $torrentid) - ->where('userid', $userid) - ->orderBy('id', 'desc') - ->first(); - if ($snatchInfo) { + if (!empty($snatchInfo)) { sql_query("UPDATE snatched SET uploaded = uploaded + $trueupthis, downloaded = downloaded + $truedownthis, to_go = $left, $announcetime, last_action = ".$dt." $finished_snatched WHERE torrentid = $torrentid AND userid = $userid") or err("SL Err 2"); - do_action('snatched_saved', $torrent, $snatchInfo->toArray()); - if ( - $event == 'completed' - && $az['class'] < \App\Models\HitAndRun::MINIMUM_IGNORE_USER_CLASS - && ($az['donor'] == 'no' || (!empty($az['donoruntil']) && $az['donoruntil'] != '0000-00-00 00:00:00' && $az['donoruntil'] < date("Y-m-d H:i:s"))) - ) { + do_action('snatched_saved', $torrent, $snatchInfo); + if ($event == 'completed' && $az['class'] < \App\Models\HitAndRun::MINIMUM_IGNORE_USER_CLASS && !$isDonor) { //think about H&R $hrMode = get_setting('hr.mode'); if ($hrMode == \App\Models\HitAndRun::MODE_GLOBAL || ($hrMode == \App\Models\HitAndRun::MODE_MANUAL && $torrent['hr'] == \App\Models\Torrent::HR_YES)) { - $sql = "insert into hit_and_runs (uid, torrent_id, snatched_id) values ($userid, $torrentid, {$snatchInfo->id}) on duplicate key update updated_at = " . sqlesc(date('Y-m-d H:i:s')); + $sql = "insert into hit_and_runs (uid, torrent_id, snatched_id) values ($userid, $torrentid, {$snatchInfo['id']}) on duplicate key update updated_at = " . sqlesc(date('Y-m-d H:i:s')); $affectedRows = sql_query($sql); do_log("[INSERT_H&R], $sql, affectedRows: $affectedRows"); } @@ -504,21 +524,20 @@ else { if ($event != 'stopped') { $isPeerExistResultSet = sql_query("select id from peers where $selfwhere limit 1"); - if ($isPeerExistResultSet && !mysqli_fetch_assoc($isPeerExistResultSet)) { - if (strlen($ip) > 15) { - $sockres = @pfsockopen("tcp://[".$ip."]",$port,$errno,$errstr,1); - } else { - $sockres = @pfsockopen($ip, $port, $errno, $errstr, 1); - } - if (!$sockres) - { - $connectable = "no"; - } - else - { - $connectable = "yes"; - @fclose($sockres); - } + if (mysql_num_rows($isPeerExistResultSet) == 0) { + $cacheKey = 'peers:connectable:'.$ip.'-'.$port.'-'.$agent; + $connectable = \Nexus\Database\NexusDB::remember($cacheKey, 3600, function () use ($ip, $port) { + if (isIPV6($ip)) { + $sockres = @fsockopen("tcp://[".$ip."]",$port,$errno,$errstr,1); + } else { + $sockres = @fsockopen($ip, $port, $errno, $errstr, 1); + } + if (is_resource($sockres)) { + fclose($sockres); + return 'yes'; + } + return 'no'; + }); $insertPeerSql = "INSERT INTO peers (torrent, userid, peer_id, ip, port, connectable, uploaded, downloaded, to_go, started, last_action, seeder, agent, downloadoffset, uploadoffset, passkey, ipv4, ipv6) VALUES ($torrentid, $userid, ".sqlesc($peer_id).", ".sqlesc($ip).", $port, '$connectable', $uploaded, $downloaded, $left, $dt, $dt, '$seeder', ".sqlesc($agent).", $downloaded, $uploaded, ".sqlesc($passkey).",".sqlesc($ipv4).",".sqlesc($ipv6).")"; do_log("[INSERT PEER] peer not exists for $selfwhere, do insert with $insertPeerSql"); diff --git a/public/pic/misc/seed-box.png b/public/pic/misc/seed-box.png new file mode 100644 index 00000000..bae1bfec Binary files /dev/null and b/public/pic/misc/seed-box.png differ diff --git a/public/usercp.php b/public/usercp.php index 116b31ef..1e02fc44 100644 --- a/public/usercp.php +++ b/public/usercp.php @@ -923,12 +923,13 @@ if ($forumposts) renderIcon($CURUSER['ip'], $CURUSER['id']); if ($enablelocation_tweak == 'yes'){ list($loc_pub, $loc_mod) = get_ip_location($CURUSER["ip"]); - tr_small($lang_usercp['row_ip_location'], $CURUSER["ip"]." [" . $loc_pub . "]", 1); + tr_small($lang_usercp['row_ip_location'], $CURUSER["ip"]." [" . $loc_pub . "]$seedBoxIcon", 1); } else{ - tr_small($lang_usercp['row_ip_location'], $CURUSER["ip"], 1); + tr_small($lang_usercp['row_ip_location'], $CURUSER["ip"] . $seedBoxIcon, 1); } if ($CURUSER["avatar"]) tr_small($lang_usercp['row_avatar'], "", 1); @@ -948,25 +949,28 @@ tr_small($lang_usercp['row_written_comments'], $commentcount." [ 0) +$columnStatus = nexus_trans('label.seed_box_record.status'); +$res = \App\Models\SeedBoxRecord::query()->where('uid', $CURUSER['id'])->where('type', \App\Models\SeedBoxRecord::TYPE_USER)->get(); +if ($res->count() > 0) { - $seedBox .= ""; - while($arr = mysql_fetch_assoc($res)) + $seedBox .= "
{$columnOperator}{$columnBandwidth}{$columnIP}{$columnComment}
"; + foreach ($res as $seedBoxRecord) { $seedBox .= ""; - $seedBox .= sprintf('', $arr['operator']); - $seedBox .= sprintf('', $arr['bandwidth'] ?: ''); - $seedBox .= sprintf('', $arr['ip'] ?: sprintf('%s ~ %s', $arr['ip_begin'], $arr['ip_end'])); - $seedBox .= sprintf('', $arr['comment']); - $seedBox .= sprintf('', $lang_functions['text_delete'], $arr['id']); + $seedBox .= sprintf('', $seedBoxRecord->id); + $seedBox .= sprintf('', $seedBoxRecord->operator); + $seedBox .= sprintf('', $seedBoxRecord->bandwidth ?: ''); + $seedBox .= sprintf('', $seedBoxRecord->ip ?: sprintf('%s ~ %s', $seedBoxRecord->ip_begin, $seedBoxRecord->ip_end)); + $seedBox .= sprintf('', $seedBoxRecord->comment); + $seedBox .= sprintf('', $seedBoxRecord->statusText); + $seedBox .= sprintf('', $lang_functions['text_delete'], $seedBoxRecord->id); $seedBox .= ""; } $seedBox .= '
ID{$columnOperator}{$columnBandwidth}{$columnIP}{$columnComment}{$columnStatus}
%s%s%s%sD%s%s%s%s%s%sD
'; diff --git a/public/userdetails.php b/public/userdetails.php index 47a92e6b..51a653d5 100644 --- a/public/userdetails.php +++ b/public/userdetails.php @@ -201,14 +201,16 @@ if (get_user_class() >= $userprofile_class) { tr_small($lang_userdetails['row_ip_history'], $lang_userdetails['text_user_earlier_used']."
" . $iphistory. $lang_userdetails['text_different_ips'].add_s($iphistory, true)."", 1); } +$seedBoxRep = new \App\Repositories\SeedBoxRepository(); if (get_user_class() >= $userprofile_class || $user["id"] == $CURUSER["id"]) { + $seedBoxIcon = $seedBoxRep->renderIcon($CURUSER['ip'], $CURUSER['id']); if ($enablelocation_tweak == 'yes'){ list($loc_pub, $loc_mod) = get_ip_location($user['ip']); $locationinfo = "[" . $loc_pub . "]"; } else $locationinfo = ""; - tr_small($lang_userdetails['row_ip_address'], $user['ip'].$locationinfo, 1); + tr_small($lang_userdetails['row_ip_address'], $user['ip'].$locationinfo.$seedBoxIcon, 1); } $clientselect = ''; $res = sql_query("SELECT peer_id, agent, ipv4, ipv6, port FROM peers WHERE userid = {$user['id']} GROUP BY agent") or sqlerr(); @@ -220,7 +222,7 @@ if (mysql_num_rows($res) > 0) $clientselect .= ""; $clientselect .= sprintf('%s', get_agent($arr['peer_id'], $arr['agent'])); if (get_user_class() >= $userprofile_class || $user["id"] == $CURUSER["id"]) { - $clientselect .= sprintf('%s%s%s', $arr['ipv4'], $arr['ipv6'], $arr['port']); + $clientselect .= sprintf('%s%s%s', $arr['ipv4'].$seedBoxRep->renderIcon($arr['ipv4'], $CURUSER['id']), $arr['ipv6'].$seedBoxRep->renderIcon($arr['ipv6'], $CURUSER['id']), $arr['port']); } else { $clientselect .= sprintf('%s%s%s', '---', '---', '---'); } diff --git a/public/viewpeerlist.php b/public/viewpeerlist.php index c698c6d9..9551aa03 100644 --- a/public/viewpeerlist.php +++ b/public/viewpeerlist.php @@ -10,13 +10,15 @@ header("Pragma: no-cache" ); header("Content-Type: text/xml; charset=utf-8"); $id = intval($_GET['id'] ?? 0); +$seedBoxRep = new \App\Repositories\SeedBoxRepository(); if(isset($CURUSER)) { function dltable($name, $arr, $torrent) { global $lang_viewpeerlist,$viewanonymous_class,$userprofile_class,$enablelocation_tweak; global $CURUSER; - global $lang_functions; + global $lang_functions, $seedBoxRep; + $s = "" . count($arr) . " $name\n"; if (!count($arr)) return $s; @@ -56,8 +58,22 @@ function dltable($name, $arr, $torrent) $secs = max(1, ($e["la"] - $e["st"])); if ($enablelocation_tweak == 'yes'){ - list($loc_pub, $loc_mod) = get_ip_location(sprintf('%s,%s', $e['ipv4'], $e['ipv6'])); - $location = get_user_class() >= $userprofile_class ? "
" . $loc_pub . "
" : $loc_pub; + $address = $ips = []; + if (!empty($e['ipv4'])) { + list($loc_pub, $loc_mod) = get_ip_location($e['ipv4']); + $seedBoxIcon = $seedBoxRep->renderIcon($e['ipv4'], $e['userid']); + $address[] = $loc_pub . $seedBoxIcon; + $ips[] = $e['ipv4']; + } + if (!empty($e['ipv6'])) { + list($loc_pub, $loc_mod) = get_ip_location($e['ipv6']); + $seedBoxIcon = $seedBoxRep->renderIcon($e['ipv6'], $e['userid']); + $address[] = $loc_pub . $seedBoxIcon; + $ips[] = $e['ipv6']; + } + $title = sprintf('%s%s%s', $lang_functions['text_user_ip'], ': ', implode(', ', $ips)); + $addressStr = implode(' + ', $address); + $location = get_user_class() >= $userprofile_class ? "
" . $addressStr . "
" : $addressStr; $s .= "" . $location . "\n"; } elseif (get_user_class() >= $userprofile_class){ diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php index 694b0349..1f9cb00d 100644 --- a/resources/lang/en/admin.php +++ b/resources/lang/en/admin.php @@ -17,7 +17,7 @@ return [ 'torrent_state' => 'Free leach', 'roles_list' => 'Roles', 'ability_list' => 'Permissions', - 'seedbox_records' => 'SeedBox', + 'seed_box_records' => 'SeedBox', ], 'resources' => [ 'agent_allow' => [ @@ -76,5 +76,8 @@ return [ 'bulk_action_attach_tag' => 'Attach tag', 'action_approval' => 'Approval', ], + 'seed_box_record' => [ + 'toggle_status' => 'Change status', + ], ] ]; diff --git a/resources/lang/en/label.php b/resources/lang/en/label.php index e5ef696f..8b0b7462 100644 --- a/resources/lang/en/label.php +++ b/resources/lang/en/label.php @@ -172,7 +172,7 @@ return [ 'role' => [ 'class' => 'Relate user class', ], - 'seedbox_record' => [ + 'seed_box_record' => [ 'label' => 'SeedBox Records', 'type' => 'Add type', 'operator' => 'Operator', @@ -181,5 +181,6 @@ return [ 'ip_begin' => 'Begin IP', 'ip_end' => 'End IP', 'ip_help' => 'Begin IP/End IP, IP(Block) Choose one', + 'status' => 'Status', ], ]; diff --git a/resources/lang/en/seed-box.php b/resources/lang/en/seed-box.php new file mode 100644 index 00000000..5a3556f6 --- /dev/null +++ b/resources/lang/en/seed-box.php @@ -0,0 +1,17 @@ + [ + \App\Models\SeedBoxRecord::TYPE_USER => 'User', + \App\Models\SeedBoxRecord::TYPE_ADMIN => 'Administrator', + ], + 'status_text' => [ + \App\Models\SeedBoxRecord::STATUS_UNAUDITED => 'Unaudited', + \App\Models\SeedBoxRecord::STATUS_ALLOWED => 'Allowed', + \App\Models\SeedBoxRecord::STATUS_DENIED => 'Denied', + ], + 'status_change_message' => [ + 'subject' => 'SeedBox record status changed', + 'body' => 'The status of your SeedBox record with ID :id was changed by :operator from :old_status to :new_status', + ], +]; diff --git a/resources/lang/zh_CN/admin.php b/resources/lang/zh_CN/admin.php index 3e3ca36a..d39e870a 100644 --- a/resources/lang/zh_CN/admin.php +++ b/resources/lang/zh_CN/admin.php @@ -17,7 +17,7 @@ return [ 'torrent_state' => '全站优惠', 'roles_list' => '角色', 'ability_list' => '权限', - 'seedbox_records' => 'SeedBox', + 'seed_box_records' => 'SeedBox', ], 'resources' => [ 'agent_allow' => [ @@ -76,5 +76,8 @@ return [ 'bulk_action_attach_tag' => '设置标签', 'action_approval' => '审核', ], + 'seed_box_record' => [ + 'toggle_status' => '更改状态', + ], ] ]; diff --git a/resources/lang/zh_CN/label.php b/resources/lang/zh_CN/label.php index 791e5d6c..aaca47c8 100644 --- a/resources/lang/zh_CN/label.php +++ b/resources/lang/zh_CN/label.php @@ -58,7 +58,17 @@ return [ 'ignore_when_ratio_reach_help' => '达标的最小分享率', 'ban_user_when_counts_reach' => 'H&R 数量上限', 'ban_user_when_counts_reach_help' => 'H&R 数量达到此值,账号会被禁用', - ] + ], + 'seed_box' => [ + 'tab_header' => 'SeedBox', + 'enabled_help' => '是否启用 SeedBox 规则', + 'no_promotion' => '无优惠', + 'no_promotion_help' => '不享受任何优惠,上传量/下载量按实际值计算', + 'max_uploaded' => '最大上传量倍数', + 'max_uploaded_help' => '总上传量最多为其体积的多少倍', + 'not_seed_box_max_speed' => '非 SeedBox 最高限速', + 'not_seed_box_max_speed_help' => '单位:Mbps。若超过此值又不能匹配 SeedBox 记录,禁用下载权限', + ], ], 'user' => [ 'label' => '用户', @@ -175,7 +185,7 @@ return [ 'name' => '标识', 'title' => '名称', ], - 'seedbox_record' => [ + 'seed_box_record' => [ 'label' => 'SeedBox 记录', 'type' => '添加类型', 'operator' => '运营商', @@ -184,5 +194,6 @@ return [ 'ip_begin' => '起始 IP', 'ip_end' => '结束 IP', 'ip_help' => '起始 IP/结束 IP、IP(段) 二选一', + 'status' => '状态', ], ]; diff --git a/resources/lang/zh_CN/seed-box.php b/resources/lang/zh_CN/seed-box.php new file mode 100644 index 00000000..dc4d88a0 --- /dev/null +++ b/resources/lang/zh_CN/seed-box.php @@ -0,0 +1,17 @@ + [ + \App\Models\SeedBoxRecord::TYPE_USER => '用户', + \App\Models\SeedBoxRecord::TYPE_ADMIN => '管理员', + ], + 'status_text' => [ + \App\Models\SeedBoxRecord::STATUS_UNAUDITED => '未审核', + \App\Models\SeedBoxRecord::STATUS_ALLOWED => '已通过', + \App\Models\SeedBoxRecord::STATUS_DENIED => '已拒绝', + ], + 'status_change_message' => [ + 'subject' => 'SeedBox 记录状态变更', + 'body' => '你的 ID 为 :id 的 SeedBox 记录状态被 :operator 由 :old_status 变更为 :new_status', + ], +]; diff --git a/resources/lang/zh_CN/seedbox.php b/resources/lang/zh_CN/seedbox.php deleted file mode 100644 index bd53d66d..00000000 --- a/resources/lang/zh_CN/seedbox.php +++ /dev/null @@ -1,8 +0,0 @@ - [ - \App\Models\SeedBoxRecord::TYPE_USER => '用户', - \App\Models\SeedBoxRecord::TYPE_ADMIN => '管理员', - ], -]; diff --git a/resources/lang/zh_TW/admin.php b/resources/lang/zh_TW/admin.php index daf9e074..c2916da1 100644 --- a/resources/lang/zh_TW/admin.php +++ b/resources/lang/zh_TW/admin.php @@ -17,7 +17,7 @@ return [ 'torrent_state' => '全站優惠', 'roles_list' => '角色', 'ability_list' => '權限', - 'seedbox_records' => 'SeedBox', + 'seed_box_records' => 'SeedBox', ], 'resources' => [ 'agent_allow' => [ @@ -76,5 +76,8 @@ return [ 'bulk_action_attach_tag' => '設置標簽', 'action_approval' => '審核', ], + 'seed_box_record' => [ + 'toggle_status' => '更改狀態', + ], ] ]; diff --git a/resources/lang/zh_TW/label.php b/resources/lang/zh_TW/label.php index fa34b73f..d0c324a0 100644 --- a/resources/lang/zh_TW/label.php +++ b/resources/lang/zh_TW/label.php @@ -172,7 +172,7 @@ return [ 'role' => [ 'class' => '關聯用户等級', ], - 'seedbox_record' => [ + 'seed_box_record' => [ 'label' => 'SeedBox 記錄', 'type' => '添加類型', 'operator' => '運營商', @@ -181,5 +181,6 @@ return [ 'ip_begin' => '起始 IP', 'ip_end' => '結束 IP', 'ip_help' => '起始 IP/結束 IP、IP(段) 二選一', + 'status' => '狀態', ], ]; diff --git a/resources/lang/zh_TW/seed-box.php b/resources/lang/zh_TW/seed-box.php new file mode 100644 index 00000000..01537b35 --- /dev/null +++ b/resources/lang/zh_TW/seed-box.php @@ -0,0 +1,17 @@ + [ + \App\Models\SeedBoxRecord::TYPE_USER => '用戶', + \App\Models\SeedBoxRecord::TYPE_ADMIN => '管理員', + ], + 'status_text' => [ + \App\Models\SeedBoxRecord::STATUS_UNAUDITED => '未審核', + \App\Models\SeedBoxRecord::STATUS_ALLOWED => '已通過', + \App\Models\SeedBoxRecord::STATUS_DENIED => '已拒絕', + ], + 'status_change_message' => [ + 'subject' => 'SeedBox 記錄狀態變更', + 'body' => '你的 ID 為 :id 的 SeedBox 記錄狀態被 :operator 由 :old_status 變更為 :new_status', + ], +]; diff --git a/resources/views/filament/resources/system/setting-resource/pages/edit-hit-and-run.blade.php b/resources/views/filament/resources/system/setting-resource/pages/edit-hit-and-run.blade.php index 1ece3ffd..6f2d1c47 100644 --- a/resources/views/filament/resources/system/setting-resource/pages/edit-hit-and-run.blade.php +++ b/resources/views/filament/resources/system/setting-resource/pages/edit-hit-and-run.blade.php @@ -1,7 +1,7 @@
{{ $this->form }} -
+