mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-03 14:10:57 +08:00
api scrape + medal wearing status
This commit is contained in:
@@ -64,4 +64,4 @@ GOOGLE_DRIVE_FOLDER_ID=
|
|||||||
GEOIP2_DATABASE=
|
GEOIP2_DATABASE=
|
||||||
EXAM_PROGRESS_UPDATE_PROBABILITY=20
|
EXAM_PROGRESS_UPDATE_PROBABILITY=20
|
||||||
|
|
||||||
ANNOUNCE_API_LOCAL_HOST=
|
TRACKER_API_LOCAL_HOST=
|
||||||
|
|||||||
@@ -22,4 +22,13 @@ class TrackerController extends Controller
|
|||||||
{
|
{
|
||||||
return $this->repository->announce($request);
|
return $this->repository->announce($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function scrape(Request $request): \Illuminate\Http\Response
|
||||||
|
{
|
||||||
|
return $this->repository->scrape($request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,8 +320,10 @@ class User extends Authenticatable
|
|||||||
public function medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
public function medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Medal::class, 'user_medals', 'uid', 'medal_id')
|
return $this->belongsToMany(Medal::class, 'user_medals', 'uid', 'medal_id')
|
||||||
->withPivot(['id', 'expire_at'])
|
->withPivot(['id', 'expire_at', 'status'])
|
||||||
->withTimestamps();
|
->withTimestamps()
|
||||||
|
->orderByPivot('id', 'desc')
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function valid_medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
public function valid_medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||||
@@ -331,6 +333,11 @@ class User extends Authenticatable
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function wearing_medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->valid_medals()->where('user_medals.status', UserMedal::STATUS_WEARING);
|
||||||
|
}
|
||||||
|
|
||||||
public function getAvatarAttribute($value)
|
public function getAvatarAttribute($value)
|
||||||
{
|
{
|
||||||
if ($value) {
|
if ($value) {
|
||||||
|
|||||||
@@ -4,5 +4,10 @@ namespace App\Models;
|
|||||||
|
|
||||||
class UserMedal extends NexusModel
|
class UserMedal extends NexusModel
|
||||||
{
|
{
|
||||||
protected $fillable = ['uid', 'medal_id', 'expire_at'];
|
protected $fillable = ['uid', 'medal_id', 'expire_at', 'status'];
|
||||||
|
|
||||||
|
const STATUS_NOT_WEARING = 0;
|
||||||
|
const STATUS_WEARING = 1;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use App\Models\HitAndRun;
|
|||||||
use App\Models\Medal;
|
use App\Models\Medal;
|
||||||
use App\Models\Setting;
|
use App\Models\Setting;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Models\UserMedal;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Nexus\Database\NexusDB;
|
use Nexus\Database\NexusDB;
|
||||||
@@ -66,7 +67,7 @@ class BonusRepository extends BaseRepository
|
|||||||
if ($medal->duration > 0) {
|
if ($medal->duration > 0) {
|
||||||
$expireAt = Carbon::now()->addDays($medal->duration)->toDateTimeString();
|
$expireAt = Carbon::now()->addDays($medal->duration)->toDateTimeString();
|
||||||
}
|
}
|
||||||
$user->medals()->attach([$medal->id => ['expire_at' => $expireAt]]);
|
$user->medals()->attach([$medal->id => ['expire_at' => $expireAt, 'status' => UserMedal::STATUS_NOT_WEARING]]);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,23 @@ class MedalRepository extends BaseRepository
|
|||||||
if ($duration > 0) {
|
if ($duration > 0) {
|
||||||
$expireAt = Carbon::now()->addDays($duration)->toDateTimeString();
|
$expireAt = Carbon::now()->addDays($duration)->toDateTimeString();
|
||||||
}
|
}
|
||||||
return $user->medals()->attach([$medal->id => ['expire_at' => $expireAt]]);
|
return $user->medals()->attach([$medal->id => ['expire_at' => $expireAt, 'status' => UserMedal::STATUS_NOT_WEARING]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleUserMedalStatus($id, $userId)
|
||||||
|
{
|
||||||
|
$userMedal = UserMedal::query()->findOrFail($id);
|
||||||
|
if ($userMedal->uid != $userId) {
|
||||||
|
throw new \LogicException("no privilege");
|
||||||
|
}
|
||||||
|
$current = $userMedal->status;
|
||||||
|
if ($current == UserMedal::STATUS_NOT_WEARING) {
|
||||||
|
$userMedal->status = UserMedal::STATUS_WEARING;
|
||||||
|
} elseif ($current == UserMedal::STATUS_WEARING) {
|
||||||
|
$userMedal->status = UserMedal::STATUS_NOT_WEARING;
|
||||||
|
}
|
||||||
|
$userMedal->save();
|
||||||
|
return $userMedal;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Handle announce and scrape
|
||||||
|
*
|
||||||
|
* @link https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/master/app/Http/Controllers/AnnounceController.php
|
||||||
|
* @link https://github.com/Rhilip/RidPT/blob/master/application/Controllers/Tracker/AnnounceController.php
|
||||||
|
*/
|
||||||
namespace App\Repositories;
|
namespace App\Repositories;
|
||||||
|
|
||||||
use App\Exceptions\ClientNotAllowedException;
|
use App\Exceptions\ClientNotAllowedException;
|
||||||
@@ -13,13 +19,14 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Exceptions\TrackerException;
|
use App\Exceptions\TrackerException;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Redis;
|
use Illuminate\Support\Facades\Redis;
|
||||||
use Rhilip\Bencode\Bencode;
|
use Rhilip\Bencode\Bencode;
|
||||||
|
|
||||||
class TrackerRepository extends BaseRepository
|
class TrackerRepository extends BaseRepository
|
||||||
{
|
{
|
||||||
const MIN_ANNOUNCE_WAIT_SECOND = 30;
|
const MIN_ANNOUNCE_WAIT_SECOND = 300;
|
||||||
|
|
||||||
const MAX_PEER_NUM_WANT = 50;
|
const MAX_PEER_NUM_WANT = 50;
|
||||||
|
|
||||||
@@ -305,10 +312,7 @@ class TrackerRepository extends BaseRepository
|
|||||||
protected function checkTorrent($queries, User $user)
|
protected function checkTorrent($queries, User $user)
|
||||||
{
|
{
|
||||||
// Check Info Hash Against Torrents Table
|
// Check Info Hash Against Torrents Table
|
||||||
$torrent = Torrent::query()
|
$torrent = $this->getTorrentByInfoHash($queries['info_hash']);
|
||||||
->selectRaw('id, owner, sp_state, seeders, leechers, added, banned, hr, visible, last_action, times_completed')
|
|
||||||
->where('info_hash', '=', $queries['info_hash'])
|
|
||||||
->first();
|
|
||||||
|
|
||||||
// If Torrent Doesnt Exists Return Error to Client
|
// If Torrent Doesnt Exists Return Error to Client
|
||||||
if ($torrent === null) {
|
if ($torrent === null) {
|
||||||
@@ -506,24 +510,23 @@ class TrackerRepository extends BaseRepository
|
|||||||
private function generateSuccessAnnounceResponse($torrent, $queries, $user, $withPeers = true): array
|
private function generateSuccessAnnounceResponse($torrent, $queries, $user, $withPeers = true): array
|
||||||
{
|
{
|
||||||
// Build Response For Bittorrent Client
|
// Build Response For Bittorrent Client
|
||||||
|
$minInterval = self::MIN_ANNOUNCE_WAIT_SECOND;
|
||||||
|
$interval = max($this->getRealAnnounceInterval($torrent), $minInterval);
|
||||||
$repDict = [
|
$repDict = [
|
||||||
'interval' => $this->getRealAnnounceInterval($torrent),
|
'interval' => $interval + random_int(10, 100),
|
||||||
'min interval' => self::MIN_ANNOUNCE_WAIT_SECOND,
|
'min interval' => $minInterval + random_int(1, 10),
|
||||||
'complete' => (int) $torrent->seeders,
|
'complete' => (int) $torrent->seeders,
|
||||||
'incomplete' => (int) $torrent->leechers,
|
'incomplete' => (int) $torrent->leechers,
|
||||||
'peers' => [],
|
'peers' => [],
|
||||||
'peers6' => [],
|
'peers6' => [],
|
||||||
];
|
];
|
||||||
do_log("[REP_DICT_BASE] " . json_encode($repDict));
|
do_log("[REP_DICT_BASE] " . json_encode($repDict));
|
||||||
if (!$withPeers) {
|
|
||||||
return $repDict;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For non `stopped` event only
|
* For non `stopped` event only
|
||||||
* We query peers from database and send peer list, otherwise just quick return.
|
* We query peers from database and send peer list, otherwise just quick return.
|
||||||
*/
|
*/
|
||||||
if (\strtolower($queries['event']) !== 'stopped') {
|
if (\strtolower($queries['event']) !== 'stopped' && $withPeers) {
|
||||||
$limit = ($queries['numwant'] <= self::MAX_PEER_NUM_WANT ? $queries['numwant'] : self::MAX_PEER_NUM_WANT);
|
$limit = ($queries['numwant'] <= self::MAX_PEER_NUM_WANT ? $queries['numwant'] : self::MAX_PEER_NUM_WANT);
|
||||||
$baseQuery = Peer::query()
|
$baseQuery = Peer::query()
|
||||||
->select(['peer_id', 'ip', 'port'])
|
->select(['peer_id', 'ip', 'port'])
|
||||||
@@ -769,4 +772,86 @@ class TrackerRepository extends BaseRepository
|
|||||||
do_log(last_query(), 'debug');
|
do_log(last_query(), 'debug');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scrape(Request $request): \Illuminate\Http\Response
|
||||||
|
{
|
||||||
|
do_log(nexus_json_encode($_SERVER));
|
||||||
|
try {
|
||||||
|
$infoHashArr = $this->checkScrapeFields($request);
|
||||||
|
$user = $this->checkUser($request);
|
||||||
|
$clientAllow = $this->checkClient($request);
|
||||||
|
|
||||||
|
if ($user->clientselect != $clientAllow->id) {
|
||||||
|
$this->userUpdates['clientselect'] = $clientAllow->id;
|
||||||
|
}
|
||||||
|
$repDict = $this->generateScrapeResponse($infoHashArr);
|
||||||
|
} catch (ClientNotAllowedException $exception) {
|
||||||
|
do_log("[ClientNotAllowedException] " . $exception->getMessage());
|
||||||
|
if (isset($user) && $user->showclienterror == 'no') {
|
||||||
|
$this->userUpdates['showclienterror'] = 'yes';
|
||||||
|
}
|
||||||
|
$repDict = $this->generateFailedAnnounceResponse($exception->getMessage());
|
||||||
|
} catch (TrackerException $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) && count($this->userUpdates)) {
|
||||||
|
$user->update($this->userUpdates);
|
||||||
|
do_log(last_query(), 'debug');
|
||||||
|
}
|
||||||
|
return $this->sendFinalAnnounceResponse($repDict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkScrapeFields(Request $request): array
|
||||||
|
{
|
||||||
|
preg_match_all('/info_hash=([^&]*)/i', urldecode($request->getQueryString()), $info_hash_match);
|
||||||
|
|
||||||
|
$info_hash_array = $info_hash_match[1];
|
||||||
|
if (count($info_hash_array) < 1) {
|
||||||
|
throw new TrackerException("key: info_hash is Missing !");
|
||||||
|
} else {
|
||||||
|
foreach ($info_hash_array as $item) {
|
||||||
|
if (strlen($item) != 20) {
|
||||||
|
throw new TrackerException("Invalid info_hash ! :attribute is not 20 bytes long");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $info_hash_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $info_hash_array
|
||||||
|
* @return array[]
|
||||||
|
* @see http://www.bittorrent.org/beps/bep_0048.html
|
||||||
|
*/
|
||||||
|
private function generateScrapeResponse($info_hash_array)
|
||||||
|
{
|
||||||
|
$torrent_details = [];
|
||||||
|
foreach ($info_hash_array as $item) {
|
||||||
|
$torrent = $this->getTorrentByInfoHash($item);
|
||||||
|
if ($torrent) {
|
||||||
|
$torrent_details[$item] = [
|
||||||
|
'complete' => (int)$torrent->seeders,
|
||||||
|
'downloaded' => (int)$torrent->times_completed,
|
||||||
|
'incomplete' => (int)$torrent->leechers,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['files' => $torrent_details];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTorrentByInfoHash($infoHash)
|
||||||
|
{
|
||||||
|
return Cache::remember(bin2hex($infoHash), 60, function () use ($infoHash) {
|
||||||
|
$fieldRaw = 'id, owner, sp_state, seeders, leechers, added, banned, hr, visible, last_action, times_completed';
|
||||||
|
$torrent = Torrent::query()->where('info_hash', $infoHash)->selectRaw($fieldRaw)->first();
|
||||||
|
do_log("cache miss, from database: " . last_query() . ", and get: " . $torrent->id);
|
||||||
|
return $torrent;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
3
artisan
3
artisan
@@ -14,9 +14,6 @@ define('LARAVEL_START', microtime(true));
|
|||||||
| loading of any our classes "manually". Feels great to relax.
|
| loading of any our classes "manually". Feels great to relax.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
require __DIR__ . '/include/globalfunctions.php';
|
|
||||||
require __DIR__ . '/include/functions.php';
|
|
||||||
|
|
||||||
require __DIR__.'/vendor/autoload.php';
|
require __DIR__.'/vendor/autoload.php';
|
||||||
|
|
||||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ defined('LARAVEL_START') || define('LARAVEL_START', microtime(true));
|
|||||||
defined('NEXUS_START') || define('NEXUS_START', LARAVEL_START);
|
defined('NEXUS_START') || define('NEXUS_START', LARAVEL_START);
|
||||||
defined('IN_NEXUS') || define('IN_NEXUS', false);
|
defined('IN_NEXUS') || define('IN_NEXUS', false);
|
||||||
require dirname(__DIR__) . '/include/constants.php';
|
require dirname(__DIR__) . '/include/constants.php';
|
||||||
|
require dirname(__DIR__) . '/include/globalfunctions.php';
|
||||||
|
require dirname(__DIR__) . '/include/functions.php';
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Create The Application
|
| Create The Application
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddStatusToUserMedalsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('user_medals', function (Blueprint $table) {
|
||||||
|
$table->integer('status')->default(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('user_medals', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('status');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,8 @@ defined('NEXUS_START') || define('NEXUS_START', microtime(true));
|
|||||||
defined('IN_NEXUS') || define('IN_NEXUS', true);
|
defined('IN_NEXUS') || define('IN_NEXUS', true);
|
||||||
defined('IN_TRACKER') || define('IN_TRACKER', true);
|
defined('IN_TRACKER') || define('IN_TRACKER', true);
|
||||||
$rootpath= dirname(__DIR__) . '/';
|
$rootpath= dirname(__DIR__) . '/';
|
||||||
|
require_once $rootpath . 'include/constants.php';
|
||||||
require_once $rootpath . 'include/globalfunctions.php';
|
require_once $rootpath . 'include/globalfunctions.php';
|
||||||
require_once $rootpath . 'include/functions_announce.php';
|
require_once $rootpath . 'include/functions_announce.php';
|
||||||
require $rootpath . 'include/core.php';
|
|
||||||
|
|
||||||
|
|||||||
@@ -2929,7 +2929,7 @@ function commenttable($rows, $type, $parent_id, $review = false)
|
|||||||
|
|
||||||
$uidArr = array_unique(array_column($rows, 'user'));
|
$uidArr = array_unique(array_column($rows, 'user'));
|
||||||
$neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title');
|
$neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title');
|
||||||
$userInfoArr = \App\Models\User::query()->with(['valid_medals'])->find($uidArr, $neededColumns)->keyBy('id');
|
$userInfoArr = \App\Models\User::query()->with(['wearing_medals'])->find($uidArr, $neededColumns)->keyBy('id');
|
||||||
|
|
||||||
foreach ($rows as $row)
|
foreach ($rows as $row)
|
||||||
{
|
{
|
||||||
@@ -2944,7 +2944,7 @@ function commenttable($rows, $type, $parent_id, $review = false)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
print("<div style=\"margin-top: 8pt; margin-bottom: 8pt;\"><table id=\"cid".$row["id"]."\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td class=\"embedded\" width=\"99%\">#" . $row["id"] . " <font color=\"gray\">".$lang_functions['text_by']."</font>");
|
print("<div style=\"margin-top: 8pt; margin-bottom: 8pt;\"><table id=\"cid".$row["id"]."\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td class=\"embedded\" width=\"99%\">#" . $row["id"] . " <font color=\"gray\">".$lang_functions['text_by']."</font>");
|
||||||
print(build_medal_image($userInfo->valid_medals, 20) . get_username($row["user"],false,true,true,false,false,true));
|
print(build_medal_image($userInfo->wearing_medals, 20) . get_username($row["user"],false,true,true,false,false,true));
|
||||||
print(" <font color=\"gray\">".$lang_functions['text_at']."</font>".gettime($row["added"]).
|
print(" <font color=\"gray\">".$lang_functions['text_at']."</font>".gettime($row["added"]).
|
||||||
($row["editedby"] && get_user_class() >= $commanage_class ? " - [<a href=\"comment.php?action=vieworiginal&cid=".$row['id']."&type=".$type."\">".$lang_functions['text_view_original']."</a>]" : "") . "</td><td class=\"embedded nowrap\" width=\"1%\"><a href=\"#top\"><img class=\"top\" src=\"pic/trans.gif\" alt=\"Top\" title=\"Top\" /></a> </td></tr></table></div>");
|
($row["editedby"] && get_user_class() >= $commanage_class ? " - [<a href=\"comment.php?action=vieworiginal&cid=".$row['id']."&type=".$type."\">".$lang_functions['text_view_original']."</a>]" : "") . "</td><td class=\"embedded nowrap\" width=\"1%\"><a href=\"#top\"><img class=\"top\" src=\"pic/trans.gif\" alt=\"Top\" title=\"Top\" /></a> </td></tr></table></div>");
|
||||||
$avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars(trim($userRow["avatar"])) : "");
|
$avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars(trim($userRow["avatar"])) : "");
|
||||||
@@ -5296,13 +5296,24 @@ function msgalert($url, $text, $bgcolor = "red")
|
|||||||
print("</td></tr></table></p><br />");
|
print("</td></tr></table></p><br />");
|
||||||
}
|
}
|
||||||
|
|
||||||
function build_medal_image(\Illuminate\Support\Collection $medals, $maxHeight = 200): string
|
function build_medal_image(\Illuminate\Support\Collection $medals, $maxHeight = 200, $withActions = false): string
|
||||||
{
|
{
|
||||||
$medalImages = [];
|
$medalImages = [];
|
||||||
|
$wrapBefore = '<div style="display: inline;">';
|
||||||
|
$wrapAfter = '</div>';
|
||||||
foreach ($medals as $medal) {
|
foreach ($medals as $medal) {
|
||||||
$medalImages[] = sprintf('<img src="%s" title="%s" style="max-height: %spx"/>', $medal->image_large, $medal->name, $maxHeight);
|
$html = sprintf('<div style="display: inline"><img src="%s" title="%s" style="max-height: %spx"/>', $medal->image_large, $medal->name, $maxHeight);
|
||||||
|
if ($withActions) {
|
||||||
|
$checked = '';
|
||||||
|
if ($medal->pivot->status == \App\Models\UserMedal::STATUS_WEARING) {
|
||||||
|
$checked = ' checked';
|
||||||
|
}
|
||||||
|
$html .= sprintf('<label>%s<input type="checkbox" name="medal_wearing_status" value="%s"%s></label>', '佩戴', $medal->id, $checked);
|
||||||
|
}
|
||||||
|
$html .= '</div>';
|
||||||
|
$medalImages[] = $html;
|
||||||
}
|
}
|
||||||
return implode('', $medalImages);
|
return $wrapBefore . implode('', $medalImages) . $wrapAfter;
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert_torrent_tags($torrentId, $tagIdArr, $sync = false)
|
function insert_torrent_tags($torrentId, $tagIdArr, $sync = false)
|
||||||
|
|||||||
@@ -337,4 +337,25 @@ function check_client($peer_id, $agent, &$agent_familyid)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function request_local_api($api)
|
||||||
|
{
|
||||||
|
$start = microtime(true);
|
||||||
|
$ch = curl_init();
|
||||||
|
$options = [
|
||||||
|
CURLOPT_URL => sprintf('%s?%s', trim($api, '/'), $_SERVER['QUERY_STRING']),
|
||||||
|
CURLOPT_USERAGENT => $_SERVER["HTTP_USER_AGENT"],
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_SSL_VERIFYPEER => false,
|
||||||
|
CURLOPT_TIMEOUT => 60,
|
||||||
|
];
|
||||||
|
curl_setopt_array($ch, $options);
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$log = sprintf(
|
||||||
|
"[LOCAL_ANNOUNCE_API] [%s] options: %s, response(%s): %s",
|
||||||
|
number_format(microtime(true) - $start, 3), nexus_json_encode($options), gettype($response), $response
|
||||||
|
);
|
||||||
|
do_log($log);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -669,24 +669,3 @@ function get_hr_ratio($uped, $downed)
|
|||||||
|
|
||||||
return $ratio;
|
return $ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
function request_local_announce_api($announceApiLocalHost)
|
|
||||||
{
|
|
||||||
$start = microtime(true);
|
|
||||||
$ch = curl_init();
|
|
||||||
$options = [
|
|
||||||
CURLOPT_URL => sprintf('%s/api/announce?%s', trim($announceApiLocalHost, '/'), $_SERVER['QUERY_STRING']),
|
|
||||||
CURLOPT_USERAGENT => $_SERVER["HTTP_USER_AGENT"],
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_SSL_VERIFYPEER => false,
|
|
||||||
CURLOPT_TIMEOUT => 60,
|
|
||||||
];
|
|
||||||
curl_setopt_array($ch, $options);
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$log = sprintf(
|
|
||||||
"[LOCAL_ANNOUNCE_API] [%s] options: %s, response(%s): %s",
|
|
||||||
number_format(microtime(true) - $start, 3), json_encode($options), gettype($response), $response
|
|
||||||
);
|
|
||||||
do_log($log);
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|||||||
16251
package-lock.json
generated
Normal file
16251
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "^0.21",
|
"axios": "^0.21",
|
||||||
|
"chokidar": "^3.5.3",
|
||||||
"laravel-mix": "^6.0.6",
|
"laravel-mix": "^6.0.6",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"postcss": "^8.1.14"
|
"postcss": "^8.1.14"
|
||||||
|
|||||||
29
public/ajax.php
Normal file
29
public/ajax.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
require "../include/bittorrent.php";
|
||||||
|
dbconn();
|
||||||
|
loggedinorreturn();
|
||||||
|
|
||||||
|
$action = $_POST['action'] ?? 'noAction';
|
||||||
|
$params = $_POST['params'] ?? [];
|
||||||
|
|
||||||
|
function noAction()
|
||||||
|
{
|
||||||
|
throw new \RuntimeException("no Action");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = call_user_func($action, $params);
|
||||||
|
exit(json_encode(success($result)));
|
||||||
|
} catch (\Throwable $exception) {
|
||||||
|
exit(json_encode(fail($exception->getMessage(), $_POST)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleUserMedalStatus($params)
|
||||||
|
{
|
||||||
|
global $CURUSER;
|
||||||
|
$rep = new \App\Repositories\MedalRepository();
|
||||||
|
return $rep->toggleUserMedalStatus($params['id'], $CURUSER['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,22 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
defined('NEXUS_START') || define('NEXUS_START', microtime(true));
|
require '../include/bittorrent_announce.php';
|
||||||
defined('IN_NEXUS') || define('IN_NEXUS', true);
|
$apiLocalHost = nexus_env('TRACKER_API_LOCAL_HOST');
|
||||||
defined('IN_TRACKER') || define('IN_TRACKER', true);
|
if ($apiLocalHost) {
|
||||||
require __DIR__ . '/../include/constants.php';
|
do_log("[TRACKER_API_LOCAL_HOST] $apiLocalHost");
|
||||||
require __DIR__ . '/../include/globalfunctions.php';
|
$response = request_local_api(trim($apiLocalHost, '/') . '/api/announce');
|
||||||
require __DIR__ . '/../include/functions_announce.php';
|
|
||||||
$announceApiLocalHost = nexus_env('ANNOUNCE_API_LOCAL_HOST');
|
|
||||||
if ($announceApiLocalHost) {
|
|
||||||
do_log("[GOING_TO_REQUEST_LOCAL_ANNOUNCE_URL] $announceApiLocalHost");
|
|
||||||
$response = request_local_announce_api($announceApiLocalHost);
|
|
||||||
if (empty($response)) {
|
if (empty($response)) {
|
||||||
err("error from ANNOUNCE_API_LOCAL_HOST");
|
err("error from TRACKER_API_LOCAL_HOST");
|
||||||
} else {
|
} else {
|
||||||
exit(benc_resp_raw($response));
|
exit(benc_resp_raw($response));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//continue the normal process
|
//continue the normal process
|
||||||
require_once('../include/bittorrent_announce.php');
|
require ROOT_PATH . 'include/core.php';
|
||||||
dbconn_announce();
|
dbconn_announce();
|
||||||
do_log(nexus_json_encode($_SERVER));
|
do_log(nexus_json_encode($_SERVER));
|
||||||
//1. BLOCK ACCESS WITH WEB BROWSERS AND CHEATS!
|
//1. BLOCK ACCESS WITH WEB BROWSERS AND CHEATS!
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ if (!$row) {
|
|||||||
) {
|
) {
|
||||||
permissiondenied();
|
permissiondenied();
|
||||||
} else {
|
} else {
|
||||||
$owner = \App\Models\User::query()->with(['valid_medals'])->find($row['owner']);
|
$owner = \App\Models\User::query()->with(['wearing_medals'])->find($row['owner']);
|
||||||
if (!$owner) {
|
if (!$owner) {
|
||||||
$owner = \App\Models\User::defaultUser();
|
$owner = \App\Models\User::defaultUser();
|
||||||
}
|
}
|
||||||
@@ -87,10 +87,10 @@ if (!$row) {
|
|||||||
if (get_user_class() < $viewanonymous_class)
|
if (get_user_class() < $viewanonymous_class)
|
||||||
$uprow = "<i>".$lang_details['text_anonymous']."</i>";
|
$uprow = "<i>".$lang_details['text_anonymous']."</i>";
|
||||||
else
|
else
|
||||||
$uprow = "<i>".$lang_details['text_anonymous']."</i> (" . build_medal_image($owner->valid_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) . ")";
|
$uprow = "<i>".$lang_details['text_anonymous']."</i> (" . build_medal_image($owner->wearing_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) . ")";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$uprow = (isset($row['owner']) ? build_medal_image($owner->valid_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) : "<i>".$lang_details['text_unknown']."</i>");
|
$uprow = (isset($row['owner']) ? build_medal_image($owner->wearing_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) : "<i>".$lang_details['text_unknown']."</i>");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($CURUSER["id"] == $row["owner"])
|
if ($CURUSER["id"] == $row["owner"])
|
||||||
|
|||||||
@@ -632,7 +632,7 @@ if ($action == "viewtopic")
|
|||||||
$uidArr = array_keys($uidArr);
|
$uidArr = array_keys($uidArr);
|
||||||
unset($arr);
|
unset($arr);
|
||||||
$neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title');
|
$neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title');
|
||||||
$userInfoArr = \App\Models\User::query()->with(['valid_medals'])->find($uidArr, $neededColumns)->keyBy('id');
|
$userInfoArr = \App\Models\User::query()->with(['wearing_medals'])->find($uidArr, $neededColumns)->keyBy('id');
|
||||||
$pn = 0;
|
$pn = 0;
|
||||||
$lpr = get_last_read_post_id($topicid);
|
$lpr = get_last_read_post_id($topicid);
|
||||||
|
|
||||||
@@ -674,7 +674,7 @@ if ($action == "viewtopic")
|
|||||||
$avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars($arr2["avatar"]) : "");
|
$avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars($arr2["avatar"]) : "");
|
||||||
|
|
||||||
$uclass = get_user_class_image($arr2["class"]);
|
$uclass = get_user_class_image($arr2["class"]);
|
||||||
$by = build_medal_image($userInfo->valid_medals, 20) . get_username($posterid,false,true,true,false,false,true);
|
$by = build_medal_image($userInfo->wearing_medals, 20) . get_username($posterid,false,true,true,false,false,true);
|
||||||
|
|
||||||
if (!$avatar)
|
if (!$avatar)
|
||||||
$avatar = "pic/default_avatar.png";
|
$avatar = "pic/default_avatar.png";
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ if (file_exists(__DIR__.'/../storage/framework/maintenance.php')) {
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require dirname(__DIR__) . '/include/globalfunctions.php';
|
|
||||||
require dirname(__DIR__) . '/include/functions.php';
|
|
||||||
|
|
||||||
require __DIR__.'/../vendor/autoload.php';
|
require __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once('../include/bittorrent_announce.php');
|
require_once('../include/bittorrent_announce.php');
|
||||||
|
$apiLocalHost = nexus_env('TRACKER_API_LOCAL_HOST');
|
||||||
|
if ($apiLocalHost) {
|
||||||
|
do_log("[TRACKER_API_LOCAL_HOST] $apiLocalHost");
|
||||||
|
$response = request_local_api(trim($apiLocalHost, '/') . '/api/scrape');
|
||||||
|
if (empty($response)) {
|
||||||
|
err("error from TRACKER_API_LOCAL_HOST");
|
||||||
|
} else {
|
||||||
|
exit(benc_resp_raw($response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require ROOT_PATH . 'include/core.php';
|
||||||
//require_once('../include/benc.php');
|
//require_once('../include/benc.php');
|
||||||
dbconn_announce();
|
dbconn_announce();
|
||||||
|
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ if ($user["avatar"])
|
|||||||
tr_small($lang_userdetails['row_avatar'], return_avatar_image(htmlspecialchars(trim($user["avatar"]))), 1);
|
tr_small($lang_userdetails['row_avatar'], return_avatar_image(htmlspecialchars(trim($user["avatar"]))), 1);
|
||||||
|
|
||||||
if ($userInfo->valid_medals->isNotEmpty()) {
|
if ($userInfo->valid_medals->isNotEmpty()) {
|
||||||
tr_small($lang_userdetails['row_medal'], build_medal_image($userInfo->valid_medals), 1);
|
tr_small($lang_userdetails['row_medal'], build_medal_image($userInfo->valid_medals, 200, $CURUSER['id'] == $user['id']), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$uclass = get_user_class_image($user["class"]);
|
$uclass = get_user_class_image($user["class"]);
|
||||||
@@ -500,5 +500,21 @@ if (get_user_class() >= $prfmanage_class && $user["class"] < get_user_class())
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
end_main_frame();
|
end_main_frame();
|
||||||
|
|
||||||
|
echo <<<EOT
|
||||||
|
<script>
|
||||||
|
jQuery('input[type="checkbox"][name="medal_wearing_status"]').on("change", function (e) {
|
||||||
|
let input = jQuery(this);
|
||||||
|
let checked = input.prop("checked")
|
||||||
|
jQuery.post('ajax.php', {params: {id: this.value}, action: 'toggleUserMedalStatus'}, function (response) {
|
||||||
|
console.log(response)
|
||||||
|
if (response.ret != 0) {
|
||||||
|
input.prop("checked", !checked)
|
||||||
|
alert(response.msg)
|
||||||
|
}
|
||||||
|
}, 'json')
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
EOT;
|
||||||
stdfoot();
|
stdfoot();
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -2,3 +2,4 @@
|
|||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::get('announce', [\App\Http\Controllers\TrackerController::class, 'announce']);
|
Route::get('announce', [\App\Http\Controllers\TrackerController::class, 'announce']);
|
||||||
|
Route::get('scrape', [\App\Http\Controllers\TrackerController::class, 'scrape']);
|
||||||
|
|||||||
Reference in New Issue
Block a user