From ab1ed60f44125e69daaf91c7ca74737164b3520d Mon Sep 17 00:00:00 2001 From: xiaomlove Date: Fri, 18 Mar 2022 15:44:04 +0800 Subject: [PATCH] finish announce api --- app/Console/Commands/Test.php | 6 +- app/Http/Controllers/TrackerController.php | 84 +----------- app/Models/Peer.php | 5 + app/Models/Snatch.php | 5 + app/Models/User.php | 2 +- app/Providers/RouteServiceProvider.php | 4 + app/Repositories/AgentAllowRepository.php | 6 +- app/Repositories/TrackerRepository.php | 122 ++++++++++++------ ..._17_202628_add_index_to_cheaters_table.php | 33 +++++ nexus/Install/settings.default.php | 1 + routes/api.php | 2 + routes/tracker.php | 4 + routes/web.php | 2 +- 13 files changed, 151 insertions(+), 125 deletions(-) create mode 100644 database/migrations/2022_03_17_202628_add_index_to_cheaters_table.php create mode 100644 routes/tracker.php diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index 05430743..230eefd7 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -65,9 +65,9 @@ class Test extends Command */ public function handle() { - - $peer = Peer::query()->first(); - echo $peer->prev_action->timestamp; + $redis = Redis::connection()->client(); + $r = $redis->get('5da94a358e67cb5181166ae2611c2fd9'); + dd($r); } diff --git a/app/Http/Controllers/TrackerController.php b/app/Http/Controllers/TrackerController.php index c30a13c0..e03948cf 100644 --- a/app/Http/Controllers/TrackerController.php +++ b/app/Http/Controllers/TrackerController.php @@ -2,96 +2,24 @@ namespace App\Http\Controllers; -use App\Http\Resources\AgentDenyResource; -use App\Models\AgentDeny; -use App\Repositories\AgentDenyRepository; +use App\Repositories\TrackerRepository; use Illuminate\Http\Request; -use Illuminate\Http\Resources\Json\JsonResource; -use Illuminate\Validation\Rule; class TrackerController extends Controller { - private $repository; + private TrackerRepository $repository; - public function __construct(AgentDenyRepository $repository) + public function __construct(TrackerRepository $repository) { $this->repository = $repository; } - private function getRules(): array - { - return [ - 'family_id' => 'required|numeric', - 'name' => 'required|string', - 'peer_id' => 'required|string', - 'agent' => 'required|string', - 'comment' => 'required|string', - - ]; - } /** - * Display a listing of the resource. - * - * @return array - */ - public function index(Request $request) - { - $result = $this->repository->getList($request->all()); - $resource = AgentDenyResource::collection($result); - return $this->success($resource); - } - - /** - * Store a newly created resource in storage. - * - * @param \Illuminate\Http\Request $request + * @param Request $request * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function announce(Request $request): \Illuminate\Http\Response { - $request->validate($this->getRules()); - $result = $this->repository->store($request->all()); - $resource = new AgentDenyResource($result); - return $this->success($resource); - } - - /** - * Display the specified resource. - * - * @param int $id - * @return array - */ - public function show($id) - { - $result = AgentDeny::query()->findOrFail($id); - $resource = new AgentDenyResource($result); - return $this->success($resource); - } - - /** - * Update the specified resource in storage. - * - * @param \Illuminate\Http\Request $request - * @param int $id - * @return array - */ - public function update(Request $request, $id) - { - $request->validate($this->getRules()); - $result = $this->repository->update($request->all(), $id); - $resource = new AgentDenyResource($result); - return $this->success($resource); - } - - /** - * Remove the specified resource from storage. - * - * @param int $id - * @return array - */ - public function destroy($id) - { - $result = $this->repository->delete($id); - return $this->success($result); + return $this->repository->announce($request); } } diff --git a/app/Models/Peer.php b/app/Models/Peer.php index 03853454..0ee9b255 100644 --- a/app/Models/Peer.php +++ b/app/Models/Peer.php @@ -89,6 +89,7 @@ class Peer extends NexusModel $tmp_ip = '['.$tmp_ip.']'; } $cacheKey = 'peers:connectable:'.$tmp_ip.'-'.$this->port.'-'.$this->agent; + $log = "cacheKey: $cacheKey"; if (!Cache::has($cacheKey)) { $con = @fsockopen($tmp_ip, $this->port, $error_code, $error_message, 1); if (is_resource($con)) { @@ -98,6 +99,10 @@ class Peer extends NexusModel $this->connectable = self::CONNECTABLE_NO; } Cache::put($cacheKey, $this->connectable, 600); + $log .= ", do check, connectable: " . $this->connectable; + } else { + $log .= ", don't do check"; } + do_log($log); } } diff --git a/app/Models/Snatch.php b/app/Models/Snatch.php index 0e7301d9..417cb32f 100644 --- a/app/Models/Snatch.php +++ b/app/Models/Snatch.php @@ -9,6 +9,11 @@ class Snatch extends NexusModel { protected $table = 'snatched'; + protected $fillable = [ + 'torrentid', 'userid', 'ip', 'port', 'uploaded', 'downloaded', 'to_go', 'seedtime', 'leechtime', + 'last_action', 'startdat', 'completedat', 'finished' + ]; + protected $casts = [ 'last_action' => 'datetime', 'startdat' => 'datetime', diff --git a/app/Models/User.php b/app/Models/User.php index e16212aa..56911b49 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -116,7 +116,7 @@ class User extends Authenticatable */ protected $fillable = [ 'username', 'email', 'passhash', 'secret', 'stylesheet', 'editsecret', 'added', 'modcomment', 'enabled', 'status', - 'leechwarn', 'leechwarnuntil', 'page', 'class' + 'leechwarn', 'leechwarnuntil', 'page', 'class', 'uploaded', 'downloaded', 'clientselect', 'showclienterror', ]; /** diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 3bd3c81e..b9228a8f 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -46,6 +46,10 @@ class RouteServiceProvider extends ServiceProvider Route::middleware('web') ->namespace($this->namespace) ->group(base_path('routes/web.php')); + + Route::prefix('api') + ->namespace($this->namespace) + ->group(base_path('routes/tracker.php')); }); } diff --git a/app/Repositories/AgentAllowRepository.php b/app/Repositories/AgentAllowRepository.php index 9af45d38..63d865f9 100644 --- a/app/Repositories/AgentAllowRepository.php +++ b/app/Repositories/AgentAllowRepository.php @@ -56,9 +56,9 @@ class AgentAllowRepository extends BaseRepository } $matchCount = count($matches) - 1; //due to old data may be matchNum > matchCount - if ($matchNum > $matchCount && !IN_NEXUS) { - throw new ClientNotAllowedException("pattern: $pattern match start: $start got matches count: $matchCount, but require $matchNum."); - } +// if ($matchNum > $matchCount && !IN_NEXUS) { +// throw new ClientNotAllowedException("pattern: $pattern match start: $start got matches count: $matchCount, but require $matchNum."); +// } return array_slice($matches, 1, $matchNum); } diff --git a/app/Repositories/TrackerRepository.php b/app/Repositories/TrackerRepository.php index 162bfa12..1671859f 100644 --- a/app/Repositories/TrackerRepository.php +++ b/app/Repositories/TrackerRepository.php @@ -12,9 +12,9 @@ use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use App\Exceptions\TrackerException; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Redis; -use Nexus\Database\NexusDB; use Rhilip\Bencode\Bencode; class TrackerRepository extends BaseRepository @@ -26,8 +26,6 @@ class TrackerRepository extends BaseRepository const MUST_BE_CHEATER_SPEED = 1024 * 1024 * 1024; //1024 MB/s const MAY_BE_CHEATER_SPEED = 1024 * 1024 * 100; //100 MB/s - private array $userUpdates = []; - // Port Blacklist protected const BLACK_PORTS = [ 22, // SSH Port @@ -42,13 +40,17 @@ class TrackerRepository extends BaseRepository 6699, // Port used by p2p software, such as WinMX, Napster. ]; + private array $userUpdates = []; + public function announce(Request $request): \Illuminate\Http\Response { + do_log(nexus_json_encode($_SERVER)); try { $withPeers = false; $queries = $this->checkAnnounceFields($request); - $clientAllow = $this->checkClient($request); + do_log("[QUERIES] " . json_encode(Arr::only($queries, ['ip', 'user_agent', 'uploaded', 'downloaded', 'left', 'event'])), 'debug'); $user = $this->checkUser($request); + $clientAllow = $this->checkClient($request); $torrent = $this->checkTorrent($queries, $user); if ($this->isReAnnounce($queries) === false) { $withPeers = true; @@ -65,14 +67,17 @@ class TrackerRepository extends BaseRepository } else { $this->checkCheater($torrent, $queries, $user, $peerSelf); } + /** + * Note: Must get before update peer! + */ + $dataTraffic = $this->getDataTraffic($torrent, $queries, $user, $peerSelf); $this->updatePeer($peerSelf, $queries); - $this->updateSnatch($peerSelf, $queries); + $this->updateSnatch($peerSelf, $queries, $dataTraffic); $this->updateTorrent($torrent, $queries); - $dataTraffic = $this->getRealUploadedDownloaded($torrent, $queries, $user, $peerSelf); - $this->userUpdates['uploaded'] = DB::raw('uploaded + ' . $dataTraffic['uploaded']); - $this->userUpdates['downloaded'] = DB::raw('downloaded + ' . $dataTraffic['downloaded']); + $this->userUpdates['uploaded'] = DB::raw('uploaded + ' . $dataTraffic['uploaded_increment_for_user']); + $this->userUpdates['downloaded'] = DB::raw('downloaded + ' . $dataTraffic['downloaded_increment_for_user']); $this->userUpdates['clientselect'] = $clientAllow->id; $this->userUpdates['showclienterror'] = 'no'; } @@ -80,13 +85,17 @@ class TrackerRepository extends BaseRepository } catch (ClientNotAllowedException $exception) { do_log("[ClientNotAllowedException] " . $exception->getMessage()); $this->userUpdates['showclienterror'] = 'yes'; - $repDict = $this->generateFailedAnnounceResponse($exception); + $repDict = $this->generateFailedAnnounceResponse($exception->getMessage()); } catch (TrackerException $exception) { - do_log("[TrackerException] " . $exception->getMessage()); - $repDict = $this->generateFailedAnnounceResponse($exception); + $repDict = $this->generateFailedAnnounceResponse($exception->getMessage()); + } catch (\Exception $exception) { + //other system exception + do_log("[" . get_class($exception) . "] " . $exception->getMessage() . "\n" . $exception->getTraceAsString(), 'error'); + $repDict = $this->generateFailedAnnounceResponse("system error, report to sysop please, hint: " . REQUEST_ID); } finally { - if (isset($user)) { + if (isset($user) && count($this->userUpdates)) { $user->update($this->userUpdates); + do_log(last_query(), 'debug'); } return $this->sendFinalAnnounceResponse($repDict); } @@ -132,7 +141,8 @@ class TrackerRepository extends BaseRepository } $agentAllowRep = new AgentAllowRepository(); - $agentAllowRep->checkClient($request->peer_id, $userAgent); + + return $agentAllowRep->checkClient($request->peer_id, $userAgent, config('app.debug')); } @@ -235,11 +245,11 @@ class TrackerRepository extends BaseRepository // Part.5 Get Users Agent $queries['user_agent'] = $request->headers->get('user-agent'); - // Part.6 bin2hex info_hash - $queries['info_hash'] = \bin2hex($queries['info_hash']); + // Part.6 info_hash, binary + $queries['info_hash'] = $queries['info_hash']; - // Part.7 bin2hex peer_id - $queries['peer_id'] = \bin2hex($queries['peer_id']); + // Part.7 + $queries['peer_id'] = $queries['peer_id']; return $queries; } @@ -247,9 +257,9 @@ class TrackerRepository extends BaseRepository protected function checkUser(Request $request) { if ($authkey = $request->query->get('authkey')) { - list($torrentId, $uid) = $this->checkAuthkey($authkey); + $checkResult = $this->checkAuthkey($authkey); $field = 'id'; - $value = $uid; + $value = $checkResult['uid']; } elseif ($passkey = $request->query->get('passkey')) { $this->checkPasskey($passkey); $field = 'passkey'; @@ -370,10 +380,14 @@ class TrackerRepository extends BaseRepository $peer = Peer::query() ->where('torrent', $torrent->id) ->where('peer_id', $queries['peer_id']) - ->where('userid', $user->id) ->first(); - if ($peer && $peer->isValidDate('prev_action') && Carbon::now()->diffInSeconds($peer->prev_action) < self::MIN_ANNOUNCE_WAIT_SECOND) { + if ( + $peer + && $queries['event'] == '' + && $peer->isValidDate('prev_action') + && Carbon::now()->diffInSeconds($peer->prev_action) < self::MIN_ANNOUNCE_WAIT_SECOND + ) { throw new TrackerException('There is a minimum announce time of ' . self::MIN_ANNOUNCE_WAIT_SECOND . ' seconds'); } return $peer; @@ -484,6 +498,7 @@ class TrackerRepository extends BaseRepository 'peers' => [], 'peers6' => [], ]; + do_log("[REP_DICT_BASE] " . json_encode($repDict)); if (!$withPeers) { return $repDict; } @@ -509,7 +524,7 @@ class TrackerRepository extends BaseRepository } else { $peers = $baseQuery->get()->toArray(); } - + do_log("[REP_DICT_PEER_QUERY] " . last_query()); $repDict['peers'] = $this->givePeers($peers, $queries['compact'], $queries['no_peer_id']); $repDict['peers6'] = $this->givePeers($peers, $queries['compact'], $queries['no_peer_id'], FILTER_FLAG_IPV6); } @@ -544,28 +559,42 @@ class TrackerRepository extends BaseRepository return $real_annnounce_interval; } - private function getRealUploadedDownloaded(Torrent $torrent, $queries, User $user, Peer $peer): array + private function getDataTraffic(Torrent $torrent, $queries, User $user, Peer $peer): array { + $log = sprintf( + "torrent: %s, user: %s, peer: %s, queriesUploaded: %s, queriesDownloaded: %s", + $torrent->id, $user->id, $peer->id, $queries['uploaded'], $queries['downloaded'] + ); if ($peer->exists) { $realUploaded = max($queries['uploaded'] - $peer->uploaded, 0); $realDownloaded = max($queries['downloaded'] - $peer->downloaded, 0); + $log .= ", [PEER_EXISTS], realUploaded: $realUploaded, realDownloaded: $realDownloaded"; } else { $realUploaded = $queries['uploaded']; $realDownloaded = $queries['downloaded']; + $log .= ", [PEER_NOT_EXISTS],, realUploaded: $realUploaded, realDownloaded: $realDownloaded"; } $spStateReal = $torrent->spStateReal; $uploaderRatio = Setting::get('torrent.uploaderdouble'); + $log .= ", spStateReal: $spStateReal, uploaderRatio: $uploaderRatio"; if ($torrent->owner == $user->id) { //uploader, use the bigger one $upRatio = max($uploaderRatio, Torrent::$promotionTypes[$spStateReal]['up_multiplier']); + $log .= ", [IS_UPLOADER], upRatio: $upRatio"; } else { $upRatio = Torrent::$promotionTypes[$spStateReal]['up_multiplier']; + $log .= ", [IS_NOT_UPLOADER], upRatio: $upRatio"; } $downRatio = Torrent::$promotionTypes[$spStateReal]['down_multiplier']; - return [ - 'uploaded' => $realUploaded * $upRatio, - 'downloaded' => $realDownloaded * $downRatio + $log .= ", downRatio: $downRatio"; + $result = [ + 'uploaded_increment' => $realUploaded, + 'uploaded_increment_for_user' => $realUploaded * $upRatio, + 'downloaded_increment' => $realDownloaded, + 'downloaded_increment_for_user' => $realDownloaded * $downRatio, ]; + do_log("$log, result: " . json_encode($result)); + return $result; } private function givePeers($peers, $compact, $noPeerId, int $filterFlag = FILTER_FLAG_IPV4): string|array @@ -593,10 +622,10 @@ class TrackerRepository extends BaseRepository return $peers; } - protected function generateFailedAnnounceResponse(\Exception $exception): array + protected function generateFailedAnnounceResponse($reason): array { return [ - 'failure reason' => $exception->getMessage(), + 'failure reason' => $reason, 'min interval' => self::MIN_ANNOUNCE_WAIT_SECOND, //'retry in' => self::MIN_ANNOUNCE_WAIT_SECOND ]; @@ -631,12 +660,15 @@ class TrackerRepository extends BaseRepository } $torrent->save(); + do_log(last_query(), 'debug'); } private function updatePeer(Peer $peer, $queries) { if ($queries['event'] == 'stopped') { - return $peer->delete(); + $peer->delete(); + do_log(last_query(), 'debug'); + return; } $nowStr = Carbon::now()->toDateTimeString(); @@ -648,23 +680,34 @@ class TrackerRepository extends BaseRepository $peer->to_go = $queries['left']; $peer->seeder = $queries['left'] == 0 ? 'yes' : 'no'; - $peer->prev_action = DB::raw('last_action'); $peer->last_action = $nowStr; $peer->uploaded = $queries['uploaded']; $peer->downloaded = $queries['downloaded']; + if ($peer->exists) { + $peer->prev_action = $peer->last_action; + } + if ($queries['event'] == 'started') { $peer->started = $nowStr; $peer->uploadoffset = $queries['uploaded']; $peer->downloadoffset = $queries['downloaded']; } elseif ($queries['event'] == 'completed') { - $peer->finishat = time(); + $peer->finishedat = time(); } $peer->save(); + do_log(last_query(), 'debug'); } - private function updateSnatch(Peer $peer, $queries) + /** + * Update snatch, uploaded & downloaded, use the increment value to do increment + * + * @param Peer $peer + * @param $queries + * @param $dataTraffic + */ + private function updateSnatch(Peer $peer, $queries, $dataTraffic) { $nowStr = Carbon::now()->toDateTimeString(); @@ -679,13 +722,13 @@ class TrackerRepository extends BaseRepository //initial $snatch->torrentid = $peer->torrent; $snatch->userid = $peer->userid; - $snatch->uploaded = $queries['uploaded']; - $snatch->downloaded = $queries['downloaded']; - $snatch->startat = $nowStr; + $snatch->uploaded = $dataTraffic['uploaded_increment']; + $snatch->downloaded = $dataTraffic['downloaded_increment']; + $snatch->startdat = $nowStr; } else { - //increase - $snatch->uploaded = DB::raw("uploaded + " . $queries['uploaded']); - $snatch->downloaded = DB::raw("downloaded + " . $queries['downloaded']); + //increase, use the increment value + $snatch->uploaded = DB::raw("uploaded + " . $dataTraffic['uploaded_increment']); + $snatch->downloaded = DB::raw("downloaded + " . $dataTraffic['downloaded_increment']); $timeIncrease = Carbon::now()->diffInSeconds($peer->last_action); if ($queries['left'] == 0) { //seeder @@ -699,7 +742,7 @@ class TrackerRepository extends BaseRepository //always update $snatch->ip = $queries['ip']; $snatch->port = $queries['port']; - $snatch->to_go = $queries['to_go']; + $snatch->to_go = $queries['left']; $snatch->last_action = $nowStr; if ($queries['event'] == 'completed') { $snatch->completedat = $nowStr; @@ -707,6 +750,7 @@ class TrackerRepository extends BaseRepository } $snatch->save(); + do_log(last_query(), 'debug'); } } diff --git a/database/migrations/2022_03_17_202628_add_index_to_cheaters_table.php b/database/migrations/2022_03_17_202628_add_index_to_cheaters_table.php new file mode 100644 index 00000000..b00f6fa7 --- /dev/null +++ b/database/migrations/2022_03_17_202628_add_index_to_cheaters_table.php @@ -0,0 +1,33 @@ +index('torrentid'); + $table->index('userid'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('cheaters', function (Blueprint $table) { + + }); + } +} diff --git a/nexus/Install/settings.default.php b/nexus/Install/settings.default.php index 57328518..68e1981f 100644 --- a/nexus/Install/settings.default.php +++ b/nexus/Install/settings.default.php @@ -352,6 +352,7 @@ return array ( 'watermarkquality' => '85', 'altthumbwidth' => '180', 'altthumbheight' => '135', + 'download_support_passkey' => 'yes', ), 'advertisement' => array ( diff --git a/routes/api.php b/routes/api.php index 5f020cc1..9fdbb4e4 100644 --- a/routes/api.php +++ b/routes/api.php @@ -79,3 +79,5 @@ Route::group(['middleware' => ['auth:sanctum', 'locale']], function () { Route::post('login', [\App\Http\Controllers\AuthenticateController::class, 'login']); +Route::get('announce', [\App\Http\Controllers\TrackerController::class, 'announce']); + diff --git a/routes/tracker.php b/routes/tracker.php new file mode 100644 index 00000000..1fbc8b0c --- /dev/null +++ b/routes/tracker.php @@ -0,0 +1,4 @@ +