diff --git a/app/Models/Peer.php b/app/Models/Peer.php index 23629870..aa585799 100644 --- a/app/Models/Peer.php +++ b/app/Models/Peer.php @@ -11,6 +11,7 @@ class Peer extends NexusModel protected $fillable = [ 'torrent', 'peer_id', 'ip', 'port', 'uploaded', 'downloaded', 'to_go', 'seeder', 'started', 'last_action', 'prev_action', 'connectable', 'userid', 'agent', 'finishedat', 'downloadoffset', 'uploadedoffset', 'passkey', + 'ipv4', 'ipv6', ]; const CONNECTABLE_YES = 'yes'; diff --git a/app/Repositories/TrackerRepository.php b/app/Repositories/TrackerRepository.php index ca2d05c8..1230f4f1 100644 --- a/app/Repositories/TrackerRepository.php +++ b/app/Repositories/TrackerRepository.php @@ -68,6 +68,7 @@ class TrackerRepository extends BaseRepository $clientAllow = $this->checkClient($request); $torrent = $this->checkTorrent($queries, $user); $isReAnnounce = $this->isReAnnounce($request); + do_log("[IS_RE_ANNOUNCE] $isReAnnounce"); if ($isReAnnounce < self::ANNOUNCE_DUPLICATE) { /** @var Peer $peerSelf */ $peerSelf = $this->checkMinInterval($torrent, $queries, $user); @@ -99,7 +100,9 @@ class TrackerRepository extends BaseRepository * Note: Must update peer first, otherwise updateTorrent() count peer not correct * Update: Will not change $peerSelf any more */ - $this->updatePeer($peerSelf, $queries); + if ($isReAnnounce == self::ANNOUNCE_FIRST || ($isReAnnounce == self::ANNOUNCE_DUPLICATE && $queries['event'] !== 'stopped')) { + $this->updatePeer($peerSelf, $queries); + } if ($isReAnnounce === self::ANNOUNCE_FIRST) { $withPeers = true; @@ -293,7 +296,25 @@ class TrackerRepository extends BaseRepository } // Part.4 Get User Ip Address - $queries['ip'] = nexus()->getRequestIp(); + $ip = nexus()->getRequestIp(); + $ipv4 = $ipv6 = ''; + $ipv4Temp = $request->query->get('ipv4', ''); + $ipv6Temp = $request->query->get('ipv6', ''); + //use the real ip first, ip from parameter second + if (isIPV4($ip)) { + $ipv4 = $ip; + } elseif (isIPV4($ipv4Temp)) { + $ipv4 = $ipv4Temp; + } + if (isIPV6($ip)) { + $ipv6 = $ip; + } elseif (isIPV6($ipv6Temp)) { + $ipv6 = $ipv6Temp; + } + + $queries['ip'] = $ip; + $queries['ipv4'] = $ipv4; + $queries['ipv6'] = $ipv6; // Part.5 Get Users Agent $queries['user_agent'] = $request->headers->get('user-agent'); @@ -445,8 +466,6 @@ class TrackerRepository extends BaseRepository $peer = Peer::query() ->where('torrent', $torrent->id) ->where('peer_id', $queries['peer_id']) - ->groupBy('peer_id') - ->selectRaw("*, group_concat(id order by id) as ids, group_concat(ip order by id) as ips") ->first(); if ($peer) { @@ -596,7 +615,7 @@ class TrackerRepository extends BaseRepository if (\strtolower($queries['event']) !== 'stopped' && $withPeers) { $limit = ($queries['numwant'] <= self::MAX_PEER_NUM_WANT ? $queries['numwant'] : self::MAX_PEER_NUM_WANT); $baseQuery = Peer::query() - ->select(['peer_id', 'ip', 'port']) + ->select(['peer_id', 'ip', 'port', 'ipv4', 'ipv6']) ->where('torrent', $torrent->id) ->where('userid', '!=', $user->id) ->limit($limit) @@ -689,17 +708,26 @@ class TrackerRepository extends BaseRepository return $result; } - private function givePeers($peers, $compact, $noPeerId, int $filterFlag = FILTER_FLAG_IPV4): string|array + private function givePeers($originalPeers, $compact, $noPeerId, int $filterFlag = FILTER_FLAG_IPV4): string|array { + $peers = []; + foreach ($originalPeers as $value) { + $ipKey = $filterFlag == FILTER_FLAG_IPV4 ? 'ipv4' : 'ipv6'; + if (!empty($value[$ipKey]) && filter_var($value[$ipKey], FILTER_VALIDATE_IP, $filterFlag)) { + $peers[] = [ + 'peer_id' => $value['peer_id'], + 'ip' => $value[$ipKey], + 'port' => $value['port'] + ]; + } + } + if ($compact) { $pcomp = ''; foreach ($peers as $p) { - if (isset($p['ip'], $p['port']) && \filter_var($p['ip'], FILTER_VALIDATE_IP, $filterFlag)) { - $pcomp .= \inet_pton($p['ip']); - $pcomp .= \pack('n', (int) $p['port']); - } + $pcomp .= \inet_pton($p['ip']); + $pcomp .= \pack('n', (int) $p['port']); } - return $pcomp; } @@ -745,27 +773,15 @@ class TrackerRepository extends BaseRepository do_log("no event, return", 'debug'); return; } - $seederResult = Peer::query() - ->where('torrent', $torrent->id) - ->where('to_go', '=',0) - ->selectRaw("count(distinct(peer_id)) as counts") - ->first(); - $torrent->seeders = $seederResult ? $seederResult->counts : 0; - - $leecherResult = Peer::query() - ->where('torrent', $torrent->id) - ->where('to_go', '>', 0) - ->selectRaw("count(distinct(peer_id)) as counts") - ->first(); - $torrent->leechers = $leecherResult ? $leecherResult->counts : 0; - + $torrentId = $torrent->id; + $torrent->seeders = count_peer("torrent = $torrentId and to_go = 0"); + $torrent->leechers = count_peer("torrent = $torrentId and to_go > 0"); $torrent->visible = Torrent::VISIBLE_YES; $torrent->last_action = Carbon::now(); if ($isPeerExists && $queries['event'] == 'completed') { $torrent->times_completed = DB::raw("times_completed + 1"); } - $torrent->save(); do_log(last_query()); } @@ -790,6 +806,12 @@ class TrackerRepository extends BaseRepository 'agent' => $queries['user_agent'], 'connectable' => $this->getConnectable($queries['ip'], $queries['port'], $queries['user_agent']) ]; + if (!empty($queries['ipv4'])) { + $update['ipv4'] = $queries['ipv4']; + } + if (!empty($queries['ipv6'])) { + $update['ipv6'] = $queries['ipv6']; + } if ($peer->exists) { $update['prev_action'] = $peer->last_action; @@ -809,36 +831,47 @@ class TrackerRepository extends BaseRepository $update['uploaded'] = $queries['uploaded']; $update['downloaded'] = $queries['downloaded']; - $idArr = explode(',', $peer->ids); - $ipArr = explode(',', $peer->ips); - $logPrefix = "update: " . json_encode($update); - $doUpdate = false; + $logData = json_encode(Arr::except($update, ['peer_id'])); if ($peer->exists) { - $logPrefix .= ", [EXISTS]"; - foreach ($idArr as $key => $id) { - $ip = $ipArr[$key]; - if (isIPV4($ip) && isIPV4($queries['ip'])) { - $update['ip'] = DB::raw("if(id = $id,'$ip', ip)"); - $doUpdate = true; - $logPrefix .= ", v4, id = $id"; - } elseif (isIPV6($ip) && isIPV6($queries['ip'])) { - $update['ip'] = DB::raw("if(id = $id,'$ip', ip)"); - $doUpdate = true; - $logPrefix .= ", v6, id = $id"; - } - } - if ($doUpdate) { - $affected = Peer::query()->where('torrent', $peer->torrent)->where('peer_id', $queries['peer_id'])->update($update); - do_log("$logPrefix, [UPDATE], affected: $affected"); - } else { - Peer::query()->insert($update); - do_log("$logPrefix, [INSERT]"); - } + $affected = Peer::query()->where('torrent', $peer->torrent)->where('peer_id', $queries['peer_id'])->update($update); + do_log("[UPDATE], affected: $affected, data: $logData"); } else { - $logPrefix .= ", [NOT_EXISTS]"; Peer::query()->insert($update); - do_log("$logPrefix, [INSERT]"); + do_log("[INSERT], data: $logData"); } + + + +// $idArr = explode(',', $peer->ids); +// $ipArr = explode(',', $peer->ips); +// $logPrefix = "update: " . json_encode($update); +// $doUpdate = false; +// if ($peer->exists) { +// $logPrefix .= ", [EXISTS]"; +// foreach ($idArr as $key => $id) { +// $ip = $ipArr[$key]; +// if (isIPV4($ip) && isIPV4($queries['ip'])) { +// $update['ip'] = DB::raw("if(id = $id,'$ip', ip)"); +// $doUpdate = true; +// $logPrefix .= ", v4, id = $id"; +// } elseif (isIPV6($ip) && isIPV6($queries['ip'])) { +// $update['ip'] = DB::raw("if(id = $id,'$ip', ip)"); +// $doUpdate = true; +// $logPrefix .= ", v6, id = $id"; +// } +// } +// if ($doUpdate) { +// $affected = Peer::query()->where('torrent', $peer->torrent)->where('peer_id', $queries['peer_id'])->update($update); +// do_log("$logPrefix, [UPDATE], affected: $affected"); +// } else { +// Peer::query()->insert($update); +// do_log("$logPrefix, [INSERT]"); +// } +// } else { +// $logPrefix .= ", [NOT_EXISTS]"; +// Peer::query()->insert($update); +// do_log("$logPrefix, [INSERT]"); +// } } private function getConnectable($ip, $port, $agent) @@ -913,7 +946,7 @@ class TrackerRepository extends BaseRepository $snatch->last_action = $nowStr; $snatch->save(); - do_log(last_query()); + do_log(last_query(), 'alert'); return $snatch; } @@ -1035,11 +1068,16 @@ class TrackerRepository extends BaseRepository private function updateUser(User $user) { + $log = "update: " . json_encode($this->userUpdates); if (count($this->userUpdates) === 0) { - return; + $log .= ", no update..."; + } else { + $user->fill($this->userUpdates); + $log .= ", dirty: " . json_encode($user->getDirty()); + $user->save(); + $log .= ", query: " . last_query(); } - $user->save($this->userUpdates); - do_log(last_query()); + do_log($log, 'alert'); } } diff --git a/classes/class_cache_redis.php b/classes/class_cache_redis.php index 7f93c4e5..87120ef1 100644 --- a/classes/class_cache_redis.php +++ b/classes/class_cache_redis.php @@ -19,21 +19,12 @@ class class_cache_redis { public $redis; function __construct() { - $success = $this->connect(); // Connect to Redis - if ($success) { - $this->isEnabled = 1; - } else { - do_log("Redis is disabled!"); - $this->isEnabled = 0; - } + $this->connect(); // Connect to Redis } private function connect() { $config = nexus_config('nexus.redis'); - if (empty($config['host'])) { - return false; - } $redis = new Redis(); $params = [ $config['host'], @@ -45,21 +36,17 @@ class class_cache_redis { $params[] = $config['timeout']; } $connectResult = $redis->connect(...$params); - $auth = []; if (!empty($config['password'])) { - $auth['pass'] = $config['password']; - if (!empty($config['username'])) { - $auth['user'] = $config['username']; - } - $connectResult = $connectResult && $redis->auth($auth); + $connectResult = $connectResult && $redis->auth($config['password']); } if ($connectResult) { $this->redis = $redis; if (is_numeric($config['database'])) { $redis->select($config['database']); } + } else { + throw new \RuntimeException("Redis connect fail."); } - return $connectResult; } function getIsEnabled() { diff --git a/config/nexus.php b/config/nexus.php index 0f0a97d1..a62a3ff2 100644 --- a/config/nexus.php +++ b/config/nexus.php @@ -20,6 +20,7 @@ return [ 'host' => nexus_env('REDIS_HOST', '127.0.0.1'), 'port' => nexus_env('REDIS_PORT', 6379), 'database' => nexus_env('REDIS_DB', 0), + 'password' => nexus_env('REDIS_PASSWORD'), ], 'elasticsearch' => [ diff --git a/database/migrations/2021_06_08_113437_create_peers_table.php b/database/migrations/2021_06_08_113437_create_peers_table.php index 135528e9..102c320d 100644 --- a/database/migrations/2021_06_08_113437_create_peers_table.php +++ b/database/migrations/2021_06_08_113437_create_peers_table.php @@ -36,7 +36,7 @@ class CreatePeersTable extends Migration $table->unsignedBigInteger('downloadoffset')->default(0); $table->unsignedBigInteger('uploadoffset')->default(0); $table->char('passkey', 32)->default(''); - $table->unique(['torrent', 'peer_id', 'ip']); + $table->unique(['torrent', 'peer_id']); }); } diff --git a/database/migrations/2022_04_18_030257_handle_peers_peer_id_unique.php b/database/migrations/2022_04_18_030257_handle_peers_peer_id_unique.php index b022de84..036f0ee8 100644 --- a/database/migrations/2022_04_18_030257_handle_peers_peer_id_unique.php +++ b/database/migrations/2022_04_18_030257_handle_peers_peer_id_unique.php @@ -28,7 +28,7 @@ return new class extends Migration } Schema::table($tableName, function (Blueprint $table) { - $table->unique(['torrent', 'peer_id', 'ip']); + $table->unique(['torrent', 'peer_id']); $table->index('peer_id'); }); } diff --git a/database/migrations/2022_04_20_195415_add_ipv6_to_peers_table.php b/database/migrations/2022_04_20_195415_add_ipv6_to_peers_table.php new file mode 100644 index 00000000..f42679b2 --- /dev/null +++ b/database/migrations/2022_04_20_195415_add_ipv6_to_peers_table.php @@ -0,0 +1,37 @@ +string('ipv4', 64)->default(''); + } + if (!Schema::hasColumn('peers', 'ipv6')) { + $table->string('ipv6', 64)->default(''); + } + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('peers', function (Blueprint $table) { + $table->dropColumn(['ipv4', 'ipv6']); + }); + } +}; diff --git a/include/functions.php b/include/functions.php index be9b623d..9538a05a 100644 --- a/include/functions.php +++ b/include/functions.php @@ -470,10 +470,14 @@ function get_user_class_name($class, $compact = false, $b_colored = false, $I18N global $SITENAME; static $en_lang_functions; static $current_user_lang_functions; + static $settingAccount; if (!$en_lang_functions) { require(get_langfile_path("functions.php",false,"en")); $en_lang_functions = $lang_functions; } + if (!$settingAccount) { + $settingAccount = get_setting('account'); + } if(!$I18N) { $this_lang_functions = $en_lang_functions; @@ -506,6 +510,12 @@ function get_user_class_name($class, $compact = false, $b_colored = false, $I18N case UC_SYSOP: {$class_name = $this_lang_functions['text_sysops']; break;} case UC_STAFFLEADER: {$class_name = $this_lang_functions['text_staff_leader']; break;} } + if ($class < UC_VIP) { + $alias = trim($settingAccount["{$class}_alias"]); + if (!empty($alias)) { + $class_name = sprintf('%s(%s)', $class_name, $alias); + } + } switch ($class) { @@ -5393,6 +5403,9 @@ function get_ip_location_from_geoip($ip) $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, diff --git a/lang/chs/lang_settings.php b/lang/chs/lang_settings.php index 891eb437..82f3c8e3 100644 --- a/lang/chs/lang_settings.php +++ b/lang/chs/lang_settings.php @@ -739,6 +739,9 @@ $lang_settings = array 'row_site_language_enabled' => '站点启用语言', 'text_site_language_enabled_note' => '选择站点启用的语言', 'keep_at_least_one' => '至少保留一个', + 'text_alias' => '等级别名:', + 'row_default_user_one' => '默认为', + 'row_default_user_two' => '', ); ?> diff --git a/public/settings.php b/public/settings.php index 3b3b6b07..5ed4a829 100644 --- a/public/settings.php +++ b/public/settings.php @@ -119,15 +119,16 @@ elseif ($action == 'savesettings_account') // save account $validConfig = array( 'neverdelete', 'neverdeletepacked', 'deletepacked', 'deleteunpacked', 'deletenotransfer', 'deletenotransfertwo', 'deletepeasant', - 'psdlone', 'psratioone', 'psdltwo', 'psratiotwo', 'psdlthree', 'psratiothree', 'psdlfour', 'psratiofour', 'psdlfive', 'psratiofive', - 'putime', 'pudl', \App\Models\User::CLASS_POWER_USER . '_min_seed_points', 'puprratio', 'puderatio', - 'eutime', 'eudl', \App\Models\User::CLASS_ELITE_USER . '_min_seed_points', 'euprratio', 'euderatio', - 'cutime', 'cudl', \App\Models\User::CLASS_CRAZY_USER . '_min_seed_points', 'cuprratio', 'cuderatio', - 'iutime', 'iudl', \App\Models\User::CLASS_INSANE_USER . '_min_seed_points', 'iuprratio', 'iuderatio', - 'vutime', 'vudl', \App\Models\User::CLASS_VETERAN_USER . '_min_seed_points', 'vuprratio', 'vuderatio', - 'exutime', 'exudl', \App\Models\User::CLASS_EXTREME_USER . '_min_seed_points', 'exuprratio', 'exuderatio', - 'uutime', 'uudl', \App\Models\User::CLASS_ULTIMATE_USER . '_min_seed_points', 'uuprratio', 'uuderatio', - 'nmtime', 'nmdl', \App\Models\User::CLASS_NEXUS_MASTER . '_min_seed_points', 'nmprratio', 'nmderatio', + 'psdlone', 'psratioone', 'psdltwo', 'psratiotwo', 'psdlthree', 'psratiothree', 'psdlfour', 'psratiofour', 'psdlfive', 'psratiofive', \App\Models\User::CLASS_PEASANT . '_alias', + \App\Models\User::CLASS_USER . '_alias', + 'putime', 'pudl', \App\Models\User::CLASS_POWER_USER . '_min_seed_points', 'puprratio', 'puderatio', \App\Models\User::CLASS_POWER_USER . '_alias', + 'eutime', 'eudl', \App\Models\User::CLASS_ELITE_USER . '_min_seed_points', 'euprratio', 'euderatio', \App\Models\User::CLASS_ELITE_USER . '_alias', + 'cutime', 'cudl', \App\Models\User::CLASS_CRAZY_USER . '_min_seed_points', 'cuprratio', 'cuderatio', \App\Models\User::CLASS_CRAZY_USER . '_alias', + 'iutime', 'iudl', \App\Models\User::CLASS_INSANE_USER . '_min_seed_points', 'iuprratio', 'iuderatio', \App\Models\User::CLASS_INSANE_USER . '_alias', + 'vutime', 'vudl', \App\Models\User::CLASS_VETERAN_USER . '_min_seed_points', 'vuprratio', 'vuderatio', \App\Models\User::CLASS_VETERAN_USER . '_alias', + 'exutime', 'exudl', \App\Models\User::CLASS_EXTREME_USER . '_min_seed_points', 'exuprratio', 'exuderatio', \App\Models\User::CLASS_EXTREME_USER . '_alias', + 'uutime', 'uudl', \App\Models\User::CLASS_ULTIMATE_USER . '_min_seed_points', 'uuprratio', 'uuderatio', \App\Models\User::CLASS_ULTIMATE_USER . '_alias', + 'nmtime', 'nmdl', \App\Models\User::CLASS_NEXUS_MASTER . '_min_seed_points', 'nmprratio', 'nmderatio', \App\Models\User::CLASS_NEXUS_MASTER . '_alias', 'getInvitesByPromotion' ); GetVar($validConfig); @@ -138,6 +139,7 @@ elseif ($action == 'savesettings_account') // save account saveSetting('account', $ACCOUNT); $actiontime = date("F j, Y, g:i a"); write_log("Tracker account settings updated by {$CURUSER['username']}. $actiontime",'mod'); + \Nexus\Database\NexusDB::cache_del('stats_classes'); go_back(); } elseif($action == 'savesettings_torrent') // save account @@ -583,14 +585,22 @@ elseif ($action == 'accountsettings'){ tr($lang_settings['row_delete_no_transfer'],$lang_settings['text_delete_transfer_note_one']."".$lang_settings['text_delete_transfer_note_two']."".$lang_settings['text_delete_transfer_note_three'], 1); print("".$lang_settings['text_user_promotion_demotion'].""); tr($lang_settings['row_ban_peasant_one'].get_user_class_name(UC_PEASANT,false,false,true).$lang_settings['row_ban_peasant_two'],get_user_class_name(UC_PEASANT,false,true,true).$lang_settings['text_ban_peasant_note_one']."".$lang_settings['text_ban_peasant_note_two'], 1); - tr($lang_settings['row_demoted_to_peasant_one'].get_user_class_name(UC_PEASANT,false,false,true).$lang_settings['row_demoted_to_peasant_two'],$lang_settings['text_demoted_peasant_note_one'].get_user_class_name(UC_PEASANT,false,true,true).$lang_settings['text_demoted_peasant_note_two']."